7 triggerexec - Run a command and do various specified actions depending on what command does
11 triggerexec [I<EVENT> I<ACTION> [I<EVENT> I<ACTION> [...]]] [--] I<COMMAND> [I<ARGS>]
15 Run I<COMMAND> and execute specific actions depending on
18 Supported I<EVENT> events:
22 =item B<stdout>:I<PATTERN>
24 =item B<stderr>:I<PATTERN>
26 Match I<PATTERN> regex pattern to B<stdout>/B<stderr> line-wise.
30 Supported I<ACTION> actions:
36 Evaluate perl expression in triggerexec(1)'s own context.
38 B<$COMMAND_PID> is the I<COMMAND>'s PID.
39 B<$PARAM> is a hash ref containing event parameters,
40 for example B<$PARAM->{line}> is the text triggered the action - if applicable (B<stdout:>/B<stderr:> events).
44 =head1 IMPLEMENTATION STATUS
57 use constant
{ STAT_DEV
=>0, STAT_INODE
=>1, STAT_PERM
=>2, STAT_NLINKS
=>3, STAT_UID
=>4, STAT_GID
=>5, STAT_RDEV
=>6, STAT_SIZE
=>7, STAT_ATIME
=>8, STAT_MTIME
=>9, STAT_CTIME
=>10, STAT_BLOCKSIZE
=>11, STAT_BLOCKS
=>12, };
58 use Cwd qw
/getcwd realpath/;
61 use DateTime
::Format
::Strptime
;
62 use Encode qw
/decode encode decode_utf8 encode_utf8/;
64 use Fcntl qw
/:flock :seek F_GETFL F_SETFL O_NONBLOCK F_GETFD F_SETFD FD_CLOEXEC/;
66 use File
::Temp qw
/tempfile/;
67 use Getopt
::Long qw
/:config no_ignore_case no_bundling no_getopt_compat no_auto_abbrev require_order/;
68 use IPC
::Run qw
/run start finish/;
69 use List
::MoreUtils qw
/all any none/;
72 no if ($] >= 5.018), 'warnings' => 'experimental::smartmatch';
76 'help' => sub { pod2usage
(-exitval
=>0, -verbose
=>99); },
77 '<>' => sub { unshift @ARGV, @_[0]; die '!FINISH'; },
78 ) or pod2usage
(-exitval
=>2, -verbose
=>99);
82 while(my $arg = shift @ARGV)
89 while(my $param = shift @Param)
91 my $action = shift @Param;
92 push @Trigger, {event
=>$param, action
=>$action,};
99 if($action =~ /^perl:(.*)$/)
104 # else {} TODO validate action expressions at start
107 sub process_output_line
109 my $stream_name = shift;
112 for my $trig (@Trigger)
114 if($trig->{event
} =~ /^(stdout|stderr):(.*)/)
116 my $event_stream_name = $1;
118 if($event_stream_name eq $stream_name)
120 if($line =~ /$pattern/)
122 do_action
($trig->{action
}, {line
=>$line});
126 # else {} # TODO validate event expressions at start
132 my $buffer_ref = shift;
133 my $last_call = shift;
134 my $func_ref = shift;
135 my $args_ref = shift;
139 my $eolpos = index $$buffer_ref, "\n";
141 $eolpos++; # include the trailing linefeed
142 my $line = substr($$buffer_ref, 0, $eolpos);
143 $func_ref->(@
$args_ref, $line);
144 $$buffer_ref = substr($$buffer_ref, $eolpos);
146 if($last_call and $$buffer_ref ne '')
148 # process the last incomplete line, if any
149 $func_ref->(@
$args_ref, $$buffer_ref);
156 $stdout_buffer .= $chunk;
157 process_buffer
(\
$stdout_buffer, 0, \
&process_output_line
, ['stdout']);
164 $stderr_buffer .= $chunk;
165 process_buffer
(\
$stderr_buffer, 0, \
&process_output_line
, ['stderr']);
172 $ipc = start
[@ARGV], \
*STDIN
, \
&read_stdout
, \
&read_stderr
;
173 $COMMAND_PID = $ipc->{KIDS
}->[0]->{PID
};
177 process_buffer
(\
$stdout_buffer, 1, \
&process_output_line
, ['stdout']);
178 process_buffer
(\
$stderr_buffer, 1, \
&process_output_line
, ['stderr']);
180 $exitcode = WEXITSTATUS
($status);
181 $exitcode = 128 + WTERMSIG
($status) if WIFSIGNALED
($status);