shell bypass 403
package Ocsinventory::Agent::Backend;
use strict;
no strict 'refs';
use warnings;
#use ExtUtils::Installed;
sub new {
my (undef, $params) = @_;
my $self = {};
$self->{accountconfig} = $params->{context}->{accountconfig};
$self->{accountinfo} = $params->{context}->{accountinfo};
$self->{config} = $params->{context}->{config};
$self->{inventory} = $params->{inventory};
$self->{common} = $params->{context}->{common};
my $logger = $self->{logger} = $params->{context}->{logger};
$self->{prologresp} = $params->{prologresp};
$self->{modules} = {};
$self->{backendSharedFuncs} = {
can_run => sub {
my $binary = shift;
my $calling_namespace = caller(0);
chomp(my $binpath=`which $binary 2>/dev/null`);
return unless -x $binpath;
$self->{logger}->debug(" - $binary found");
1
},
can_load => sub {
my $module = shift;
my $calling_namespace = caller(0);
eval "package $calling_namespace; use $module;";
# print STDERR "$module not loaded in $calling_namespace! $!: $@\n" if $@;
return if $@;
$self->{logger}->debug(" - $module loaded");
# print STDERR "$module loaded in $calling_namespace!\n";
1;
},
can_read => sub {
my $file = shift;
return unless -r $file;
$self->{logger}->debug(" - $file can be read");
1;
},
runcmd => sub {
my $cmd = shift;
return unless $cmd;
# $self->{logger}->debug(" - run $cmd");
return `$cmd`;
}
};
bless $self;
}
sub initModList {
my $self = shift;
my $logger = $self->{logger};
my $config = $self->{config};
my @dirToScan;
my @installed_mods;
my @installed_files;
# This is a workaround for PAR::Packer. Since it resets @INC
# I can't find the backend modules to load dynamically. So
# I prepare a list and include it.
eval "use Ocsinventory::Agent::Backend::ModuleToLoad;";
if (!$@) {
$logger->debug("use Ocsinventory::Agent::Backend::ModuleToLoad to get the modules ".
"to load. This should not append unless you use the standalone agent built with ".
"PAR::Packer (pp)");
push @installed_mods, @Ocsinventory::Agent::Backend::ModuleToLoad::list;
}
if ($config->{devlib}) {
# devlib enable, I only search for backend module in ./lib
push (@dirToScan, './lib');
} else {
# my ($inst) = ExtUtils::Installed->new();
# eval {@installed_files =
# $inst->files('Ocsinventory')};
# ExtUtils::Installed is nice but it needs properly installed package with
# .packlist
# This is a workaround for 'invalide' installations...
foreach (@INC) {
next if ! -d || (-l && -d readlink) || /^(\.|lib)$/;
push @dirToScan, $_;
}
}
if (@dirToScan) {
eval {require File::Find};
if ($@) {
$logger->debug("Failed to load File::Find");
} else {
# here I need to use $d to avoid a bug with AIX 5.2's perl 5.8.0. It
# changes the @INC content if i use $_ directly
# thanks to @rgs on irc.perl.org
File::Find::find(
{
wanted => sub {
push @installed_files, $File::Find::name if $File::Find::name =~ /Ocsinventory\/Agent\/Backend\/.*\.pm$/;
},
follow => 1,
follow_skip => 2
}
, @dirToScan);
}
}
foreach my $file (@installed_files) {
my $t = $file;
next unless $t =~ s!.*?(Ocsinventory/Agent/Backend/)(.*?)\.pm$!$1$2!;
my $m = join ('::', split /\//, $t);
push @installed_mods, $m;
}
if (!@installed_mods) {
$logger->info("ZERO backend module found! Is Ocsinventory-Agent ".
"correctly installed? Use the --devlib flag if you want to run the agent ".
"directly from the source directory.")
}
foreach my $m (@installed_mods) {
my @runAfter;
my @runMeIfTheseChecksFailed;
my $enable = 1;
if (exists ($self->{modules}->{$m}->{name})) {
$logger->debug($m." already loaded.");
next;
}
eval "use $m;";
if ($@) {
$logger->debug ("Failed to load $m: $@");
$enable = 0;
}
my $package = $m."::";
# Load in the module the backendSharedFuncs
# foreach my $func (keys %{$self->{backendSharedFuncs}}) {
# $package->{$func} = $self->{backendSharedFuncs}->{$func};
# }
$self->{modules}->{$m}->{name} = $m;
$self->{modules}->{$m}->{done} = 0;
$self->{modules}->{$m}->{inUse} = 0;
$self->{modules}->{$m}->{enable} = $enable;
$self->{modules}->{$m}->{checkFunc} = $package->{"check"};
$self->{modules}->{$m}->{runAfter} = $package->{'runAfter'};
$self->{modules}->{$m}->{runMeIfTheseChecksFailed} = $package->{'runMeIfTheseChecksFailed'};
# $self->{modules}->{$m}->{replace} = \@replace;
$self->{modules}->{$m}->{runFunc} = $package->{'run'};
$self->{modules}->{$m}->{mem} = {};
# Load the Storable object is existing or return undef
$self->{modules}->{$m}->{storage} = $self->retrieveStorage($m);
}
# the sort is just for the presentation
foreach my $m (sort keys %{$self->{modules}}) {
next unless $self->{modules}->{$m}->{checkFunc};
# find modules to disable and their submodules
if($self->{modules}->{$m}->{enable} &&
!$self->runWithTimeout(
$m,
$self->{modules}->{$m}->{checkFunc},
{
accountconfig => $self->{accountconfig},
accountinfo => $self->{accountinfo},
config => $self->{config},
inventory => $self->{inventory},
logger => $self->{logger},
params => $self->{params}, #Â Compatibiliy with agent 0.0.10 <=
prologresp => $self->{prologresp},
mem => $self->{modules}->{$m}->{mem},
storage => $self->{modules}->{$m}->{storage},
common => $self->{common},
})) {
$logger->debug ($m." ignored");
foreach (keys %{$self->{modules}}) {
$self->{modules}->{$_}->{enable} = 0 if /^$m($|::)/;
}
}
# add submodule in the runAfter array
my $t;
foreach (split /::/,$m) {
$t .= "::" if $t;
$t .= $_;
if (exists $self->{modules}->{$t} && $m ne $t) {
push @{$self->{modules}->{$m}->{runAfter}}, \%{$self->{modules}->{$t}}
}
}
}
# Remove the runMeIfTheseChecksFailed if needed
foreach my $m (sort keys %{$self->{modules}}) {
next unless $self->{modules}->{$m}->{enable};
next unless $self->{modules}->{$m}->{runMeIfTheseChecksFailed};
foreach my $condmod (@{${$self->{modules}->{$m}->{runMeIfTheseChecksFailed}}}) {
if ($self->{modules}->{$condmod}->{enable}) {
foreach (keys %{$self->{modules}}) {
next unless /^$m($|::)/ && $self->{modules}->{$_}->{enable};
$self->{modules}->{$_}->{enable} = 0;
$logger->debug ("$_ disabled because of a 'runMeIfTheseChecksFailed' in '$m'\n");
}
}
}
}
}
sub runMod {
my ($self, $params) = @_;
my $logger = $self->{logger};
my $m = $params->{modname};
my $common = $params->{common};
return if (!$self->{modules}->{$m}->{enable});
return if ($self->{modules}->{$m}->{done});
$self->{modules}->{$m}->{inUse} = 1; # lock the module
# first I run its "runAfter"
foreach (@{$self->{modules}->{$m}->{runAfter}}) {
if (!$_->{name}) {
# The name is defined during module initialisation so if I
# can't read it, I can suppose it had not been initialised.
$logger->fault ("Module `$m' need to be runAfter a module not found.".
"Please fix its runAfter entry or add the module.");
}
if ($_->{inUse}) {
# In use 'lock' is taken during the mod execution. If a module
# need a module also in use, we have provable an issue :).
$logger->fault ("Circular dependency hell with $m and $_->{name}");
}
$self->runMod({
common => $common,
modname => $_->{name},
});
}
$logger->debug ("Running $m");
if ($self->{modules}->{$m}->{runFunc}) {
$self->runWithTimeout(
$m,
$self->{modules}->{$m}->{runFunc},
{
accountconfig => $self->{accountconfig},
accountinfo => $self->{accountinfo},
config => $self->{config},
common => $common,
logger => $logger,
params => $self->{params}, #Â For compat with agent 0.0.10 <=
prologresp => $self->{prologresp},
mem => $self->{modules}->{$m}->{mem},
storage => $self->{modules}->{$m}->{storage},
common => $self->{common},
}
);
} else {
$logger->debug("$m has no run() function -> ignored");
}
$self->{modules}->{$m}->{done} = 1;
$self->{modules}->{$m}->{inUse} = 0; # unlock the module
$self->saveStorage($m, $self->{modules}->{$m}->{storage});
}
sub feedInventory {
my ($self, $params) = @_;
my $common = $self->{common};
my $inventory;
if ($params->{inventory}) {
$inventory = $params->{inventory};
}
if (!keys %{$self->{modules}}) {
$self->initModList();
}
my $begin = time();
foreach my $m (sort keys %{$self->{modules}}) {
die ">$m Houston!!!" unless $m;
$self->runMod ({
common => $common,
modname => $m,
});
}
# Execution time
# $common->setHardware({ETIME => time() - $begin});
$inventory->{xmlroot}->{CONTENT} = $common->{xmltags};
}
sub retrieveStorage {
my ($self, $m) = @_;
my $logger = $self->{logger};
my $storagefile = $self->{config}->{vardir}."/$m.storage";
if (!exists &retrieve) {
eval "use Storable;";
if ($@) {
$logger->debug("Storable.pm is not available, can't load Backend module data");
return;
}
}
return (-f $storagefile)?retrieve($storagefile):{};
}
sub saveStorage {
my ($self, $m, $data) = @_;
my $logger = $self->{logger};
# Perl 5.6 doesn't provide Storable.pm
if (!exists &store) {
eval "use Storable;";
if ($@) {
$logger->debug("Storable.pm is not available, can't save Backend module data");
return;
}
}
my $storagefile = $self->{config}->{vardir}."/$m.storage";
if ($data && keys (%$data)>0) {
store ($data, $storagefile) or die;
} elsif (-f $storagefile) {
unlink $storagefile;
}
}
sub runWithTimeout {
my ($self, $m, $func, $params) = @_;
my $logger = $self->{logger};
my $ret;
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n require
my $timeout = $params->{config}{backendCollectTimeout};
alarm $timeout;
$ret = &{$func}($params);
};
alarm 0;
if ($@) {
if ($@ ne "alarm\n") {
$logger->debug("runWithTimeout(): unexpected error: $@");
} else {
$logger->debug("$m killed by a timeout.");
return;
}
} else {
return $ret;
}
}
1;