I am new to JSch and I have a problem with some of my script I try to execute remotely and which seems to never end (and does not do the same thing as when I run it using putty).
I have redirected error and output stream to my System.out and see indeed error when the script is executed but the script is finished! Therefore I don't understand why the channel is still open (isClosed and isEOF are false).
When I run the command when connecting in SSH with putty the script execute correctly and does not show any errors. When I do ssh user#host "my command" using ssh command in Ubuntu I get the same output (std + err) as when I use JSch but the ssh command does not hangs!
Do you have any idea of what I do wrong, why does I have different output/behavior? Here is the java code I run (by the way I CAN'T send several command with different channel on the same sessions and I have no idea why, I therefore open one session for each cmd).
public static void runCommand(String user, String password, String cmd) throws JSchException, IOException{
Session session = jSsh.getSession(user, SERVER, SSH_PORT);
session.setPassword(password);
session.setConfig(SSH_PROPERTIES);
session.connect();
SshCommand sshCmd = new SshCommand(session, cmd);
runCommand(sshCmd);
session.disconnect();
}
private static void runCommand(SshCommand sshCmd) throws IOException, JSchException{
Session session = sshCmd.getSshSession();
String cmd = sshCmd.getCmd();
UtilityLogger.log(Level.FINE, "Running command on ssh : "+cmd);
ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(cmd);
channel.setInputStream(null);
InputStream in = channel.getInputStream();
InputStream err = channel.getErrStream();
UtilityLogger.log(Level.FINEST, "Connecting to channel");
channel.connect();
UtilityLogger.log(Level.FINEST, "Channel connected");
byte[] tmp = new byte[1024];
byte[] tmp2 = new byte[1024];
while (true) {
//Flush channel
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
UtilityLogger.log(Level.FINE, new String(tmp, 0, i));
}
//Flush Error stream
while (err.available() > 0) {
int i = err.read(tmp2, 0, 1024);
if (i < 0)
break;
UtilityLogger.log(Level.FINE, new String(tmp2, 0, i));
}
if(DONT_WAIT_PROCESS_END)
break;
if (channel.isEOF()) {
UtilityLogger.log(Level.FINE, "Channel exit-status: " + channel.getExitStatus());
break;
}
}
try{Thread.sleep(TIME_BETWEEN_COMMAND);}catch(Exception ee){}
channel.disconnect();
UtilityLogger.log(Level.FINEST, "Channel disconnected");
}
Try appending "exit;" after your commands even while using exec channels.
Our app did not receive an EOF on the exec, too. Appending an exit; to the command did not solve the problem.
It had something to do with the stderr output. Redirecting stderr to stdout solved (workarounded?!) the problem.
So we appended 2>&1 to the command:
${command} 2>&1
Related
I'm trying to run a restart server command on WLST through a remote Java web app.
This is what I'm trying to execute:
StringBuilder sb = new StringBuilder();
sb.append("/u01/app/oracle/jdk1.8.0_65/bin/./java -cp /u01/app/oracle/product/Oracle_Home/wlserver/server/lib/weblogic.jar weblogic.WLST");
sb.append(";connect(\'weblogic\',\'" + consolePass + "\',\'" + fullAddress + "\')");
sb.append(";domainRuntime()");
sb.append(";cd(\'/ServerLifeCycleRuntimes/" + serverName + "\')");
sb.append(";cmo.shutdown())");
sb.append(";start(" + serverName + ",'Server')");
String command = sb.toString();
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
session.setUserInfo(new OracleUserInfo(pass));
session.connect();
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
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()) {
if (in.available() > 0)
continue;
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channel.disconnect();
session.disconnect();
I'm using ';' to separate the commands, since I thought it was required to run multiple commands.
Unfortunately, it gives a syntax error on line 2.
bash: -c: line 0: syntax error near unexpected token 'weblogic','password','t3://host:7001''
bash: -c: line 0:/u01/app/oracle/jdk1.8.0_65/bin/./java -cp /u01/app/oracle/product/Oracle_Home/wlserver/server/lib/weblogic.jar weblogic.WLST;connect('weblogic','password','t3://host:7001')'
I tried to add \n after the first line, and the result was that the first line was executed (so it entered WLST), but none of the remaining commands were.
StringBuilder sb = new StringBuilder();
sb.append("/u01/app/oracle/jdk1.8.0_65/bin/./java -cp /u01/app/oracle/product/Oracle_Home/wlserver/server/lib/weblogic.jar weblogic.WLST\n");
sb.append(";connect(\'weblogic\',\'" + consolePass + "\',\'" + fullAddress + "\')\n");
sb.append(";domainRuntime()\n");
sb.append(";cd(\'/ServerLifeCycleRuntimes/" + serverName + "\')\n");
sb.append(";cmo.shutdown())\n");
String command = sb.toString();
Result:
Initializing WebLogic Scripting Tool (WLST) ...
Welcome to WebLogic Server Administration Scripting Shell
Type help() for help on available commands
wls:/offline>
I tested the command manually and it worked. The problem seems to be with JSch with WLST interface, since it opens another shell interface.
Any ideas how I could run WLST commands with JSch?
PS1: I know my JSch code works because I have a feature on the same app to deploy. Basically, it runs a jscp to upload the war, and then ssh to execute the weblogic.Deployer -deploy command.
PS2: I do have a .py script to do that, but as of now, it must be on the server to be executed. I'm thinking about doing an jscp to a temp folder, run the script and then delete. But I'm curious to find out how to run multiple commands on WLST with JSch.
Thanks in advance.
UPDATE
Code working (Thanks Martin)
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
InputStream in = channel.getInputStream();
OutputStream out = channel.getOutputStream();
((ChannelExec) channel).setErrStream(System.err);
channel.connect();
for (String wlstCommand : wlstCommands) {
out.write((wlstCommand).getBytes());
}
out.flush();
The ; can indeed by used in *nix based system to execute multiple commands in one shell command-line.
But what you are executing are not shell commands. Those are WLST commands, right? So you have to feed them to WLST.
Like this:
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand("java -cp /.../weblogic.jar weblogic.WLST");
OutputStream out = channel.getOutputStream();
channel.connect();
out.write(("connect('weblogic'...)\n").getBytes());
out.write(("domainRuntime()\n").getBytes());
...
It's basically the same as generic Providing input/subcommands to command executed over SSH with JSch.
I am supposed to connect to a Unix server, then go to the specific folder(which has got access restrictions) and fetch the file details from there. For the same , the code that I have written is
try{
Session session = new JSch().getSession("username", "host");
session.setPassword("password");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setCommand("cd a/b/node01/c.ear && ls -la");
channel.connect();
channel.run();
InputStream in = channel.getInputStream();
System.out.println(channel.isConnected());
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;
}
}
channel.disconnect();
session.disconnect();
}
catch(Exception exception){
System.out.println("Got exception "+exception);
}
I am not getting the list of files that are present in the location supplied.
The output that I am getting is
true
exit-status: 1
How do I get the desired output?
Do not use shell commands to retrieve file information. Use SFTP!
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp c = (ChannelSftp)channel;
java.util.Vector vv = c.ls("/a/b/node01/c.ear");
for (int i = 0; i < vv.size(); i++)
{
System.out.println(vv.elementAt(i).toString());
}
See http://www.jcraft.com/jsch/examples/Sftp.java.html
Regarding your code for "exec" channel:
First, the code has a recurring flaw described in:
Randomly getting empty output while executing shell command via JSch.
Though to actually debug your problem, you have to read both stdout and stderr to collect any errors (which you are obviously getting).
For that see Log stdout and stderr from ssh command in the same order it was created.
A possible problem here is the usage of in.available(). It returns the number of bytes that can be read without blocking.
The correct way to read a stream until eof is:
byte[] buf = new byte[1024];
int len;
while ((len=in.read(buf)) >= 0) {
// do something with buf[0 .. len]
}
Short Version: Is it possible to log the stdout and stderr on the local side of a command executed remotely via ssh in the same order as it was output on the remote host? If so, how?
Long Version:
I am attempting to log the standard and error output from a remotely exec'd SSH command (using Jsch) in the same order as it was output by the remote command. In other words, if the remote command writes "a" to stdout, then "b" to stderr, then "c" to stdout, I want the log on the client (local) side to read:
a
b
c
Below is what I have so far. It comes relatively close to what I want, but I think it will be apparent that it does not guarantee the correct output order on the client side.
public int exec(String strCommand) throws ExceptionUnableToExecCommand {
JSch jsch = new JSch();
Session session = null;
ChannelExec channel = null;
try {
session = jsch.getSession(user, host, 22);
UserInfo ui = new cyclOps.jsch.UserInfo(password);
session.setUserInfo(ui);
session.connect();
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(strCommand);
channel.setInputStream(null);
InputStream in = channel.getInputStream();
InputStream err = channel.getErrStream();
channel.connect();
/* getOutput() defined below. */
return this.getOutput(channel, in, err);
} catch (JSchException | IOException e) {
throw new ExceptionUnableToExecCommand("Unable to execute " + strCommand + " " + this.toString(), e);
} finally {
if (channel != null) channel.disconnect();
if (session != null) session.disconnect();
}
}
private int getOutput(ChannelExec channel, InputStream in, InputStream err) throws IOException {
byte[] tmp = new byte[1024];
while(true){
while(in.available() > 0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
this.sshLogger.logOutputFromSSH(new String(tmp, 0, i));
}
while(err.available() > 0){
int i=err.read(tmp, 0, 1024);
if(i<0)break;
this.sshLogger.logOutputFromSSH(new String(tmp, 0, i));
}
if(channel.isClosed()){
return channel.getExitStatus();
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
}
I think I should point out that this is a modified version of Exec.java from the Jsch web site examples.
How about the following chunk of code?
channel.setCommand(command);
PipedOutputStream pos=new PipedOutputStream();
PipedInputStream pis=new PipedInputStream(pos);
channel.setOutputStream(pos);
channel.setExtOutputStream(pos);
InputStream in=pis;
channel.connect();
You are working with separate streams, so no, there is no way you can possibly guarantee to get the exact same output.
that said, your code has a number of potential problems, among them:
InputStream.available is a generally useless method and should be avoided
you are reading bytes and converting arbitrary byte arrays to chars/Strings, which could result in broken output (if you are getting multi-byte chars you could end up splitting them).
reading 2 different blocking streams with a single thread can result in deadlock
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.
My aim is to connect to my linux server using a java application and execute a linux command. I have already achieved this using the JSch API but I can't seem to figure out how to execute more than one command at a time.
It's a problem for me because I need to navigate to a certain directory, then execute another command FROM that directory. My application just exits before the second command can be executed in the correct directory.
Here is my method to execute one linux command as a string when it is passed as a parameter and print any output.
public void connect(String command1){
try {
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
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){
ee.printStackTrace();
}
}
channel.disconnect();
session.disconnect();
System.out.println("DONE");
}
catch(Exception e){
e.printStackTrace();
}
}
Any ideas how to do 2 commands at once?
If a normal shell is started on the remote machine, use ; or && constructs to do more things.
As in
cd /home/foo/banana ; do_things
or
cd /home/foo/banan && do_things #do_things will only be run if the cd command is successfull