Creating a Perl Daemon in Ubuntu

If you have a process that need to run in the background, creating a daemon is the key. Some examples of what your can do:

  • Wait for video files to be dropped via FTP and then process them
  • Check for a recurring problem and report it to admin
  • Monitor system load and report to the admin at a certain threshold

In this post we’ll take a look at how to create a Perl daemon script and how to get it to run as a daemon whenever the system is started. For the Perl script, a template will be created and for control of the daemon process, standard Debian facilities will be used.

What will be covered:

  • Create the start/stop script in /etc/init.d
  • Create the Perl daemon script in /usr/bin/
  • Create the log file in /var/log/
  • Get the daemon to start automatically when the system boots
  • How to manage your daemon

The start/stop script starts and stops the daemon. It is also used by the system to start your daemon when the system starts.

The Perl daemon script contains your custom Perl code to run in the background. It executes your code every x number of seconds.

Create the Start/stop Script

Happily, Debian and therefore Ubuntu, supplies a template that uses the Debian start-stop-daemon command to start and stop daemons. It is only necessary to copy this template to a new file of the correct name and modify it for the purpose.

These scripts reside in the /etc/init.d/ directory. The script you create will have the exact same filename as the Perl daemon script you will create and the name cannot have a file extension – just the bare name.

Substitute your daemon’s name for “mydaemon” in the examples.

sudo cp /etc/init.d/skeleton /etc/init.d/mydaemon
sudo chmod +x /etc/init.d/mydaemon
sudo vi /etc/init.d/mydaemon

Make changes to your start/stop script. Locate the header in the script:

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Description of the service"
NAME=daemonexecutablename
DAEMON=/usr/bin/$NAME
DAEMON_ARGS="--options args"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
  • Change the content of DESC to a meaningful description of your daemon.
  • Change the content for NAME to the exact name of your daemon.
  • Create the Perl Daemon Script

    Use the Perl Daemon Script Template located at the end of this post. In the following, the script template is copied to /usr/bin/where executables specific to this system are located. Actually, you would copy the template to your home directory, make the changes and then copy the daemon script to /usr/bin/.

    sudo cp daemon_template.pl /usr/bin/mydaemon
    sudo vi /usr/bin/mydaemon
    sudo chmod +x /usr/bin/mydaemon
    

    The script template is commented with the changes that are needed. In particular, change the value of $daemonName to the exact name of your new daemon. Ten, add your custom code where # do something appears.

    Prerequisites

    The script uses File::Pid and POSIX from CPAN, so you’ll need to install that module:

    sudo aptitude install cpan
    sudo cpan POSIX
    sudo cpan File::Pid
    

    Create the Log File

    We’ll create the log file so it has the correct ownership and permissions. The log fie has the daemon name appended with “.log”. It is located in the /var/log/ directory.

    sudo touch /var/log/mydaemon.log
    sudo chmod 640 /var/log/mydaemon.log
    sudo chown root:adm /var/log/mydaemon.log
    

    The permissions and ownership change allow adm group members to read the log, per convention. Add yourself to adm group to view logs.

    Run your Daemon

    As you have created a standard start/stop script for your daemon, you can start it the standard way:

    sudo /etc/init.d/mydaemon start
    

    Stop your Daemon

    Similarly, your daemon is easy to stop and restart:

    sudo /etc/init.d/mydaemon stop
    
    sudo /etc/init.d/mydaemon restart
    

    Automatically Start your Daemon

    This command tells your system to start your daemon automatically when the system starts:

    update-rc.d mydaemon defaults 99
    

    To keep your daemon from starting, if needed:

    update-rc.d -f mydaemon remove
    

    Managing Daemons

    List running daemons

    Well-behaved daemons generally create a PID file in /var/run/. List that directory and your have a fair list of running daemons.

    sudo ls /var/run/
    

    Get PID for a particular daemon

    If you know the name of a daemon and want to see if it is running and get its PID, use pgrep.

    pgrep mydaemon
    

    Also useful is the ps command, which list processes.

    ps aux
    

    The aux switch limits output to processes not associated with a terminal.

    Show programs running as daemons

    This line attempts to show programs that are running as daemons.

    which `ps aux | cut -c 66- | cut -d\  -f 1` | sort | uniq
    

    The Perl Daemon Script Template

    #!/usr/bin/perl -w
    #
    # mydaemon.pl by Andrew Ault, www.andrewault.net
    #
    # Free software. Use this as you wish.
    #
    # Throughout this template "mydaemon" is used where the name of your daemon should
    # be, replace occurrences of "mydaemon" with the name of your daemon.
    #
    # This name will also be the exact name to give this file (WITHOUT a ".pl" extension).
    #
    # It is also the exact name to give the start-stop script that will go into the
    # /etc/init.d/ directory.
    #
    # It is also the name of the log file in the /var/log/ directory WITH a ".log"
    # file extension.
    #
    # Replace "# do something" with your super useful code.
    #
    # Use "# logEntry("log something");" to log whatever your need to see in the log.
    #
    use strict;
    use warnings;
    use POSIX;
    use File::Pid;
     
    # make "mydaemon.log" file in /var/log/ with "chown root:adm mydaemon"
     
    # TODO: change "mydaemon" to the exact name of your daemon.
    my $daemonName    = "mydaemon";
    #
    my $dieNow        = 0;                                     # used for "infinte loop" construct - allows daemon mode to gracefully exit
    my $sleepMainLoop = 120;                                    # number of seconds to wait between "do something" execution after queue is clear
    my $logging       = 1;                                     # 1= logging is on
    my $logFilePath   = "/var/log/";                           # log file path
    my $logFile       = $logFilePath . $daemonName . ".log";
    my $pidFilePath   = "/var/run/";                           # PID file path
    my $pidFile       = $pidFilePath . $daemonName . ".pid";
     
    # daemonize
    use POSIX qw(setsid);
    chdir '/';
    umask 0;
    open STDIN,  '/dev/null'   or die "Can't read /dev/null: $!";
    open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
    open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
    defined( my $pid = fork ) or die "Can't fork: $!";
    exit if $pid;
     
    # dissociate this process from the controlling terminal that started it and stop being part
    # of whatever process group this process was a part of.
    POSIX::setsid() or die "Can't start a new session.";
     
    # callback signal handler for signals.
    $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signalHandler;
    $SIG{PIPE} = 'ignore';
     
    # create pid file in /var/run/
    my $pidfile = File::Pid->new( { file => $pidFile, } );
     
    $pidfile->write or die "Can't write PID file, /dev/null: $!";
     
    # turn on logging
    if ($logging) {
    	open LOG, ">>$logFile";
    	select((select(LOG), $|=1)[0]); # make the log file "hot" - turn off buffering
    }
     
    # "infinite" loop where some useful process happens
    until ($dieNow) {
    	sleep($sleepMainLoop);
     
    	# TODO: put your custom code here!
    	# do something
     
    	# logEntry("log something"); # use this to log whatever you need to
    }
     
    # add a line to the log file
    sub logEntry {
    	my ($logText) = @_;
    	my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
    	my $dateTime = sprintf "%4d-%02d-%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
    	if ($logging) {
    		print LOG "$dateTime $logText\n";
    	}
    }
     
    # catch signals and end the program if one is caught.
    sub signalHandler {
    	$dieNow = 1;    # this will cause the "infinite loop" to exit
    }
     
    # do this stuff when exit() is called.
    END {
    	if ($logging) { close LOG }
    	$pidfile->remove if defined $pidfile;
    }
    

    UPDATE:

    Looks like there’s a page in French about this post: http://www.duncane.net/2011/02/25/perl-daemon/. Merci Duncane!

    Be Sociable, Share!

    26 thoughts on “Creating a Perl Daemon in Ubuntu

    1. Hi,
      Nice Code.. Working too.. i found log entry.. using logEntry(“log something”); function in “TODO” area..
      so i guess it work with my codes too..

      Thanks

    2. my $pidFile = $pidFIlePath . $daemonNam my $pid;

      hey man i am getting error here can u please check it….

      please do it i need it very urgent …

    3. my $pidFile = $pidFIlePath . $daemonNam . “.pid”;

      hello friend can you please check this code again and according to me it might be

      my $pidFile = $pidFilePath . $daemonName . “.pid”;

      please check the code again…. and check spelling of scalar variables.

      :)

    4. Friends it is also required to install the CPAN and use POSIX; File::Pid; modules previously in Perl to run this module.

      Cpan Install : sudo apt-get cpan
      Modules : sudo cpan POSIX
      sudo cpan File::Pid

      Enjoy Coding..!!

    5. please correct me if im wrong, but…

      if you call exit in the parent process(at line 48 “exit if $pid;”) before the $pidfile object has been created, the END{} block is called and tries to cleanup $pidfile before it has been created? thus throwing an error/warning?

    6. hi , thank you for the code , it looks working !

      but , it seems that log file is filled only when i stop the daemon , not while it’s running

    7. @oznero:

      I modified the begin log file @ line 63 to make the log file “hot” (removed buffering) so it will write to the log as it runs.

      -Andrew

    8. where i doing wrong?

      sudo aptitude install cpan
      sudo cpan POSIX
      sudo cpan File::Pid

      i haveinstalled all of that with default walues except where it asks for location country i set to my country Croatia thats all.

      1. sudo cp /etc/init.d/skeleton /etc/init.d/dder
      2. sudo chmod +x /etc/init.d/dder
      3. sudo vi /etc/init.d/dder

      4.
      PATH=/sbin:/usr/sbin:/bin:/usr/bin
      DESC=”dder Module”
      NAME=dder
      DAEMON=/usr/sbin/$NAME
      DAEMON_ARGS=”–options args”
      PIDFILE=/var/run/$NAME.pid
      SCRIPTNAME=/etc/init.d/$NAME

      5. thescript.pl file i typed
      http://pastesite.com/20114

      6. sudo cp thescript.pl /usr/bin/dder
      7. sudo vi /usr/bin/dder
      8. sudo chmod +x /usr/bin/dder
      9. sudo touch /var/log/dder.log
      10. sudo chmod 640 /var/log/dder.log
      11. sudo chown root:adm /var/log/dder.log
      12. sudo /etc/init.d/dder start

      and nothing hapens
      ps aux -> not there :(
      htop -> not there :(
      sudo ls /var/run/ -> not there :(
      pgrep dder -> not there :(

      i have fallowing all steps 5 times and i cant get it work! :(
      anyone can help?

      ubuntu server 8 something 0.4 TNL TLS?

    9. @doublezero,

      Looks like your script is “/usr/bin/dder”, but you have “/usr/sbin/dder” in “/etc/init.d/dder”.

      Also, on a less important note, you might use logEntry() @line 40 of your script instead of printing directly to the log.

      Best regards,
      Andrew

    10. damnt!
      thanks for quick response! ;)

      yes “/usr/sbin/dder” was the problem but i copied it from this site here…

    11. -A ?? :P
      btw. nice code… iwe done years back something similar but havent use linux for a while…

    12. Muchas gracias por el how to, tengo problemas al usar el cpan por los modulos posix y file.
      no logro instalr

    13. Script works, if run a second time it wont start up, but if it is run a third time a second instance of the script is in memory.

      So if you run the script 4 times then 2 stances in memory and so on and so forth.

      I cannot find the problem.

      Please help

    14. mydaemon isn’t working for me. The hardware is a MX945GSE (Intel Atom N270 processor) running Debian Squeeze kernel version 2.6.32-5-686.

      $ ls -ls /etc/init.d/mydaemon
      8 -rwxr-xr-x 1 root root 4287 Sep 22 06:19 /etc/init.d/mydaemon

      $ ls -ls /usr/bin/mydaemon
      4 -rwxr-xr-x 1 root root 3263 Sep 22 06:11 /usr/bin/mydaemon

      $ perl -c /usr/bin/mydaemon
      /usr/bin/mydaemon syntax OK

      $ sudo /etc/init.d/mydaemon start
      $

      $ sudo ls /var/run | grep mydaemon
      $

      $ sudo more /var/log/mydaemon.log
      $

      I checked that the POSIX and File::Pid modules are installed, with cpan -a

      I edited /etc/init.d/mydaemon as indicated above and set the permissions. When I try to start mydaemon, nothing happens … no mydaemon running, no logging.

      I’d be grateful for suggestions about where to go from here. I’ve tried seeing how far the perl mydaemon gets in the debugger but things go sideways when I get to the fork so I can’t see past that. Code I put immediately after open LOG, “>>$logFile”; to write to the logFile, doesn’t get executed because nothing appears in the log.

    15. I can’t see the pid file after I start the daemon. And there are no warnings or errors.
      What did I miss?

    Comments are closed.