Run local/remote terminal commands with java using ssh
Sometimes you need to use some CLI-Tools before you want to create or search for a native JNI Binding.
So there is a common way, using the Java Process-Class. But then you might meet two problems i had to face in the past during several problems:
- There are (a really small) number of CLI-Tools, that giving no constant output over the STD-OUT (the standard output the Process-Class uses for output)
- There is no “elegant” way to implement a process call into your project.
To solve this Problem I created a basic HelperClass, that calls the System over SSH (with the Convenience to work remote and the side-effect to always get STD-compatible output).
I am primarely using it for a fun project SAM i started some months ago to try to create a Management-Tool for Unices and Windows with a very low client-side footprint.
The first Class is used to capsulate the basic SSH Calls:
// some imports... public class SystemHelper { private Runtime r; private String sshPrefix = ""; // call with $user and 127.0.0.1 to run local command. public SystemHelper(String user, String ip) { r = Runtime.getRuntime(); sshPrefix = " ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " + user + "@" + ip; } public void runCommand(String command, ProcessParser pp) { try { Logger.info("running: " + this.sshPrefix + " " + command); Process p = r.exec(this.sshPrefix + " " + command); InputStream in = p.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); InputStreamReader inread = new InputStreamReader(buf); BufferedReader bufferedreader = new BufferedReader(inread); pp.parse(bufferedreader); try { if (p.waitFor() != 0) { Logger.info("exit value = " + p.exitValue()); } } catch (InterruptedException e) { System.err.println(e); } finally { // Close the InputStream bufferedreader.close(); inread.close(); buf.close(); in.close(); } } catch (IOException ex) { Logger.error(ex.getLocalizedMessage()); } } }
ProcessParser is an interface that defines the methode parse, accepting a BufferedReader for parsing the output of the Process. Unfortunately there is no timeout ATM to kill a hanging SSH-Call.
public interface ProcessParser { public void parse(BufferedReader bufferedreader); }
The most basic (Output-)Parser looks like this:
public String getPublicSSHKey() { SimpeOutputPP so = new SimpeOutputPP(); String command = "cat ~/.ssh/id_rsa.pub"; runCommand(command, so); if (!so.getOutput().isEmpty()) { return so.getOutput().get(0); } return ""; }
This returns just the public SSH-Key of the current user. I implemented some more parsers for the output of apt (dpkg), rpm and pacman. You can find them in the github project here.
To prevent ssh to prompt for a password you can use the following command to check if a password is necessary before you run the command (and then forces the java-app to hang)
example here: https://github.com/phaus/sam/blob/master/app/helper/sam/SamHelper.java
Then you can use a BooleanParser (i just created for that purpose) to check the output (see it here: https://github.com/phaus/sam/blob/master/app/helper/parser/BooleanPP.java ). But i have to admit, that the whole SAM Source needs some rewrite and is currently just WiP.
Logger.info(“running: ” + this.sshPrefix + ” ” + command);
info is a non static method.
Hi Noe. It is static, since i used the Play 1 Logger. see https://www.playframework.com/documentation/1.3.x/logs
But you are right, if you have the default Java Utils Logger, it wouldn’t be static.