The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Script::Daemonizer - Turns your script into a UNIX daemon process (the easy way).

VERSION

Version 0.90.00

SYNOPSIS

    use Script::Daemonizer;
    ...
    Script::Daemonizer::daemonize();
    Script::Daemonizer::drop_privileges(
        uid => $to_uid, 
        gid => $to_gid
    );
    Script::Daemonizer::restart();
    ...

ABSTRACT

This module turns your script into a UNIX daemon by requiring as little modification as possible, thus letting you concentrate on solving your problem, rather than on writing a daemon. Just get your job done, then turn your script into a daemon by calling daemonize().

It tries to redirect all messages to syslog by default, using Tie::Syslog.

DESCRIPTION

daemonize() is the main routine of this module. What it does, out-of-the-box, is:

1.* it sets umask() to 0. You must then set explicitly file and directory permissions upon creating them, restore umask() after initialization, or specify umask option (see "ADVANCED USAGE" for details).
2. it calls fork(), then the parent exits;
3. it calls POSIX::setsid() (see POSIX::setsid()), so the process becomes session leader;
4. it calls fork() again, then the parent exits;
5.* it changes its working directory to "/";
6.* it closes all open file descriptors and tries to reopen STDIN from /dev/null;
7.* it ties STDOUT and STDERR to Syslog using Tie::Syslog (if available, otherwise it reopens them on /dev/null) so that all output is logged to syslog (see Tie::Syslog);

Steps marked by * are configurable; some additional steps are also available if explicitly requested; see "ADVANCED USAGE" for details.

EXPORT

Nothing is exported by default, and nothing needs to be imported. You can

    use Script::Daemonize qw(daemonize);
    ...
    daemonize();

or simply call daemonize() with its full name:

    use Script::Daemonize;
    ...
    Script::Daemonizer::daemonize();

SUBROUTINES

daemonize()

It runs through all the steps required to send your program to background as a daemon. Its behaviour can be customized a little, see "ADVANCED USAGE" for details.

drop_privileges()

    # Just drop effective user/group id:
    drop_privileges(
        euid => $to_euid,
        egid => $to_egid,
    );

    # Drop both effective and real ids: 
    drop_privileges(
        uid  => $to_uid,
        gid  => $to_gid,
    );

Tries to drop priviles to given EUID/EGID or UID/GID (single (e)uid/(e)gid allowed). See "perldoc perlvar" for details on IDs.

daemonize() will automatically call drop_privileges() if configured to do so (guess what? See "ADVANCED USAGE" for details) but this will happen before anything else (think of this as step 0). If you need to drop privileges at a later moment, use drop_privileges(), otherwise it's probably safer to do so while daemonize()-ing.

restart()

restart() is there to let you restart completely the daemon. A simple way to handle SIGHUP might be restarting, for example (see perlipc for details).

    # Restart upon sighup - use a closure to call restart()
    $SIG{HUP} = sub {
        Script::Daemonizer::restart;
    }; 

pidfile (see "ADVANCED USAGE" for details) is kept open and locked upon restart (if configured) so no race condition should happen.

You can pass command line args to restart() to modify command line on-the-fly:

    use Script::Daemonizer qw/restart/;

    # Do some mangling to @ARGV
    ...

    restart(@my_modified_argv);

The defaul is to use a copy of @ARGV taken at compile-time (before any command-line-parsing modifies @ARGV, for example).

sigunmask()

    Script::Daemonizer::sigunmask( @signals );

Strictly related to restart(), sigunmask() is there to let you unmask signals without pain. See http://docstore.mik.ua/orelly/perl4/cook/ch17_19.htm for details on why you should unmask signals. In short: inside a signal handler the signal that triggered the handler is blocked. If inside a signal handler you re-exec() yourself, the new process inherits the blocked signal. That is why you could want to unmask that signal after a new start.

SGIHUP is unmasked by defautl, just by saying use Script::Daemonizer.

If you use restart() with other signals, remember to unmask them:

    # Restart on SIGHUP, SIGQUIT and SIGUSR1
    for my $nal in (qw/HUP QUIT USR1/) {
        $SIG{$nal} = sub {
            Script::Daemonizer::restart;
        }
    }

    # Just in case we came from another instance of ourselves via an exec()
    # (no need to bother for SIGHUP since we unmask it by default, anyway
    # listing it here it's harmless): 

    Script::Daemonizer::sigunmask(qw{QUIT USR1});

ADVANCED USAGE

I strive to make this module support "standard" daemon features out-of-the-box (for some definition of "standard"). Some of these features can be configured, and some other are enabled only if configured.

ADVANCED SYNOPSYS

Advanced configuration syntax is the following:

    use Script::Daemonizer;
    Script::Daemonizer::daemonize(
        name            => "My wonderful new daemon",      # tag for logging
        do_not_close_fh => 1,                              # don't touch my filehandles!
        umask           => $my_umask,                      # set umask to $my_umask
        working_dir     => "/var/ftp",                     # try to chdir here
        drop_privileges => {                               # same as drop_privileges()
            uid  => $to_uid,
            gid  => $to_gid,
        },
    );

    # or

    Script::Daemonizer::daemonize(
        name => "My wonderful new daemon",                 # tag for logging
        keep => [ 0, 1, 2, $myfh, $anotherfh, 42 ],        # don't close these FD/FH
    );

    # or

    Script::Daemonizer::daemonize(
        name                  => "ddddddaeeemonnn",        # tag for logging
        do_not_tie_stdhandles => 1,                        # skip tie-to-syslog
    );
    
    # or

    Script::Daemonizer::daemonize(
        name                  => "Yeah",                   # tag for logging
        tie_to_log4perl       => 1,                        # try tying with Tie::Log4perl
    );
    

OPTIONAL ACTIONS

Some options have no default and thus corresponding actions are skipped if not configured. These are:

  • Step 0.0 - privileges dropping

    It happens before anything else. This simply calls drop_privileges() internally.

  • Step 0.1 - pidfile creation (and locking)

    This implicitly provides a method to ensure that only one copy of your daemon is running at once, because pidfile is locked. PID is written into pidfile only after the second fork() has been completed.

  • Handling of SIGHUP as a restart()

    You must set up restart() as the handler of SIGHUP yourself. See "restart()" for details.

  • Unmaksing signals

    If you use signals other than SIGHUP for restarting, remember to sigunmask() them (see "sigunmaks()" for details).

ADVANCED OPTIONS

Advanced options are the following:

name

Sets the name of the daemon. This is used for logging.

default: script name, got from $0, split on '/';

do_not_close_fh

Skips the close and re-open filehandles phase of initialization. This means that the tie-to-syslog part will be skipped as well.

default: undef - close all filehandles; if possible, tie STDOUT and STDERR to syslog.

do_not_tie_stdhandles

Define this to close STDOUT/STDERR and reopen them from /dev/null.

    # close both stdhandles and reopen them on /dev/null
    Script::Daemonizer::daemonize(
        name                  => 'Sort of a daemon',
        do_not_tie_stdhandles => 1,
    );

Default is to tie STDOUT and STDERR with to facility LOG_DAEMON, priorities LOG_INFO and LOG_ERR, logopt 'pid,ndely' (see Tie::Syslog for details).

default: undef - tie stdhandles so that output will go to syslog.

keep

    # Unqualified NAMES will be qualified as *main::$FH_NAME
    Script::Daemonizer::daemonize(
        keep => [
            "STDIN",                # A filehandle, by name
            *STDOUT,                # A filehandle
            \*STDERR,               # A fh reference
            3,                      # file descriptor number
            "MyPkg::FH",            # F-Q filehandle name
            $myfh                   # Another fh reference
        ],
    );

This option requires a reference to an array containing the filehandles to be kept open, or the corresponding file descriptor (as returned by fileno()). It is ingored if do_not_close_fh was specified (because redundant).

If STDOUT/STDERR (or the corresponding file descriptor: 1 or 2) were specified, then that handle would not be closed and, consequently, not tied to Syslog. To tie just one of the two filehandles, specify the other in keep and then close it (or keep it open, or whatever you prefer):

    # Tie just STDERR to syslog, discard STDOUT:
    Script::Daemonizer::daemonize(
        name => 'Sort of a daemon',
        keep => [ 1 ],
    );
    open(STDOUT, '>', '/dev/null') or die "Cannot reopen STDOUT on /dev/null: $!"

default: undef - close all filehandles.

pidfile

This will try to:

- open named pidfile, creating it if non-existent;
- lock it (exclusively);

If this operation fails, daemonize() will croak(). Otherwise, PID of the process will be written to the named pidfile after the second fork() is done.

    Script::Daemonizer::daemonize(
        name    => 'A new daemon',
        pidfile => '/var/run/anewdaemon.pid',
    );

This lock mechanism provides the following advantages:

  • no other instances of the same daemon will overwrite the pidfile on purpose;

  • the pid file will serve as a lock file, ensuring no other instances of the same daemon will start;

  • an fuser (see fuser(1)) on the pidfile will reveal the daemon's pid. If the daemon is not running, the pidfile will not be in use by any process (hopefully). A simple:

        $ fuser -k $PIDFILE

    will end up killing the daemon (or provides an easy way to signal the daemon).

tie_to_log4perl

Try using Tie::Log4perl instead of Tie::Syslog to redirect STDOUT and STDERR.

default: undef - use Tie::Syslog.

umask

Set the specified umask.

default: 0

working_dir

Try to chdir() to the specified directory.

default: '/'

LOCKING

If you want to be sure no multiple instances of your daemon will be running, just use pidfile advanced option. See "pidfile" for details.

CAVEATS

Concerning filehandles

Filehandles cannot really be closed. See the sources of Proc::Daemon and this discussion for details.

Briefly: POSIX::close() issues a close on Operating System's side, but our process keeps a filehandle opened somewhere. Tracking these filehandles (or fh references) from inside a namespace (such as Script::Daemonizer) is neither easy, nor possible in general (think about lexical references in other namespaces: they're just non accessible from Script::Daemonizer at all, nor maybe from main::, if they're confined somewhere in modules). So the best we can do is go through a POSIX::close() on all file descriptors and then try to reopen as many fh as needed to get all those fd opened on /dev/null, so that associated filehandles are still open, but redirected on /dev/null.

In other words, if your script opens a lot of filehandles before calling daemonize(), you will still find them open, but towards /dev/null, after daemonize()-ing. The only way to grt rid of them is to close() them all just where they were created (or let the filehandles/references go out of scope).

FAQ

Q: Why is there no built-in start/stop/* method?

A: Because to start/stop/* a daemon you usually don't call the daemon itself, instead you rely on system tools that allow process control (if you need an "init script" then write one) and interact with processes through singals. Lack of start/stop/* methods is regardes as a feature.

Q: Why are Tie::Syslog and Tie::Log4perl not listed as prerequisites?

A: Because you may want to skip Syslog/Log4perl support and not install those modules. This module will work nonetheless, without using them (and without the features they provide, of course).

TODO

Some ideas:

  • Let user set her own options for tied STDOUT/STDERR (i.e. logopt for syslog (see Sys::Syslog), facility, priority).

  • Provide a function to automatically parse command line (via Getopt::Long).

  • Provide a function to automatically handle configuration file (via Config::General).

AUTHOR

Giacomo Montagner, <gmork at entirelyunlike.net >, <gmork.gmork at gmail.com >

BUGS

Please report any bugs or feature requests to bug-script-daemonizer at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Script-Daemonizer. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Script::Daemonizer

You can also look for information at:

ACKNOWLEDGEMENTS

  • "Advanced Programming in the UNIX Environment: Second Edition", by W. Richard Stevens, Stephen A. Rago, Copyright 2005 Addison Wesley Professional

  • Part of the code was inspired (or taken) from Proc::Daemon, by Earl Hood and Detlef Pilzecker.

LICENSE AND COPYRIGHT

Copyright (C) 2012 Giacomo Montagner, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.