Wherein I realize the bliss of writing init scripts with Daemon::Control

Init scripts are annoying little things – almost entirely boilerplate. Here’s how I learned to stop struggling, and love Daemon::Control to control my daemons.

The module really is as simple as the synopsis - you describe the daemon, have it write an init script (which actually just runs your Daemon::Control script) for you, then update-rc.d and you’re golden. It really is that simple.

Example: php-fcgi

This site currently runs nginx, proxying dynamic requests to a socket where FCGI handles the request. It used to be that I had to start the FCGI daemon manually when I rebooted – I just never got around to setting up a proper init script and making it run on the appropriate runlevels.

use strict;
use warnings;
use Daemon::Control;
 
$ENV{PHP_FCGI_CHILDREN} = 10;
$ENV{PHP_FCGI_MAX_REQUESTS} = 1000;
 
Daemon::Control->new({
    name => 'php-fcgi',
    program => '/usr/bin/php-cgi',
    program_args=> [-b => '/tmp/php-cgi.sock'],
    fork => 2,
    user => 'www-data',
    group => 'www-data',
    pid_file => '/var/run/php-fcgi.pid',
    stdout_file => '/var/log/php-fcgi.log',
    stderr_file => '/var/log/php-fcgi.log',
 
    lsb_start => '$nginx',
    lsb_stop => '$nginx',
    lsb_sdesc => 'Starts PHP under FCGI',
    lsb_desc => 'Starts PHP under FCGI',
})->run;

The first thing you need to know is the program you want to daemonize, and the arguments you want to pass to it. Here, it is php-cgi, and the arguments are an arrayref. I’m telling it to bind to a local socket, listening for requests to handle.

The next thing you need to understand is whether the program you’re going to run will daemonize itself. If so, you want fork => 1, which will let the program do it’s own daemonization, including dropping privileges and writing its own PID file (make sure you provide Daemon::Control with the right filename for the PID file). Otherwise, use fork => 2, which makes Daemon::Control double-fork, permitting it to do things like dropping elevated privileges and writing the PID file. Here, we’re using Daemon::Control’s double-fork because php-cgi doesn’t daemonize itself.

Give a filename to store the PID in so you can stop and restart the daemon.

The last block allows you to give some extra information for writing LSB-compatible init scripts. LSB-compatible init scripts use dependencies to figure out what order to run the init scripts for the current runlevel. Here, I’ve declared that this thing needs to have nginx running before you start it. And stop this before you stop nginx. The next 2 are just descriptions.

Starting your daemons on boot

To get your daemons to start up when your server boots, just ask the script you’ve written for an init script:

mike@deschamps:~$ php-fcgi get_init_file
#!/bin/sh
 
### BEGIN INIT INFO
# Provides: php-fcgi
# Required-Start: $nginx
# Required-Stop: $nginx
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts PHP under FCGI
# Description: Starts PHP under FCGI
### END INIT INFO`
 
if [ -x /home/mike/perl5/perlbrew/perls/perl-5.14.2/bin/php-fcgi ];
then
/home/mike/perl5/perlbrew/perls/perl-5.14.2/bin/php-fcgi $1
else
echo "Requred program /home/mike/perl5/perlbrew/perls/perl-5.14.2/bin/php-fcgi not found!"
exit 1;
fi

You can see that your init script has the standard LSB header, so it’ll play nice with the dependency-based boot. After that, all it does is pass off to your perl script.

Save that as the init script, make it executable, and update your runlevels:

$ sudo -i
# php-fcgi get_init_file > /etc/init.d/php-fcgi
# chmod +w !$
# update-rc.d php-fcgi defaults

And you’re done! When you reboot, the init script will be executed at the appropriate point in the boot process. It’ll call your Daemon::Control script, and start your daemon for you.

Daemon::Control takes care of almost all the complexity. Beautiful!

Comments
Comment from Alexander Hartmaier (abraxxa) - April 16, 2012 at 8:33 pm

Thanks for this post Mike!
I’ve struggled with getting the init scripts for our Catalyst apps right and will try Daemon::Control immediately tomorrow. Maybe I’ll write a doc patch for Catalyst::Manual.

Comment from Htbaa - April 23, 2012 at 9:16 pm

Thanks for posting this. I tried it out for a Catalyst application and it works great! Also easy to set up. Will do the same for another Catalyst app tomorrow as well.

Comment from Olaf Alders - May 2, 2012 at 3:57 pm

This actually fills a giant gap for me. I spent some time playing with this last night and I now no longer need to ask someone else to set up live Plack deployments for me. Thanks so much for posting this!