Capture server command output using JSch and Java - java

I am using JSch to ssh to multiple servers and run few commands. My output file capture everything. But I am looking for a way to capture only the server respond.
Code:
try (OutputStream log = new BufferedOutputStream( new FileOutputStream("OUTPUT_FILE"))) {
JSch jsch = new JSch();
for (String host : jssh.listOfhost()) {
Session session = jsch.getSession(user, host, 22);
session.setPassword(password);
session.setConfig(getProperties());
session.connect(10 * 1000);
Channel channel = session.openChannel("shell");
channel.setOutputStream(log, true);
try (PipedInputStream commandSource = new PipedInputStream();
OutputStream commandSink = new PipedOutputStream(commandSource)) {
CommandSender sender = new CommandSender(commandSink);
Thread sendThread = new Thread(sender);
sendThread.start();
channel.setInputStream(commandSource);
channel.connect(15 * 1000);
sendThread.join();
if (sender.exception != null) {
throw sender.exception;
}
}
channel.disconnect();
session.disconnect();
}
}
Current Output:
Last login: Thu Jan 14 15:06:17 2016 from 192.168.1.4
mypc:~ user$
mypc:~ user$ hostname
mypc
mypc:~ user$ df -l | grep disk0s3 |tr -s [:blank:]|cut -d ' ' -f 7
19098537
but I only want to output the following
mypc 19098537
Which is an outcome of the following two commands
hostname
df -l | grep disk0s3 |tr -s [:blank:]|cut -d ' ' -f 7

Use exec channel, not shell channel. The exec channel is intended for command execution. The shell channel is intended for implementing an interactive session. That's why you get all the prompts and such. You do not get these in the exec channel.
See the official JSCh example for using exec channel.
If you need to read both stdout and stderr, see my answer to How to read JSch command output?

Related

Unable to access the cmd prompt in remote machine using openchannel in java

I'm trying to access the cmd prompt in administrator mode and run a batch file in the remote machine,but right now I'm not able to access the cmd prompt through openchannel. Did anybody tried to access it from remote machine in java?
Here is the code
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
session = jsch.getSession(user, ip, 22);
session.setPassword(password);
session.setTimeout(timeOut);
session.setConfig(config);
session.connect();
System.out.println("session connected");
//open command prompt to run the command = "C:\\executeBatchFile.bat" file
Channel channel = (ChannelExec) session.openChannel("exec");
((ChannelExec)channel).setCommand("cmd.exe /c \"echo %cd%\"\\executeBatchFile.bat");
channel.connect();
InputStream outputstream_from_the_channel = channel.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(outputstream_from_the_channel));
String jarOutput;
while ((jarOutput = reader.readLine()) != null)
{
System.out.println("Inside while loop");
System.out.println(jarOutput + "\n");
}
reader.close();
session.disconnect();
Expected behavior :set command should run as an administrator(though I have logged in as admin),come back to the c:drive (cd) and execute the batch file ie; C:executeBatchFile.bat
Actual behaviour : command gives the user path(not as admin) when I print the jarOutput. ie; C:\Users\Admin\executeBatchFile.bat
could you suggest any solution on the same?
This has been resolved using PsExec command instead of JSCH
String pscommand=E:\\Tool\\psexec -u user -p pwd \\\\ip -s -d cmd.exe /c C:\\executescript.bat
process = Runtime.getRuntime().exec(pscommand);
InputStream es = process.getErrorStream();
BufferedReader errReader = new BufferedReader(new InputStreamReader(es));
String line;
// Read STDOUT into a buffer.
while ((line = errReader.readLine()) != null)
{
system.out.println(line);
}
Could any one tell me how to open the cmd prompt in administrator mode(I logged in using admin credentials only but still it does not open in admin mode).Here I need to run the script in administrator.

Java JSCH - send EOF in exec or shell channel

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.

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.

'Echo' linux command is not working through Java Code

I am trying to execute a simple linux command to append some text to a file in remote server through my java code. But it isn't working. When I run the same command in the linux box it works fine.
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
Session sessionwrite = jsch.getSession(user2, host2, 22);
sessionwrite.setPassword(password2);
sessionwrite.setConfig(config);
sessionwrite.connect();
System.out.println("Connected");
Channel channel = sessionwrite.openChannel("exec");
BufferedReader in = new BufferedReader(new InputStreamReader(
channel.getInputStream()));
String command = "echo \"hello\" >> welcome.txt";
((ChannelExec) channel).setCommand(command);
System.out.println("done");
}
Channel channel = sessionwrite.openChannel("exec");
BufferedReader in = new BufferedReader(new InputStreamReader(
channel.getInputStream()));
String command = "echo \"hello\" >> welcome.txt";
((ChannelExec) channel).setCommand(command);
System.out.println("done");
You're missing the call to channel.connect(). connect() is the method which actually sends the request to the remote server to invoke the command. You should also call channel.disconnect() when finished with the channel to terminate it. Your code might look something like this:
Channel channel = sessionwrite.openChannel("exec");
BufferedReader in = new BufferedReader(new InputStreamReader(
channel.getInputStream()));
String command = "echo \"hello\" >> welcome.txt";
((ChannelExec) channel).setCommand(command);
channel.connect();
channel.disconnect();
System.out.println("done");
I'll add that, in this particular example, there's no reason to open an input stream on the exec channel's standard input, so you could leave that line out.

Categories