Rewrite Daemon.pm: don't require an OptionParser, one public func, run(), that does the whole daemonize process or just check the PID file.

This commit is contained in:
Daniel Nichter
2013-04-05 12:16:16 -06:00
parent afa6533f43
commit 3d5325ae03
4 changed files with 435 additions and 278 deletions

View File

@@ -4091,59 +4091,86 @@ package Daemon;
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
use POSIX qw(setsid);
use Fcntl qw(:DEFAULT);
sub new {
my ($class, %args) = @_;
foreach my $arg ( qw(o) ) {
die "I need a $arg argument" unless $args{$arg};
}
my $o = $args{o};
my $self = {
o => $o,
log_file => $o->has('log') ? $o->get('log') : undef,
PID_file => $o->has('pid') ? $o->get('pid') : undef,
log_file => $args{log_file},
pid_file => $args{pid_file},
daemonize => $args{daemonize},
};
check_PID_file(undef, $self->{PID_file});
PTDEBUG && _d('Daemonized child will log to', $self->{log_file});
return bless $self, $class;
}
sub daemonize {
my ( $self ) = @_;
sub run {
my ($self, %args) = @_;
my $pid ||= $PID;
my $pid_file ||= $self->{pid_file};
my $log_file ||= $self->{log_file};
PTDEBUG && _d('About to fork and daemonize');
defined (my $pid = fork()) or die "Cannot fork: $OS_ERROR";
if ( $pid ) {
PTDEBUG && _d('Parent PID', $PID, 'exiting after forking child PID',$pid);
exit;
if ( $self->{daemonize} ) {
$self->_daemonize(
pid => $pid,
pid_file => $pid_file,
log_file => $log_file,
);
}
elsif ( $pid_file ) {
$self->_make_pid_file(
pid => $pid,
pid_file => $pid_file,
);
$self->{pid_file_owner} = $pid;
}
else {
PTDEBUG && _d('Neither --daemonize nor --pid was specified');
}
PTDEBUG && _d('Daemonizing child PID', $PID);
$self->{PID_owner} = $PID;
$self->{child} = 1;
return;
}
POSIX::setsid() or die "Cannot start a new session: $OS_ERROR";
chdir '/' or die "Cannot chdir to /: $OS_ERROR";
sub _daemonize {
my ($self, %args) = @_;
my $pid = $args{pid};
my $pid_file = $args{pid_file};
my $log_file = $args{log_file};
$self->_make_PID_file();
PTDEBUG && _d('Daemonizing');
if ( $pid_file ) {
eval {
$self->_make_pid_file(
pid => $pid, # parent's pid
pid_file => $pid_file,
);
};
if ( $EVAL_ERROR ) {
die "Cannot daemonize: $EVAL_ERROR\n";
}
}
defined (my $child_pid = fork())
or die "Cannot fork: $OS_ERROR";
if ( $child_pid ) {
PTDEBUG && _d('Forked child', $child_pid);
exit 0;
}
$OUTPUT_AUTOFLUSH = 1;
PTDEBUG && _d('Redirecting STDIN to /dev/null');
close STDIN;
open STDIN, '/dev/null'
or die "Cannot reopen STDIN to /dev/null: $OS_ERROR";
if ( $self->{log_file} ) {
PTDEBUG && _d('Redirecting STDOUT and STDERR to', $self->{log_file});
if ( $log_file ) {
PTDEBUG && _d('Redirecting STDOUT and STDERR to', $log_file);
close STDOUT;
open STDOUT, '>>', $self->{log_file}
or die "Cannot open log file $self->{log_file}: $OS_ERROR";
open STDOUT, '>>', $log_file
or die "Cannot open log file $log_file: $OS_ERROR";
close STDERR;
open STDERR, ">&STDOUT"
@@ -4166,82 +4193,123 @@ sub daemonize {
}
}
PTDEBUG && _d('I am child', $PID);
if ( $pid_file ) {
$self->_update_pid_file(
pid => $PID, # child's pid
pid_file => $pid_file,
);
$self->{pid_file_owner} = $PID;
}
POSIX::setsid() or die "Cannot start a new session: $OS_ERROR";
chdir '/' or die "Cannot chdir to /: $OS_ERROR";
return;
}
sub check_PID_file {
my ( $self, $file ) = @_;
my $PID_file = $self ? $self->{PID_file} : $file;
PTDEBUG && _d('Checking PID file', $PID_file);
if ( $PID_file && -f $PID_file ) {
my $pid;
eval {
chomp($pid = (slurp_file($PID_file) || ''));
sub _make_pid_file {
my ($self, %args) = @_;
my @required_args = qw(pid pid_file);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
if ( $EVAL_ERROR ) {
die "The PID file $PID_file already exists but it cannot be read: "
. $EVAL_ERROR;
my $pid = $args{pid};
my $pid_file = $args{pid_file};
eval {
sysopen(PID_FH, $pid_file, O_RDWR|O_CREAT|O_EXCL) or die $OS_ERROR;
print PID_FH $PID, "\n";
close PID_FH;
};
if ( my $e = $EVAL_ERROR ) {
if ( $e =~ m/file exists/i ) {
my $old_pid = $self->_check_pid_file(
pid_file => $pid_file,
);
if ( $old_pid ) {
warn "Overwriting PID file $pid_file because PID $old_pid "
. "is not running.\n";
}
PTDEBUG && _d('PID file exists; it contains PID', $pid);
$self->_update_pid_file(
pid => $PID,
pid_file => $pid_file
);
}
else {
die "Error creating PID file $pid_file: $e\n";
}
}
return;
}
sub _check_pid_file {
my ($self, %args) = @_;
my @required_args = qw(pid_file);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
my $pid_file = $args{pid_file};
PTDEBUG && _d('Checking if PID in', $pid_file, 'is running');
if ( ! -f $pid_file ) {
PTDEBUG && _d('PID file', $pid_file, 'does not exist');
return;
}
open my $fh, '<', $pid_file
or die "Error opening $pid_file: $OS_ERROR";
my $pid = do { local $/; <$fh> };
chomp($pid) if $pid;
close $fh
or die "Error closing $pid_file: $OS_ERROR";
if ( $pid ) {
PTDEBUG && _d('Checking if PID', $pid, 'is running');
my $pid_is_alive = kill 0, $pid;
if ( $pid_is_alive ) {
die "The PID file $PID_file already exists "
. " and the PID that it contains, $pid, is running";
}
else {
warn "Overwriting PID file $PID_file because the PID that it "
. "contains, $pid, is not running";
die "PID file $pid_file exists and PID $pid is running\n";
}
}
else {
die "The PID file $PID_file already exists but it does not "
. "contain a PID";
die "PID file $pid_file exists but it is empty. Remove the file "
. "if the process is no longer running.\n";
}
return $pid;
}
else {
PTDEBUG && _d('No PID file');
}
sub _update_pid_file {
my ($self, %args) = @_;
my @required_args = qw(pid pid_file);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
my $pid = $args{pid};
my $pid_file = $args{pid_file};
open my $fh, '>', $pid_file
or die "Cannot open $pid_file: $OS_ERROR";
print { $fh } $pid, "\n"
or die "Cannot print to $pid_file: $OS_ERROR";
close $fh
or warn "Cannot close $pid_file: $OS_ERROR";
return;
}
sub make_PID_file {
my ( $self ) = @_;
if ( exists $self->{child} ) {
die "Do not call Daemon::make_PID_file() for daemonized scripts";
}
$self->_make_PID_file();
$self->{PID_owner} = $PID;
return;
}
sub _make_PID_file {
my ( $self ) = @_;
my $PID_file = $self->{PID_file};
if ( !$PID_file ) {
PTDEBUG && _d('No PID file to create');
return;
}
$self->check_PID_file();
open my $PID_FH, '>', $PID_file
or die "Cannot open PID file $PID_file: $OS_ERROR";
print $PID_FH $PID
or die "Cannot print to PID file $PID_file: $OS_ERROR";
close $PID_FH
or die "Cannot close PID file $PID_file: $OS_ERROR";
PTDEBUG && _d('Created PID file:', $self->{PID_file});
return;
}
sub _remove_PID_file {
my ( $self ) = @_;
if ( $self->{PID_file} && -f $self->{PID_file} ) {
unlink $self->{PID_file}
or warn "Cannot remove PID file $self->{PID_file}: $OS_ERROR";
sub remove_pid_file {
my ($self, $pid_file) = @_;
$pid_file ||= $self->{pid_file};
if ( $pid_file && -f $pid_file ) {
unlink $self->{pid_file}
or warn "Cannot remove PID file $pid_file: $OS_ERROR";
PTDEBUG && _d('Removed PID file');
}
else {
@@ -4253,16 +4321,11 @@ sub _remove_PID_file {
sub DESTROY {
my ($self) = @_;
$self->_remove_PID_file() if ($self->{PID_owner} || 0) == $PID;
return;
if ( ($self->{pid_file_owner} || 0) == $PID ) {
$self->remove_pid_file();
}
sub slurp_file {
my ($file) = @_;
return unless $file;
open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR";
return do { local $/; <$fh> };
return;
}
sub _d {
@@ -4795,17 +4858,15 @@ sub main {
# ########################################################################
# Daemonize first so all output goes to the --log.
# ########################################################################
my $daemon;
my $daemon = Daemon->new(
daemonize => $o->get('daemonize'),
pid_file => $o->get('pid'),
log_file => $o->get('log'),
);
if ( !$o->get('send-data') ) {
if ( $o->get('daemonize') ) {
$daemon = new Daemon(o=>$o);
$daemon->daemonize();
$daemon->run();
PTDEBUG && _d('I am a daemon now');
}
elsif ( $o->get('pid') ) {
$daemon = new Daemon(o=>$o);
$daemon->make_PID_file();
}
# If we daemonized, the parent has already exited and we're the child.
# We shared a copy of every Cxn with the parent, and the parent's copies
# were destroyed but the dbhs were not disconnected because the parent

View File

@@ -1,4 +1,4 @@
# This program is copyright 2008-2011 Percona Ireland Ltd.
# This program is copyright 2008-2013 Percona Ireland Ltd.
# Feedback and improvements are welcome.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
@@ -17,59 +17,91 @@
# ###########################################################################
# Daemon package
# ###########################################################################
{
# Package: Daemon
# Daemon daemonizes the caller and handles daemon-related tasks like PID files.
package Daemon;
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
use POSIX qw(setsid);
use Fcntl qw(:DEFAULT);
# The required o arg is an OptionParser object.
sub new {
my ($class, %args) = @_;
foreach my $arg ( qw(o) ) {
die "I need a $arg argument" unless $args{$arg};
}
my $o = $args{o};
my $self = {
o => $o,
log_file => $o->has('log') ? $o->get('log') : undef,
PID_file => $o->has('pid') ? $o->get('pid') : undef,
log_file => $args{log_file},
pid_file => $args{pid_file},
daemonize => $args{daemonize},
};
# undef because we can't call like $self->check_PID_file() yet.
check_PID_file(undef, $self->{PID_file});
PTDEBUG && _d('Daemonized child will log to', $self->{log_file});
return bless $self, $class;
}
sub daemonize {
my ( $self ) = @_;
sub run {
my ($self, %args) = @_;
my $pid ||= $PID;
my $pid_file ||= $self->{pid_file};
my $log_file ||= $self->{log_file};
PTDEBUG && _d('About to fork and daemonize');
defined (my $pid = fork()) or die "Cannot fork: $OS_ERROR";
if ( $pid ) {
PTDEBUG && _d('Parent PID', $PID, 'exiting after forking child PID',$pid);
exit;
if ( $self->{daemonize} ) {
$self->_daemonize(
pid => $pid,
pid_file => $pid_file,
log_file => $log_file,
);
}
elsif ( $pid_file ) {
$self->_make_pid_file(
pid => $pid,
pid_file => $pid_file,
);
$self->{pid_file_owner} = $pid;
}
else {
PTDEBUG && _d('Neither --daemonize nor --pid was specified');
}
# I'm daemonized now.
PTDEBUG && _d('Daemonizing child PID', $PID);
$self->{PID_owner} = $PID;
$self->{child} = 1;
return;
}
POSIX::setsid() or die "Cannot start a new session: $OS_ERROR";
chdir '/' or die "Cannot chdir to /: $OS_ERROR";
sub _daemonize {
my ($self, %args) = @_;
my $pid = $args{pid};
my $pid_file = $args{pid_file};
my $log_file = $args{log_file};
$self->_make_PID_file();
PTDEBUG && _d('Daemonizing');
$OUTPUT_AUTOFLUSH = 1;
# First obtain the pid file or die trying. NOTE: we're still the parent
# so the pid file will contain the parent's pid at first. This is done
# to avoid a race condition between the parent checking for the pid file,
# forking, and the child actually obtaining the pid file. This way, if
# the parent obtains the pid file, the child is guaranteed to be the only
# process running.
if ( $pid_file ) {
eval {
$self->_make_pid_file(
pid => $pid, # parent's pid
pid_file => $pid_file,
);
};
if ( $EVAL_ERROR ) {
die "Cannot daemonize: $EVAL_ERROR\n";
}
}
# Fork, exit parent, continue as child process.
defined (my $child_pid = fork())
or die "Cannot fork: $OS_ERROR";
if ( $child_pid ) {
# I'm the parent.
PTDEBUG && _d('Forked child', $child_pid);
exit 0;
}
# I'm the child. First, open the log file, if any. Do this first
# so that all daemon/child output goes there.
# We used to only reopen STDIN to /dev/null if it's a tty because
# otherwise it may be a pipe, in which case we didn't want to break
@@ -82,12 +114,11 @@ sub daemonize {
close STDIN;
open STDIN, '/dev/null'
or die "Cannot reopen STDIN to /dev/null: $OS_ERROR";
if ( $self->{log_file} ) {
PTDEBUG && _d('Redirecting STDOUT and STDERR to', $self->{log_file});
if ( $log_file ) {
PTDEBUG && _d('Redirecting STDOUT and STDERR to', $log_file);
close STDOUT;
open STDOUT, '>>', $self->{log_file}
or die "Cannot open log file $self->{log_file}: $OS_ERROR";
open STDOUT, '>>', $log_file
or die "Cannot open log file $log_file: $OS_ERROR";
# If we don't close STDERR explicitly, then prove Daemon.t fails
# because STDERR gets written before STDOUT even though we print
@@ -114,94 +145,138 @@ sub daemonize {
}
}
# XXX: I don't think we need this?
# $OUTPUT_AUTOFLUSH = 1;
PTDEBUG && _d('I am child', $PID);
# Now update the pid file to contain the correct pid, i.e. the child's pid.
if ( $pid_file ) {
$self->_update_pid_file(
pid => $PID, # child's pid
pid_file => $pid_file,
);
$self->{pid_file_owner} = $PID;
}
# Last: other misc daemon stuff.
POSIX::setsid() or die "Cannot start a new session: $OS_ERROR";
chdir '/' or die "Cannot chdir to /: $OS_ERROR";
# We're not fully daemonized.
return;
}
# The file arg is optional. It's used when new() calls this sub
# because $self hasn't been created yet.
sub check_PID_file {
my ( $self, $file ) = @_;
my $PID_file = $self ? $self->{PID_file} : $file;
PTDEBUG && _d('Checking PID file', $PID_file);
if ( $PID_file && -f $PID_file ) {
my $pid;
eval {
chomp($pid = (slurp_file($PID_file) || ''));
};
if ( $EVAL_ERROR ) {
# Be safe and die if we can't check that a process is
# or is not already running.
die "The PID file $PID_file already exists but it cannot be read: "
. $EVAL_ERROR;
}
PTDEBUG && _d('PID file exists; it contains PID', $pid);
if ( $pid ) {
my $pid_is_alive = kill 0, $pid;
if ( $pid_is_alive ) {
die "The PID file $PID_file already exists "
. " and the PID that it contains, $pid, is running";
}
else {
warn "Overwriting PID file $PID_file because the PID that it "
. "contains, $pid, is not running";
}
}
else {
# Be safe and die if we can't check that a process is
# or is not already running.
die "The PID file $PID_file already exists but it does not "
. "contain a PID";
}
}
else {
PTDEBUG && _d('No PID file');
}
return;
}
# Call this for non-daemonized scripts to make a PID file.
sub make_PID_file {
my ( $self ) = @_;
if ( exists $self->{child} ) {
die "Do not call Daemon::make_PID_file() for daemonized scripts";
sub _make_pid_file {
my ($self, %args) = @_;
my @required_args = qw(pid pid_file);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
my $pid = $args{pid};
my $pid_file = $args{pid_file};
# "If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
# The check for the existence of the file and the creation of the file
# if it does not exist shall be atomic with respect to other threads
# executing open() naming the same filename in the same directory with
# O_EXCL and O_CREAT set.
eval {
sysopen(PID_FH, $pid_file, O_RDWR|O_CREAT|O_EXCL) or die $OS_ERROR;
print PID_FH $PID, "\n";
close PID_FH;
};
if ( my $e = $EVAL_ERROR ) {
if ( $e =~ m/file exists/i ) {
# Check if the existing pid is running. If yes, then die,
# else this returns and we overwrite the pid file.
my $old_pid = $self->_check_pid_file(
pid_file => $pid_file,
);
if ( $old_pid ) {
warn "Overwriting PID file $pid_file because PID $old_pid "
. "is not running.\n";
}
$self->_make_PID_file();
# This causes the PID file to be auto-removed when this obj is destroyed.
$self->{PID_owner} = $PID;
$self->_update_pid_file(
pid => $PID,
pid_file => $pid_file
);
}
else {
die "Error creating PID file $pid_file: $e\n";
}
}
return;
}
# Do not call this sub directly. For daemonized scripts, it's called
# automatically from daemonize() if there's a --pid opt. For non-daemonized
# scripts, call make_PID_file().
sub _make_PID_file {
my ( $self ) = @_;
sub _check_pid_file {
my ($self, %args) = @_;
my @required_args = qw(pid_file);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
my $pid_file = $args{pid_file};
my $PID_file = $self->{PID_file};
if ( !$PID_file ) {
PTDEBUG && _d('No PID file to create');
PTDEBUG && _d('Checking if PID in', $pid_file, 'is running');
if ( ! -f $pid_file ) {
PTDEBUG && _d('PID file', $pid_file, 'does not exist');
return;
}
# We checked this in new() but we'll double check here.
$self->check_PID_file();
open my $fh, '<', $pid_file
or die "Error opening $pid_file: $OS_ERROR";
my $pid = do { local $/; <$fh> };
chomp($pid) if $pid;
close $fh
or die "Error closing $pid_file: $OS_ERROR";
open my $PID_FH, '>', $PID_file
or die "Cannot open PID file $PID_file: $OS_ERROR";
print $PID_FH $PID
or die "Cannot print to PID file $PID_file: $OS_ERROR";
close $PID_FH
or die "Cannot close PID file $PID_file: $OS_ERROR";
if ( $pid ) {
PTDEBUG && _d('Checking if PID', $pid, 'is running');
my $pid_is_alive = kill 0, $pid;
if ( $pid_is_alive ) {
die "PID file $pid_file exists and PID $pid is running\n";
}
}
else {
# PID file but no PID: not sure what to do, so be safe and die;
# let the user figure it out (i.e. rm the pid file).
die "PID file $pid_file exists but it is empty. Remove the file "
. "if the process is no longer running.\n";
}
return $pid;
}
sub _update_pid_file {
my ($self, %args) = @_;
my @required_args = qw(pid pid_file);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
my $pid = $args{pid};
my $pid_file = $args{pid_file};
open my $fh, '>', $pid_file
or die "Cannot open $pid_file: $OS_ERROR";
print { $fh } $pid, "\n"
or die "Cannot print to $pid_file: $OS_ERROR";
close $fh
or warn "Cannot close $pid_file: $OS_ERROR";
PTDEBUG && _d('Created PID file:', $self->{PID_file});
return;
}
sub _remove_PID_file {
my ( $self ) = @_;
if ( $self->{PID_file} && -f $self->{PID_file} ) {
unlink $self->{PID_file}
or warn "Cannot remove PID file $self->{PID_file}: $OS_ERROR";
sub remove_pid_file {
my ($self, $pid_file) = @_;
$pid_file ||= $self->{pid_file};
if ( $pid_file && -f $pid_file ) {
unlink $self->{pid_file}
or warn "Cannot remove PID file $pid_file: $OS_ERROR";
PTDEBUG && _d('Removed PID file');
}
else {
@@ -220,19 +295,14 @@ sub DESTROY {
# have it. 2) When daemonized code forks its children get copies of
# the Daemon obj which will also call this sub when they exit. We
# don't remove it then because the daemonized parent code won't have it.
# This trick works because $self->{PID_owner}=$PID is set once to the
# This trick works because $self->{pid_file_owner}=$PID is set once to the
# owner's $PID then this value is copied on fork. But the "== $PID"
# here is the forked copy's PID which won't match the owner's PID.
$self->_remove_PID_file() if ($self->{PID_owner} || 0) == $PID;
return;
if ( ($self->{pid_file_owner} || 0) == $PID ) {
$self->remove_pid_file();
}
sub slurp_file {
my ($file) = @_;
return unless $file;
open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR";
return do { local $/; <$fh> };
return;
}
sub _d {
@@ -244,7 +314,6 @@ sub _d {
}
1;
}
# ###########################################################################
# End Daemon package
# ###########################################################################

View File

@@ -9,31 +9,28 @@ BEGIN {
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More;
use Time::HiRes qw(sleep);
use File::Temp qw(tempfile);
use Daemon;
use OptionParser;
use PerconaTest;
#plan skip_all => "Hm";
use constant PTDEVDEBUG => $ENV{PTDEVDEBUG} || 0;
my $o = new OptionParser(file => "$trunk/t/lib/samples/daemonizes.pl");
my $d = new Daemon(o=>$o);
my $pid_file = '/tmp/daemonizes.pl.pid';
my $log_file = '/tmp/daemonizes.output';
my $cmd = "$trunk/t/lib/samples/daemonizes.pl";
my $pid_file = "/tmp/pt-daemon-test.pid.$PID";
my $log_file = "/tmp/pt-daemon-test.log.$PID";
sub rm_tmp_files() {
-e $pid_file && (unlink $pid_file || die "Error removing $pid_file");
-e $log_file && (unlink $log_file || die "Error removing $log_file");
-f $pid_file && (unlink $pid_file || die "Error removing $pid_file");
-f $log_file && (unlink $log_file || die "Error removing $log_file");
}
# ############################################################################
# Test that it daemonizes, creates a PID file, and removes that PID file.
# ############################################################################
rm_tmp_files();
my $cmd = "$trunk/t/lib/samples/daemonizes.pl";
my $ret_val = system("$cmd 5 --daemonize --pid $pid_file >/dev/null 2>&1");
die 'Cannot test Daemon.pm because t/daemonizes.pl is not working'
unless $ret_val == 0;
@@ -41,16 +38,34 @@ die 'Cannot test Daemon.pm because t/daemonizes.pl is not working'
PerconaTest::wait_for_files($pid_file);
my $output = `ps wx | grep '$cmd 5' | grep -v grep`;
like($output, qr/$cmd/, 'Daemonizes');
ok(-f $pid_file, 'Creates PID file');
my ($pid) = $output =~ /\s*(\d+)\s+/;
like(
$output,
qr/$cmd/,
'Daemonizes'
);
ok(
-f $pid_file,
'Creates PID file'
);
my ($pid) = $output =~ /^\s*(\d+)\s+/;
$output = slurp_file($pid_file);
is($output, $pid, 'PID file has correct PID');
chomp($output) if $output;
is(
$output,
$pid,
'PID file has correct PID'
);
# Wait until the process goes away
PerconaTest::wait_until(sub { !kill(0, $pid) });
ok(! -f $pid_file, 'Removes PID file upon exit');
ok(
! -f $pid_file,
'Removes PID file upon exit'
);
# ############################################################################
# Check that STDOUT can be redirected
@@ -59,10 +74,19 @@ rm_tmp_files();
system("$cmd 0 --daemonize --log $log_file");
PerconaTest::wait_for_files($log_file);
ok(-f $log_file, 'Log file exists');
ok(
-f $log_file,
'Log file exists'
);
$output = slurp_file($log_file);
like($output, qr/STDOUT\nSTDERR\n/, 'STDOUT and STDERR went to log file');
like(
$output,
qr/STDOUT\nSTDERR\n/,
'STDOUT and STDERR went to log file'
);
my $log_size = -s $log_file;
PTDEVDEBUG && PerconaTest::_d('log size', $log_size);
@@ -71,6 +95,7 @@ PTDEVDEBUG && PerconaTest::_d('log size', $log_size);
system("$cmd 0 --daemonize --log $log_file");
PerconaTest::wait_until(sub { -s $log_file > $log_size });
$output = slurp_file($log_file);
like(
$output,
qr/STDOUT\nSTDERR\nSTDOUT\nSTDERR\n/,
@@ -82,6 +107,7 @@ like(
# ##########################################################################
rm_tmp_files();
diag(`touch $pid_file`);
ok(
-f $pid_file,
'PID file already exists'
@@ -90,7 +116,7 @@ ok(
$output = `$cmd 2 --daemonize --pid $pid_file 2>&1`;
like(
$output,
qr{The PID file $pid_file already exists},
qr{PID file $pid_file exists},
'Dies if PID file already exists'
);
@@ -182,7 +208,7 @@ like(
like(
slurp_file($tempfile),
qr/$pid, is not running/,
qr/Overwriting PID file $pid_file because PID $pid is not running/,
'Says that old PID is not running (issue 419)'
);
@@ -209,54 +235,55 @@ chomp($pid = slurp_file($pid_file));
$output = `$cmd 0 --daemonize --pid $pid_file 2>&1`;
like(
$output,
qr/$pid, is running/,
qr/PID file $pid_file exists and PID $pid is running/,
'Says that PID is running (issue 419)'
);
kill SIGKILL => $pid
if $pid;
if ( $pid ) {
kill 9, $pid;
}
sleep 1;
sleep 0.25;
rm_tmp_files();
# #############################################################################
# Test auto-PID file removal without having to daemonize (for issue 391).
# #############################################################################
my $pid_file2 = "/tmp/pt-daemon-test.pid2.$PID";
{
@ARGV = qw(--pid /tmp/d2.pid);
$o->get_specs("$trunk/t/lib/samples/daemonizes.pl");
$o->get_opts();
my $d2 = new Daemon(o=>$o);
$d2->make_PID_file();
my $d2 = Daemon->new(
pid_file => $pid_file2,
);
$d2->run();
ok(
-f '/tmp/d2.pid',
-f $pid_file2,
'PID file for non-daemon exists'
);
}
# Since $d2 was locally scoped, it should have been destoryed by now.
# This should have caused the PID file to be automatically removed.
ok(
!-f '/tmpo/d2.pid',
!-f $pid_file2,
'PID file auto-removed for non-daemon'
);
# We should still die if the PID file already exists,
# even if we're not a daemon.
{
`touch /tmp/d2.pid`;
@ARGV = qw(--pid /tmp/d2.pid);
$o->get_opts();
diag(`touch $pid_file2`);
eval {
my $d2 = new Daemon(o=>$o); # should die here actually
$d2->make_PID_file();
my $d2 = Daemon->new(
pid_file => $pid_file2,
);
$d2->run();
};
like(
$EVAL_ERROR,
qr{PID file /tmp/d2.pid already exists},
qr/PID file $pid_file2 exists/,
'Dies if PID file already exists for non-daemon'
);
diag(`rm -rf /tmp/d2.pid >/dev/null`);
unlink $pid_file2 if -f $pid_file2;
}
# #############################################################################

View File

@@ -12,7 +12,9 @@ BEGIN {
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use constant PTDEVDEBUG => $ENV{PTDEVDEBUG};
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
use constant PTDEVDEBUG => $ENV{PTDEVDEBUG} || 0;
use Time::HiRes qw(sleep);
@@ -31,14 +33,13 @@ if ( !defined $sleep_time ) {
$o->usage_or_errors();
my $daemon;
if ( $o->get('daemonize') ) {
PTDEVDEBUG && PerconaTest::_d('daemonizing');
my $daemon = Daemon->new(
daemonize => $o->get('daemonize'),
pid_file => $o->get('pid'),
log_file => $o->get('log'),
);
$OUTPUT_AUTOFLUSH = 1;
$daemon = new Daemon(o=>$o);
$daemon->daemonize();
$daemon->run();
PTDEVDEBUG && PerconaTest::_d('daemonized');
print "STDOUT\n";
@@ -46,7 +47,6 @@ if ( $o->get('daemonize') ) {
PTDEVDEBUG && PerconaTest::_d('daemon sleep', $sleep_time);
sleep $sleep_time;
}
PTDEVDEBUG && PerconaTest::_d('daemon done');
exit;