Alex73
Alex73

Reputation: 11

Remote Script Execution via SSH command: how to know if it's done correctly or not

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

Answers (1)

Kristian
Kristian

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:

  • execute a command in file called ./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 process

Now 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:

  • execute a command in file called ./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 command

Note 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,

  • previously, your java program should finish in about 08:00:01 (very fast since it doesn't wait for bash script to finish)
  • after the change, your java program will finish in about 08:05:01 (since it waited for the bash script to finish before exiting)

Edit:

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)

Edit 2:

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

Related Questions