Reputation:
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?
Upvotes: 2
Views: 1647
Reputation:
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;
}
}
Upvotes: 0
Reputation: 4496
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
Upvotes: 2