hi Pedro,

these are interesting questions. As for fetching the exit code from spawn,
SEC does that and produces a warning message if it is non-zero, but there
is no equivalent to bash $? variable. Firstly, command lines are executed
asynchronously by spawn, and secondly, many such command lines may be
running simultaneously. It is therefore difficult to tell which command's
exit code $? is currently holding. For addressing this issue, it is
probably best to write a simple wrapper script around the program that
needs execution, and pass the exit code value to SEC as a synthetic event
(just like program's standard output is passed). For example, the following
simple script creates "TEST_EXIT_<code>" synthetic event:

#!/bin/bash

/bin/false
echo TEST_EXIT_$?

However, running a child process from SEC for at most given number of
seconds is a trickier issue. Although the spawn action does not have
parameters for setting this timeout, this task can again be handled with a
wrapper script, and there is a relevant example in the SEC FAQ:
http://simple-evcorr.github.io/FAQ.html#20
The example wrapper is universal and works not only for spawn but also
other actions that fork child processes from SEC. Also, in addition to
limiting the run time of child processes, one can define a signal which is
used for terminating the child process.

However, since in your case you also need to capture the standard output
and exit code of the command that was started by spawn, I have modified the
example wrapper from FAQ a bit:

#!/usr/bin/perl -w
#
# wrapper.pl

if (scalar(@ARGV) < 3) { exit(1); }
$int = shift @ARGV;
$sig = shift @ARGV;

$cmd = join(" ", @ARGV);

$SIG{TERM} = sub { $term{$$} = 1; };

if (!pipe(READ, WRITE)) { exit(1); }

$pid = fork();

if ($pid == -1) {
   exit(1);
} elsif ($pid == 0) {
   $SIG{TERM} = 'DEFAULT';
   if (exists($term{$$})) { exit(0); }
   close(READ);
   if (!open(STDOUT, ">&WRITE")) { exit(1); }
   exec($cmd);
   exit(1);
} else {
   $SIG{TERM} = sub { kill TERM, $pid; exit(0); };
   if (exists($term{$$})) { kill TERM, $pid; exit(0); };
   $SIG{ALRM} = sub { kill $sig, $pid;
                      print "Command $cmd timed out\n";
                      exit(0); };
   alarm($int);
   close(WRITE);
   @lines = <READ>;
   chomp(@lines);
   waitpid($pid, 0);
   $exitcode = $? >> 8;
   print "Command $cmd output: ", join(" ", @lines), "\n";
   print "Command $cmd exit code: $exitcode\n";
   exit($exitcode);
}

The first parameter of the wrapper is timeout in seconds, the second
parameter the number of the signal which is sent to child process on
timeout expiration, and the rest of the parameters define the command line
to be executed. The wrapper forks the command line as a child process,
acting as an intermediary between SEC and command line. The wrapper sets a
timer for itself with the alarm() system call, delivering the signal to
child when the timer expires, and reporting child standard output and exit
code back to SEC if the child process finishes before timeout. Child
standard output is collected through a pipe and reported in two lines, with
the first line representing entire standard output and the second line the
exit code of the child. If SEC is shut down, the wrapper also forwards the
TERM signal received from SEC to child process, so that the command line
would not stay in the process table after SEC has finished.

Here is an example ruleset that utilizes the wrapper for starting command
lines with spawn, and collecting their outputs and exit codes:

type=Single
ptype=RegExp
pattern=start (\d+) (.+)
desc=allow child process to run for $1 seconds and terminate it with TERM
(15) signal
action=spawn ./wrapper.pl $1 15 $2

type=Single
ptype=RegExp2
pattern=Command (.+) output: (.*)\nCommand \1 exit code: (\d+)
desc=Catch the output and exit code of the child process
action=write - command $1, output $2, exit code $3

type=Single
ptype=RegExp
pattern=Command (.+) timed out
desc=Child process has timed out
action=write - command $1 has timed out

The first rule runs a command line via wrapper, e.g., if line "start 1
sleep 60" is provided to SEC, command line "sleep 60" is allowed to run for
1 second. Since this command line runs for 1 minute, it gets terminated
after 1 second with TERM signal (signal number 15), and wrapper generates
the following synthetic event "Command sleep 60 timed out". This event is
matched by the third rule which writes the string "command sleep 60 has
timed out" to standard output. On the other hand, if line "start 5
/bin/date" is provided to SEC, /bin/date is allowed to run for 5 seconds
which is more than enough for successful completion. Therefore, the wrapper
reports back output and exit code from /bin/date which are matched by the
second rule, and a string similar to the following gets written to standard
output:
"command /bin/date, output Tue Jul 30 00:44:16 EEST 2019, exit code 0"
(the second rule utilizes the RegExp2 pattern for matching two consecutive
synthetic events received from wrapper).

Finally, the wrapper doesn't have to be written in Perl, but any other
language could be used for putting it together.

Hopefully these examples are helpful.

kind regards,
risto


Kontakt Pedro Samarra (<julio.sama...@yandex.com>) kirjutas kuupƤeval E,
29. juuli 2019 kell 02:32:

> Hello,
>
> I was looking into spawn to get both the output of a command and it's
> exit code. Output is obvious, but exit code I cannot find documentation.
> I'm looking for the Bash equivalent of '$?'.
>
> Also, if the command hangs, is there a timeout setting that kills the
> command and sets a context for the fact that the timeout threshold was
> reached?
>
> Thanks.
> _______________________________________________
> Simple-evcorr-users mailing list
> Simple-evcorr-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/simple-evcorr-users
>
_______________________________________________
Simple-evcorr-users mailing list
Simple-evcorr-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/simple-evcorr-users

Reply via email to