2

I am having trouble executing commands on a remote GNU/Linux system over SSH from Java. The following commands work fine when executed in the local Bash (of course the user and host are different but the behaviour is unchanged).

$ ssh [email protected] 'hostname'
host
$ ssh [email protected] 'hostname -f'
host.example.com
$ ssh [email protected] "hostname -f"
host.example.com

Doing what I think is the same from Java fails for anything more complex than hostname without arguments.

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;

public class SOPlayground {

    public static void main(String[] args) throws Exception {
        for (String argument : new String[]{"hostname", "'hostname'", "\"hostname\"",
            "'hostname -f'", "\"hostname -f\""}) {
            CommandLine commandLine = new CommandLine("ssh");
            commandLine.addArgument("[email protected]");
            commandLine.addArgument(argument);
            System.out.println(commandLine);

            final Executor executor = new DefaultExecutor();

            try (ByteArrayOutputStream os = new ByteArrayOutputStream();
                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
                executor.setStreamHandler(new PumpStreamHandler(os, err));
                int exitcode = executor.execute(commandLine);
                System.out.println("exitcode=" + exitcode);
                System.out.println(new String(os.toByteArray(), "UTF-8"));
                System.err.println(new String(err.toByteArray(), "UTF-8"));
            } catch (IOException ex) {
                System.err.println(ex.getMessage());
            }
        }
    }
}

The output is:

ssh [email protected] hostname
exitcode=0

host

ssh [email protected] 'hostname'
exitcode=0

host

ssh [email protected] "hostname"
exitcode=0
host


ssh [email protected] 'hostname -f'
Process exited with an error: 127 (Exit value: 127)
ssh [email protected] "hostname -f"
Process exited with an error: 127 (Exit value: 127)

As you can see, executing hostname -f over SSH from Java fails with an exit code of 127. I wonder what bash (local or remote) was unable to find what command.

I've tried to use the variant

addArgument(String argument, boolean handleQuoting)

but there was no difference in the result.

How must I build a CommandLine from Java that works over SSH?

7
  • 2
    try using jsch (com.jcraft.jsch.JSch) - it is purpose built for ssh Commented Jun 18, 2014 at 6:58
  • I've tried JSch in a different approach but failed to configure publickey authentication. Any hint about that? Commented Jun 18, 2014 at 7:02
  • sorry I have only used it with username/passwd as in JSch shell = new JSch(); session = shell.getSession(userName, serverIP, 22); session.setPassword(password); Commented Jun 18, 2014 at 7:05
  • @LutzHorn I've a working example of JSch, with pubkey authentication. Would be an answer to this question? Commented Jun 18, 2014 at 7:15
  • 2
    @rpax If it uses exec to execute a single remote command and then closes the connection, please post it as an answer. Commented Jun 18, 2014 at 7:16

2 Answers 2

2

You can use JSch with publickey authentication.

If you only want to use exec to execute a single remote command and then closes the connection, here you have a working example:

public static void main(String[] args) {

        String user = "--";
        String host = "--";

        try
        {
            JSch jsch = new JSch();
            // key authentication
            jsch.addIdentity("id_rsa");
            // open a new session on port 22
            Session session = jsch.getSession(user, host, 22);
            session.setConfig("StrictHostKeyChecking", "no");

            session.connect();
            String command = "ls /";
            Channel channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);

            channel.setInputStream(null);

            ((ChannelExec) channel).setErrStream(System.err);

            InputStream in = channel.getInputStream();

            channel.connect();
            StringBuilder sb = new StringBuilder();
            byte[] tmp = new byte[1024];
            while (true)
            {
                while (in.available() > 0)
                {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0)
                        break;
                    sb.append(new String(tmp, 0, i));
                }
                if (channel.isClosed())
                {
                    if (in.available() > 0)
                        continue;
                    System.out.println("exit-status: "
                            + channel.getExitStatus());
                    break;
                }
                try
                {
                    Thread.sleep(500);
                }
                catch (Exception ee)
                {
                }
            }
            //disconnecting and closing
            channel.disconnect();

            session.disconnect();
            System.out.println("Output: ");
            System.out.println(sb.toString());
        }
        catch (Exception e)
        {
             //something should be done here
            e.printStackTrace();

        }
    }

Output:

exit-status: 0
Output: 
1
bin
boot
cgroup
dev
etc
home
lib
lib64
lost+found
....

Hope it helps

Note: id_rsa is the path to the key file

Sign up to request clarification or add additional context in comments.

Comments

0

Thanks for the answer regarding Jsch. I've tried a different approach which writes the command to a temporary file and then executes it locally.

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.IOUtils;

public class SOPlayground {

    public static void main(String[] args) throws Exception {
        final String command = "ssh user@host 'hostname -f'";
        int exitCode = executeCommand(command);
    }

    private static int executeCommand(final String command) {
        int exitcode = -1;
        File temp = null;
        try {
            temp = File.createTempFile("foo", ".tmp");
            try (OutputStream os = new FileOutputStream(temp);) {
                IOUtils.write(command, os);
            } finally {
                // os is closed
            }

            CommandLine commandLine = new CommandLine("bash");
            commandLine.addArgument(temp.getAbsolutePath());

            final Executor executor = new DefaultExecutor();
            try (ByteArrayOutputStream os = new ByteArrayOutputStream();
                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
                executor.setStreamHandler(new PumpStreamHandler(os, err));
                exitcode = executor.execute(commandLine);
                System.out.println("exitcode=" + exitcode);
                System.out.println(new String(os.toByteArray(), "UTF-8"));
                System.err.println(new String(err.toByteArray(), "UTF-8"));
            } finally {
                // os and err are closed
            }
        } catch (IOException ex) {
            System.err.println(ex.getMessage);
        } finally {
            if (temp != null) {
                temp.delete();
            }
        }
        return exitcode;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.