aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java519
1 files changed, 519 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
new file mode 100644
index 00000000..a04dfefa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
@@ -0,0 +1,519 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.StringReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.KeepAliveInputStream;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Executes a command on a remote machine via ssh.
+ * @since Ant 1.6 (created February 2, 2003)
+ */
+public class SSHExec extends SSHBase {
+
+ private static final int BUFFER_SIZE = 8192;
+ private static final int RETRY_INTERVAL = 500;
+
+ /** the command to execute via ssh */
+ private String command = null;
+
+ /** units are milliseconds, default is 0=infinite */
+ private long maxwait = 0;
+
+ /** for waiting for the command to finish */
+ private Thread thread = null;
+
+ private String outputProperty = null; // like <exec>
+ private String errorProperty = null;
+ private String resultProperty = null;
+ private File outputFile = null; // like <exec>
+ private File errorFile = null;
+ private String inputProperty = null;
+ private String inputString = null; // like <exec>
+ private File inputFile = null; // like <exec>
+ private boolean append = false; // like <exec>
+ private boolean appenderr = false;
+ private boolean usePty = false;
+ private boolean useSystemIn = false;
+
+ private Resource commandResource = null;
+
+ private static final String TIMEOUT_MESSAGE =
+ "Timeout period exceeded, connection dropped.";
+
+ /**
+ * To suppress writing logs to System.out
+ */
+ private boolean suppressSystemOut = false;
+
+ /**
+ * To suppress writing logs to System.err
+ */
+ private boolean suppressSystemErr = false;
+
+ /**
+ * Constructor for SSHExecTask.
+ */
+ public SSHExec() {
+ super();
+ }
+
+ /**
+ * Sets the command to execute on the remote host.
+ *
+ * @param command The new command value
+ */
+ public void setCommand(final String command) {
+ this.command = command;
+ }
+
+ /**
+ * Sets a commandResource from a file
+ * @param f the value to use.
+ * @since Ant 1.7.1
+ */
+ public void setCommandResource(final String f) {
+ this.commandResource = new FileResource(new File(f));
+ }
+
+ /**
+ * The connection can be dropped after a specified number of
+ * milliseconds. This is sometimes useful when a connection may be
+ * flaky. Default is 0, which means &quot;wait forever&quot;.
+ *
+ * @param timeout The new timeout value in seconds
+ */
+ public void setTimeout(final long timeout) {
+ maxwait = timeout;
+ }
+
+ /**
+ * If used, stores the output of the command to the given file.
+ *
+ * @param output The file to write to.
+ */
+ public void setOutput(final File output) {
+ outputFile = output;
+ }
+
+ /**
+ * If used, stores the erroutput of the command to the given file.
+ *
+ * @param output The file to write to.
+ * @since Apache Ant 1.9.4
+ */
+ public void setErrorOutput(final File output) {
+ errorFile = output;
+ }
+
+ /**
+ * If used, the content of the file is piped to the remote command
+ *
+ * @param input The file which provides the input data for the remote command
+ *
+ * @since Ant 1.8.0
+ */
+ public void setInput(final File input) {
+ inputFile = input;
+ }
+
+ /**
+ * If used, the content of the property is piped to the remote command
+ *
+ * @param inputProperty The property which contains the input data
+ * for the remote command.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setInputProperty(final String inputProperty) {
+ this.inputProperty = inputProperty;
+ }
+
+ /**
+ * If used, the string is piped to the remote command.
+ *
+ * @param inputString the input data for the remote command.
+ *
+ * @since Ant 1.8.3
+ */
+ public void setInputString(final String inputString) {
+ this.inputString = inputString;
+ }
+
+ /**
+ * Determines if the output is appended to the file given in
+ * <code>setOutput</code>. Default is false, that is, overwrite
+ * the file.
+ *
+ * @param append True to append to an existing file, false to overwrite.
+ */
+ public void setAppend(final boolean append) {
+ this.append = append;
+ }
+
+ /**
+ * Determines if the output is appended to the file given in
+ * <code>setErrorOutput</code>. Default is false, that is, overwrite
+ * the file.
+ *
+ * @param appenderr True to append to an existing file, false to overwrite.
+ * @since Apache Ant 1.9.4
+ */
+ public void setErrAppend(final boolean appenderr) {
+ this.appenderr = appenderr;
+ }
+
+ /**
+ * If set, the output of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the command output
+ * will be stored.
+ */
+ public void setOutputproperty(final String property) {
+ outputProperty = property;
+ }
+
+ /**
+ * If set, the erroroutput of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the command erroroutput
+ * will be stored.
+ * @since Apache Ant 1.9.4
+ */
+ public void setErrorproperty (final String property) {
+ errorProperty = property;
+ }
+
+ /**
+ * If set, the exitcode of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the exitcode
+ * will be stored.
+ * @since Apache Ant 1.9.4
+ */
+ public void setResultproperty(final String property) {
+ resultProperty = property;
+ }
+
+ /**
+ * Whether a pseudo-tty should be allocated.
+ * @since Apache Ant 1.8.3
+ */
+ public void setUsePty(final boolean b) {
+ usePty = b;
+ }
+
+ /**
+ * If set, input will be taken from System.in
+ *
+ * @param useSystemIn True to use System.in as InputStream, false otherwise
+ * @since Apache Ant 1.9.4
+ */
+ public void setUseSystemIn(final boolean useSystemIn) {
+ this.useSystemIn = useSystemIn;
+ }
+
+ /**
+ * If suppressSystemOut is <code>true</code>, output will not be sent to System.out<br/>
+ * If suppressSystemOut is <code>false</code>, normal behavior
+ * @since Ant 1.9.0
+ */
+ public void setSuppressSystemOut(final boolean suppressSystemOut) {
+ this.suppressSystemOut = suppressSystemOut;
+ }
+
+ /**
+ * If suppressSystemErr is <code>true</code>, output will not be sent to System.err<br/>
+ * If suppressSystemErr is <code>false</code>, normal behavior
+ * @since Ant 1.9.4
+ */
+ public void setSuppressSystemErr(final boolean suppressSystemErr) {
+ this.suppressSystemErr = suppressSystemErr;
+ }
+
+ /**
+ * Execute the command on the remote host.
+ *
+ * @exception BuildException Most likely a network error or bad parameter.
+ */
+ @Override
+ public void execute() throws BuildException {
+
+ if (getHost() == null) {
+ throw new BuildException("Host is required.");
+ }
+ if (getUserInfo().getName() == null) {
+ throw new BuildException("Username is required.");
+ }
+ if (getUserInfo().getKeyfile() == null
+ && getUserInfo().getPassword() == null) {
+ throw new BuildException("Password or Keyfile is required.");
+ }
+ if (command == null && commandResource == null) {
+ throw new BuildException("Command or commandResource is required.");
+ }
+
+ final int numberOfInputs = (inputFile != null ? 1 : 0)
+ + (inputProperty != null ? 1 : 0)
+ + (inputString != null ? 1 : 0);
+ if (numberOfInputs > 1) {
+ throw new BuildException("You can't specify more than one of"
+ + " inputFile, inputProperty and"
+ + " inputString.");
+ }
+ if (inputFile != null && !inputFile.exists()) {
+ throw new BuildException("The input file "
+ + inputFile.getAbsolutePath()
+ + " does not exist.");
+ }
+
+ Session session = null;
+ final StringBuffer output = new StringBuffer();
+ try {
+ session = openSession();
+ /* called once */
+ if (command != null) {
+ log("cmd : " + command, Project.MSG_INFO);
+ executeCommand(session, command, output);
+ } else { // read command resource and execute for each command
+ try {
+ final BufferedReader br = new BufferedReader(
+ new InputStreamReader(commandResource.getInputStream()));
+ String cmd;
+ while ((cmd = br.readLine()) != null) {
+ log("cmd : " + cmd, Project.MSG_INFO);
+ output.append(cmd).append(" : ");
+ executeCommand(session, cmd, output);
+ output.append("\n");
+ }
+ FileUtils.close(br);
+ } catch (final IOException e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(),
+ Project.MSG_ERR);
+ }
+ }
+ }
+ } catch (final JSchException e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+ }
+ } finally {
+ if (outputProperty != null) {
+ getProject().setNewProperty(outputProperty, output.toString());
+ }
+ if (session != null && session.isConnected()) {
+ session.disconnect();
+ }
+ }
+ }
+
+ private void executeCommand(final Session session, final String cmd, final StringBuffer sb)
+ throws BuildException {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final ByteArrayOutputStream errout = new ByteArrayOutputStream();
+ final OutputStream teeErr = suppressSystemErr ? errout : new TeeOutputStream(errout, KeepAliveOutputStream.wrapSystemErr());
+ final OutputStream tee = suppressSystemOut ? out : new TeeOutputStream(out, KeepAliveOutputStream.wrapSystemOut());
+
+ InputStream istream = null;
+ if (inputFile != null) {
+ try {
+ istream = new FileInputStream(inputFile);
+ } catch (final IOException e) {
+ // because we checked the existence before, this one
+ // shouldn't happen What if the file exists, but there
+ // are no read permissions?
+ log("Failed to read " + inputFile + " because of: "
+ + e.getMessage(), Project.MSG_WARN);
+ }
+ }
+ if (inputProperty != null) {
+ final String inputData = getProject().getProperty(inputProperty);
+ if (inputData != null) {
+ istream = new ByteArrayInputStream(inputData.getBytes());
+ }
+ }
+ if (inputString != null) {
+ istream = new ByteArrayInputStream(inputString.getBytes());
+ }
+
+ if (useSystemIn) {
+ istream = KeepAliveInputStream.wrapSystemIn();
+ }
+
+ try {
+ final ChannelExec channel;
+ session.setTimeout((int) maxwait);
+ /* execute the command */
+ channel = (ChannelExec) session.openChannel("exec");
+ channel.setCommand(cmd);
+ channel.setOutputStream(tee);
+ channel.setExtOutputStream(tee);
+ channel.setErrStream(teeErr);
+ if (istream != null) {
+ channel.setInputStream(istream);
+ }
+ channel.setPty(usePty);
+ channel.connect();
+ // wait for it to finish
+ thread =
+ new Thread() {
+ @Override
+ public void run() {
+ while (!channel.isClosed()) {
+ if (thread == null) {
+ return;
+ }
+ try {
+ sleep(RETRY_INTERVAL);
+ } catch (final Exception e) {
+ // ignored
+ }
+ }
+ }
+ };
+
+ thread.start();
+ thread.join(maxwait);
+
+ if (thread.isAlive()) {
+ // ran out of time
+ thread = null;
+ if (getFailonerror()) {
+ throw new BuildException(TIMEOUT_MESSAGE);
+ } else {
+ log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+ }
+ } else {
+ // stdout to outputFile
+ if (outputFile != null) {
+ writeToFile(out.toString(), append, outputFile);
+ }
+ // set errorProperty
+ if (errorProperty != null) {
+ getProject().setNewProperty(errorProperty, errout.toString());
+ }
+ // stderr to errorFile
+ if (errorFile != null) {
+ writeToFile(errout.toString(), appenderr, errorFile);
+ }
+ // this is the wrong test if the remote OS is OpenVMS,
+ // but there doesn't seem to be a way to detect it.
+ final int ec = channel.getExitStatus();
+ // set resultproperty
+ if (resultProperty != null) {
+ getProject().setNewProperty(resultProperty, Integer.toString(ec));
+ }
+ if (ec != 0) {
+ final String msg = "Remote command failed with exit status " + ec;
+ if (getFailonerror()) {
+ throw new BuildException(msg);
+ } else {
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ }
+ } catch (final BuildException e) {
+ throw e;
+ } catch (final JSchException e) {
+ if (e.getMessage().indexOf("session is down") >= 0) {
+ if (getFailonerror()) {
+ throw new BuildException(TIMEOUT_MESSAGE, e);
+ } else {
+ log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+ }
+ } else {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(),
+ Project.MSG_ERR);
+ }
+ }
+ } catch (final Exception e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+ }
+ } finally {
+ sb.append(out.toString());
+ FileUtils.close(istream);
+ }
+ }
+
+ /**
+ * Writes a string to a file. If destination file exists, it may be
+ * overwritten depending on the "append" value.
+ *
+ * @param from string to write
+ * @param to file to write to
+ * @param append if true, append to existing file, else overwrite
+ * @exception Exception most likely an IOException
+ */
+ private void writeToFile(final String from, final boolean append, final File to)
+ throws IOException {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(to.getAbsolutePath(), append);
+ final StringReader in = new StringReader(from);
+ final char[] buffer = new char[BUFFER_SIZE];
+ int bytesRead;
+ while (true) {
+ bytesRead = in.read(buffer);
+ if (bytesRead == -1) {
+ break;
+ }
+ out.write(buffer, 0, bytesRead);
+ }
+ out.flush();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+}