Skip to content

Instantly share code, notes, and snippets.

@yinyin
Created May 11, 2011 18:12
Show Gist options
  • Save yinyin/966982 to your computer and use it in GitHub Desktop.
Save yinyin/966982 to your computer and use it in GitHub Desktop.
An utility which runs program as daemon
#!/usr/bin/perl
use strict;
use POSIX;
our $PID = '/tmp/pid.txt';
our $WORKDIR = '/tmp';
our $TRMLOG = '/dev/null'; # '/tmp/terminal-log.txt';
our @DAEMON_CMD = ();
our $VERBOSE = 0;
our $RUNTIME_STOP_SIGNAL = 0;
my $operation_mode = 'start';
eval {
my $readopt = 1;
OPTLOOP: for(my $i = 0; $i <= $#ARGV; $i++) {
my $opt = $ARGV[$i];
my $processed = 0;
if(0 != $readopt)
{
$processed = 1;
if($opt =~ /^--pid=(.+)$/)
{ $PID = $1; $PID =~ s/\s+$//; }
elsif($opt =~/^--(working-dir|wd)=(.+)$/)
{ $WORKDIR = $2; $WORKDIR =~ s/\s+$//; }
elsif($opt =~/^--(log|terminal-log)=(.+)$/)
{ $TRMLOG = $2; $TRMLOG =~ s/\s+$//; }
elsif( ($opt eq '-v') or ($opt eq '--verbose') )
{ $VERBOSE = 1; }
elsif($opt eq '--')
{ $readopt = 0; }
elsif($opt eq 'start')
{ $operation_mode = 'start'; }
elsif($opt eq 'stop')
{ $operation_mode = 'stop'; }
elsif( ($opt eq 'check') or ($opt eq 'status') )
{ $operation_mode = 'check'; }
elsif($opt eq 'startstart')
{ $operation_mode = 'startstart'; }
else
{ $processed = 0; }
}
if(0 == $processed)
{ push @DAEMON_CMD, $opt; }
}
};
if(0 != $VERBOSE)
{
print "PID-file = ${PID}\n";
print "Working-directory = ${WORKDIR}\n";
print "Terminal-log = ${TRMLOG}\n";
print "Command = @{DAEMON_CMD}\n";
print "Operation-mode = ${operation_mode}\n";
}
sub my_fork # {{{
{
my $child_pid;
FORKLOOP: {
if(defined($child_pid = fork))
{ return $child_pid; }
elsif($! =~ /No more process/)
{
sleep 10;
redo FORKLOOP;
}
else
{
print "can not fork $!\n";
exit;
}
}
} # }}}
sub have_process # {{{
{
my $line;
my $result;
my $parget;
$parget = int(shift);
# print "parget: $parget\n";
open(PSFH, '-|', '/bin/ps x -o pid,command') or die 'can not get process list';
$result = undef;
while($line = <PSFH>) {
# print $line;
$line =~ s/^\s+//;
$line =~ s/\s+$//;
if( $line =~ /^([0-9]+)\s+/ )
{
my $v = int($1);
if($v == $parget)
{ $result = $line; }
# print "> $line\n";
}
}
close PSFH;
return $result;
} # }}}
sub signalhandler_GOSTOP
{
$RUNTIME_STOP_SIGNAL = 1;
}
# {{{ check ps to see if any running bot
my $prev_pid;
my $prev_cmd;
my $curr_cmd;
if(open(PIDF, '<', $PID))
{
$prev_pid = <PIDF>; $prev_pid =~ s/^\s+//; $prev_pid =~ s/\s+$//; $prev_pid = int($prev_pid);
$prev_cmd = <PIDF>; $prev_cmd =~ s/^\s+//; $prev_cmd =~ s/\s+$//;
close PIDF;
}
else
{
$prev_pid = 0;
$prev_cmd = 'NA';
}
$curr_cmd = have_process($prev_pid);
if( (undef ne $curr_cmd) and ($prev_cmd eq $curr_cmd) )
{
if(0 != $VERBOSE)
{ print "Daemon still running.\n"; }
if('check' eq $operation_mode)
{ exit 0; }
if('stop' eq $operation_mode)
{
kill SIGTERM, $prev_pid;
exit 0;
}
exit 10;
}
# }}}
if( ('check' eq $operation_mode) or ('stop' eq $operation_mode) )
{
if(0 != $VERBOSE)
{ print "Daemon not running.\n"; }
exit 1;
}
# {{{ 1st fork, detach from user command
my $now_pid;
if($now_pid = my_fork)
{
exit 0;
}
# }}}
# {{{ set session id, detach from control console
POSIX::setsid() or die 'Can not detach from controll console';
# }}}
# {{{ 2nd fork, release from control console
$SIG{'HUP'} = 'IGNORE';
if($now_pid = my_fork)
{
sleep 6;
open(PIDF, "> $PID") or die 'can not open pid.txt for write';
my $now_cmd = have_process($now_pid);
# print "writing pid.txt ($now_pid, $now_cmd)\n";
print PIDF "$now_pid\n$now_cmd\n\n";
close PIDF;
exit 0;
}
# }}}
chdir $WORKDIR;
my $openmax;
$openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX );
$openmax = (!defined($openmax) || $openmax < 0) ? 64 : $openmax;
foreach my $i (0 .. $openmax) { POSIX::close($i); }
open(STDIN, '+> /dev/null');
open(STDOUT, "> $TRMLOG");
open(STDERR, '+>& STDOUT');
if('startstart' eq $operation_mode)
{
$SIG{TERM} = \&signalhandler_GOSTOP;
$SIG{INT} = \&signalhandler_GOSTOP;
while(0 == $RUNTIME_STOP_SIGNAL) {
my $server_pid = undef;
if($server_pid = my_fork)
{
my $kid = -1;
do {
if(0 != $RUNTIME_STOP_SIGNAL)
{ kill SIGTERM, $server_pid; }
sleep 3;
$kid = waitpid($server_pid, WNOHANG);
} while($kid < 1);
my $kid_status = $?;
print "\n-- daemon terminated. (status-code=${kid_status})\n";
}
else
{
print "-- Starting daemon...\n";
exec @DAEMON_CMD;
exit 0;
}
}
print "-- Stopped.\n";
exit 0;
}
else
{
exec @DAEMON_CMD;
}
# vim: ts=4 sw=4 foldmethod=marker ai
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment