diff options
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.java | 519 |
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 "wait forever". + * + * @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(); + } + } + } + +} |