http://qs1969.pair.com?node_id=241875

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

HI Monks,

I am in the process of building a daemonized webservice and am looking for a good way to turn it on/off.

What I'd really like to do is something like what apache does where you can call:
httpd start
httpd stop
httpd restart

I've seen shell scripts used to do something like this and would be a lot happier doing it in Perl, but there's still one nagging problem: I need to jeep the job running even if I kill my session.

I can not find any way to do this other than through the use of nohup on the linux/unix. The trouble is that when I use nohup, I get issues in some of the instantiations within my script (it has an HTTP::Daemon listener which takes up to 30 seconds to reply under nohup for a reason I have not yet understood).

So my questions are the following:

1/ Is the a pure-perl replacement for nohup that would allow me to start a perl daemon and then keep it running even if I close my session?

2/ Other than using something like semaphores or equivalent, can anyone think of a cute way to do 1/ above and restart the script by calling it?

Thanks for putting up with this ignorance,

Hackmare

Roasp: Perl SVG Portal

  • Comment on How to get a persistent daemons after exiting the session?

Replies are listed 'Best First'.
Re: How to get a persistent daemons after exiting the session?
by valdez (Monsignor) on Mar 10, 2003 at 23:58 UTC

    1) You can use Proc::Daemon:

    This module contains the routine Init which can be called by a perl program to initialize itself as a daemon. A daemon is a process that runs in the background with no controlling terminal. Generally servers (like FTP and HTTP servers) run as daemon processes.

    An example useful to understand the technique behind this can be found in perlipc, under section Background Processes

    2) Proc::PID::File instead is useful for writers of daemons and other processes that need to tell whether they are already running.

    HTH, Valerio

Re: How to get a persistent daemons after exiting the session?
by zengargoyle (Deacon) on Mar 11, 2003 at 00:23 UTC

    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.

      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.

Re: How to get a persistent daemons after exiting the session?
by rjray (Chaplain) on Mar 10, 2003 at 23:58 UTC

    Rather than offering code, which would look like hundreds of other code-fragments that do basically the same thing, I instead recommend that you look at either or both of the following:

    The Net::Server module offers some very easy-to-use models for network daemon applications. And I consider Lincoln Stein's NPWP to be indispensible.

    --rjray

Re: How to get a persistent daemons after exiting the session?
by tachyon (Chancellor) on Mar 11, 2003 at 00:22 UTC

    By the sounds of it what you want to do is have a server deamon running all the time. Your CGI then interacts with this re-initializing it and doing whatever else as required. There are endless examples of server daemons on this site as we as plenty of modules on CPAN.

    A problem with system calls per se and restarting services is permission. If the service is not running as the webuser 'nobody', 'apache', 'www', etc you need to have a suid script which opens a whole new bag of worms. With a custom server you can run it as the web user and avoid the issue entirely.

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: How to get a persistent daemons after exiting the session?
by bronto (Priest) on Mar 11, 2003 at 09:40 UTC
    I can not find any way to do this other than through the use of nohup on the linux/unix.

    You could start your program in background in a subshell, i.e.: instead of simply calling something like:

    /usr/local/bin/myprogram.pl &

    you could run it as

    ( /usr/local/bin/myprogram.pl & )

    Putting those parentheses around starts a new shell and runs the program in it. When you close your "main shell" (that is, the one from which you ran the program) the program remains alive.

    Ciao!
    --bronto


    The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
    --John M. Dlugosz

      Simple, elegant, unfortunately won't work on DOS/win systems.

      I'm using this great idea and it is working very well. At least it buys me time to get something more rigorous in place. Thanks for this pointer.

      hackmare.