SituationSoap has asked for the wisdom of the Perl Monks concerning the following question:
The issue I'm having is that the END{} subroutine is called when I initialize the program for the parent subroutine, but is not called for the child subroutine (the one the loop actually runs in) in the code below. As a result, while I can see when the code starts running, I have no way to tell when it ends.
use strict; use warnings; use diagnostics; use POSIX qw(setsid); use DBI; use Config::Simple '-strict'; use Qualys; use vars qw($hup); use Class::Date qw(:errors date localdate gmdate now -DateParse -EnvC) +; use Net::SMTP; use Config::Simple '-strict'; ###################################################################### +########## # Qualys API Daemon # Written By: Eric Boersma # Date: 7/14/2009 # # Function: This daemon is the API call handler for the Qualys API. +Due to # restrictions placed on the API by Qualys, this system handles +desired # API calls in a timely fashion. By daemonizing this process, we + ensure # that the maximum number of API calls (300) happens in a 24-hou +r period. # This loop opens the priority modified FIFO (First In First Out +) queue in # the MySQL database, reads the highest priority, oldest entry, # determines the type of API call to be made, and hands that req +uest off # to a subroutine to do the heavy lifting. Once the subroutine i +s finished # the daemon waits five minutes before starting the next call, s +o as not # to exceed the maximum number of API function calls in a 24-hou +r period. # # Usage: In order to interface with this Daemon, send your desired A +PI call to # the database table APIQueue in the designated format. ###################################################################### +########## ###################################################################### +########## # # Preparing our Subroutines # ###################################################################### +########## sub processCall{ my $conf = $-[1]; my $call = $_[0]; my $qualys = new Qualys; #Instantiate Qualys my $uname = $conf->param("qualysuname"); my $pass = $conf->param("qualyspass"); #Log in $qualys->userid($uname); $qualys->passwd($pass); #Connect to the requested script, using our modified format. #$call should just be a querystring with the api function call as +the start. return $qualys->connect_to($call); } #This script is run on program exit, it is called after errorOut() #If errorOut() has already printed an error, we won't print another on +e sub programExit{ print "Exiting program!\n"; my $progname = $0; #program name my $now = date now; #right now my $exit = $?; #exit error code my $log; #prepare the line to be written to the log if($exit == 7142){ open (LOG, ">>/some/directory/APIlog") or die "$!"; #open the +log file print LOG "The program successfully began execution at $now\n" +; close LOG; return; } open (LOG, ">>/some/directory/APIlog") or die "$!"; #open the log +file $log = "$now Qualys API Queue daemon exited unexepectedly. "; #sta +ndard statement if($exit != 0){ $log = $log . "The program exited with errors\n The error was: + $!"; #append additional information } else{ $log = $log . "The program exited without errors\n"; #ditto } if($exit != 5432){ print LOG $log; #if this isn't a code provided by errorOut(), +then print the log } SendMail($log, "$progname terminated unexpectedly", "failmail"); # +no matter what, tell the programmer about the exit close LOG; #cleanliness } sub SendMail{ #standard SendMail() sub which you've probably seen before if you' +ve edited any of my other code. #Don't really feel necessary to go into the inner workings, it's p +retty straightforward my $report = $_[0]; my $subject = $_[1]; my $list = $_[2]; my $conf = new Config::Simple('bind.conf') or die "Can't find conf +ig file"; my @email = $conf->param($list); my $smtp = Net::SMTP->new( Host => 'mail.company.com', Hello => 'deptname.company.com', Timeout => 30, Debug => 0, ); $smtp->mail('deptname@company.com'); foreach my $email (@email) { $smtp->to($email); } $smtp->data(); $smtp->datasend("From: Qualys API Daemon\n"); foreach my $email (@email) { $smtp->datasend("To: $email\n"); } $smtp->datasend("Subject: $subject\n"); $smtp->datasend("MIME-Version: 1.0\n"); $smtp->datasend("Content-type: text/html\n"); $smtp->datasend("Content-Transfer-Encoding: 7bit\n"); $smtp->datasend("\n"); $smtp->datasend("$report\n"); $smtp->dataend(""); $smtp->quit(); } #Captures the $! error input in placing of dying the program, and prin +ts it to the log. sub errorOut{ my $error = $_[0]; open (LOG, ">>/some/directory/APIlog") or die "$!"; print LOG "$error\n"; close LOG; exit 5432; } sub handleErorr{ #TODO: Add additional error handling for multiple typ +es of erros here my $error = $_[0]; open (LOG, ">>/some/directory/APIlog"); print LOG "$error\n"; close LOG; } sub dailyTasks{ my $currentfile = $_[0]; my $date = date now; my @day = split(/ /, $date); } $| = 1; #Signal handling variables for HUP restarts. $hup = 0; #if we get a kill -hup call, increment the hup counter so the loop kno +ws to restart $SIG{HUP} = sub{ $hup++; }; ###################################################################### +########## # # public static void main() # ###################################################################### +########## #Due to the looping nature of this program, any configuration changes +will require #a service restart. This is by design. open(LOG, ">>/some/directory/APIlog") or errorOut("$!"); #We will prin +t to a log file chdir '/some/directory' or errorOut("Can't change directory: $!"); #se +t our root umask 0; #mask ourselves w/ userid 0, so the calling user isn't tied t +o us open (STDIN, "/dev/null") or errorOut("$!"); #no STDIN for this servic +e open (STDOUT, ">/dev/null") or errorOut("$!"); #Or STDOUT open (STDERR, ">/dev/null") or errorOut("$!"); #or STDERR defined(my $pid = fork) or errorOut("Can't fork: $!"); #fork the proce +ss to create a child exit 7142 if $pid; #the fork makes it so that we're not tied to the us +er's shell session setsid or errorOut("Can't start a new session: $!"); #start a new sess +ion #Config files to Log us into the database my $conf = new Config::Simple('/some/directory/conf') or errorOut("Can +'t find config file"); my $username = $conf->param("db_username"); my $password = $conf->param("db_password"); my $dbh = DBI->connect("dbi:mysql:database=DBName; host=server.company +.com; port=3306", $username, $password,) or errorOut(DBI->errstr); my $now = date now; open (my $currentfile, ">", "/some/directory/CurrentLog"); while(1){ #This while loop will never turn false, so the service is de +signed to always run. my $timer = $now + '1D'; my $report = date now; if($report >= $timer){ dailyTasks($currentfile); $now = $report; } my $sql = "SELECT RequestTime, Priority, RequestString, OutputFile +, OutputScript, QueueID FROM APIQueue ORDER BY Priority, RequestTime +LIMIT 1"; #get our next call to process my $sth = $dbh->prepare($sql); my $sql2 = "DELETE FROM APIQueue WHERE QueueID = ?"; my $sth2 = $dbh->prepare($sql2); $sth->execute(); while(my @row = $sth->fetchrow_array()){ #assign the relevant values my $time = $row[0]; my $priority = $row[1]; my $request = $row[2]; my $output = $row[3]; my $script = $row[4]; my $id = $row[5]; open(OUTPUT, $output) or errorOut("Couldn't open output buffer + $output: $!"); my $result = processCall($request, $conf); #make the call print OUTPUT $result; #print the result to the requested file close OUTPUT; my $parser; #check which file parser we're going to be using, based on a r +egex in the script name. if($script =~ m/\.php/i){ $parser = "php"; } if($script =~ m/\.pl/i){ $parser = "perl"; } if(!($pid = fork)){ #Run the child process to process the outp +ut. system("$parser $script"); } $sth2->execute($id); #Delete the record my $log = "ID: $id, Requested: $time, Priority: $priority, Cal +l: $request, Processed: $report\n"; print LOG $log; #TODO: schedule nightly logging and result emails #TODO: Exception handling. } #check our hup, and restart the program with default arguments if +we get a hup signal. if ($hup > 0){ SendMail("Qualys API Daemon restarted successfully", "Qualys A +PI Daemon Restart", "failmail"); exec($0, @ARGV) || warn "$!"; } sleep(300); } END{ programExit(); }
I have no doubt there are probably some other issues with this, so if you see anything, please feel free to point them out. While Perl was my first language, it's certainly not my best, and I'd be much more comfortable writing something like this in PHP or Java than I am in Perl. As a result, I'm sure there are errors in the script which aren't apparent, as this isn't in production yet.
First and foremost is the standing question: can you see why END{} is called when the program starts and forks, but not when it die()s or is killed from the command line?
|
|---|