Java JSCH - send EOF in exec or shell channel - java

I'm trying to send the CTRL + C (^C) or CTRL + D (^D) behavior to the SSH server using Java's JSCH.
I currently have no idea on how I would send an EOF message through the exec or ssh channel.
Can anyone help me with this?
Edit:
Below the code I'm trying to execute:
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import java.io.InputStream;
public class Test {
private static final JSch JSCH_INSTANCE = new JSch();
public static void main(String[] args) throws Exception {
Session session = JSCH_INSTANCE.getSession("username", "domain");
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setInputStream(null);
InputStream err = channel.getErrStream();
InputStream in = channel.getInputStream();
channel.setCommand("sudo service rscedit start");
channel.connect();
while (channel.getExitStatus() == -1) {
Thread.sleep(10);//command stays stuck here because EOF isn't being sent?
}
InputStream result = err.available() > 0 ? err : in;
StringBuilder responseBuilder = new StringBuilder();
while(result.available() > 0) {
responseBuilder.append((char) result.read());
}
System.out.println(responseBuilder);
channel.disconnect();
session.disconnect();
}
}
The service is executing the following bash script:
#!/bin/sh
(java -jar -Xmx20480m /home/path/Somejar.jar)
read –n1
Can I add EOF at the end of the command?

That looks like a bug in the "rscedit" service start script to me.
As such there's no correct solution -- all you are looking for, is the best workaround.
From your comments, it does not look like you actually need to send Ctrl+C. The service is already up and running when the startup script *hangs.
So you can just close the channel instead, maybe after a short time interval. That's effectively the same as what Ctrl+C does in an interactive terminal.

Related

Invoke WLST commands with JSch

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.

Providing input/subcommands to command executed over SSH with JSch

I'm trying to manage router via Java application using Jcraft Jsch library.
I'm trying to send Router Config via TFTP server. The problem is in my Java code because this works with PuTTY.
This my Java code:
int port=22;
String name ="R1";
String ip ="192.168.18.100";
String password ="root";
JSch jsch = new JSch();
Session session = jsch.getSession(name, ip, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("Establishing Connection...");
session.connect();
System.out.println("Connection established.");
ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
InputStream in = channelExec.getInputStream();
channelExec.setCommand("enable");
channelExec.setCommand("copy run tftp : ");
//Setting the ip of TFTP server
channelExec.setCommand("192.168.50.1 : ");
// Setting the name of file
channelExec.setCommand("Config.txt ");
channelExec.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
int index = 0;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null)
{
System.out.println(line);
}
session.disconnect();
I get
Line has an invalid autocommand '192.168.50.1'
The problem is how can I run those successive commands.
Calling ChannelExec.setCommand multiple times has no effect.
And even if it had, I'd guess that the 192.168.50.1 : and Config.txt are not commands, but inputs to the copy run tftp : command, aren't they?
If that's the case, you need to write them to the command input.
Something like this:
ChannelExec channel = (ChannelExec) session.openChannel("exec");
channelExec.setCommand("copy run tftp : ");
OutputStream out = channelExec.getOutputStream();
channelExec.connect();
out.write(("192.168.50.1 : \n").getBytes());
out.write(("Config.txt \n").getBytes());
out.flush();
In general, it's always better to check if the command has better "API" than feeding the commands to input. Commands usually have command-line arguments/switches that serve the desired purpose better.
A related question: Provide inputs to individual prompts separately with JSch.

jcraft with copSSH throws Algorithm negotiation fail error in windows environment

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

Java code to run command on remote Linux with sesu access

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

JSch : channel never closed or EOF

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

Categories