Attached are updates for the SSHExec task and documentation as diffs. The updates fix the problem with output not always showing on the screen (or log).

This task relies on jsch-0.1.3-rc1, available at http://www.jcraft.com/jsch/jsch-0.1.3-rc1.zip. Earlier versions of jsch will not work as one of the fixes to this version of jsch was implemented specifically for the SSHExec task.

While I was at it, I fixed the "timeout" attribute so it actually does something, plus I added some attributes from the <exec> task that seemed appropriate:
"output" write the output to a file
"append" append to or overwrite the output file
"outputproperty" store the output in a property


Dale Anson


Steve Loughran wrote:

----- Original Message -----
From: "Dale Anson" <[EMAIL PROTECTED]>
To: "Ant Developers List" <[EMAIL PROTECTED]>
Sent: Tuesday, March 18, 2003 14:57
Subject: Re: ssh exec task...




How do I post changes to the optional/ssh files to cvs?

Dale




run cvs diff -u against the files to create .diff files that you post as patches, then keep reminding people till it gets taken up. Committers take the files, patch them and commit the changes.

On this subject, who knows where I can get a copy of patch.exe that does not
fail on a two-cpu winXP box, the way the wincvs version does. I am having to
ftp files back to a unix server to patch right now. one extra caveat: I dont
want to have to install cygwin. I just need a URL of an up to date
port/build of the core Gnu unix command toolchain.

-steve


--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]



Index: SSHExec.java
===================================================================
RCS file: 
/home/cvspublic/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java,v
retrieving revision 1.1
diff -u -r1.1 SSHExec.java
--- SSHExec.java        11 Mar 2003 13:15:43 -0000      1.1
+++ SSHExec.java        25 Mar 2003 16:32:13 -0000
@@ -1,90 +1,94 @@
 /*
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2003 The Apache Software Foundation.  All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution, if
- *    any, must include the following acknowlegement:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowlegement may appear in the software itself,
- *    if and wherever such third-party acknowlegements normally appear.
- *
- * 4. The names "Ant" and "Apache Software
- *    Foundation" must not be used to endorse or promote products derived
- *    from this software without prior written permission. For written
- *    permission, please contact [EMAIL PROTECTED]
- *
- * 5. Products derived from this software may not be called "Apache"
- *    nor may "Apache" appear in their names without prior written
- *    permission of the Apache Group.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
-
+* The Apache Software License, Version 1.1
+*
+* Copyright (c) 2003 The Apache Software Foundation.  All rights
+* reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in
+*    the documentation and/or other materials provided with the
+*    distribution.
+*
+* 3. The end-user documentation included with the redistribution, if
+*    any, must include the following acknowlegement:
+*       "This product includes software developed by the
+*        Apache Software Foundation (http://www.apache.org/)."
+*    Alternately, this acknowlegement may appear in the software itself,
+*    if and wherever such third-party acknowlegements normally appear.
+*
+* 4. The names "Ant" and "Apache Software
+*    Foundation" must not be used to endorse or promote products derived
+*    from this software without prior written permission. For written
+*    permission, please contact [EMAIL PROTECTED]
+*
+* 5. Products derived from this software may not be called "Apache"
+*    nor may "Apache" appear in their names without prior written
+*    permission of the Apache Group.
+*
+* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+* ====================================================================
+*
+* This software consists of voluntary contributions made by many
+* individuals on behalf of the Apache Software Foundation.  For more
+* information on the Apache Software Foundation, please see
+* <http://www.apache.org/>.
+*/
 package org.apache.tools.ant.taskdefs.optional.ssh;
 
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.TaskContainer;
-import org.apache.tools.ant.taskdefs.LogOutputStream;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.util.Vector;
-import java.util.Enumeration;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringReader;
 
 import com.jcraft.jsch.Channel;
 import com.jcraft.jsch.ChannelExec;
 import com.jcraft.jsch.Session;
 
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
 /**
  * Executes a command on a remote machine via ssh.
+ *
  * @author    Robert Anderson, [EMAIL PROTECTED]
+ * @author    Dale Anson, [EMAIL PROTECTED]
+ * @version   $Revision$
  * @created   February 2, 2003
- * @since Ant 1.6
+ * @since     Ant 1.6
  */
 public class SSHExec extends SSHBase {
 
-    private String command = null;
-    private int maxwait = 30000;
-
-    /**
-     * Constructor for SSHExecTask.
-     */
-    public SSHExec() {
-        super();
+    private String command = null;   // the command to execute via ssh
+    private int maxwait = 30000;   // units are milliseconds, default is 30 
seconds
+    private Thread thread = null;   // for waiting for the command to finish
+
+    private String output_property = null;   // like <exec>
+    private File output_file = null;   // like <exec>
+    private boolean append = false;   // like <exec>
+
+    /** Initialize the task. */
+    public void init() {
+        super.init();
     }
 
     /**
@@ -92,56 +96,211 @@
      *
      * @param command  The new command value
      */
-    public void setCommand(String command) {
+    public void setCommand( String command ) {
         this.command = command;
     }
 
     /**
-     * The connection will be dropped after maxwait seconds. This is 
-     * sometimes useful when a connection may be flaky. Default is to 
-     * wait forever.
+     * The connection can be dropped after a specified number of seconds. This
+     * is sometimes useful when a connection may be flaky. Default is to wait 
30
+     * seconds, set to 0 to wait forever.
+     *
+     * @param timeout  The new timeout value in seconds
+     */
+    public void setTimeout( int timeout ) {
+        maxwait = timeout * 1000;
+    }
+
+    /**
+     * If used, stores the output of the command to the given file.
      *
-     * @param maxwait  The new maxwait value
+     * @param output  The file in which the command output will be stored.
      */
-    public void setMaxwait(int maxwait) {
-        this.maxwait = maxwait;
+    public void setOutput( File output ) {
+        output_file = output;
     }
 
+    /**
+     * Should the output be 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( boolean append ) {
+        this.append = append;
+    }
+
+    /**
+     * 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( String property ) {
+        output_property = property;
+    }
 
     /**
      * Execute the command on the remote host.
-     * @exception BuildException  Most likely a network error or bad
-     * parameter.
+     *
+     * @exception BuildException  Most likely a network error or bad parameter.
      */
     public void execute() throws BuildException {
-        if (getHost() == null) {
-            throw new BuildException("Host is null.");
+        if ( getHost() == null ) {
+            throw new BuildException( "Host is required." );
         }
-        if (getUserInfo().getName() == null) {
-            throw new BuildException("Username is null.");
+        if ( getUserInfo().getName() == null ) {
+            throw new BuildException( "Username is required." );
         }
-        if (getUserInfo().getKeyfile() == null 
-            && getUserInfo().getPassword() == null) {
-            throw new BuildException("Password and Keyfile are null.");
+        if ( getUserInfo().getKeyfile() == null
+                 && getUserInfo().getPassword() == null ) {
+            throw new BuildException( "Password or Keyfile is required." );
         }
-        if (command == null) {
-            throw new BuildException("Command is null.");
+        if ( command == null ) {
+            throw new BuildException( "Command is required." );
         }
 
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        Tee tee = new Tee( out, System.out );
+
         try {
+            // execute the command
             Session session = openSession();
-            ChannelExec channel=(ChannelExec) session.openChannel("exec");
-            channel.setCommand(command);
-            channel.setInputStream(System.in);
-            channel.setOutputStream(System.out);
+            session.setTimeout( maxwait );
+            final ChannelExec channel = (ChannelExec)session.openChannel( 
"exec" );
+            channel.setCommand( command );
+            channel.setOutputStream( tee );
             channel.connect();
-        } catch(Exception e){
-            if (getFailonerror()) {
-                throw new BuildException(e);
-            } else {
-                log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+
+            // wait for it to finish
+            thread =
+                new Thread() {
+                    public void run() {
+                        while ( !channel.isEOF() ) {
+                            if ( thread == null )
+                                return;
+                            try {
+                                sleep( 500 );
+                            }
+                            catch ( Exception e ) {
+                                // ignored
+                            }
+                        }
+                    }
+                };
+                    
+            thread.start();
+            thread.join( maxwait );
+            
+            if ( thread.isAlive() ) {
+                // ran out of time
+                thread = null;
+                log( "Timeout period exceeded, connection dropped." );
+            }
+            else {
+                // completed successfully
+                if ( output_property != null ) {
+                    getProject().setProperty( output_property, out.toString() 
);
+                }
+                if ( output_file != null ) {
+                    writeToFile( out.toString(), append, output_file );
+                }
+            }
+        }
+        catch ( Exception e ) {
+            if ( getFailonerror() ) {
+                throw new BuildException( e );
             }
+            else {
+                log( "Caught exception: " + e.getMessage(), Project.MSG_ERR );
+            }
+        }
+    }
+
+
+    /**
+     * 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( String from, boolean append, File to ) throws 
Exception {
+        FileWriter out = new FileWriter( to, append );
+        StringReader in = new StringReader( from );
+        char[] buffer = new char[8192];
+        int bytes_read;
+        while ( true ) {
+            bytes_read = in.read( buffer );
+            if ( bytes_read == -1 )
+                break;
+            out.write( buffer, 0, bytes_read );
         }
+        out.flush();
+        out.close();
     }
+
+    /**
+     * Similar to standard unix "tee" utility, sends output to two streams.
+     *
+     * @author    Dale Anson, [EMAIL PROTECTED]
+     * @version   $Revision$
+     */
+    public class Tee extends OutputStream {
+
+        private OutputStream left = null;
+        private OutputStream right = null;
+
+        /**
+         * Constructor for Tee, sends output to both of the given streams, 
which
+         * are referred to as the "teed" streams.
+         *
+         * @param left   one stream to write to
+         * @param right  the other stream to write to
+         */
+        public Tee( OutputStream left, OutputStream right ) {
+            if ( left == null || right == null )
+                throw new IllegalArgumentException( "Both streams are 
required." );
+            this.left = left;
+            this.right = right;
+        }
+
+        /**
+         * Writes the specified byte to both of the teed streams. Per java api,
+         * the general contract for write is that one byte is written to the
+         * output stream. The byte to be written is the eight low-order bits of
+         * the argument b. The 24 high-order bits of b are ignored.
+         *
+         * @param b
+         * @exception IOException  If an IO error occurs
+         */
+        public void write( int b ) throws IOException {
+            left.write( b );
+            right.write( b );
+        }
+
+        /**
+         * Closes both of the teed streams.
+         *
+         * @exception IOException  If an IO error occurs
+         */
+        public void close() throws IOException {
+            left.close();
+            right.close();
+        }
+
+        /**
+         * Flushes both of the teed streams.
+         *
+         * @exception IOException  If an IO error occurs
+         */
+        public void flush() throws IOException {
+            left.flush();
+            right.flush();
+        }
+    }
+
 }
 
Index: sshexec.html
===================================================================
RCS file: /home/cvspublic/ant/docs/manual/OptionalTasks/sshexec.html,v
retrieving revision 1.2
diff -u -r1.2 sshexec.html
--- sshexec.html        14 Mar 2003 15:10:27 -0000      1.2
+++ sshexec.html        25 Mar 2003 16:35:28 -0000
@@ -80,6 +80,28 @@
      <td valign="top">Passphrase for your private key.</td>
      <td valign="top" align="center">No, defaults to an empty string.</td>
   </tr>
+  <tr>
+    <td valign="top">output</td>
+    <td valign="top">Name of a file to which to write the output.</td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">append</td>
+    <td valign="top">Whether output file should be appended to or overwritten. 
Defaults to false, meaning overwrite any existing file.</td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">outputproperty</td>
+    <td valign="top">The name of a property in which the output of the 
+      command should be stored.</td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">timeout</td>
+    <td valign="top">Stop the command if it doesn't finish within the
+      specified time (given in seconds). Defaults to 30 seconds, use 0 to wait 
forever.</td>
+    <td align="center" valign="top">No</td>
+  </tr>
 </table>
 
 <h3>Examples</h3>

Reply via email to