Perl on Linux – Making a Daemon

It's me!One if the nice advantages to linux is the ability to create daemons relatively quickly and get them to do your brute and routine work for you. For this example, I am going to use perl to create the daemon, since perl is the glue that holds the bricks of linux together. Its regex functionality combined with fast and easy syntax makes it almost too easy to use for quick and dirty programming in the text based realm of linux. So without further adieu, the daemon:

#! /usr/bin/perl -w

use strict;
use lib '/usr/local/hsw/libs';
use POSIX;
use DateTime;
use Fcntl qw(:flock);
use File::CacheDir qw(cache_dir);

First, the shebang line, of course. I use the -w option to get output about bad constructs and things that aren’t particularly bad syntax, but probably something I forgot about or left in by accident. use strict is just good coding practice. My lib directory is where I keep all my custom perl modules I program, so I include that too. POSIX is the library that has the function (setsid) we will use to make the daemon autonomous. DateTime will be used to do some comparing. Flock will be used to lock files we open for writing. File::CacheDir allows us to do file caching of the output and error logs of the child processes.

I have two functions I use, the first is as follows:

sub Log
{
  my $string = shift;
  if($string)
  {
    my $time = DateTime->now();
    if(open(LOG, ">>/var/log/deamon.log"))
    {
      flock(LOG, LOCK_EX);
      print LOG $$." [".$time->ymd." ".$time->hms."] - ".$string."n";
      close LOG;
    }
  }
}

This, as you can see, just takes a string, and writes it to a log file with some other info, like the current PID ($$) and the current time. Fairly straight forward, and is easy to use.

The second function is rather cool:

sub Status
{
  my $string = shift;
  if($string) 
  {
    $0 = "My Daemon- ".$string;
  }
  return $0;
}

Perl allows you to change the $0 variable, which is the commandline file in the PID’s proc directory (/proc/(whatever PID)/cmdline). This can be a great way to see what the process is doing with a simple ps command call. This function takes in a string, and changes that string into the $0 variable with the daemon’s name in it for some consistency.

Moving on…

Log("Initializing...");

my $file = $0;
my $age = -M $file;

Log("File - ".$file.", age - ".$age);

Since the Status function will change the $0 variable, we save what it initially was (the daemon file we are executing) so we can watch it for changes. Then we store the last modified time of the file so we can tell when it is changed. Next…

Status("Daemonizing...");

my $pid = fork;

if(!defined $pid)
{
  Log("Unable to fork : $!");
  die;
}

if($pid)
{
  Log("Parent process exiting, let the deamon (".$pid.") go...");
  sleep 3;
  exit;
}

POSIX::setsid;

So first we set the status to Daemonizing then we fork. Then we check to see if the fork even worked, if not we log it and die. Then we see if we are the parent or the child (forking, for those of you that don’t know, creates an exact duplicate of the parent process and calls it the child). If we are the parent, we log it, wait to make sure the child has time to daemonize then exit. As the child we setsid which means we tell linux we are our own parent with our own session. Since we are now the session leader, we are now daemonized!

if(-e "/usr/local/hsw/pids/".$file.".pid")
{
  open(PID, "</usr/local/hsw/pids/".$file.".pid");
  my $runningpid = <PID>;
  close PID;
  unlink "/usr/local/hsw/pids/".$file.".pid";
  while(-e "/proc/".$runningpid)
  {
    Status("Waiting for ".$runningpid." to exit...");
    Log("Waiting for ".$runningpid." to exit...");
    sleep 1;
  }
}

I keep all my custom pid files in my custom directory, so I first check to see if the pid file exists for this daemon already. If it does, I open it, read the PID in it, delete the file and wait until that process ends. Once it’s done, I continue safe in the knowledge that I am the only running daemon of this type.

open PID, "+%gt;/usr/local/hsw/pids/".$file.".pid";
print PID $$;
close PID;

Here we digitally mark our territory to let other daemons know we are here and running by opening the pid file and writing our PID in it.

Log("The deamon is now running...");
Status("Deamon running");
my $stdout = cache_dir({base_dir => '/tmp/hsw/cache', ttl => '1 day', filename => "STDOUT".$$});
my $stderr = cache_dir({base_dir => '/tmp/hsw/cache', ttl => '1 day', filename => "STDERR".$$});
Log("STDOUT : ".$stdout);
Log("STDERR : ".$stderr);
open STDIN, '/dev/null';
open STDOUT, '>>'.$stdout;
open STDERR, '>>'.$stderr;

Here we setup two files in our cache directory (in my case /tmp/hsw/cache) to hold the STDOUT and STDERR streams, which normally go to the terminal window. Then we redirect those streams to those cache files and log the names.

while(1)
{
  
#### Code to be performed by the daemon

  if($age - (-M $file))
  {
    Log("File modified, restarting");
    open(FILE, $file ." |");
    close(FILE);
    last;
  }
  if(!-e "/usr/local/hsw/pids/".$file.".pid")
  {
    Log("Pid file doesn't exist, time go exit.");
    last;
  }
  sleep 1;
}

Here we start the main while loop of our daemon. This will always run until told to stop. At the end of the loop, we check the age of the original file. If it has changed, log it, restart the $file and exit the loop with a last statement. Then we check the PID file to see if it still exists, if not, that’s our queue to exit. Log it and last. We sleep for a second then start the loop again!

So there you go! A self restartable, easily killable daemon written in perl! This also works well with the Forking Server Tutorial we have as well. Play with it, have fun.

3 thoughts on “Perl on Linux – Making a Daemon
  1. Pingback: High Speed Web Blog » Perl on Linux - Iterative, Forking, Multi-Request Handling Server

Leave a Reply