diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent index 51005a3f66..0461b2ff47 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -33,6 +33,9 @@ use IO::Socket; use Sys::Syslog; use Time::Local; +use lib '/usr/lib/perl5'; +use PandoraFMS::Omnishell; + # Agent XML data my $Xml; @@ -43,7 +46,7 @@ my $Sem = undef; my $ThreadSem = undef; use constant AGENT_VERSION => '7.0NG.749'; -use constant AGENT_BUILD => '200930'; +use constant AGENT_BUILD => '201001'; # Agent log default file size maximum and instances use constant DEFAULT_MAX_LOG_SIZE => 600000; @@ -1058,8 +1061,6 @@ sub fix_directory ($) { return $dir . $char; } - - ################################################################################ # Sends a file to the server. ################################################################################ @@ -3079,8 +3080,11 @@ my $main_agent = -1; # base time to start eatch iteration with the same interval. my $iter_base_time = time(); $LogFileIdx = -1; + # Loop while (1) { + my $omnishell; + if (-e $Conf{'logfile'} && (stat($Conf{'logfile'}))[7] > $Conf{'logsize'}) { rotate_log(); } @@ -3095,15 +3099,17 @@ while (1) { # Check file collections check_collections () unless ($Conf{'debug'} eq '1'); - if ($Conf{'debug'} ne '1') { - # Check scheduled commands - my $omni = new PandoraFMS::Omnishell(\%Conf); - log_message('log', "Running omnishell process"); - if (!$omni->run()) { - log_message('error', "Failed to run omnishell process: ".$omni->get_last_error()); - } else { - log_message('log', "Omnishell process completed."); - } + eval { + # Omnishell controller. + $omnishell = new PandoraFMS::Omnishell( + { + %Conf, + 'ConfDir' => $ConfDir + } + ); + }; + if ($@) { + log_message('error', "Omnishell process error: ".$@); } # Launch broker agents @@ -3237,18 +3243,22 @@ while (1) { if (ref ($Conf{'commands'}) eq "HASH") { foreach my $command (keys %{$Conf{'commands'}}) { - my $result = evaluate_command($command); - if (ref($result) eq "HASH") { - # Process command result. - $Xml .= "\n"; - $Xml .= " \n"; - $Xml .= " {'name'}."]]>\n"; - $Xml .= " \n"; - $Xml .= " {'error_level'}."]]>\n"; - $Xml .= " {'stdout'}."]]>\n"; - $Xml .= " {'stderr'}."]]>\n"; - $Xml .= " \n"; - $Xml .= "\n"; + eval { + if ($Conf{'debug'} eq '1') { + log_message('debug', 'Running omnishell commmand ['.$command.']'); + } + + my $output = $omnishell->run($command, 'xml'); + if (!empty($output)) { + $Xml .= $output; + } else { + if ($Conf{'debug'} eq '1') { + log_message('error', 'Omnishell result: '.$omnishell->get_last_error()); + } + } + }; + if ($@) { + log_message('error', 'Omnishell error: '.$@); } } } diff --git a/pandora_server/lib/PandoraFMS/Omnishell.pm b/pandora_server/lib/PandoraFMS/Omnishell.pm index 3a46a81b49..223bf5bca9 100644 --- a/pandora_server/lib/PandoraFMS/Omnishell.pm +++ b/pandora_server/lib/PandoraFMS/Omnishell.pm @@ -8,15 +8,20 @@ package PandoraFMS::Omnishell; use strict; use warnings; +use File::Copy; +use Scalar::Util qw(looks_like_number); +use lib '/usr/lib/perl5'; +use PandoraFMS::PluginTools qw/init read_configuration read_file empty trim/; + my $YAML = 0; # Dynamic load. Avoid unwanted behaviour. eval { - eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available'); + eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available'); }; if ($@) { - $YAML = 0; + $YAML = 0; } else { - $YAML = 1; + $YAML = 1; } use lib '/usr/lib/perl5'; @@ -26,6 +31,137 @@ our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); +# 2 to the power of 32. +use constant POW232 => 2**32; + +################################################################################ +# Return the MD5 checksum of the given string as a hex string. +# Pseudocode from: http://en.wikipedia.org/wiki/MD5#Pseudocode +################################################################################ +my @S = ( + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 +); +my @K = ( + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +); +sub md5 { + my $str = shift; + + # No input! + if (!defined($str)) { + return ""; + } + + # Note: All variables are unsigned 32 bits and wrap modulo 2^32 when + # calculating. + + # Initialize variables. + my $h0 = 0x67452301; + my $h1 = 0xEFCDAB89; + my $h2 = 0x98BADCFE; + my $h3 = 0x10325476; + + # Pre-processing. + my $msg = unpack ("B*", pack ("A*", $str)); + my $bit_len = length ($msg); + + # Append "1" bit to message. + $msg .= '1'; + + # Append "0" bits until message length in bits ≡ 448 (mod 512). + $msg .= '0' while ((length ($msg) % 512) != 448); + + # Append bit /* bit, not byte */ length of unpadded message as 64-bit + # little-endian integer to message. + $msg .= unpack ("B32", pack ("V", $bit_len)); + $msg .= unpack ("B32", pack ("V", ($bit_len >> 16) >> 16)); + + # Process the message in successive 512-bit chunks. + for (my $i = 0; $i < length ($msg); $i += 512) { + + my @w; + my $chunk = substr ($msg, $i, 512); + + # Break chunk into sixteen 32-bit little-endian words w[i], 0 <= i <= + # 15. + for (my $j = 0; $j < length ($chunk); $j += 32) { + push (@w, unpack ("V", pack ("B32", substr ($chunk, $j, 32)))); + } + + # Initialize hash value for this chunk. + my $a = $h0; + my $b = $h1; + my $c = $h2; + my $d = $h3; + my $f; + my $g; + + # Main loop. + for (my $y = 0; $y < 64; $y++) { + if ($y <= 15) { + $f = $d ^ ($b & ($c ^ $d)); + $g = $y; + } + elsif ($y <= 31) { + $f = $c ^ ($d & ($b ^ $c)); + $g = (5 * $y + 1) % 16; + } + elsif ($y <= 47) { + $f = $b ^ $c ^ $d; + $g = (3 * $y + 5) % 16; + } + else { + $f = $c ^ ($b | (0xFFFFFFFF & (~ $d))); + $g = (7 * $y) % 16; + } + + my $temp = $d; + $d = $c; + $c = $b; + $b = ($b + leftrotate (($a + $f + $K[$y] + $w[$g]) % POW232, $S[$y])) % POW232; + $a = $temp; + } + + # Add this chunk's hash to result so far. + $h0 = ($h0 + $a) % POW232; + $h1 = ($h1 + $b) % POW232; + $h2 = ($h2 + $c) % POW232; + $h3 = ($h3 + $d) % POW232; + } + + # Digest := h0 append h1 append h2 append h3 #(expressed as little-endian) + return unpack ("H*", pack ("V", $h0)) . + unpack ("H*", pack ("V", $h1)) . + unpack ("H*", pack ("V", $h2)) . + unpack ("H*", pack ("V", $h3)); +} + +################################################################################ +# MD5 leftrotate function. See: http://en.wikipedia.org/wiki/MD5#Pseudocode +################################################################################ +sub leftrotate { + my ($x, $c) = @_; + + return (0xFFFFFFFF & ($x << $c)) | ($x >> (32 - $c)); +} ################################################################################ # return last error. @@ -33,46 +169,57 @@ our @EXPORT = qw(); sub get_last_error { my ($self) = @_; - if (!is_empty($self->{'last_error'})) { + if (!empty($self->{'last_error'})) { return $self->{'last_error'}; } return ''; } +################################################################################ +# Update last error. +################################################################################ +sub set_last_error { + my ($self, $error) = @_; + + $self->{'last_error'} = $error; +} + ################################################################################ # Try to load extra libraries.c ################################################################################ sub load_libraries { - my $self = shift; + my $self = shift; - # Dynamic load. Avoid unwanted behaviour. - eval {eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available');}; - if ($@) { - $self->set_last_error($@); - return 0; - } else { - return 1; - } + # Dynamic load. Avoid unwanted behaviour. + eval {eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available');}; + if ($@) { + $self->set_last_error($@); + return 0; + } else { + return 1; + } } ################################################################################ # Create new omnishell handler. ################################################################################ sub new { - my ($class,$args) = @_; + my ($class, $args) = @_; if (ref($args) ne 'HASH') { return undef; } + my $system = init(); my $self = { 'last_error' => undef, - %{$args} + %{$system}, + %{$args}, }; - $self = bless($self, $class); + $self->prepare_commands(); return $self; } @@ -81,11 +228,35 @@ sub new { # Run process. ################################################################################ sub run { - my ($self) = @_; + my ($self, $ref, $output_mode) = @_; - if($self->load_libraries()) { - $self->prepare_commands(); - } + if($self->load_libraries()) { + # Functionality possible. + my $command = $self->{'commands'}->{$ref}; + my $result = $self->evaluate_command($ref); + if (ref($result) eq "HASH") { + # Process command result. + if (defined($output_mode) && $output_mode eq 'xml') { + my $output = ''; + $output .= "\n"; + $output .= " \n"; + $output .= " {'name'}."]]>\n"; + $output .= " \n"; + $output .= " {'error_level'}."]]>\n"; + $output .= " {'stdout'}."]]>\n"; + $output .= " {'stderr'}."]]>\n"; + $output .= " \n"; + $output .= "\n"; + + return $output; + } + return $result; + } else { + $self->set_last_error('Failed to process ['.$ref.']: '.$result); + } + } + + return undef; } ################################################################################ @@ -94,127 +265,117 @@ sub run { sub prepare_commands { my ($self) = @_; - if ($YAML == 0) { - log_message( - 'error', - 'Cannot use commands without YAML dependency, please install it.' - ); - return; - } + if ($YAML == 0) { + $self->set_last_error('Cannot use commands without YAML dependency, please install it.'); + return; + } - # Force configuration file read. - my @commands = read_config('cmd_file'); + # Force configuration file read. + my $commands = $self->{'commands'}; - if (empty(\@commands)) { - $self->{'commands'} = {}; - } else { - foreach my $rcmd (@commands) { - $self->{'commands'}->{trim($rcmd)} = {}; - } - } + if (empty($commands)) { + $self->{'commands'} = {}; + } else { + foreach my $rcmd (keys %{$commands}) { + $self->{'commands'}->{trim($rcmd)} = {}; + } + } - # Cleanup old commands. Not registered. - cleanup_old_commands(); + # Cleanup old commands. Not registered. + $self->cleanup_old_commands(); - foreach my $ref (keys %{$self->{'commands'}}) { - my $file_content; - my $download = 0; - my $rcmd_file = $self->{'ConfDir'}.'/commands/'.$ref.'.rcmd'; + foreach my $ref (keys %{$self->{'commands'}}) { + my $file_content; + my $download = 0; + my $rcmd_file = $self->{'ConfDir'}.'/commands/'.$ref.'.rcmd'; - # Check for local .rcmd.done files - if (-e $rcmd_file.'.done') { - # Ignore. - delete $self->{'commands'}->{$ref}; - next; - } + # Search for local .rcmd file + if (-e $rcmd_file) { + my $remote_md5_file = $self->{'temporal'}.'/'.$ref.'.md5'; - # Search for local .rcmd file - if (-e $rcmd_file) { - my $remote_md5_file = $self->{'temporal'}.'/'.$ref.'.md5'; + $file_content = read_file($rcmd_file); + if ($self->recv_file($ref.'.md5', $remote_md5_file) != 0) { + # Remote file could not be retrieved, skip. + delete $self->{'commands'}->{$ref}; + next; + } - $file_content = read_file($rcmd_file); - if (recv_file($ref.'.md5', $remote_md5_file) != 0) { - # Remote file could not be retrieved, skip. - delete $self->{'commands'}->{$ref}; - next; - } + my $local_md5 = md5($file_content); + my $remote_md5 = md5(read_file($remote_md5_file)); - my $local_md5 = md5($file_content); - my $remote_md5 = md5(read_file($remote_md5_file)); + if ($local_md5 ne $remote_md5) { + # Must be downloaded again. + $download = 1; + } + } else { + $download = 1; + } + + # Search for remote .rcmd file + if ($download == 1) { + # Download .rcmd file + if ($self->recv_file($ref.'.rcmd') != 0) { + # Remote file could not be retrieved, skip. + delete $self->{'commands'}->{$ref}; + next; + } else { + # Success + move($self->{'temporal'}.'/'.$ref.'.rcmd', $rcmd_file); + } + } + + # Parse and prepare in memory skel. + eval { + $self->{'commands'}->{$ref} = YAML::Tiny->read($rcmd_file); + }; + if ($@) { + # Failed. + $self->set_last_error('Failed to decode command. ' . "\n".$@); + delete $self->{'commands'}->{$ref}; + next; + } - if ($local_md5 ne $remote_md5) { - # Must be downloaded again. - $download = 1; - } - } else { - $download = 1; - } - - # Search for remote .rcmd file - if ($download == 1) { - # Download .rcmd file - if (recv_file($ref.'.rcmd') != 0) { - # Remote file could not be retrieved, skip. - delete $self->{'commands'}->{$ref}; - next; - } else { - # Success - move($self->{'temporal'}.'/'.$ref.'.rcmd', $rcmd_file); - } - } - - # Parse and prepare in memory skel. - eval { - $self->{'commands'}->{$ref} = YAML::Tiny->read($rcmd_file); - }; - if ($@) { - # Failed. - log_message('error', 'Failed to decode command. ' . "\n".$@); - delete $self->{'commands'}->{$ref}; - next; - } - - } + } } ################################################################################ # Command report. ################################################################################ sub report_command { - my ($self, $ref, $err_level) = @_; + my ($self, $ref, $err_level) = @_; # Retrieve content from .stdout and .stderr - my $stdout_file = $self->{'temporal'}.'/'.$ref.'.stdout'; - my $stderr_file = $self->{'temporal'}.'/'.$ref.'.stderr'; + my $stdout_file = $self->{'temporal'}.'/'.$ref.'.stdout'; + my $stderr_file = $self->{'temporal'}.'/'.$ref.'.stderr'; - my $return; - eval { - $return = { - 'error_level' => $err_level, - 'stdout' => read_file($stdout_file), - 'stderr' => read_file($stderr_file), - }; + my $return; + eval { + $return = { + 'error_level' => $err_level, + 'stdout' => read_file($stdout_file), + 'stderr' => read_file($stderr_file), + }; - $return->{'name'} = $self->{'commands'}->{$ref}->[0]->{'name'}; - }; - if ($@) { - log_message('error', 'Failed to report command output. ' . $@); - } + $return->{'name'} = $self->{'commands'}->{$ref}->[0]->{'name'}; + }; + if ($@) { + $self->set_last_error('Failed to report command output. ' . $@); + } - # Cleanup - unlink($stdout_file) if (-e $stdout_file); - unlink($stderr_file) if (-e $stderr_file); + # Cleanup + unlink($stdout_file) if (-e $stdout_file); + unlink($stderr_file) if (-e $stderr_file); - # Mark command as done. - open (my $R_FILE, '> '.$self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done'); - print $R_FILE $err_level; - close($R_FILE); + # Mark command as done. + open (my $R_FILE, '> '.$self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done'); + print $R_FILE $err_level; + close($R_FILE); - $return->{'stdout'} = '' unless defined ($return->{'stdout'}); - $return->{'stderr'} = '' unless defined ($return->{'stderr'}); + $return->{'stdout'} = '' unless defined ($return->{'stdout'}); + $return->{'stderr'} = '' unless defined ($return->{'stderr'}); - return $return; + return $return; } ################################################################################ @@ -223,28 +384,28 @@ sub report_command { sub cleanup_old_commands { my ($self) = @_; - # Cleanup old .rcmd and .rcmd.done files. - my %registered = map { $_.'.rcmd' => 1 } keys %{$self->{'commands'}}; - if(opendir(my $dir, $self->{'ConfDir'}.'/commands/')) { - while (my $item = readdir($dir)) { + # Cleanup old .rcmd and .rcmd.done files. + my %registered = map { $_.'.rcmd' => 1 } keys %{$self->{'commands'}}; + if(opendir(my $dir, $self->{'ConfDir'}.'/commands/')) { + while (my $item = readdir($dir)) { - # Skip other files. - next if ($item !~ /\.rcmd$/); + # Skip other files. + next if ($item !~ /\.rcmd$/); - # Clean .rcmd.done file if its command is not referenced in conf. - if (!defined($registered{$item})) { - if (-e $self->{'ConfDir'}.'/commands/'.$item) { - unlink($self->{'ConfDir'}.'/commands/'.$item); - } - if (-e $self->{'ConfDir'}.'/commands/'.$item.'.done') { - unlink($self->{'ConfDir'}.'/commands/'.$item.'.done'); - } - } - } + # Clean .rcmd.done file if its command is not referenced in conf. + if (!defined($registered{$item})) { + if (-e $self->{'ConfDir'}.'/commands/'.$item) { + unlink($self->{'ConfDir'}.'/commands/'.$item); + } + if (-e $self->{'ConfDir'}.'/commands/'.$item.'.done') { + unlink($self->{'ConfDir'}.'/commands/'.$item.'.done'); + } + } + } - # Close dir. - closedir($dir); - } + # Close dir. + closedir($dir); + } } @@ -252,68 +413,68 @@ sub cleanup_old_commands { # Executes a command using defined timeout. ################################################################################ sub execute_command_timeout { - my ($cmd, $timeout) = @_; + my ($self, $cmd, $timeout) = @_; - if (!defined($timeout) - || !looks_like_number($timeout) - || $timeout <= 0 - ) { - `$cmd`; - return $?>>8; - } + if (!defined($timeout) + || !looks_like_number($timeout) + || $timeout <= 0 + ) { + `$cmd`; + return $?>>8; + } - my $remaining_timeout = $timeout; + my $remaining_timeout = $timeout; - my $RET; - my $output; + my $RET; + my $output; - my $pid = open ($RET, "-|"); - if (!defined($pid)) { - # Failed to fork. - log_message('error', '[command] Failed to fork.'); - return undef; - } - if ($pid == 0) { - # Child. - my $ret; - eval { - local $SIG{ALRM} = sub { die "timeout\n" }; - alarm $timeout; - `$cmd`; - alarm 0; - }; + my $pid = open ($RET, "-|"); + if (!defined($pid)) { + # Failed to fork. + $self->set_last_error('[command] Failed to fork.'); + return undef; + } + if ($pid == 0) { + # Child. + my $ret; + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + alarm $timeout; + `$cmd`; + alarm 0; + }; - my $result = ($?>>8); - return $result; + my $result = ($?>>8); + return $result; - # Exit child. - # Child finishes. - exit; + # Exit child. + # Child finishes. + exit; - } else { - # Parent waiting. - while( --$remaining_timeout > 0 ){ - if (wait == -1) { - last; - } - # Wait child up to timeout seconds. - sleep 1; - } - } + } else { + # Parent waiting. + while( --$remaining_timeout > 0 ){ + if (wait == -1) { + last; + } + # Wait child up to timeout seconds. + sleep 1; + } + } - if ($remaining_timeout > 0) { - # Retrieve output from child. - $output = do { local $/; <$RET> }; - $output = $output>>8; - } - else { - # Timeout expired. - return 124; - } + if ($remaining_timeout > 0) { + # Retrieve output from child. + $output = do { local $/; <$RET> }; + $output = $output>>8; + } + else { + # Timeout expired. + return 124; + } - close($RET); + close($RET); - return $output; + return $output; } ################################################################################ @@ -322,104 +483,183 @@ sub execute_command_timeout { # $std_files = ' >> /tmp/stdout 2>> /tmp/stderr ################################################################################ sub execute_command_block { - my ($commands, $std_files, $timeout, $retry) = @_; + my ($self, $commands, $std_files, $timeout, $retry) = @_; - return 0 unless defined($commands); + return 0 unless defined($commands); - my $retries = $retry; + my $retries = $retry; - $retries = 1 unless looks_like_number($retries) && $retries > 0; + $retries = 1 unless looks_like_number($retries) && $retries > 0; - my $err_level = 0; - $std_files = '' unless defined ($std_files); + my $err_level = 0; + $std_files = '' unless defined ($std_files); - if (ref($commands) ne "ARRAY") { - return 0 if $commands eq ''; + if (ref($commands) ne "ARRAY") { + return 0 if $commands eq ''; - do { - $err_level = execute_command_timeout( - "($commands) $std_files", - $timeout - ); + do { + $err_level = $self->execute_command_timeout( + "($commands) $std_files", + $timeout + ); - # Do not retry if success. - last if looks_like_number($err_level) && $err_level == 0; - } while ((--$retries) > 0); + # Do not retry if success. + last if looks_like_number($err_level) && $err_level == 0; + } while ((--$retries) > 0); - } else { - foreach my $comm (@{$commands}) { - next unless defined($comm); - $retries = $retry; - $retries = 1 unless looks_like_number($retries) && $retries > 0; + } else { + foreach my $comm (@{$commands}) { + next unless defined($comm); + $retries = $retry; + $retries = 1 unless looks_like_number($retries) && $retries > 0; - do { - $err_level = execute_command_timeout( - "($comm) $std_files", - $timeout - ); + do { + $err_level = $self->execute_command_timeout( + "($comm) $std_files", + $timeout + ); - # Do not retry if success. - $retries = 0 if looks_like_number($err_level) && $err_level == 0; + # Do not retry if success. + $retries = 0 if looks_like_number($err_level) && $err_level == 0; - } while ((--$retries) > 0); + } while ((--$retries) > 0); - # Do not continue evaluating block if failed. - last unless ($err_level == 0); - } - } + # Do not continue evaluating block if failed. + last unless ($err_level == 0); + } + } - return $err_level; + return $err_level; } ################################################################################ # Evalate given command. ################################################################################ sub evaluate_command { - my ($self, $ref) = @_; + my ($self, $ref) = @_; - # Not found. - return unless defined $self->{'commands'}->{$ref}; + # Not found. + return "undefined command" unless defined $self->{'commands'}->{$ref}; - # Already completed. - return if (-e $self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done'); + # Already completed. + return "already executed" if (-e $self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done'); - # [0] because how library works. - my $cmd = $self->{'commands'}->{$ref}->[0]; + # [0] because how library works. + my $cmd = $self->{'commands'}->{$ref}->[0]; - my $std_files = ' >> '.$self->{'temporal'}.'/'.$ref.'.stdout '; - $std_files .= ' 2>> '.$self->{'temporal'}.'/'.$ref.'.stderr '; + my $std_files = ' >> '.$self->{'temporal'}.'/'.$ref.'.stdout '; + $std_files .= ' 2>> '.$self->{'temporal'}.'/'.$ref.'.stderr '; - # Check preconditions - my $err_level; - - $err_level = execute_command_block( - $cmd->{'preconditions'}, - $std_files, - $cmd->{'timeout'} - ); + # Check preconditions + my $err_level; + + $err_level = $self->execute_command_block( + $cmd->{'preconditions'}, + $std_files, + $cmd->{'timeout'} + ); - # Precondition not satisfied. - return report_command($ref, $err_level) unless ($err_level == 0); + # Precondition not satisfied. + return $self->report_command($ref, $err_level) unless ($err_level == 0); - # Main run. - $err_level = execute_command_block( - $cmd->{'script'}, - $std_files, - $cmd->{'timeout'} - ); + # Main run. + $err_level = $self->execute_command_block( + $cmd->{'script'}, + $std_files, + $cmd->{'timeout'} + ); - # Script not success. - return report_command($ref, $err_level) unless ($err_level == 0); + # Script not success. + return $self->report_command($ref, $err_level) unless ($err_level == 0); - # Check postconditions - $err_level = execute_command_block( - $cmd->{'postconditions'}, - $std_files, - $cmd->{'timeout'} - ); + # Check postconditions + $err_level = $self->execute_command_block( + $cmd->{'postconditions'}, + $std_files, + $cmd->{'timeout'} + ); - # Return results. - return report_command($ref, $err_level); + # Return results. + return $self->report_command($ref, $err_level); } +################################################################################ +# File transference and imported methods +################################################################################ +################################################################################ +## Remove any trailing / from directory names. +################################################################################ +sub fix_directory ($) { + my $dir = shift; + + my $char = chop($dir); + return $dir if ($char eq '/'); + return $dir . $char; +} + +################################################################################ +# Receive a file from the server. +################################################################################ +sub recv_file { + my ($self, $file, $relative) = @_; + my $output; + + my $DevNull = $self->{'__system'}->{'devnull'}; + my $CmdSep = $self->{'__system'}->{'cmdsep'}; + + my $pid = fork(); + return 1 unless defined $pid; + + # Fix remote dir to some transfer mode + my $remote_dir = $self->{'server_path'}; + $remote_dir .= "/" . fix_directory($relative) if defined($relative); + + if ($pid == 0) { + # execute the transfer program by child process. + eval { + local $SIG{'ALRM'} = sub {die}; + alarm ($self->{'transfer_timeout'}); + if ($self->{'transfer_mode'} eq 'tentacle') { + $output = `cd "$self->{'temporal'}"$CmdSep tentacle_client -v -g -a $self->{'server_ip'} -p $self->{'server_port'} $self->{'server_opts'} $file 2>&1 >$DevNull` + } elsif ($self->{'transfer_mode'} eq 'ssh') { + $output = `scp -P $self->{'server_port'} pandora@"$self->{'server_ip'}:$self->{'server_path'}/$file" $self->{'temporal'} 2>&1 >$DevNull`; + } elsif ($self->{'transfer_mode'} eq 'ftp') { + my $base = basename ($file); + my $dir = dirname ($file); + + $output = `ftp -n $self->{'server_opts'} $self->{'server_ip'} $self->{'server_port'} 2>&1 >$DevNull <{'server_user'} + quote PASS $self->{'server_pwd'} + lcd "$self->{'temporal'}" + cd "$self->{'server_path'}" + get "$file" + quit + FEOF1` + } elsif ($self->{'transfer_mode'} eq 'local') { + $output = `cp "$remote_dir/$file" $self->{'temporal'} 2>&1 >$DevNull`; + } + alarm (0); + }; + + if ($@) { + $self->set_last_error("Error retrieving file: '.$file.' File transfer command is not responding."); + exit 1; + } + + # Get the errorlevel + my $rc = $? >> 8; + if ($rc != 0) { + $self->set_last_error("Error retrieving file: '$file' $output"); + } + exit $rc; + } + + # Wait the child process termination and get the errorlevel + waitpid ($pid, 0); + my $rc = $? >> 8; + + return $rc; +} + + 1; \ No newline at end of file diff --git a/pandora_server/lib/PandoraFMS/PluginTools.pm b/pandora_server/lib/PandoraFMS/PluginTools.pm index bbf419afe1..b3584c8762 100644 --- a/pandora_server/lib/PandoraFMS/PluginTools.pm +++ b/pandora_server/lib/PandoraFMS/PluginTools.pm @@ -1165,6 +1165,7 @@ sub init_system { $system{echo} = "echo"; $system{wcl} = "wc -l"; $system{tmp} = ".\\"; + $system{cmdsep} = "\&"; } else { $system{devnull} = "/dev/null"; @@ -1175,6 +1176,7 @@ sub init_system { $system{echo} = "echo"; $system{wcl} = "wc -l"; $system{tmp} = "/tmp"; + $system{cmdsep} = ";"; if ($^O =~ /hpux/i) { $system{os} = "HPUX";