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

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

I have a script that does some stuff in a tight loop and than sleeps at the end of each loop for an unpredicatable amount of time (few seconds to few minutes).

(A) I want it to run continuously in the background

(B) I don't want to accidentally run two of them at the same time.

(C) I want it to automatically come back alive after system boot.

(D) It might accidentally get killed, so I want it to be able to tollerate that.

My guesses are as follows:

? Open a dummy lock file when the script starts and don't close it? Die if you cant open it? Code for this? open or die?

? Edit some kind of file in my /home/andrew directory that gets automatically executed and run it with a trailing &? to run in the background.

Will this satisfy the above requirements?

Update: It's not a networking or IP related program. Sorry, the daemon title through you off. I meant it in the sense of a program that stays uniquely alive. I guess daemon's accepted meaning is something small that listens on a port like daytime or ntp - this is not what I mean.

-Andrew.


Andrew Tomazos  |  andrew@tomazos.com  |  www.tomazos.com

Replies are listed 'Best First'.
Re: Writing a Perl Daemon
by cees (Curate) on Jul 28, 2005 at 13:42 UTC

    Using Proc::PID_File and Proc::Daemon should get you most of the way there. Here is a chopped down version of a daemon script that I use, complete with logging. Hope it is a helpful starting point.

    #!/usr/bin/perl use strict; use warnings; use constant LOG_DIR => '/var/log/mydaemon'; use constant LOG_FILE => 'mydaemon.log'; use constant PIDDIR => LOG_DIR; use Proc::PID_File; use Proc::Daemon; use Log::Dispatch; use Log::Dispatch::File; use Date::Format; use File::Spec; sub dienice ($); # # fork and background process # our $ME = $0; $ME =~ s|.*/||; our $PIDFILE = PIDDIR."/$ME.pid"; startDaemon(); # # Setup a logging agent # our $HOSTNAME = `hostname`; chomp $HOSTNAME; my $log = new Log::Dispatch( callbacks => sub { my %h=@_; return Date::Format::time2str('%B % +e %T', time)." ".$HOSTNAME." $0\[$$]: ".$h{message}."\n"; } ); $log->add( Log::Dispatch::File->new( name => 'file1', min_level => 'warning', mode => 'append', filename => File::Spec->catfile( +LOG_DIR, LOG_FILE), ) ); $log->warning("Starting Processing: ".time()); # # Setup signal handlers so that we have time to cleanup before shuttin +g down # my $keep_going = 1; $SIG{HUP} = sub { $log->warning("Caught SIGHUP: exiting gracefully") +; $keep_going = 0; }; $SIG{INT} = sub { $log->warning("Caught SIGINT: exiting gracefully") +; $keep_going = 0; }; $SIG{QUIT} = sub { $log->warning("Caught SIGQUIT: exiting gracefully" +); $keep_going = 0; }; #$SIG{TERM} = sub { $log->warning("Caught SIGTERM: exiting gracefully +"); $keep_going = 0; }; # # enter main loop # while ($keep_going) { # do something useful here } # # Mark a clean exit in the log # $log->warning("Stopping Processing: ".time()); # # startDaemon # # Fork and detach from the parent process # sub startDaemon { # # Fork and detach from the parent process # # eval { close DATA; }; # having __END__ will trigger __DATA__ to ope +n and should be closed eval { Proc::Daemon::Init; }; if ($@) { dienice("Unable to start daemon: $@"); } # # Get a PID file # dienice("Already running!") if hold_pid_file($PIDFILE); } # # dienice # # write die messages to the log before die'ing # sub dienice ($) { my ($package, $filename, $line) = caller; $log->critical("$_[0] at line $line in $filename"); die $_[0]; }
      # # startDaemon # # Fork and detach from the parent process # sub startDaemon { # # Fork and detach from the parent process # eval { Proc::Daemon::Init; }; if ($@) { dienice("Unable to start daemon: $@"); } # # Get a PID file # dienice("Already running!") if hold_pid_file($PIDFILE); } # # dienice # # write die messages to the log before die'ing # sub dienice ($) { my ($package, $filename, $line) = caller; $log->critical("$_[0] at line $line in $filename"); die $_[0]; }
      I am not getting any error ! but the daemon is also not running ? What might be wrong ! I tried to debug it , but it hangs !
      One gotcha that got me is if you are using sockets to talk to the daemon, then remember to catch SIGPIPE, or ignore it:
      $SIG{PIPE} = 'IGNORE';
      You forgot to include your hold_pid_file function - prob why it doesn't work.
      DB<1> stop in Proc::Daemon::Init Debugged program terminated. Use q to quit or R to restart, use O inhibit_exit to avoid stopping after program termination, h q, h R or h O to get additional info. DB<2> Debugged program terminated. Use q to quit or R to restart, use O inhibit_exit to avoid stopping after program termination, h q, h R or h O to get additional info. ######### Forked, but do not know how to create a new TTY. ######### Since two debuggers fight for the same TTY, input is severely entangled. I know how to switch the output to a different window in xterms and OS/2 consoles only. For a manual switch, put the name of the created TTY in $DB::fork_TTY, or define a function DB::get_fork_TTY() returning this. On UNIX-like systems one can get the name of a TTY for the given window by typing tty, and disconnect the shell from TTY by sleep 1000000. {pid=5701} DB<2> s
Re: Writing a Perl Daemon
by jdhedden (Deacon) on Jul 28, 2005 at 13:58 UTC
    cees' example above is extremely comprehensive, but here's something to show just the bare bones of what's needed.
    #!/usr/bin/perl use strict; use warnings; use Proc::Daemon; use Proc::PID::File; MAIN: { # Daemonize Proc::Daemon::Init(); # If already running, then exit if (Proc::PID::File->running()) { exit(0); } # Perform initializes here # Enter loop to do work for (;;) { # Do whatcha gotta do } }

    Remember: There's always one more bug.
Re: Writing a Perl Daemon
by Zaxo (Archbishop) on Jul 28, 2005 at 06:48 UTC

    A) Super Search for daemon will find some nodes on writing perpetual "background" processes. There are some fiddly bits to look after, but Proc::Daemon will do a fine job of that for you.

    B) A convenient way of using a lockfile is with sysopen;

    sysopen my $fh, '/var/lock/mydaemon', O_EXCL | O_RDWR or die "$0 is already running";
    You should ensure that you call unlink '/var/lock/mydaemon'; in every exit path your code may take.

    After Compline,
    Zaxo

Re: Writing a Perl Daemon
by ikegami (Patriarch) on Jul 28, 2005 at 06:20 UTC

    (B) Try to flock a lockfile without blocking. Exit if you didn't get the lock.

    Alternatively, if your application is an IP server which always listens on the same port, forget the lockfile and exit if you can't create the server socket. You can't create two sockets on the same port, so the socket will act as the lockfile.

    (C) Launch it in your OS's startup script.

    (D) Create a cron job that starts the script periodically. (B) will make sure only one is running.

    Update: By the way, you might want to have the script check for the presence of a specific file (perhaps called "no_start") at startup. If the file exists, then exit immediately. That will allow you to kill the server to do maintenance without worrying about it restarting itself.

Re: Writing a Perl Daemon
by Your Mother (Archbishop) on Jul 28, 2005 at 06:29 UTC

    I recommend it one more time; get "Network Programming with Perl" ISBN 0201615711. All the code is available, by press and author's permission, for free so you can peruse: Network Programming with Perl. Interruptable, restartable, reinit'able daemons. You won't have any guesses, or slow, system specific memory leaks, or problems afterward. You can probably set up your final daemon to start with your system in /etc/inetd.conf but some of the more system savvy kids here might have better advice.

Re: Writing a Perl Daemon
by cosimo (Hermit) on Jul 28, 2005 at 08:08 UTC

    1. In your script, a while(1)-like cycle with some kind of sleep or select(undef, undef, undef, [time]) will do.

    2. Use a lock file and write your pid ($$) in it. In this way, daemon startup code can check for lock file, and if the pid stored in it is not alive (kill 0, $pid) you can safely start the current instance and overwrite the pid file.

    3. A simple way is to append a line in your /etc/rc.d/rc.local file, like the following:
      # Start my daemon... logger "My Daemon starting. All you out there, watch out!" /mypath/mydaemon.pl &
    4. AFAIK, it isn't possible to "tolerate" a SIGKILL. However, you can restart your deamon ASAP. To achieve that, you probably want to modify your /etc/inittab file. In this way, your init process takes care of restarting your daemon when it detects that it is not running. Put a line like this in inittab:
      # Run my daemon in runlevels 345 x:345:respawn:/mypath/mydaemon.pl

    Are you running on *nix, are you? :-)

Re: Writing a Perl Daemon
by sk (Curate) on Jul 28, 2005 at 06:23 UTC
    tomazos,

    I had a similar issue (at least the checking if the script is already running) and they way i handled it is by creating a simple file (not necessarily locked). Every time the script starts it checks for .iamrunning file. If it exists then it just dies or exits without doing any processing or it creates one and starts the process.

    If you want it to come back after system boot then you can put it as cron job or you can add it to your .cshrc/.bashrc or whatever you shell start up is.

    regarding the kill - are you talking about Kill signals? Then you can use SIG INT to handle that part

    If you are interested in locking i would do  perldoc -f flock

    cheers SK