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!

    share and enjoy:
    • Print
    • Digg
    • Sphinn
    • del.icio.us
    • Facebook
    • Mixx
    • Google Bookmarks
    • Blogplay
    • Fark
    • Reddit
    • Slashdot
    • StumbleUpon
    • Twitter

    22 comments to Creating a Perl Daemon in Ubuntu

    • Dinesh

      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

    • Good to hear, Dinesh!

    • Gaurav tango

      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 …

    • Sorry Gauvav, my mistake! I updated the code.

      -A

    • Gaurav tango

      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.

      :)

    • Dinesh

      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..!!

    • @Gaurav: You are absolutely right – I made the change.

    • That is an excellent point. I’ll add a note in the post. -A

    • nick fox

      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?

    • @nick: You are right, I updated the code. -A

    • oznerol

      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

    • @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

    • doublezero

      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?

    • @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

    • doublezero

      damnt!
      thanks for quick response! ;)

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

    • I’ve corrected the post so the paths are the same. Well done, doubezero! -A

    • doublezero

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

    • Glad that it was useful. Not fancy, just understandable. -Andrew ;-)

    • Added a link to Duncane’s post.

    • Thanks for adding the link :)

      And thanks for your howto that helped me when i needed it :)

      Regards,
      Duncane

    • hi,

      has served me.

    Leave a Reply

      

      

      


    *

    You can use these HTML tags

    <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>