Hello!
( wow, it's been 5! years since I last logged into perlmonks... ah, memories )
Ok, anyhow - I'm just getting back into perl again after a pretty long hiatus, and I hope some of you kind monks can help me out with a question/problem.
I've written a daemonizer in perl - for educational purposes - and now I'm trying to make it more useful by modularizing it. I still got the hang of oop in perl, but I'm lacking the camel book, and I need to know how best to get this thing to work as a module that can be easily instantiated by other scripts and/or modules.
The main problem I'm having, is when I run this ( code following, below ), I get errors such as 'SIGTERM handler "sigterm_catcher" not defined.' for each of my signal handlers... how do I get the namespace/context correct for this to work as expected as a module? ( it works just fine when I "unmodularize" it, and just run it as a self-contained script. )
Also, it's obviously coded as a mere module currently - but I'd like to make this into an actual first-class Class; will that be possible, or do the realities of "daemonization" prevent such a thing?
Many thanks!
Here is an example/test script that uses my Daemonizer.pm:
#!/usr/bin/perl -w
@INC = [ @INC, './' ];
use Daemonizer;
Daemonizer::init;
while (1) {
sleep 20
}
And here is the Daemonizer.pm itself:
package Daemonizer;
use strict;
use vars qw( @ISA @EXPORT_OK );
use Exporter;
@ISA = qw( Exporter );
use IO::Seekable;
use File::Basename;
use POSIX qw( setsid );
use Sys::Syslog qw( :DEFAULT setlogsock );
my ( $PID, $lckfile );
$lckfile = '/tmp/' . fileparse( $0, qr{\..*} ) . '.pid';
# appease perl taint checking
$ENV{'PATH'} = "/bin:/usr/bin";
# setup syslogging
setlogsock('unix');
openlog( 'parselogd', 'pid', 'local6');
# setup signal traps
$SIG{'INT'} = 'sigint_catcher';
$SIG{'TERM'} = 'sigterm_catcher';
$SIG{'QUIT'} = 'sigquit_catcher';
$SIG{'ABRT'} = 'sigabrt_catcher';
$SIG{'HUP'} = 'sighup_catcher';
# not strictly needed, but...
$SIG{'CHLD'} = 'IGNORE';
# define sigtrap routines
sub sigterm_catcher { &term('TERM') }
sub sigquit_catcher { &term('QUIT') }
sub sigabrt_catcher { &term('ABRT') }
sub sigint_catcher { &term('INT') }
sub sighup_catcher { &term('HUP') }
# perl interpreter traps ( semi-dangerous )
$SIG{'__DIE__'} = sub { &crit( "Caught die: $_[0]" ) };
$SIG{'__WARN__'} = sub { syslog( 'err', "warn! $_[0]" ) };
### cleanup ##
##
sub clean {
syslog( 'debug', "cleaning up" );
rm_lock() if $lckfile;
}
### die elegantly ##
##
sub crit {
my $msg = shift;
syslog( 'err', "CRIT!: $msg" );
syslog( 'err', "Aborting!" );
clean();
syslog( 'err', "Terminating daemon" );
closelog();
undef $SIG{'__DIE__'};
die "$msg";
}
### terminate process ##
##
sub term {
my $cause = shift;
syslog( 'notice', "recieved $cause signal");
if ( $cause eq 'HUP' ) {
syslog( 'notice', "Restarting daemon");
clean();
init();
} else {
clean();
syslog( 'notice', "Terminating daemon");
exit;
}
}
### create lock file ##
##
sub write_lock {
syslog( 'info', "writing lock" );
open( LCK, ">$lckfile" ) or do {
crit( "Could not create lock file: $!" );
};
print LCK "$PID\n";
close( LCK );
}
### remove lock file ##
##
sub rm_lock {
my ( $found_pid );
syslog( 'info', "removing lock" );
if ( -e $lckfile ) {
open( LCK, "<$lckfile" ) or
crit( "Could not read lock file: $!" );
while( <LCK> ) {
chomp;
/$PID/ and $found_pid = 1;
}
close( LCK );
if ( $found_pid ) {
unlink $lckfile or
syslog( 'warning', "Could not delete lock file: $!" );
} else {
syslog( 'warning', "Unable to find pid: $PID" );
}
} else {
syslog( 'warning', "No lockfile found" );
}
}
### initialize and spawn off daemon ##
##
sub init {
my ( $pid );
# grab current process id
$PID = $$;
syslog( 'notice', "starting daemon" );
if ( -e $lckfile ) {
crit( "lock file exists! daemon may already be running" );
}
syslog( 'debug', "initializing" );
# create the lock file
write_lock();
# create child process
defined( $pid = fork ) or
crit( "unable to fork process!: $!" );
# be a nice little daemon and go to your root
chdir('/') or
crit( "unable to chdir to \'/\'!: $!" );
# say goodbye to our progeny
exit if $pid;
# redirect filehandles
open STDOUT, ">/dev/null" or
crit( "unable to write to /dev/null!: $!" );
open STDIN, "/dev/null" or
crit( "unable to read from /dev/null!: $!" );
# create new session
POSIX::setsid or
crit( "unable to create new session!: $!" );
# fully establish our own identity
$PID = $$;
$0 = fileparse( $0, qr{\..*} );
# rewrite lock with new pid
write_lock();
# hairy part is over, finally dissociate stderr
open STDERR, '>&STDOUT' or
crit( "unable to attach STDERR to STDOUT!: $!" );
# we're on our own now
}
1;