61

What's a simple way to get a Perl script to run as a daemon in linux?

Currently, this is on CentOS. I'd want it to start up with the system and shutdown with the system, so some /etc/rc.d/init.d integration would also be nice, but I could always add a custom line to /etc/rc.d/rc.local.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
jedihawk
  • 814
  • 1
  • 12
  • 12

4 Answers4

88

The easiest way is to use Proc::Daemon.

#!/usr/bin/perl

use strict;
use warnings;
use Proc::Daemon;

Proc::Daemon::Init;

my $continue = 1;
$SIG{TERM} = sub { $continue = 0 };

while ($continue) {
     #do stuff
}

Alternately you could do all of the things Proc::Daemon does:

  1. Fork a child and exits the parent process.
  2. Become a session leader (which detaches the program from the controlling terminal).
  3. Fork another child process and exit first child. This prevents the potential of acquiring a controlling terminal.
  4. Change the current working directory to "/".
  5. Clear the file creation mask.
  6. Close all open file descriptors.

Integrating with the runlevel system is easy. You need a script like the following (replace XXXXXXXXXXXX with the Perl script's name, YYYYYYYYYYYYYYYYYYY with a description of what it does, and /path/to with path to the Perl script) in /etc/init.d. Since you are using CentOS, once you have the script in /etc/init.d, you can just use chkconfig to turn it off or on in the various runlevels.

#!/bin/bash
#
# XXXXXXXXXXXX This starts and stops XXXXXXXXXXXX
#
# chkconfig: 2345 12 88
# description: XXXXXXXXXXXX is YYYYYYYYYYYYYYYYYYY
# processname: XXXXXXXXXXXX
# pidfile: /var/run/XXXXXXXXXXXX.pid
### BEGIN INIT INFO
# Provides: $XXXXXXXXXXXX
### END INIT INFO

# Source function library.
. /etc/init.d/functions

binary="/path/to/XXXXXXXXXXXX"

[ -x $binary ] || exit 0

RETVAL=0

start() {
    echo -n "Starting XXXXXXXXXXXX: "
    daemon $binary
    RETVAL=$?
    PID=$!
    echo
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/XXXXXXXXXXXX

    echo $PID > /var/run/XXXXXXXXXXXX.pid
}

stop() {
    echo -n "Shutting down XXXXXXXXXXXX: "
    killproc XXXXXXXXXXXX
    RETVAL=$?
    echo
    if [ $RETVAL -eq 0 ]; then
        rm -f /var/lock/subsys/XXXXXXXXXXXX
        rm -f /var/run/XXXXXXXXXXXX.pid
    fi
}

restart() {
    echo -n "Restarting XXXXXXXXXXXX: "
    stop
    sleep 2
    start
}

case "$1" in
    start)
        start
    ;;
    stop)
        stop
    ;;
    status)
        status XXXXXXXXXXXX
    ;;
    restart)
        restart
    ;;
    *)
        echo "Usage: $0 {start|stop|status|restart}"
    ;;
esac

exit 0
Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
  • I got Service X does not support chkconfig. when run a chkconfig -add X. any suggestion? – Jirapong Jul 31 '09 at 08:04
  • ah, sorry turn out to be my fault. typo on #description. many thanks! – Jirapong Jul 31 '09 at 08:08
  • Can I just copy this script after replacing XX and YY values to /etc/init.d ? When I run it is not running like a service (In background), How can I make the daemon like httpd ? – Kishore Relangi Jul 24 '13 at 15:03
  • @KishoreRelangi Hmmm, the `daemon $binary` line should start the program up in the background. Can you post your script to another question or send it to me via email (my email is in my profile)? – Chas. Owens Jul 25 '13 at 02:26
  • Some additional comments would be helpful to me. I gather that the Proc::Daemon example illustrates a server process becoming a daemon (i.e. forking) at the ::Init statement? What becomes of the parent process? (Also, the Proc::Daemon link is dead.) – Chap Sep 09 '13 at 15:27
  • 1
    @Chap The parent and child processes both exit. The grandchild runs as the daemon. I have updated the link to the new permalink, would one think that a permalink would be permanent. – Chas. Owens Sep 09 '13 at 16:14
  • It's worth noting that `Proc::Daemon::Init` only does what you want here (having the parent exit) if called in a void context. In other contexts, the parent and child both return, and you have to check the return value to decide which is which and what to do. For the full story (and a more object-oriented interface), read the manual. – Nate Eldredge May 01 '19 at 16:23
45

If you don't have Proc::Daemon as suggested by Chas. Owens, here's how you'd do it by hand:

sub daemonize {
   use POSIX;
   POSIX::setsid or die "setsid: $!";
   my $pid = fork() // die $!; #//
   exit(0) if $pid;

   chdir "/";
   umask 0;
   for (0 .. (POSIX::sysconf (&POSIX::_SC_OPEN_MAX) || 1024))
      { POSIX::close $_ }
   open (STDIN, "</dev/null");
   open (STDOUT, ">/dev/null");
   open (STDERR, ">&STDOUT");
 }
mpapec
  • 50,217
  • 8
  • 67
  • 127
Bklyn
  • 2,559
  • 2
  • 20
  • 16
  • 1
    I'd say it's a lot easier to just install Proc::Daemon than to bother hand-rolling it, but it's always good that TMTOWTDI :) – David Precious Apr 20 '09 at 18:07
  • Actually that one worked better for me than the previous one. I have control over every input output. Guess that you can find it in Proc::Daemon too! –  Aug 19 '11 at 09:50
  • 10
    Upvoted for not requiring a non-core module, which matters sometimes. – Aaron Miller Jul 22 '13 at 22:58
  • Removed the fork and substituted Parallel::ForkManager and I had a nice daemon spawner. Thanks! – fbicknel Jun 30 '15 at 18:14
  • Didn't this or something smiler used to be in the perldoc for the fork() function? – JGNI Jun 24 '21 at 08:58
8

I think the easiest way is to use daemon. It allows you to run any process as a daemon. This means you don't have to worry about libraries if you, for example, decided to change to python. To use it, just use:

daemon myscript args

This should be available on most distros, but it might not be installed by default.

Zifre
  • 26,504
  • 11
  • 85
  • 105
  • It's a cool idea, but I know some production environments don't have access to the daemon binary. Personally I would see this more of a test/development platform than for something you'd use in an enterprise production environment. Good info though - I'd not heard of it before. (Edit: didn't realize the thread was 5 years old, but still pertinent IMO) – Tim S. Dec 15 '14 at 17:32
1

I used supervisor for running a perl script.

As a system administrator, I like to minimise changes and variations among server and like to stick to core services or bare minimum.

Supervisor was already installed and available for a python-flask application running on the same box. So, I just added a conf file for the perl script I wanted to run as a service. Now, I can do

supervisorctl start/stop/restart my_perl_script_supervisor_service_name
Ajitabh Pandey
  • 123
  • 2
  • 10