Say you have a simple resource.cgi
:
#! /usr/bin/perl
use warnings;
use strict;
use Reader;
use CGI qw/ :standard /;
print header("text/plain"),
"Contents:\n",
Reader::data,
"-" x 40, "\n";
Its output is
Content-Type: text/plain; charset=ISO-8859-1
Contents:
This is a data file
with some very interesting
bits.
----------------------------------------
The fun part is in Reader.pm
, which begins with familiar-looking boilerplate:
package Reader;
use warnings;
use strict;
use Fcntl qw/ :DEFAULT :flock :seek /;
use POSIX qw/ setsid /;
Next it defined rendezvous points:
my $PIDFILE = "/tmp/reader.pid";
my $DATA = "/tmp/file.dat";
my $PIPE = "/tmp/reader.pipe";
The sub import
is called as part of use Module
. If the daemon is already running, then there's nothing to do. Otherwise, we fork off the daemon and write its process ID to $PIDFILE
.
sub import {
return unless my $fh = take_lock();
my $child = fork;
die "$0: fork: $!" unless defined $child;
if ($child) {
print $fh "$child\n" or die "$0: write $PIDFILE: $!";
close $fh or die "$0: close $PIDFILE: $!";
return;
}
# daemonize
close $fh;
chdir "/";
open STDIN, "<", "/dev/null";
open STDOUT, ">", "/dev/null";
open STDERR, ">", "/dev/null";
setsid;
open $fh, "<", $DATA or die;
undef $/;
my $data = <$fh>;
close $fh;
while (1) {
open my $fh, ">", $PIPE or die;
print $fh $data or die;
close $fh;
}
}
Every client needs to wait its turn getting a lock on $PIDFILE
. Once we have the lock, we then check that the process identified is still running and create the named pipe if necessary.
sub take_lock {
sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT or die "$0: open $PIDFILE: $!";
flock $fh => LOCK_EX or die "$0: flock $PIDFILE: $!";
my $pid = <$fh>;
if (defined $pid) {
chomp $pid;
if (kill 0 => $pid) {
close $fh;
return;
}
}
else {
die "$0: readline $PIDFILE: $!" if $!;
}
sysseek $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!";
truncate $fh, 0 or die "$0: truncate $PIDFILE: $!";
unless (-p $PIPE) {
system("mknod", $PIPE, "p") == 0
or die "$0: mknod exited " . ($? >> 8);
}
$fh;
}
Finally, reading the pipe is trivial:
sub data {
open my $fh, "<", $DATA or die "$0: open $DATA: $!";
local $/;
scalar <$fh>;
}
Don't forget to return a true value from the module:
1;
You'll note that operations can still fail in the daemon. For your sanity, you'll want to log events somehow rather than choking silently.
As to whether hosts will permit long-running processes, that will vary from provider to provider, but even if your daemon is killed off from time to time, the code above will restart it on demand.