Zarathustra has asked for the wisdom of the Perl Monks concerning the following question:

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;

Replies are listed 'Best First'.
Re: daemon ... oop'ified?
by tlm (Prior) on Jun 17, 2005 at 08:38 UTC

    I was not able to reproduce the problem you report. Can you give more explicit instructions to do this?

    The following from perlipc may be of interest to you:

    For example, to trap an interrupt signal, set up a handler like this:
    sub catch_zap { my $signame = shift; $shucks++; die "Somebody sent me a SIG$signame"; } $SIG{INT} = ’catch_zap’; # could fail in modules $SIG{INT} = \&catch_zap; # best strategy
    In other words, you may want to change your handler definitions to something like:
    $SIG{'INT'} = \&sigint_catcher;

    Can you elaborate on why you want to turn this into a class?

    the lowliest monk

      > was not able to reproduce the problem you report. Can you give more explicit instructions to do this?

      Sorry that I didn't think to include a debug mode before posting that... in order to see what I'm talking about, you need to tail/watch wherever you put local6 logs; or change the log facility from local6 to something else that you're already logging w/ your syslogger.

      > In other words, you may want to change your handler definitions to something like: >snip<

      Aha, I'll go and try that - thanks for reminding me about the perlipc perldoc!

      > Can you elaborate on why you want to turn this into a class?

      Well, it's part of a larger piece of software that I'm working on; an event processing engine - part of the functionality will be a master process that dynamically forks off other daemons... I thought having a real Class for this Daemonizer thing would be beneficial and cleaner and more powerful once I added some features.

      Thanks for your help!

      >In other words, you may want to change your handler definitions to something like: $SIG{'INT'}  = \&sigint_catcher;

      Bingo - that was the ticket; thanks man!

      ( I've obviously got some re-conditioning ahead, in order to get my perl groove back on! heheh )

      As far as the OOP'ification goes -- I'll tweak around a little more so that I have some code that will show what I'm trying to get at; in order to help clarify my goals with this.

      Beers!

Re: daemon ... oop'ified?
by japhy (Canon) on Jun 17, 2005 at 14:57 UTC
    Um, just a reminder. @INC = [ @INC, './' ]; probably should be push @INC, "./";, except in your case, you want to say use lib "./";.

    You didn't get bit by this (making @INC an array reference, or more appropriately, putting an array reference in @INC) because all your modules (and interactions with @INC) are done by the time Perl actually executes your @INC = ... line, but you should be aware of the mistake.


    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      Thanks for the heads-up!
Re: daemon ... oop'ified?
by kiat (Vicar) on Jun 17, 2005 at 08:34 UTC
    Welocme back :)

    ( wow, it's been 5! years since I last logged into perlmonks... ah, memories )
    I believe all your stuff (writeups, scratchpad, etc) PM is preserved. I had a Hotmail account and because I hadn't accessed it for slightly over 30 days, everything was erased - old mail, contacts - when I re-activated the account recently. A re-activation is required by Hotmail in such cases.

      Yep, everything's all still there - untouched; I was really surprised!

      Cheers!