i have an almost what you want. i'm still stuck in a couple of places. Proc::PID::File has some issues (at least the way i want to use it) in that it will delete your pidfile when it shouldn't. i started on a patch/replacement but haven't finished. (can't figure a good way to hold the pidfile locked while i daemonize since Proc::Daemon closes open files (losing the locks).
#!/usr/bin/perl
use strict;
use warnings;
$|++;
my $base = '/home/me/testing';
my $name = 'simpledaemon';
my $default_configfile = join '/', $base, $name . ".conf";
=head1 NAME
B<simpledaemon> -- A daemon to ...
=head1 SYNOPSIS
simpledaemon [--start|--stop|--check] [--configfile <file>]
Options:
--help Display help
--man Display man page
--start Start the daemon (default)
--stop Stop the daemon
--check Check the daemon
--configfile <file> Specify alternate configuration file
(default: simpledaemon.conf)
=cut
#
# handle command line options
#
use Getopt::Long;
use Pod::Usage;
my %opt = (
help => 0,
man => 0,
configfile => $default_configfile,
check => 0,
stop => 0,
start => 1,
fork => 1,
);
GetOptions(\%opt,
'help',
'man',
'configfile=s',
'check',
'stop',
'start!',
'fork!',
);
#
# handy dandy help and man options
#
pod2usage(-verbose => 1), exit if $opt{help};
pod2usage(-verbose => 2), exit if $opt{man};
#
# load the config file
#
use Config::General;
my $conf; # the object
my %conf; # the handy hash
# we eval this just to give pretty error messages...
eval {
$conf = Config::General->new(
-ConfigFile => $opt{configfile},
-InterPolateVars => 1,
);
};
if (local $_ = $@) {
s/\n.*//;
chomp;
die "load config failed with: $_\n";
}
# and voila! here's our conf
%conf = $conf->getall;
if (0) { # debugging
use Data::Dumper;
print Data::Dumper->Dump([\%conf],['conf']);
}
my $myname = $conf{Simpledaemon}{name}; # save some typing late
+r on
#
# now we start the real work... handling --start, --stop, --check
#
use Proc::PID::File;
my $pidfile;
$pidfile = Proc::PID::File->new(
# dir => $conf{Simpledaemon}{base}, # default: /var/run
# name => 'simpledaemon', # default: prognam
+e (.pid g
ets added)
);
# only one of --check, --stop, --start will be performed.
# handle --check
if ($opt{check}) {
if ($pidfile->alive) {
print "$myname appears to be alive (pid ",$pidfile->re
+ad,").$/";
} else {
print "$myname appears to be dead.$/";
}
$pidfile = undef;
exit 0;
}
# handle --stop
if ($opt{stop}) {
if ($pidfile->alive) {
my $pid = $pidfile->read;
kill TERM => $pid;
sleep 2;
if (kill 0, $pid) {
print "$myname is hard to kill!$/";
exit 1;
} else {
print "$myname appears to be dead now.$/";
exit 0;
}
} else {
print "$myname wasn't alive.$/";
exit 1;
}
}
# handle --start
sub logfailsafe {
open(F, '>>', $conf{Simpledaemon}{logfailsafe})
and print(F @_)
and close(F);
die "@_";
}
use Proc::Daemon;
if ($opt{start}) {
die "$myname already running!$/" if $pidfile->alive;
#
# fork if needed
# we get a new pidfile here, Proc::Daemon::Init closes the open files
+(losing lo
cks)
#
if ($opt{fork}) {
$pidfile = undef;
Proc::Daemon::Init; # become a dae
+mon
$pidfile = Proc::PID::File->new(
# dir => '$conf{Simpledaemon}{base}', # default:
+ /var/run
# name => 'simpledaemon', # default:
+ progname
(.pid is added)
);
}
$pidfile->write;
#
# start our logging, try and fail gracefully
#
use Log::Log4perl qw(get_logger);
eval {
# Log::Log4perl::init(\$conf{logconfig});
Log::Log4perl::init_and_watch($conf{Simpledaemon}{logc
+onfigfile}
, 10);
};
if (local $_ = $@) {
s/ at .*//;
chomp;
logfailsafe("logging config failed with: $_\n");
}
my $log = get_logger($conf{Simpledaemon}{logger})
or logfailsafe("get_logger failed!\n");
#
# safeish death, pidfile is picky
#
$SIG{INT} = $SIG{TERM} = sub {
$log->info("stopping");
$pidfile = undef;
exit 0;
};
$log->info("starting");
#
# here's our loop
#
while (1) {
no strict 'refs';
sleep 20;
for my $i (qw/ debug info warn error fatal /) {
$log->$i("log $i");
}
}
#
# end of our loop
#
} # start
__END__
=head1 DESCRIPTION
This program will...
=cut
i never found a CPAN module that did all of the stuff i wanted (like you, stop/start/check). my plan is to eventually get this finished up as a module that i can use for all of my daemons to provide common functionality.
package MyDaemon;
use base qw(SimpleDaemon);
sub MainLoop {
#do work here
}
or something similar. i'm open for ideas/comments/pointers to modules etc. |