the-bastion/lib/perl/OVH/Bastion/execute.inc

391 lines
15 KiB
PHP
Raw Normal View History

2020-10-16 00:32:37 +08:00
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
package OVH::Bastion;
use common::sense;
use IO::Handle;
use IPC::Open3;
use Symbol 'gensym';
use IO::Select;
use POSIX ":sys_wait_h";
use JSON;
use Config;
# Get signal names, i.e. signal 9 is SIGKILL, etc.
my %signum2string;
@signum2string{split ' ', $Config{sig_num}} = map { "SIG$_" } split ' ', $Config{sig_name};
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
sub sysret2human {
2020-10-16 00:32:37 +08:00
my $sysret = shift;
if ($sysret == -1) {
return R('OK', msg => "error: failed to execute ($!)");
}
elsif ($sysret & 127) {
my $signal = $sysret & 127;
my $coredump = $sysret & 128;
return R(
'OK',
value => {
coredump => $coredump ? \1 : \0,
signal => $signum2string{$signal} || $signal,
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
status => undef,
2020-10-16 00:32:37 +08:00
},
msg => sprintf("signal %d (%s)%s", $signal, $signum2string{$signal}, $coredump ? ' and coredump' : '')
);
}
else {
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
return R('OK', value => {coredump => \0, signal => undef, status => $sysret >> 8}, msg => sprintf("status %d", $sysret >> 8));
2020-10-16 00:32:37 +08:00
}
}
## no critic(ControlStructures::ProhibitDeepNests)
2020-10-16 00:32:37 +08:00
sub execute {
my %params = @_;
my $cmd = $params{'cmd'}; # command to execute, must be an array ref (with possible parameters)
my $expects_stdin = $params{'expects_stdin'}; # the command called expects stdin, pipe caller stdin to it
my $noisy_stdout = $params{'noisy_stdout'}; # capture stdout but print it too
my $noisy_stderr = $params{'noisy_stderr'}; # capture stderr but print it too
my $is_helper = $params{'is_helper'}; # hide JSON returns from stdout even if noisy_stdout
my $is_binary = $params{'is_binary'}; # used for e.g. scp, don't bother mimicking readline(), we lose debug and stdout/stderr are NOT returned to caller
my $stdin_str = $params{'stdin_str'}; # string to push to the STDIN of the command
my $must_succeed = $params{'must_succeed'}; # if the executed command returns a non-zero exit value, turn OK_NON_ZERO_EXIT to ERR_NON_ZERO_EXIT
my $max_stdout_bytes = $params{'max_stdout_bytes'}; # if the amount of stored stdout bytes exceeds this, halt the command and return to caller
my $system = $params{'system'}; # if set to 1, will use system() instead of open3(), needed for some plugins
$noisy_stderr = $noisy_stdout = 1 if ($ENV{'PLUGIN_DEBUG'} or $is_binary);
my $readsize = $is_binary ? 16384 : 1; # XXX needs to be enhanced to be > 1 even for non-binary
my $fnret;
=cut only to debug slow calls
if (not $is_binary)
{
require Carp;
open(SLOW, '>>', '/dev/shm/slowexecute');
print SLOW Carp::longmess(join('^',@$cmd))."\n\n";
close(SLOW);
}
=cut
require Scalar::Util;
foreach (@$cmd) {
if (Scalar::Util::tainted($_) && /(.+)/) {
2020-10-16 00:32:37 +08:00
# to be able to warn under -T; untaint it. we're going to crash right after anyway.
require Carp;
warn(Carp::longmess("would exec <" . join('^', @$cmd) . "> but param '$1' is tainted!"));
2020-10-16 00:32:37 +08:00
}
}
if ($system) {
my $child_exit_status = system(@$cmd);
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
$fnret = sysret2human($child_exit_status);
2020-10-16 00:32:37 +08:00
return R(
$child_exit_status == 0 ? 'OK' : ($must_succeed ? 'ERR_NON_ZERO_EXIT' : 'OK_NON_ZERO_EXIT'),
2020-10-16 00:32:37 +08:00
value => {
sysret => $child_exit_status + 0,
status => $fnret->value->{'status'},
coredump => $fnret->value->{'coredump'},
signal => $fnret->value->{'signal'},
},
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
msg => "Command exited with " . sysret2human($child_exit_status)->msg,
2020-10-16 00:32:37 +08:00
);
}
my ($child_stdin, $child_stdout, $child_stderr);
$child_stderr = gensym;
osh_debug("about to run_cmd ['" . join("','", @$cmd) . "']");
my $pid;
eval { $pid = open3($child_stdin, $child_stdout, $child_stderr, @$cmd); };
if ($@) {
chomp $@;
return R('ERR_EXEC_FAILED', msg => "Couldn't exec requested command ($@)");
}
osh_debug("waiting for child PID $pid to complete...");
my %output = ();
my %lineBuffer;
my $currentActive = undef;
my $currently_in_json_block = 0;
my %bytesnb;
# always monitor our child stdout and stderr
my $select = IO::Select->new($child_stdout, $child_stderr);
binmode $child_stdin;
binmode $child_stdout;
binmode $child_stderr;
# if some fd are closed, binmode may fail
eval { binmode STDIN; };
eval { binmode STDOUT; };
eval { binmode STDERR; };
if ($stdin_str) {
# we have some stdin data to push, do it now
syswrite $child_stdin, $stdin_str;
close($child_stdin);
}
elsif ($expects_stdin) {
# ... and also monitor our own stdin only if we expect it (we'll pipe it to our child's stdin)
$select->add(\*STDIN);
}
# then, while we still have fh to monitor
while ($select->count() > 1 || ($select->count() == 1 && !$select->exists(\*STDIN))) {
2020-10-16 00:32:37 +08:00
# block only for 50ms, before checking if child is dead
my @ready = $select->can_read(0.05);
# yep, we have something to read on at least one fh
if (@ready) {
# guarantee we're still reading this fh while it has something to say
$currentActive = $ready[0];
my $subSelect = IO::Select->new($currentActive);
# can_read(0) because we don't need a timeout: we KNOW there's something to read on this fh
while ($subSelect->can_read(0)) {
my $buffer;
my $nbread = sysread $currentActive, $buffer, $readsize;
# if size 0, it means it's an EOF, if undef, it's an error
if (not $nbread) {
# error, we'll warn and close
if (not defined $nbread) {
# awwww, not cool at all
warn("execute(): error while sysreading($!), closing fh!");
}
# we got an EOF on this fh, remove it from the monitor list
$select->remove($currentActive);
# if this is an EOF on our own STDIN, we need to close our child's STDIN
if ($currentActive->fileno == STDIN->fileno) {
close(STDIN); # we got eof on it, so close it
close($child_stdin); # and close our child stdin
}
else {
; # eof on our child's stdout or stderr, nothing to do
}
last;
}
# we got data, is this our child's stderr ?
if ($currentActive->fileno == $child_stderr->fileno) {
$bytesnb{'stderr'} += $nbread;
# syswrite on our own STDERR what we received
if ($noisy_stderr) {
my $offset = 0;
while ($offset < $nbread) {
my $written = syswrite STDERR, $buffer, $readsize, $offset;
if (not defined $written) {
# oww, abort writing for this cycle
warn("execute(): error while syswriting($!) on stderr, aborting this cycle");
last;
}
$offset += $written;
}
}
# mimic line-based reading (for debug, and also data will be returned to caller)
if (not $is_binary) {
# if this is a newline, push it to our output array
if ($buffer eq $/) {
osh_debug("stderr($pid): " . $lineBuffer{'stderr'}) unless $noisy_stderr; # avoid double print
push @{$output{'stderr'}}, $lineBuffer{'stderr'};
$lineBuffer{'stderr'} = '';
}
# or push it to our temp line buffer
else {
$lineBuffer{'stderr'} .= $buffer;
}
}
}
# we got data, is this our child's stdout ?
elsif ($currentActive->fileno == $child_stdout->fileno) {
$bytesnb{'stdout'} += $nbread;
# syswrite on our own STDOUT what we received
if ($noisy_stdout and not $is_helper) {
# the "if is_helper" case is handled below per-line
my $offset = 0;
while ($offset < $nbread) {
my $written = syswrite STDOUT, $buffer, $readsize, $offset;
if (not defined $written) {
# oww, abort writing for this cycle
warn("execute(): error while syswriting($!) on stdout, aborting this cycle");
last;
}
$offset += $written;
}
}
# mimic line-based reading (for debug, and also data will be returned to caller)
if (not $is_binary) {
if ($buffer eq $/) {
osh_debug("stdout($pid): " . $lineBuffer{'stdout'}) unless $noisy_stdout; # avoid double print
push @{$output{'stdout'}}, $lineBuffer{'stdout'};
if ($noisy_stdout and $is_helper) {
# in that case, we didn't noisy print each char, we wait for $/
# then print it IF this is not the result_from_helper (json)
if ($lineBuffer{'stdout'} eq 'JSON_START') {
$currently_in_json_block = 1;
}
if (not $currently_in_json_block) {
print $lineBuffer{'stdout'} . $/;
}
if ($currently_in_json_block and $lineBuffer{'stdout'} eq 'JSON_END') {
$currently_in_json_block = 0;
}
}
$lineBuffer{'stdout'} = '';
}
else {
$lineBuffer{'stdout'} .= $buffer;
}
}
if ($max_stdout_bytes && $bytesnb{'stdout'} >= $max_stdout_bytes) {
# caller got enough data, close all our child channels
$select->remove($child_stdout);
$select->remove($child_stderr);
close($child_stdin);
close($child_stdout);
close($child_stderr);
# and also our own STDIN if we're listening for it
if ($select->exists(\*STDIN)) {
$select->remove(\*STDIN);
close(STDIN);
}
# don't forget to push any pending data to our output buffer
push @{$output{'stdout'}}, $lineBuffer{'stdout'};
}
}
# we got data, is this our stdin ?
elsif ($currentActive->fileno == STDIN->fileno) {
$bytesnb{'stdin'} += $nbread;
# we just write the data to our child's own stdin
syswrite $child_stdin, $buffer;
}
# wow, we got data from an unknown fh ... it's not possible
else {
# ... but just in case:
require Data::Dumper;
osh_warn("unknown fh: " . Data::Dumper::Dumper($currentActive) . " with char <$buffer>");
osh_warn(Data::Dumper::Dumper($child_stdout));
osh_warn(Data::Dumper::Dumper($child_stderr));
osh_warn(Data::Dumper::Dumper(\*STDIN));
}
}
# /guarantee
}
}
# here, all fd went EOF (except maybe STDIN but we don't care)
# so we need to waitpid
# (might be blocking, but we have nothing to read/write anyway)
osh_debug("all fds are EOF, waiting for pid $pid indefinitely");
waitpid($pid, 0);
my $child_exit_status = $?;
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
$fnret = sysret2human($child_exit_status);
2020-10-16 00:32:37 +08:00
osh_debug("cmd returned with " . $fnret->msg);
return R(
$fnret->value->{'status'} == 0 ? 'OK' : ($must_succeed ? 'ERR_NON_ZERO_EXIT' : 'OK_NON_ZERO_EXIT'),
2020-10-16 00:32:37 +08:00
value => {
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
sysret => $child_exit_status >> 8,
sysret_raw => $child_exit_status,
stdout => $output{stdout},
stderr => $output{stderr},
bytesnb => \%bytesnb,
status => $fnret->value->{'status'},
coredump => $fnret->value->{'coredump'},
signal => $fnret->value->{'signal'},
2020-10-16 00:32:37 +08:00
},
feat: revamp logs All connections and plugin executions emit two logs, an 'open' and a 'close' log. We now add all the details of the connection to the 'close' logs, those that were previously only available in the corresponding 'open' log. This way, it is no longer required to correlate both logs with their uniqid to have all the data: the 'close' log should suffice. The 'open' log is still there if for some reason the 'close' log can't be emitted (kill -9, system crash, etc.), or if the 'open' and the 'close' log are several hours, days or months appart. An additional field "duration" has been added to the 'close' logs, this represents the number of seconds (with millisecond precision) the connection lasted. Two new fields "globalsql" and "accountsql" have been added to the 'open'-type logs. These will contain either "ok" if we successfully logged to the corresponding log database, "no" if it is disabled, or "error $aDetailedMessage" if we got an error trying to insert the row. The 'close'-type log also has the new "accountsql_close" field, but misses the "globalsql_close" field as we never update the global database on this event. On the 'close' log, we can also have the value "missing", indicating that we couldn't update the access log row in the database, as the corresponding 'open' log couldn't insert it. The "ttyrecsize" log field for the 'close'-type logs has been removed, as it was never completely implemented, and contains bogus data if ttyrec log rotation occurs. It has also been removed from the sqlite log databases. The 'open' and 'close' events are now pushed to our own log files, in addition to syslog, if logging to those files is enabled (see ``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously the 'close' events were only pushed to syslog. The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the global log is instead written to /home/logkeeper/global-log-YYYYMM.log. The global sql file, enabled with ``enableGlobalSqlLog``, is now split by year-month instead of by year, to /home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
msg => "Command exited with " . sysret2human($child_exit_status)->msg,
2020-10-16 00:32:37 +08:00
);
}
sub result_from_helper {
my $input = shift;
if (ref $input ne 'ARRAY') {
$input = [$input];
}
my $state = 1;
my @json;
foreach my $line (@$input) {
chomp;
if ($state == 1) {
if ($line eq 'JSON_START') {
# will now capture data
@json = ();
$state = 2;
}
}
elsif ($state == 2) {
if ($line eq 'JSON_END') {
# done capturing data, might still see a new JSON_START however
$state = 1;
}
else {
# capturing data
push @json, $line;
}
}
}
if (not @json) {
return R('ERR_HELPER_RETURN_EMPTY', msg => "The helper didn't return any data, maybe it crashed, please report to your sysadmin!");
}
my $json_decoded;
eval { $json_decoded = decode_json(join("\n", @json)); };
if ($@) {
return R('ERR_HELPER_RETURN_INVALID', msg => $@);
}
return R('OK', value => $json_decoded);
}
sub helper_decapsulate {
my $value = shift;
return R($value->{'error_code'}, value => $value->{'value'}, msg => $value->{'error_message'});
}
sub helper {
my %params = @_;
my @command = @{$params{'cmd'} || []};
my $expects_stdin = $params{'expects_stdin'};
my $stdin_str = $params{'stdin_str'};
my $fnret = OVH::Bastion::execute(cmd => \@command, noisy_stdout => 1, noisy_stderr => 1, is_helper => 1, expects_stdin => $expects_stdin, stdin_str => $stdin_str);
$fnret or return R('ERR_HELPER_FAILED', "something went wrong in helper script (" . $fnret->msg . ")");
$fnret = OVH::Bastion::result_from_helper($fnret->value->{'stdout'});
$fnret or return $fnret;
return OVH::Bastion::helper_decapsulate($fnret->value);
}
1;