File Coverage

blib/lib/Net/Autoconfig.pm
Criterion Covered Total %
statement 66 354 18.6
branch 0 132 0.0
condition 0 10 0.0
subroutine 22 36 61.1
pod 10 10 100.0
total 98 542 18.0


line stmt bran cond sub pod time code
1             package Net::Autoconfig;
2              
3 6     6   93610 use 5.008008;
  6         20  
  6         249  
4 6     6   49 use strict;
  6         11  
  6         256  
5 6     6   28 use warnings;
  6         10  
  6         214  
6              
7 6     6   13477 use Log::Log4perl qw(:levels :easy);
  6         581942  
  6         49  
8 6     6   6531 use Net::Autoconfig::Device;
  6         18  
  6         188  
9 6     6   7907 use Net::Autoconfig::Template;
  1         3  
  1         25  
10 1     1   7 use Data::Dumper;
  1         1  
  1         90  
11 1     1   7 use POSIX ":sys_wait_h";
  1         3  
  1         11  
12 1     1   237 use Cwd;
  1         3  
  1         69  
13 1     1   5 use version; our $VERSION = version->new("v1.13.2");
  1         2  
  1         6  
14              
15             ################################################################################
16             # Constants and Global Variables
17             ################################################################################
18              
19 1     1   126 use constant TRUE => 1;
  1         1  
  1         79  
20 1     1   5 use constant FALSE => 0;
  1         2  
  1         42  
21              
22 1     1   7 use constant MAXIMUM_MAX_CHILDREN => 256; # Absolute Maximum # of child processes (if using bulk mode)
  1         2  
  1         45  
23 1     1   5 use constant DEFAULT_MAX_CHILDREN => 64; # Default max # of child processes (if using bulk mode)
  1         3  
  1         46  
24 1     1   5 use constant MINIMUM_MAX_CHILDREN => 1; # Absolute Minimum # of child processes (if using bulk mode)
  1         2  
  1         40  
25              
26 1     1   6 use constant DEFAULT_DIR => '/usr/local/etc/autoconfig';
  1         2  
  1         62  
27 1     1   5 use constant DEFAULT_LOGFILE => DEFAULT_DIR . '/logging.conf';
  1         2  
  1         43  
28              
29 1     1   6 use constant MAXIMUM_LOG_LEVEL => 5; # Absolute Maximum log level
  1         1  
  1         54  
30 1     1   5 use constant DEFAULT_LOG_LEVEL => 3; # Set the default log level to info
  1         2  
  1         50  
31 1     1   6 use constant MINIMUM_LOG_LEVEL => 0; # Absolute Minimum log level
  1         2  
  1         64  
32              
33 1     1   12 use constant DEFAULT_BULK_MODE => TRUE; # Enable parallel processing by default
  1         3  
  1         256  
34              
35             ####################
36             # Friendly User Prompt Messages
37             ####################
38 1         4145 use constant USER_PROMPTS => {
39             'password' => "Device Access Password",
40             'enable_password' => "Device Admin Password",
41             'console_password' => "Console Server Access Password",
42 1     1   13 };
  1         2  
43              
44              
45             # A hash ref to store child processes.
46             # Contains active processes, and return values
47             our $CHILD_PROCESSES = {};
48             $CHILD_PROCESSES->{'active'} = {};
49             $CHILD_PROCESSES->{'finished'} = {};
50             $CHILD_PROCESSES->{'info'} = {};
51              
52             # Zombies are dead child processes that need to
53             # be reaped.
54             our $ZOMBIES;
55              
56             # Setup signal handling for reaping our own zombies.
57             # Zombie handling is done by _reaper, which is in the
58             # private methods section.
59             $SIG{'CHLD'} = sub { $ZOMBIES++ };
60              
61             ################################################################################
62             # Methods
63             ################################################################################
64              
65             ############################################################
66             # Public Methods
67             ############################################################
68              
69             ########################################
70             # new
71             # public method
72             #
73             # Create a new Net::Autoconfig object.
74             #
75             # Log levels (not implemented yet):
76             # 0 = Fatal => Least verbose
77             # 1 = Error
78             # 2 = Warn
79             # 3 = Info
80             # 4 = Debug
81             # 5 = Trace => Most verbose
82             #
83             ########################################
84             sub new {
85 0     0 1   my $invocant = shift; # calling class
86 0   0       my $class = ref($invocant) || $invocant;
87 0           my $log = Log::Log4perl->get_logger($class);
88 0           my %user_data = @_;
89              
90 0           my $self = {
91             bulk_mode => DEFAULT_BULK_MODE,
92             log_level => DEFAULT_LOG_LEVEL,
93             max_children => DEFAULT_MAX_CHILDREN,
94             logfile => DEFAULT_LOGFILE,
95             };
96              
97 0           $self = bless $self, $class;
98              
99 0           $self->logfile( $user_data{'logfile'} );
100 0           $self->init_logging();
101              
102 0           $self->bulk_mode( $user_data{'bulk_mode'} );
103 0           $self->log_level( $user_data{'log_level'} );
104 0           $self->max_children( $user_data{'max_children'} );
105              
106 0           $log->info("########################################");
107 0           $log->info("# Net::Autoconfig Started #");
108 0           $log->info("########################################");
109 0           return $self;
110             }
111              
112             ########################################
113             # init_logging
114             # public method
115             #
116             # Initialize logging for Net::Autoconfig.
117             # If multiple Net::Autoconfig objects
118             # are created, calling this will affect
119             # all of them; it changes their logging
120             # definitions.
121             #
122             # Returns undef
123             ########################################
124             sub init_logging {
125 0     0 1   my $self = shift;
126              
127             # XXX - Setup a saner, \$log_string
128             # config so it's not just to stdout/stderr
129              
130 0 0         if ( -e $self->logfile )
131             {
132 0           eval {
133 0           Log::Log4perl::init( $self->logfile );
134             };
135 0 0         if ($@) {
136 0           print STDERR "Failed to initialize '" . $self->logfile
137             . "' even though it exists.\n";
138 0           print STDERR "Logging to STDERR.";
139 0           Log::Log4perl->easy_init($WARN);
140             }
141             }
142             else
143             {
144 0           print STDERR "logging.conf does not exist!";
145 0           print STDERR "Logging to STDERR.";
146 0           Log::Log4perl->easy_init($INFO);
147             }
148 0           return;
149             }
150              
151             ########################################
152             # bulk_mode
153             # public method
154             #
155             # Accessor/Mutator method
156             # If passed a parameter, set the
157             # bulk_mode value to TRUE or FAlSE
158             #
159             # If passed undef, return the
160             # bulk_mode value (TRUE or FALSE);
161             ########################################
162             sub bulk_mode {
163 0     0 1   my $self = shift;
164 0           my $mode = shift;
165 0           my $log = Log::Log4perl->get_logger( ref($self) );
166              
167 0 0         if (defined $mode)
168             {
169 0 0         $log->debug("Setting bulk_mode to " . ($mode ? TRUE : FALSE));
170 0 0         $self->{'bulk_mode'} = $mode ? TRUE : FALSE;
171             }
172 0 0         return defined $mode ? undef : $self->{'bulk_mode'};
173             }
174              
175             ########################################
176             # log_level
177             # public method
178             #
179             # Accessor/Mutator method
180             # If passed a parameter, set the
181             # log_level to the passed value (or within 0-5)
182             #
183             # If passed undef, return the
184             # log_level value.
185             ########################################
186             sub log_level {
187 0     0 1   my $self = shift;
188 0           my $level = shift;
189 0           my $log = Log::Log4perl->get_logger( ref($self) );
190            
191 0 0         if (defined $level)
192             {
193 0           $level = int($level);
194 0 0         if ($level > MAXIMUM_LOG_LEVEL)
    0          
195             {
196 0           $level = MAXIMUM_LOG_LEVEL;
197 0           $log->warn("Log level set too high. Setting to " . MAXIMUM_LOG_LEVEL);
198             }
199             elsif ($level < MINIMUM_LOG_LEVEL)
200             {
201 0           $level = MINIMUM_LOG_LEVEL;
202 0           $log->warn("Log level set too low. Setting to " . MINIMUM_LOG_LEVEL);
203             }
204 0           $log->debug("Setting log_level to $level");
205 0           $self->{'log_level'} = $level;
206             }
207 0 0         return defined $level ? undef : $self->{'log_level'};
208             }
209              
210             ########################################
211             # max_children
212             # public method
213             #
214             # Accessor/Mutator method
215             # If passed a parameter, set the
216             # maximum number of child processes.
217             # Only used when "bulk_mode" is enabled.
218             #
219             # If passed undef, return the
220             # max number of children.
221             ########################################
222             sub max_children {
223 0     0 1   my $self = shift;
224 0           my $max_children = shift;
225 0           my $log = Log::Log4perl->get_logger( ref($self) );
226              
227 0 0         if (defined $max_children)
228             {
229 0 0         if ($max_children > MAXIMUM_MAX_CHILDREN)
    0          
230             {
231 0           $max_children = MAXIMUM_MAX_CHILDREN;
232 0           $log->warn("Log max_children set too high. Setting to '256'.");
233             }
234             elsif ($max_children < MINIMUM_MAX_CHILDREN)
235             {
236 0           $max_children = MINIMUM_MAX_CHILDREN;
237 0           $log->warn("Log max_children set too low. Setting to '1'.");
238             }
239 0           $log->debug("Setting max_children to $max_children");
240 0           $self->{'max_children'} = $max_children;
241             }
242 0 0         return defined $max_children ? undef : $self->{'max_children'};
243             }
244              
245             ########################################
246             # get_report
247             # public method
248             #
249             # Return info about the finished processes.
250             # Returns a hash ref with:
251             # 'succeded'=> { list of hostnames }
252             # 'failed' => {list of hostnames }
253             ########################################
254             sub get_report {
255 0     0 1   my $self = shift;
256 0           my $report = {};
257 0           my @succeded; # devices that exited successfully
258             my @failed; # devices that exited unsuccessfully
259              
260 0           foreach my $device_pid (keys %{ $CHILD_PROCESSES->{'info'} })
  0            
261             {
262 0 0         if ($CHILD_PROCESSES->{'finished'}->{$device_pid})
263             {
264             # it failed
265 0           push(@failed, $CHILD_PROCESSES->{'info'}->{$device_pid});
266             }
267             else
268             {
269             # it succeded
270 0           push(@succeded, $CHILD_PROCESSES->{'info'}->{$device_pid});
271             }
272             }
273 0           $report->{'succeded'} = \@succeded;
274 0           $report->{'failed'} = \@failed;
275 0 0         return wantarray ? %$report : $report;
276             }
277              
278             ########################################
279             # logfile
280             # public method
281             #
282             # Accessor/Mutator
283             #
284             ####################
285             # Mutator
286             #
287             # Sets the logfile if it exists
288             # (assumes current working directory if
289             # the filename is not specified absolutely.)
290             #
291             # Else, it sets the logfile to the default.
292             #
293             # Returns:
294             # Success => undef
295             # Failure => error message
296             ####################
297             # Accessor
298             #
299             # Returns
300             # the logfile's absolute path
301             ########################################
302             sub logfile {
303 0     0 1   my $self = shift;
304 0           my $logfile = shift;
305 0           my $return_value;
306              
307 0 0         if ( defined $logfile )
308             {
309             # Check for abs path
310 0 0         if ( $logfile !~ /^\// )
311             {
312 0           $logfile = join('/', getcwd(), $logfile);
313             }
314              
315 0 0         if (-e $logfile)
316             {
317 0           $self->{'logfile'} = $logfile;
318             }
319             else
320             {
321 0           $self->{'logfile'} = DEFAULT_LOGFILE;
322 0           print STDERR "\n'$logfile' either does not exist or is unreadable\n";
323 0           print STDERR "\nUsing default logfile " . DEFAULT_LOGFILE . "\n";
324             }
325 0           undef $return_value;
326             }
327             else
328             {
329 0           $return_value = $self->{'logfile'};
330             }
331 0           return $return_value;
332             }
333              
334             ########################################
335             # load_devices
336             # public method
337             #
338             # This method looks at a device config
339             # file and returns
340             # an array ref of Net::Autoconfig::Device's.
341             # Devices are returned in the same order as
342             # in the device file.
343             #
344             # Returns:
345             # array context => an array of Devices
346             # scalar context => an array ref of Devices
347             # undef => failure
348             ########################################
349             sub load_devices {
350 0     0 1   my $self = shift;
351 0           my $filename = shift;
352 0           my $log = Log::Log4perl->get_logger( ref($self) );
353 0           my $devices = []; # an array ref of Net::Autoconfig::Devices, key = hostname
354 0           my $file_format; # indicates if the file is a hash of arrays, or as hash of hashes of arrays
355             my $file_hash_depth; # an integer of the number of levels of hashes in the device file
356 0           my $current_device; # the name of the current device to add parameters too
357 0           my $line_counter; # The current line we're on in the device config file (used for logging)
358 0           my $default_device; # The default device, helps populate new devices
359 0 0         $filename or $filename = "";
360              
361             # Check for abs path
362 0 0         if ( $filename !~ /\// )
363             {
364 0           $filename = join("/", getcwd(), $filename);
365             }
366              
367 0 0         (&_file_not_usable($filename, "device config")) and return;
368              
369             eval
370 0           {
371 0 0         open(DEVICES, $filename) || die print "Could not open '$filename' for reading: $!";
372             };
373 0 0         if ($@)
374             {
375 0           $log->warn("Unable to open '$filename': $@");
376 0           return;
377             }
378              
379             # Create this here in case someone decides not to use the default
380             # device. Re-set the auto-disover bit
381 0           $default_device = Net::Autoconfig::Device->new( 'auto_discover' => FALSE );
382 0           $default_device->hostname('autoconfig-default');
383              
384 0           while (my $line = )
385             {
386 0           chomp $line;
387 0 0         next if $line =~ /^#/;
388 0 0         next if $line =~ /^\s*$/;
389              
390 0           $line_counter++;
391              
392 0 0         if ($line =~ /^:/)
    0          
393             {
394             # some type of host declaration (host or default)
395 0           $line =~ s/^://;
396 0           $line =~ s/:$//;
397 0           $line =~ s/\s*(.*?)\s*/$1/; #remove preceding and trailing whitespace
398              
399 0 0         if (not $line)
400             {
401 0           $log->warn("In device file, undef device '::' line at $line_counter.");
402 0           next;
403             }
404              
405 0 0         if ($line =~ /default/i)
    0          
406             {
407 0           $current_device = $default_device;
408             }
409             elsif ($line =~ /^end$/)
410             {
411 0 0         if ($current_device->hostname !~ /autoconfig-default/i)
412             {
413 0           $log->trace("Adding " . $current_device->hostname
414             . " to the list of devices.");
415 0           $current_device = $current_device->auto_discover;
416 0           push(@$devices, $current_device);
417             }
418 0           undef $current_device;
419             }
420             else
421             {
422 0           $current_device = Net::Autoconfig::Device->new(
423             $default_device->get(),
424             'hostname' => $line,
425             );
426             }
427             }
428             elsif ($line =~ /\s*(.*?)\s*=\s*(.*?)\s*$/)
429             {
430 0           my $key = $1;
431 0           my $value = $2;
432 0 0         if (not $current_device)
433             {
434 0           $log->warn("No device is currently configured at line $line_counter.");
435 0           next;
436             }
437 0 0         if ($log->is_trace())
438             {
439 0           $log->trace("line # = $line_counter");
440 0           $log->trace("line = '$line'");
441 0           $log->trace("key = '$key'");
442 0           $log->trace("value = '$value'");
443             }
444              
445 0 0         if ($value =~ /\/i)
446             {
447 0           my $user_prompts = USER_PROMPTS;
448 0           my $hostname = $current_device->hostname;
449 0           my $message = $user_prompts->{$key};
450 0 0         if (not $message)
451             {
452 0           $message = $key;
453             }
454 0 0         if ($hostname eq $default_device->hostname)
455             {
456 0           $hostname = "Default";
457             }
458 0           $message = "[$hostname] - $message";
459 0           $value = &_get_password($message);
460             }
461              
462 0           $current_device->set($key => $value);
463             }
464             else
465             {
466 0           $log->warn("Invalid key = value line. Line = '$line'.");
467             }
468             }
469              
470 0           close(DEVICES);
471 0 0         return wantarray ? @$devices : $devices;
472             }
473              
474             ########################################
475             # load_template
476             # public method
477             #
478             # Load a configuration template from disk.
479             # These files use the colon file format.
480             # See documentation for more details.
481             #
482             # Returns
483             # success => a hash ref of the different hosts/devices types
484             # failure => undef
485             ########################################
486             sub load_template {
487 0     0 1   my $self = shift;
488 0           my $filename = shift;
489 0           my $log = Log::Log4perl->get_logger( ref($self) );
490 0           my $template;
491 0 0         $filename or $filename = "";
492              
493             # Check for abs path
494 0 0         if ( $filename !~ /^\// )
495             {
496 0           $filename = join("/", getcwd(), $filename);
497             }
498              
499 0 0         (&_file_not_usable($filename, "template file")) and return;
500              
501 0           $template = Net::Autoconfig::Template->new($filename);
502              
503 0           return $template;
504             }
505              
506             ########################################
507             # autoconfig
508             # public method
509             #
510             # Takes a hash ref of device files and
511             # a template file, and executes the commands on all of the devices.
512             #
513             # There are two ways a template can be applied to a device.
514             # If the device model matches a template entry, then that is
515             # applied first. If the device name matches a template entry,
516             # then that is applied to the device second.
517             #
518             # This allows for a device to receive a "generic" configuration
519             # destined for all devices first, and a more "specific" configuration
520             # later.
521             #
522             # Takes:
523             # $devices_hash_ref, Net::Autoconfig::Template
524             #
525             # Returns:
526             # success = undef
527             # failure = An array or array ref (contextual) of the failed devices
528             ########################################
529             sub autoconfig {
530 0     0 1   my $self = shift;
531 0           my $devices = shift;
532 0           my $template = shift;
533 0           my $failed_ping_test; # results from doing the ping test on the device
534 0           my $log = Log::Log4perl->get_logger( ref($self) );
535              
536 0 0         if (ref($self) !~ /Net::Autoconfig/)
537             {
538 0           $log->warn("Autoconfig not called as a method.");
539 0           return "Autoconfig not called as a method.";
540             }
541              
542 0 0         if (not $devices)
543             {
544 0           $log->warn("No devices passed to autoconfig.");
545 0           return "No devices passed to autoconfig.";
546             }
547            
548 0 0         if (not ref ($devices) eq "ARRAY")
549             {
550 0           $log->warn("Devices were not passed as an array ref.");
551 0           return "Devices were not passed as an array ref.";
552             }
553              
554 0 0         if (not $template)
555             {
556 0           $log->warn("No template passed to autoconfig.");
557 0           return "No template passed to autoconfig.";
558             }
559              
560 0           foreach my $device ( @$devices )
561             {
562 0           my $child_pid; # PID of the child process (if used)
563              
564 0 0         if ($log->is_trace)
565             {
566 0           $log->trace("Device about to be configured: " . Dumper($device));
567             }
568              
569 0           while (keys %{ $CHILD_PROCESSES->{'active'} } > $self->max_children)
  0            
570             {
571 0           $log->debug("Reached max # of child processes (" . $self->max_children
572             . ") Waiting for some processes to clear up...");
573 0 0         &_reaper() if $ZOMBIES;
574 0           sleep(1);
575             }
576              
577 0 0         if ($self->bulk_mode)
578             {
579 0           $log->trace("Forking process");
580 0           $child_pid = fork();
581             #$log->trace("Fork created for Parent $$ - Child $child_pid. Device " . $device->hostname);
582 0 0         if ($child_pid == -1)
    0          
583             {
584             # Failed to fork!
585 0           $log->warn("Failed to create child process for device " . $device->hostname);
586 0           next;
587             }
588             elsif ($child_pid)
589             {
590             # I'm the parent
591 0           $log->debug("Parent $$ - Child $child_pid - Current Device : " . $device->hostname);
592 0           $log->debug("Child $child_pid bulk_mode = " . $self->bulk_mode());
593 0           $CHILD_PROCESSES->{'active'}->{$child_pid} = $device->hostname;
594 0           $CHILD_PROCESSES->{'info'}->{$child_pid} = $device->hostname;
595 0           next;
596             }
597             else
598             {
599             # This is the child
600 0           $log->debug("Child process started for " . $device->hostname);
601             }
602             }
603              
604 0 0         if ($device->provision)
605             {
606 0           $device->hostname =~ /\A(.*)\@(.*)$/;
607 0 0 0       if ($1 and $2)
608             {
609             # Well formed provisioning hostname
610 0           $failed_ping_test = &_failed_ping_test($2);
611             }
612             else
613             {
614             # Poorly formed or normal hostname
615 0           $log->warn("Provisioning hostname " . $device->hostname . " poorly formed.");
616 0           $failed_ping_test = TRUE;
617             }
618             }
619             else
620             {
621 0           $failed_ping_test = &_failed_ping_test($device->hostname);
622             }
623              
624 0 0         if ($failed_ping_test)
625             {
626 0   0       my $hostname = $device->hostname || "";
627 0           $log->warn("$hostname was not reachable via ping. Aborting configuration attempt.");
628 0 0         if ($self->bulk_mode)
629             {
630 0           exit;
631             }
632             else
633             {
634 0           next;
635             }
636             }
637              
638             # Establish a connection to the device first
639 0 0         $device->provision and $device->console_connect();
640 0           $device->connect();
641 0           $device->get_admin_rights();
642 0           $device->disable_paging();
643              
644 0 0         $device->provision and $device->lookup_model;
645              
646             # Do the generic, device model/type template first
647             # device->model returns an array ref, a device can match more
648             # than one device type. Take the first one that exists in the template.
649 0           MODEL_CONFIG:
650 0           foreach my $model ( @{ $device->model } )
651             {
652 0 0         if ($template->{ $model } )
653             {
654 0           $log->info("Starting generic, model based using template"
655             . " '$model' to configure " . $device->hostname);
656 0           $device->configure($template->{ $model });
657 0           last MODEL_CONFIG;
658             }
659             else
660             {
661 0           $log->info("No generic, model based template called '$model'" .
662             " for host " . $device->hostname);
663 0           next MODEL_CONFIG;
664             }
665             }
666              
667 0 0         if ($template->{$device->hostname})
668             {
669             # Do the host specific template second.
670 0           $log->info("Starting specific, hostname based configuration");
671 0           $device->configure($template->{ $device->hostname });
672             }
673             else
674             {
675 0           $log->info("No specific, hostnamed based template defined for host " . $device->hostname);
676             }
677 0           $device->end_session();
678              
679 0 0         if ($self->bulk_mode)
680             {
681 0 0         if ($child_pid == 0)
682             {
683 0           $log->trace("Terminating child process $$ for host " . $device->hostname);
684 0           exit;
685             }
686             }
687             }
688              
689 0           while (keys %{ $CHILD_PROCESSES->{'active'}})
  0            
690             {
691 0           $log->debug("Waiting for child processes to terminate. Sleeping for 5 seconds.");
692 0 0         &_reaper() if $ZOMBIES;
693 0           sleep(5);
694             }
695              
696 0           $log->info("Autoconfig Finished.");
697              
698 0           return;
699             }
700              
701             ############################################################
702             # Private Methods
703             ############################################################
704             #
705             ########################################
706             # _get_password
707             # private function
708             #
709             # Get a password from the user.
710             # I.e. prevent local echoing of their
711             # password.
712             ########################################
713             sub _get_password {
714 0     0     my $prompt = shift;
715 0           my $log = Log::Log4perl->get_logger( __PACKAGE__ );
716 0           my $message = "";
717 0           my $user_input = "";
718              
719 0 0         if (not $prompt)
720             {
721 0           $log->debug("get_password - Prompt not specified");
722 0           $prompt = "(Not Specified)";
723             }
724              
725 0           $message = "[User Input] - $prompt: ";
726              
727 0           $log->trace("get_password - Message = '$message'");
728 0           print $message;
729              
730             # Hide user input
731             # This only works on linux/unix machines.
732 0           $log->trace("get_password - hiding user text input");
733 0           system("stty -echo");
734              
735 0           $user_input = ;
736 0           chomp($user_input);
737 0           $log->trace("get_password - password = '$user_input'");
738              
739             # Show user input again.
740 0           $log->trace("get_password - showing user text input");
741 0           system("stty echo");
742 0           print "\n";
743              
744 0           return $user_input;
745             }
746              
747              
748             ########################################
749             # _file_not_usable
750             # private method
751             #
752             # Check to see if a file exists, is readable,
753             # etc.
754             #
755             # Takes a filename and a description about the file
756             # E.g. "device configs" or "firmware tempalte"
757             #
758             # Return FALSE if it's okay.
759             # Return TRUE if it's not
760             ########################################
761             sub _file_not_usable {
762 0     0     my $filename = shift;
763 0           my $file_descrip = shift;
764 0           my $working_dir = getcwd();
765 0           my $log = Log::Log4perl->get_logger( __PACKAGE__ );
766 0   0       $file_descrip = $file_descrip || "";
767              
768 0 0         if (! $filename)
769             {
770 0           $log->warn("$file_descrip: filename not defined.");
771 0           return TRUE;
772             }
773              
774 0 0         if (-d $filename)
775             {
776 0           $log->warn("$file_descrip: filename, '$filename' is a directory.");
777 0           return TRUE;
778             }
779              
780 0 0         if (not -e $filename)
781             {
782 0           $log->warn("$file_descrip: '$filename', does not exist.");
783 0           return TRUE;
784             }
785              
786 0 0         if (not -r $filename)
787             {
788 0           $log->warn("$file_descrip: '$filename', is not readable. Check file permissions.");
789 0           return TRUE;
790             }
791              
792             # Ergo, it must be okay!
793 0           return FALSE;
794             }
795              
796              
797             sub _failed_ping_test {
798 0     0     my $hostname = shift;
799 0           return;
800             }
801              
802             ##############################
803             # _reaper
804             # private method
805             #
806             # Takes care of waiting for child processes to finish
807             # so they don't become zombies. Also does some
808             # book keeping so we know which processes succeded
809             # and which ones failed; which ones/how many are active
810             ##############################
811             sub _reaper {
812 0     0     my $zombie;
813 0           my $log = Log::Log4perl->get_logger( __PACKAGE__ );
814              
815 0           $log->trace("Start reaping zombies.");
816 0           $log->trace("Number of zombies : $ZOMBIES");
817 0           $log->trace("Number of active children: " . int(keys %{ $CHILD_PROCESSES->{active} }));
  0            
818              
819 0           $ZOMBIES = 0;
820              
821             # This is a little tricky.
822             # waitpid returns the process id of a zombie that needs to
823             # be reaped. It returns 0 for active procsses. It returns
824             # -1 when there are no child processes left.
825             # You'll see code that has != -1, We want to
826             # keep going when child processes are active and
827             # only reap the dead processes.
828 0           while (($zombie = waitpid(-1, WNOHANG)) > 0)
829             {
830 0           $CHILD_PROCESSES->{'finished'}->{$zombie} = $? >> 8;
831 0           delete $CHILD_PROCESSES->{'active'}->{$zombie};
832             }
833              
834 0           $log->trace("Done reaping zombies.");
835 0           return;
836             }
837              
838             # Module must return true.
839             TRUE;
840              
841             __END__