Reputation: 11
I need to execute a script Linux via SSH with Java, that resides on a remote Linux machine. At runtime I retrieve from DB the name of the script, the host and the credentials and try to launch via ssh the script. This is OK because I get the execution PID in return! The problem is that I get always a PID of execution, even if the script is wrong or it doesn't exist or it doesn't have the execution rights.
Is there a way to know if the execution is right or if the command is wrong? I need to know if it is possible in the same ssh launch command. Thank you in advance
The code we are using is this below:
JSch jsch = new JSch();
String post_process_host="123.123.123.123";
Integer post_process_port=22;
String post_process_user="therock";
String post_process_pwd="XxXdfG$L";
//String command="testpp.sh";
String command="./export/home/therock/test/hello.bsh";
command=command + " > /dev/null 2>&1 & \n echo $!";
Session session = jsch.getSession(post_process_user, post_process_host, post_process_port.intValue());
UserInfo ui = new SUserInfo(post_process_pwd, null);
session.setUserInfo(ui);
session.setPassword(post_process_pwd);
session.connect();
int timeout=10; //timeout di 5 minuti
StringBuffer text = new StringBuffer();
int exitCode = -1;
try {
logger.info("Execution command: " + command);
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
InputStream err = ((ChannelExec) channel).getErrStream();
channel.connect(timeout);
byte[] tmp = new byte[1024];
while (true) {
timeout--;
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
String line = new String(tmp, 0, i, StandardCharsets.UTF_8);
text.append(line);
if (text.toString().contains("\n")) break;
}
while (err.available() > 0) {
int i = err.read(tmp, 0, 1024);
if (i < 0)
break;
String line = new String(tmp, 0, i, StandardCharsets.UTF_8);
text.append(line);
}
if (channel.isClosed()) {
if (in.available() > 0)
continue;
exitCode = channel.getExitStatus();
logger.info("Eseguito comando codice di ritorno: " + exitCode);
logger.debug("output comando: "+text.toString());
if (exitCode!=0) throw new Exception("Execution Command "+command+" error with Return Code="+exitCode);
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
throw ee;
}
}
channel.disconnect();
} catch (Exception e) {
throw e;
}
logger.debug("output command: "+text.toString());
Upvotes: 1
Views: 463
Reputation: 2505
The highlight of your code above is on this 2 line:
String command="./export/home/therock/test/hello.bsh";
command=command + " > /dev/null 2>&1 & \n echo $!";
Which made a command
variable that have such value:
./export/home/therock/test/hello.bsh > /dev/null 2>&1 & \n echo $!
To understand what this command does, you might want to study bash
, but here's my quick explanation:
./export/home/therock/test/hello.bsh
(from its extension, I assume it's a shell script)> /dev/null
= then redirect its STDOUT
to /dev/null
(a.k.a.: discard the output)2>&1
= then redirect its STDERR
to wherever STDOUT
goes (as stated above, its gone to /dev/null or discarded)&
= execute it in background\n echo $!
= then echo (print in STDOUT) the PID (process id) of the resulting background processNow I don't know the content of your ./export/home/therock/test/hello.bsh
file, but if it's well written, then it should return an exit code indicating whether it runs successfully or not (e.g.: using exit 0
for successful run, exit 1
for failed run, exit 2
for other kind of error)
After that, we can see the exit code of last command by echo $?
So, you might want to rewrite your code to:
String command="./export/home/therock/test/hello.bsh > /dev/null 2>&1 ; \n echo $?";
Explanation:
./export/home/therock/test/hello.bsh
(from its extension, I assume it's a shell script)> /dev/null
= then redirect its STDOUT
to /dev/null
(a.k.a.: discard the output)2>&1
= then redirect its STDERR
to wherever STDOUT
goes (as stated above, its gone to /dev/null or discarded);
= after finish executing this command, execute next command\n echo $?
= then echo (print in STDOUT) the exit code of last commandNote that after this change, you won't have the PID of the process but instead have the exit code instead. And this change also make your java program to wait the bash script to finish instead.
So if your bash script runs for 5 minute, and you start executing your java program in 08:00:00,
OP said that
The business requirements are to launch a script in a remote machine in background, so I can't wait for the complete execution of the script. My personal solution is to get the PID at SSH launch time and then make another inquiry to the remote system
You can always save the exit code to a file, then read that file later, example:
In ./export/home/therock/test/hello.bsh
, instead of writing exit 0
or exit 1
, you can write
echo 0 >> ./export/home/therock/test/result.txt
exit 0
later when you want to check the result, you just need to read last line of ./export/home/therock/test/result.txt
(e.g.: tail -n1 ./export/home/therock/test/result.txt
)
OP said that he/she needs to know if the process is already finished or not.
Easy approach to this is to simply create a lock file (or a reverse lock file)
In beginning of ./export/home/therock/test/hello.bsh
, add:
rm -f ./export/home/therock/test/result.txt
And then, instead of writing exit 0
or exit 1
, you can write
echo 0 > ./export/home/therock/test/result.txt
exit 0
So you know that the bash script is still running if file ./export/home/therock/test/result.txt
does not exist. And if the file exist, it means the bash script is finished and you can read its content to see its result.
Other approach (more recommended) is to write a lock file that gets created when bash script process started, and gets deleted when its exited. This way it also ensure that your bash script is only executed once at a time.
Upvotes: 1