source: ocsinventory-agent/trunk/fuentes/lib/Ocsinventory/Agent/Backend.pm @ 468

Last change on this file since 468 was 468, checked in by mabarracus, 5 years ago

Copyt trusty code

File size: 10.6 KB
Line 
1package Ocsinventory::Agent::Backend;
2
3use strict;
4no strict 'refs';
5use warnings;
6
7#use ExtUtils::Installed;
8
9sub new {
10  my (undef, $params) = @_;
11
12  my $self = {};
13
14  $self->{accountconfig} = $params->{context}->{accountconfig};
15  $self->{accountinfo} = $params->{context}->{accountinfo};
16  $self->{config} = $params->{context}->{config};
17  $self->{inventory} = $params->{inventory};
18
19  $self->{common} = $params->{context}->{common};
20
21  my $logger = $self->{logger} = $params->{context}->{logger};
22  $self->{prologresp} = $params->{prologresp};
23
24  $self->{modules} = {};
25
26  $self->{backendSharedFuncs} = {
27
28    can_run => sub {
29      my $binary = shift;
30
31      my $calling_namespace = caller(0);
32      chomp(my $binpath=`which $binary 2>/dev/null`);
33      return unless -x $binpath;
34      $self->{logger}->debug(" - $binary found");
35      1
36    },
37    can_load => sub {
38      my $module = shift;
39
40      my $calling_namespace = caller(0);
41      eval "package $calling_namespace; use $module;";
42#      print STDERR "$module not loaded in $calling_namespace! $!: $@\n" if $@;
43      return if $@;
44      $self->{logger}->debug(" - $module loaded");
45#      print STDERR "$module loaded in $calling_namespace!\n";
46      1;
47    },
48    can_read => sub {
49      my $file = shift;
50      return unless -r $file;
51      $self->{logger}->debug(" - $file can be read");
52      1;
53    },
54    runcmd => sub {
55      my $cmd = shift;
56      return unless $cmd;
57
58      # $self->{logger}->debug(" - run $cmd");
59
60      return `$cmd`;
61    }
62  };
63
64
65  bless $self;
66
67}
68
69sub initModList {
70  my $self = shift;
71
72  my $logger = $self->{logger};
73  my $config = $self->{config};
74
75  my @dirToScan;
76  my @installed_mods;
77  my @installed_files;
78
79  # This is a workaround for PAR::Packer. Since it resets @INC
80  # I can't find the backend modules to load dynamically. So
81  # I prepare a list and include it.
82  eval "use Ocsinventory::Agent::Backend::ModuleToLoad;";
83  if (!$@) {
84    $logger->debug("use Ocsinventory::Agent::Backend::ModuleToLoad to get the modules ".
85      "to load. This should not append unless you use the standalone agent built with ".
86      "PAR::Packer (pp)");
87    push @installed_mods, @Ocsinventory::Agent::Backend::ModuleToLoad::list;
88  }
89
90  if ($config->{devlib}) {
91  # devlib enable, I only search for backend module in ./lib
92    push (@dirToScan, './lib');
93  } else {
94  #  my ($inst) = ExtUtils::Installed->new();
95
96  #  eval {@installed_files =
97  #    $inst->files('Ocsinventory')};
98
99# ExtUtils::Installed is nice but it needs properly installed package with
100# .packlist
101# This is a workaround for 'invalide' installations...
102    foreach (@INC) {
103      next if ! -d || (-l && -d readlink) || /^(\.|lib)$/;
104      push @dirToScan, $_;
105    }
106  }
107  if (@dirToScan) {
108    eval {require File::Find};
109    if ($@) {
110      $logger->debug("Failed to load File::Find");
111    } else {
112# here I need to use $d to avoid a bug with AIX 5.2's perl 5.8.0. It
113# changes the @INC content if i use $_ directly
114# thanks to @rgs on irc.perl.org
115      File::Find::find(
116        {
117          wanted => sub {
118            push @installed_files, $File::Find::name if $File::Find::name =~ /Ocsinventory\/Agent\/Backend\/.*\.pm$/;
119          },
120          follow => 1,
121          follow_skip => 2
122        }
123        , @dirToScan);
124    }
125  }
126
127  foreach my $file (@installed_files) {
128    my $t = $file;
129    next unless $t =~ s!.*?(Ocsinventory/Agent/Backend/)(.*?)\.pm$!$1$2!;
130    my $m = join ('::', split /\//, $t);
131    push @installed_mods, $m;
132  }
133
134  if (!@installed_mods) {
135    $logger->info("ZERO backend module found! Is Ocsinventory-Agent ".
136    "correctly installed? Use the --devlib flag if you want to run the agent ".
137    "directly from the source directory.")
138  }
139
140  foreach my $m (@installed_mods) {
141    my @runAfter;
142    my @runMeIfTheseChecksFailed;
143    my $enable = 1;
144
145    if (exists ($self->{modules}->{$m}->{name})) {
146      $logger->debug($m." already loaded.");
147      next;
148    }
149
150    eval "use $m;";
151    if ($@) {
152      $logger->debug ("Failed to load $m: $@");
153      $enable = 0;
154    }
155
156    my $package = $m."::";
157    # Load in the module the backendSharedFuncs
158    foreach my $func (keys %{$self->{backendSharedFuncs}}) {
159      $package->{$func} = $self->{backendSharedFuncs}->{$func};
160    }
161
162    $self->{modules}->{$m}->{name} = $m;
163    $self->{modules}->{$m}->{done} = 0;
164    $self->{modules}->{$m}->{inUse} = 0;
165    $self->{modules}->{$m}->{enable} = $enable;
166    $self->{modules}->{$m}->{checkFunc} = $package->{"check"};
167    $self->{modules}->{$m}->{runAfter} = $package->{'runAfter'};
168    $self->{modules}->{$m}->{runMeIfTheseChecksFailed} = $package->{'runMeIfTheseChecksFailed'};
169#    $self->{modules}->{$m}->{replace} = \@replace;
170    $self->{modules}->{$m}->{runFunc} = $package->{'run'};
171    $self->{modules}->{$m}->{mem} = {};
172# Load the Storable object is existing or return undef
173    $self->{modules}->{$m}->{storage} = $self->retrieveStorage($m);
174
175  }
176
177# the sort is just for the presentation
178  foreach my $m (sort keys %{$self->{modules}}) {
179    next unless $self->{modules}->{$m}->{checkFunc};
180# find modules to disable and their submodules
181    if($self->{modules}->{$m}->{enable} &&
182    !$self->runWithTimeout(
183        $m,
184        $self->{modules}->{$m}->{checkFunc},
185        {
186            accountconfig => $self->{accountconfig},
187            accountinfo => $self->{accountinfo},
188            config => $self->{config},
189            inventory => $self->{inventory},
190            logger => $self->{logger},
191            params => $self->{params}, # Compatibiliy with agent 0.0.10 <=
192            prologresp => $self->{prologresp},
193            mem => $self->{modules}->{$m}->{mem},
194            storage => $self->{modules}->{$m}->{storage},
195            common => $self->{common},
196        })) {
197      $logger->debug ($m." ignored");
198      foreach (keys %{$self->{modules}}) {
199        $self->{modules}->{$_}->{enable} = 0 if /^$m($|::)/;
200      }
201    }
202
203
204
205
206# add submodule in the runAfter array
207    my $t;
208    foreach (split /::/,$m) {
209      $t .= "::" if $t;
210      $t .= $_;
211      if (exists $self->{modules}->{$t} && $m ne $t) {
212        push @{$self->{modules}->{$m}->{runAfter}}, \%{$self->{modules}->{$t}}
213      }
214    }
215  }
216
217  # Remove the runMeIfTheseChecksFailed if needed
218  foreach my $m (sort keys %{$self->{modules}}) {
219    next unless $self->{modules}->{$m}->{enable};
220    next unless $self->{modules}->{$m}->{runMeIfTheseChecksFailed};
221    foreach my $condmod (@{${$self->{modules}->{$m}->{runMeIfTheseChecksFailed}}}) {
222       if ($self->{modules}->{$condmod}->{enable}) {
223         foreach (keys %{$self->{modules}}) {
224           next unless /^$m($|::)/ && $self->{modules}->{$_}->{enable};
225           $self->{modules}->{$_}->{enable} = 0;
226           $logger->debug ("$_ disabled because of a 'runMeIfTheseChecksFailed' in '$m'\n");
227         }
228      }
229    }
230  }
231}
232
233sub runMod {
234  my ($self, $params) = @_;
235
236  my $logger = $self->{logger};
237
238  my $m = $params->{modname};
239  my $common = $params->{common};
240
241  return if (!$self->{modules}->{$m}->{enable});
242  return if ($self->{modules}->{$m}->{done});
243
244  $self->{modules}->{$m}->{inUse} = 1; # lock the module
245# first I run its "runAfter"
246
247  foreach (@{$self->{modules}->{$m}->{runAfter}}) {
248    if (!$_->{name}) {
249# The name is defined during module initialisation so if I
250# can't read it, I can suppose it had not been initialised.
251      $logger->fault ("Module `$m' need to be runAfter a module not found.".
252        "Please fix its runAfter entry or add the module.");
253    }
254
255    if ($_->{inUse}) {
256# In use 'lock' is taken during the mod execution. If a module
257# need a module also in use, we have provable an issue :).
258      $logger->fault ("Circular dependency hell with $m and $_->{name}");
259    }
260    $self->runMod({
261        common => $common,
262        modname => $_->{name},
263      });
264  }
265
266  $logger->debug ("Running $m");
267
268  if ($self->{modules}->{$m}->{runFunc}) {
269      $self->runWithTimeout(
270          $m,
271          $self->{modules}->{$m}->{runFunc},
272          {
273              accountconfig => $self->{accountconfig},
274              accountinfo => $self->{accountinfo},
275              config => $self->{config},
276              common => $common,
277              logger => $logger,
278              params => $self->{params}, # For compat with agent 0.0.10 <=
279              prologresp => $self->{prologresp},
280              mem => $self->{modules}->{$m}->{mem},
281              storage => $self->{modules}->{$m}->{storage},
282              common => $self->{common},
283          }
284      );
285  } else {
286      $logger->debug("$m has no run() function -> ignored");
287  }
288  $self->{modules}->{$m}->{done} = 1;
289  $self->{modules}->{$m}->{inUse} = 0; # unlock the module
290  $self->saveStorage($m, $self->{modules}->{$m}->{storage});
291}
292
293sub feedInventory {
294  my ($self, $params) = @_;
295
296  my $common = $self->{common};
297
298
299  my $inventory;
300  if ($params->{inventory}) {
301    $inventory = $params->{inventory};
302  }
303
304  if (!keys %{$self->{modules}}) {
305    $self->initModList();
306  }
307
308  my $begin = time();
309  foreach my $m (sort keys %{$self->{modules}}) {
310    die ">$m Houston!!!" unless $m;
311      $self->runMod ({
312          common => $common,
313          modname => $m,
314          });
315  }
316
317# Execution time
318  #$common->setHardware({ETIME => time() - $begin});
319
320  $inventory->{xmlroot}->{CONTENT} = $common->{xmltags};
321
322
323}
324
325sub retrieveStorage {
326    my ($self, $m) = @_;
327
328    my $logger = $self->{logger};
329
330    my $storagefile = $self->{config}->{vardir}."/$m.storage";
331
332    if (!exists &retrieve) {
333        eval "use Storable;";
334        if ($@) {
335            $logger->debug("Storable.pm is not available, can't load Backend module data");
336            return;
337        }
338    }
339
340    return (-f $storagefile)?retrieve($storagefile):{};
341
342}
343
344sub saveStorage {
345    my ($self, $m, $data) = @_;
346
347    my $logger = $self->{logger};
348
349# Perl 5.6 doesn't provide Storable.pm
350    if (!exists &store) {
351        eval "use Storable;";
352        if ($@) {
353            $logger->debug("Storable.pm is not available, can't save Backend module data");
354            return;
355        }
356    }
357
358    my $storagefile = $self->{config}->{vardir}."/$m.storage";
359    if ($data && keys (%$data)>0) {
360        store ($data, $storagefile) or die;
361    } elsif (-f $storagefile) {
362        unlink $storagefile;
363    }
364
365}
366
367sub runWithTimeout {
368    my ($self, $m, $func, $params) = @_;
369
370    my $logger = $self->{logger};
371
372    my $ret;
373   
374    eval {
375        local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n require
376        my $timeout = $params->{config}{backendCollectTimeout};
377        alarm $timeout;
378        $ret = &{$func}($params);
379    };
380    alarm 0;
381
382
383    if ($@) {
384        if ($@ ne "alarm\n") {
385            $logger->debug("runWithTimeout(): unexpected error: $@");
386        } else {
387            $logger->debug("$m killed by a timeout.");
388            return;
389        }
390    } else {
391        return $ret;
392    }
393}
394
3951;
Note: See TracBrowser for help on using the repository browser.