I'm running a test case using testng. This testcase uses Jsch to try to send a ssh command to a remote machine. The Jsch function is supposed to return output from command (including any exceptions). However, I can't view the output and the test passes, even when there's been an timeout exception!
Here's the JSch function:
public static String sshCommand (String username, String password, String host, int port, String command) {
StringBuilder outputBuffer = new StringBuilder();
try {
System.out.println("about to send command: " + command);
JSch jschSSHChannel = new JSch();
Session session=jschSSHChannel.getSession(username, host, port);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setPassword(password);
session.connect();
Channel channel = session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
InputStream commandOutput = channel.getInputStream();
channel.connect();
int readByte = commandOutput.read();
while(readByte != 0xffffffff)
{
outputBuffer.append((char)readByte);
readByte = commandOutput.read();
}
channel.disconnect();
session.disconnect();
} catch (Exception e) {
System.out.println("Exception encountered returning: " + e.toString());
return e.toString();
}
return outputBuffer.toString();
}
Here's the code that evaluates the output from the ssh function:
String output = jschClass.sshCommand("username", "password", ipAddress, 22, command);
System.out.println("Command output: " + output);
Assert.assertTrue(!output.contains("Exception"));
System.out.println("Test complete");
The problem is I never see the System.out.println("Command output: " + output); printed. It skips right over that statement and executes System.out.println("Test complete"); before exiting.
Additionally, the catch statement I put in the sshcommand function never seems to execute. An exception is displayed, but it doesn't seem to be executing the code I wrote. In fact it's almost like the exception is coming from somewhere else. Here's what I see in the program's output:
about to send command: ping -c 5 google.com
com.jcraft.jsch.JSchException: java.net.ConnectException: Connection refused: connect
Test complete
at com.jcraft.jsch.Util.createSocket(Util.java:349)
at com.jcraft.jsch.Session.connect(Session.java:215)
at com.jcraft.jsch.Session.connect(Session.java:183)
at com.cisco.ui2.openstack.jsch.JschHelper.sshCommand(JschHelper.java:28)
at com.cisco.ui2.openstack.PingPublicSite.createImageAndPingPublicSite(PingPublicSite.java:91)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[removed for space]
at com.jcraft.jsch.Util.createSocket(Util.java:343)
... 28 more
Any idea what's going on?
Related
I'm developing java program to connect with windows server over ssh. For this I used jcraft on java. And the ssh server is copSSH. The implementation throws
Error: com.jcraft.jsch.JSchException: Algorithm negotiation fail
error on java. At the same time it shows
fatal: Unable to negotiate with 192.168.28.111: no matching cipher
found. Their offer: aes128-cbc,3des-cbc,blowfish-cbc [preauth]
on CopSSH.
Java code block
public void sshExecPassword(String host, String USERNAME, String PASSWORD, String command) {
App objApp = new App();
int port = 22;
try {
/**
* Create a new Jsch object This object will execute shell commands
* or scripts on server
*/
JSch jsch = new JSch();
/*
* Open a new session, with your username, host and port Set the
* password and call connect. session.connect() opens a new
* connection to remote SSH server. Once the connection is
* established, you can initiate a new channel. this channel is
* needed to connect to remotely execution program
*/
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
Session session = jsch.getSession(USERNAME, host, port);
session.setConfig(config);
session.setPassword(PASSWORD);
session.connect();
// create the excution channel over the session
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
// Gets an InputStream for this channel. All data arriving in as
// messages from the remote side can be read from this stream.
InputStream in = channelExec.getInputStream();
// Set the command that you want to execute
// In our case its the remote shell script
String str = command;
channelExec.setCommand(str);
channelExec.connect();
// Read the output from the input stream we set above
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// retrieve the exit status of the remote command corresponding to
// this channel
int exitStatus = channelExec.getExitStatus();
// Safely disconnect channel and disconnect session. If not done
// then it may cause resource leak
channelExec.disconnect();
session.disconnect();
if (exitStatus < 0) {
System.out.println("Done, but exit status not set! " + exitStatus);
objApp.writeLogs("120","Done, but exit status not set! ");
} else if (exitStatus > 0) {
System.out.println("Done, but with error!");
objApp.writeLogs("120","Done, but with error!");
} else {
System.out.println("Done!");
objApp.writeLogs("121","SSH connection successful");
}
} catch (Exception e) {
System.err.println("Error: " + e);
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
e.printStackTrace(pw);
objApp.writeLogs("120", sw.getBuffer().toString());
}
}
And the CopSSH host following versions
OpenSSH_7.1p2, OpenSSL 1.0.2e 3 Dec 2015
Can any one suggest a fix for it?
That happens due to lacking support for legacy ciphers in more recent releases of OpenSSH. Check this Copssh FAQ for a solution. Background information can also be found here.
Latest jcraft jar fix the issue
I need java code that logs into linux box with my credentials, then do a sesu , and then execute a shell script. Permission to execute the shell script if for only sesu user, hence sesu-ing after login is critical. I used te following code which can help me execute a command with my credential scope, however, I need sesu login following my login. Please suggest a way.
I tried adding the sesu command in teh command list, but it prompts for a password. I want a way to pass the password as well and completly automate it.
import java.io.InputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class SSHCommandExecutor {
/** * #param args */
public static void main(String[] args) {
String host = "xxxxxxx";
String user = "xxxxxxx";
String password = "xxxxxxx";
String command1 = "cd /test; ./test.sh";
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
session.setPassword(password);
session.setConfig(config);
session.connect();
System.out.println("Connected");
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command1);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
System.out.print(new String(tmp, 0, i));
}
if (channel.isClosed()) {
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {}
}
channel.disconnect();
session.disconnect();
System.out.println("DONE");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Variant A: Using JSch all the way
This minimal script (test.sh) requests an input from stdin before putting out one line of data:
#!/bin/bash
echo -n "Is this a good question (y/n)? "
read answer
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
So it should be equivalent to your call requesting a password to be given. Now have a look at this code on how to send data into that process
String command1 = "cd /home/jan; ./test.sh";
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
session.setPassword(password);
session.setConfig(config);
session.connect();
System.out.println("Connected");
ChannelExec channel = (ChannelExec)session.openChannel("exec");
OutputStream o = channel.getOutputStream();
PrintWriter pw = new PrintWriter(o);
InputStream in = channel.getInputStream();
((ChannelExec) channel).setCommand(command1);
channel.connect();
// 1 - Reading the prompt to input password
byte[] buf = new byte[255];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
// 2 - Send (password) data and flush stream
pw.println("y");
pw.flush();
// 3 - Read result
BufferedReader br = new BufferedReader(new InputStreamReader(in));
System.out.println(br.readLine());
// 4 - Clean up
channel.disconnect();
session.disconnect();
Variant B: Shell magic
String command1 = "cd /test; echo 'password' | ./test.sh";
or
String command1 = "cd /test; ./test.sh <<< 'password'";
(of cause you'd need to specify correct password there)
To run remote script with root privileges even if login user does not have them see here:
https://askubuntu.com/questions/155791/how-do-i-sudo-a-command-in-a-script-without-being-asked-for-a-password
I'm working on a project to restart something within a screen on a remote server using JSch... however I'm running into an issue. It will send the first 2 commands 100% of the time, but the 3rd command will only be sent some of the time. Any help would be appreciated.
public static void stopServer(String name, String ip, String passwd)
{
try {
Session session = Main.jsch.getSession("user",ip,22);
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(passwd);
session.connect();
Channel channel = session.openChannel("shell");
channel.connect();
ChannelShell cs = (ChannelShell) channel;
cs.setPty(true);
DataInputStream dataIn = new DataInputStream(channel.getInputStream());
PrintStream dataOut = new PrintStream(channel.getOutputStream());
dataOut.println("screen -x "+name);
dataOut.flush();
dataOut.println("stop");
dataOut.flush();
String line = dataIn.readLine();
System.out.println(line);
while(true) {
line = dataIn.readLine();
System.out.println(line);
if(line.contains("\"quit\""))
{
break;
}
}
TimeUnit.SECONDS.sleep(1);
dataOut.println("quit");
dataOut.flush();
System.out.println("Shutdown");
dataIn.close();
dataOut.close();
channel.disconnect();
session.disconnect();
}catch(Exception e)
{
e.printStackTrace();
}
}
This sends the screen -x 'name' and the stop 100% of the time from what I've seen, but the 'quit' command only actually gets sent/understood by the console a fraction of the time. Any help would be appreciated!!
I managed to fix this in a way that doesn't use the shell channel, but it is reliable enough that it works:
//Start EXEC
ChannelExec ce = (ChannelExec) session.openChannel("exec");
ce.setCommand("screen -S " + name + " -X stuff 'quit\n'");
ce.setInputStream(null);
ce.setErrStream(System.err);
InputStream in=ce.getInputStream();
ce.connect();
byte[] tmp=new byte[1024];
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
//System.out.print(new String(tmp, 0, i));
}
if(ce.isClosed()){
if(in.available()>0) continue;
//System.out.println("exit-status: "+ce.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
ce.disconnect();
//STOP EXEC
As much as this doesn't work along the original vein of the problem, it fixed it for me.
If an exception gets thrown sometime before the "quit" command gets sent, then the quit command will never get sent.
If the while loop never breaks, then that will prevent the "quit"
command from being sent too.
I'm running shell script on a remote machine using JSch and printing the command logs in my log file using JSch channel. Problem is that as the script ends, I do a channel.disconnect and soon after disconnect, the System.out stop printing into the log file. Here is the code:
private int runShellScript(HashMap bundleDetails) {
int exitStatus = -1;
Channel channel = null;
Session session = null;
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
String host = (String) bundleDetails.get("host");
String userName = (String) bundleDetails.get("userName");
String password = (String) bundleDetails.get("password");
String bundleName = findFileName((String) bundleDetails.get("bundleName"));
String sourceLocation = (String) bundleDetails.get("sourceLocation");
String logFileName = findFileName((String) bundleDetails.get("logFileName"));
String targetLocation = (String)bundleDetails.get("targetLocation");
String command1 = "sh "+(String) bundleDetails.get("targetIndexerLocation") + (String) bundleDetails.get("deployScript")+" "+
targetLocation + bundleName + " " +
targetLocation + logFileName;
JSch ssh = new JSch();
session = ssh.getSession(userName, host, 22);
session.setConfig(config);
session.setPassword(password);
session.connect();
channel = session.openChannel("exec");
((ChannelExec)channel).setCommand(command1);
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
InputStream in=channel.getInputStream();
channel.connect();
byte[] tmp=new byte[1024];
while(true){
//System.out.println("inside while second");
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
System.out.print("*****NEW ONE*****$$$$$**$$########"+new String(tmp, 0, i));
}
if(channel.isClosed()){
exitStatus = channel.getExitStatus();
System.out.println("Before Disconnected Here exit-status: "+exitStatus);
channel.disconnect();
System.out.println("Disconnected Here exit-status: "+exitStatus);
break;
}
}
//logger("runShellScript", "END");
System.out.println("***** out of infinite loop");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Copy to remote location failed.... ");
}finally{
System.out.println("finally DISCONNECT channel and session");
if(channel!=null && channel.isConnected()){
channel.disconnect();
}
if(session!=null){
session.disconnect();
}
System.out.println("finally DISCONNECTED channel and session");
}
System.out.println("Before return exit-status: "+exitStatus);
return exitStatus;
}
The lines from log file:
*****NEW ONE*****$$$$$**$$########Starting...
Before Disconnected Here exit-status: 0
If you see in the method I pasted above, the sysout printed is actually the one just above the 'channel.disconnect'. The one below it is not printed! Al the functioning is correct and the overall output is what I expect
System.out.println("Before Disconnected Here exit-status:
"+exitStatus); channel.disconnect(); System.out.println("Disconnected
Here exit-status: "+exitStatus);
All the functioning is correct and the overall output is what I expect. The only problem is that log freeze. Where am I going wrong?
Edit
Also, I'm not able to see the syouts from my finally block!!
Most likely it is related to this line:
((ChannelExec)channel).setErrStream(System.err);
By doing that, you've tied the System.err stream to the channel stream. And per the documentation, by default, the stream is closed when the channel is disconnected. You don't say what platform you are running on, but I think most platforms connect System.err and System.out in certain ways, so Jsch is most likely closing the System.out when it disconnects. You might try doing this to prevent JSch from closing the stream:
((ChannelExec)channel).setErrStream(System.err, true);
Javadoc
Even if that does work though, I think hooking in to the System.err like this is a bit risky. I think safer design would be to create a stream that wrote to the log file directly, not via System.err.
This is because of
((ChannelExec)channel).setErrStream(System.err);
And when you are disconnecting the channel, the connected streams are also being disconnected.
So please write the below statement before disconnecting the channel:
((ChannelExec)channel).setErrStream(null);
I am connecting to a Digi Transport WR21 router using jsch's ChannelExec and if I execute a command, say "modemstat ?" I can capture the results but if I try to run a python script, say "python hello.py" all I get back is "OK" and the channel then closes before I can capture the output from the script. Does anybody know how to get the python scripts output?
the command code:
private void sendCommand(String ipAddress, String aCommand) {
JSch jsch = new JSch();
try {
Session session = jsch.getSession("username", ipAddress, 22);
session.setPassword("password");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect(3*1000);
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(aCommand);
channel.setInputStream(System.in);
InputStream in = channel.getInputStream();
channel.connect(3*1000);
StringBuilder commandOut = new StringBuilder();
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)break;
//System.out.print(new String(tmp, 0, i));
//System.out.println(channel.getInputStream().toString());
commandOut.append(new String(tmp, 0, i));
//setChanged();
//notifyObservers(System.err.toString() + "\n");
}
if (channel.isClosed()) {
System.out.println("exit-status: "
+ channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
throw new JSchException("Cannot execute remote command: " + aCommand + " : " + ee.getMessage());
}
}
channel.disconnect();
session.disconnect();
System.out.println(commandOut);
} catch (JSchException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
When run from the command line, the script hello.py first outputs "OK" then about a second later it outputs "Hello World".
The answer is two fold. Part one is that I was missing "\r\n" from the end of my command, thus "python hello.py" should have been "python hello.py\r\n". Part two is that I needed to be using a ChannelShell instead of a ChannelExec.