in reply to How to get a persistent daemons after exiting the session?

i have an almost what you want. i'm still stuck in a couple of places. Proc::PID::File has some issues (at least the way i want to use it) in that it will delete your pidfile when it shouldn't. i started on a patch/replacement but haven't finished. (can't figure a good way to hold the pidfile locked while i daemonize since Proc::Daemon closes open files (losing the locks).

#!/usr/bin/perl use strict; use warnings; $|++; my $base = '/home/me/testing'; my $name = 'simpledaemon'; my $default_configfile = join '/', $base, $name . ".conf"; =head1 NAME B<simpledaemon> -- A daemon to ... =head1 SYNOPSIS simpledaemon [--start|--stop|--check] [--configfile <file>] Options: --help Display help --man Display man page --start Start the daemon (default) --stop Stop the daemon --check Check the daemon --configfile <file> Specify alternate configuration file (default: simpledaemon.conf) =cut # # handle command line options # use Getopt::Long; use Pod::Usage; my %opt = ( help => 0, man => 0, configfile => $default_configfile, check => 0, stop => 0, start => 1, fork => 1, ); GetOptions(\%opt, 'help', 'man', 'configfile=s', 'check', 'stop', 'start!', 'fork!', ); # # handy dandy help and man options # pod2usage(-verbose => 1), exit if $opt{help}; pod2usage(-verbose => 2), exit if $opt{man}; # # load the config file # use Config::General; my $conf; # the object my %conf; # the handy hash # we eval this just to give pretty error messages... eval { $conf = Config::General->new( -ConfigFile => $opt{configfile}, -InterPolateVars => 1, ); }; if (local $_ = $@) { s/\n.*//; chomp; die "load config failed with: $_\n"; } # and voila! here's our conf %conf = $conf->getall; if (0) { # debugging use Data::Dumper; print Data::Dumper->Dump([\%conf],['conf']); } my $myname = $conf{Simpledaemon}{name}; # save some typing late +r on # # now we start the real work... handling --start, --stop, --check # use Proc::PID::File; my $pidfile; $pidfile = Proc::PID::File->new( # dir => $conf{Simpledaemon}{base}, # default: /var/run # name => 'simpledaemon', # default: prognam +e (.pid g ets added) ); # only one of --check, --stop, --start will be performed. # handle --check if ($opt{check}) { if ($pidfile->alive) { print "$myname appears to be alive (pid ",$pidfile->re +ad,").$/"; } else { print "$myname appears to be dead.$/"; } $pidfile = undef; exit 0; } # handle --stop if ($opt{stop}) { if ($pidfile->alive) { my $pid = $pidfile->read; kill TERM => $pid; sleep 2; if (kill 0, $pid) { print "$myname is hard to kill!$/"; exit 1; } else { print "$myname appears to be dead now.$/"; exit 0; } } else { print "$myname wasn't alive.$/"; exit 1; } } # handle --start sub logfailsafe { open(F, '>>', $conf{Simpledaemon}{logfailsafe}) and print(F @_) and close(F); die "@_"; } use Proc::Daemon; if ($opt{start}) { die "$myname already running!$/" if $pidfile->alive; # # fork if needed # we get a new pidfile here, Proc::Daemon::Init closes the open files +(losing lo cks) # if ($opt{fork}) { $pidfile = undef; Proc::Daemon::Init; # become a dae +mon $pidfile = Proc::PID::File->new( # dir => '$conf{Simpledaemon}{base}', # default: + /var/run # name => 'simpledaemon', # default: + progname (.pid is added) ); } $pidfile->write; # # start our logging, try and fail gracefully # use Log::Log4perl qw(get_logger); eval { # Log::Log4perl::init(\$conf{logconfig}); Log::Log4perl::init_and_watch($conf{Simpledaemon}{logc +onfigfile} , 10); }; if (local $_ = $@) { s/ at .*//; chomp; logfailsafe("logging config failed with: $_\n"); } my $log = get_logger($conf{Simpledaemon}{logger}) or logfailsafe("get_logger failed!\n"); # # safeish death, pidfile is picky # $SIG{INT} = $SIG{TERM} = sub { $log->info("stopping"); $pidfile = undef; exit 0; }; $log->info("starting"); # # here's our loop # while (1) { no strict 'refs'; sleep 20; for my $i (qw/ debug info warn error fatal /) { $log->$i("log $i"); } } # # end of our loop # } # start __END__ =head1 DESCRIPTION This program will... =cut

i never found a CPAN module that did all of the stuff i wanted (like you, stop/start/check). my plan is to eventually get this finished up as a module that i can use for all of my daemons to provide common functionality.

package MyDaemon; use base qw(SimpleDaemon); sub MainLoop { #do work here }

or something similar. i'm open for ideas/comments/pointers to modules etc.

Replies are listed 'Best First'.
Re: Re: How to get a persistent daemons after exiting the session?
by rob_au (Abbot) on Mar 11, 2003 at 22:21 UTC
    can't figure a good way to hold the pidfile locked while i daemonize since Proc::Daemon closes open files (losing the locks)

    This is expected behaviour for Proc::Daemon or indeed any daemon written in line with the recommendations for daemon processes in "Advanced Programming in the Unix Environment" by W. Richard Stevens - This behaviour results from the closure of all open file handles, specifically, the line:

    foreach $i (0 .. OpenMax) { POSIX::close($i); }

    The best way to avoid this behaviour affecting your lock files is to acquire the lock after becoming a daemon process. If this lock acquisition fails, write to the application log and exit - This is relatively standard practice. For example, the Apache web server does something similar with regard to open a listening socket connection - The server will daemonise before opening this socket, which if fails, will write the error to the log file and exit.

    Proc::PID::File has some issues

    I would note that there is also a race condition between the opening of the file and acquisition of the lock and that the return value of the flock function is not checked to ensure that the lock acquisition has been successful.

     

    perl -le 'print+unpack("N",pack("B32","00000000000000000000001000111100"))'

      yes, i know why the files are closed and the locks lost. i'm trying to figure out if it's worth trying to work around by tweaking the fork code. i'm just a little worried about the time between closing all files and reaquiring the pidfile and having logging configured (for any error messages). i think i might just fall back on 'set logging to STDOUT and run with -nofork' if things don't appear to be working.