Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

daemon ... oop'ified?

by Zarathustra (Beadle)
on Jun 17, 2005 at 08:05 UTC ( [id://467611]=perlquestion: print w/replies, xml ) Need Help??

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!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://467611]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-04-24 12:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found