X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMooseX%2FDaemonize%2FCore.pm;h=3eb72521fddf734b145de1b20623368c59607bab;hb=b37fcc5f9a086f8204bea2490775aaea6d3859e3;hp=b2914afd112d8bce1af92844b9ff730082397d18;hpb=289fcbc7afd4eee6c6b41e48dda1507942c5fd29;p=gitmo%2FMooseX-Daemonize.git diff --git a/lib/MooseX/Daemonize/Core.pm b/lib/MooseX/Daemonize/Core.pm index b2914af..3eb7252 100644 --- a/lib/MooseX/Daemonize/Core.pm +++ b/lib/MooseX/Daemonize/Core.pm @@ -1,18 +1,59 @@ package MooseX::Daemonize::Core; +use strict; # cause Perl::Critic errors are annoying +use MooseX::Getopt; # to load the NoGetopt metaclass use Moose::Role; -our $VERSION = 0.01; - use POSIX (); has is_daemon => ( - isa => 'Bool', - is => 'rw', - default => sub { 0 }, + # NOTE: + # this should never be accessible + # from the command line + # - SL + metaclass => 'NoGetopt', + isa => 'Bool', + is => 'rw', + default => sub { 0 }, +); + +has ignore_zombies => ( + metaclass => 'Getopt', + isa => 'Bool', + is => 'rw', + default => sub { 0 }, ); -sub daemon_fork { - my $self = shift; +has no_double_fork => ( + metaclass => 'Getopt', + isa => 'Bool', + is => 'rw', + default => sub { 0 }, +); + +has dont_close_all_files => ( + metaclass => 'Getopt', + isa => 'Bool', + is => 'rw', + default => sub { 0 }, +); + +sub _get_options { + my ($self, %options) = @_; + # backwards compatibility.. old code might be calling daemon_fork/_detach with options + foreach my $opt (qw( ignore_zombies no_double_fork dont_close_all_files )) { + $self->$opt( $options{ $opt } ) if ( defined $options{ $opt } ); + } +} + + +sub daemon_fork { + my ($self, %options) = @_; + + $self->_get_options( %options ); + + $SIG{CHLD} = 'IGNORE' + if $self->ignore_zombies;; + if (my $pid = fork) { return $pid; } @@ -21,67 +62,118 @@ sub daemon_fork { return; } } -sub daemon_detach { - my $self = shift; - - return unless $self->is_daemon; - + +sub daemon_detach { + my ($self, %options) = @_; + + return unless $self->is_daemon; # return if parent ... + + $self->_get_options( %options ); + # now we are in the daemon ... + (POSIX::setsid) # set session id - || confess "Cannot detach from controlling process"; - + || confess "Cannot detach from controlling process"; + + unless ( $self->no_double_fork ) { + $SIG{'HUP'} = 'IGNORE'; + fork && exit; + } + chdir '/'; # change to root directory - umask 0; # clear the file creation mask - - # get the max numnber of possible file descriptors - my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX ); - $openmax = 64 if !defined($openmax) || $openmax < 0; - - # close them all - POSIX::close($_) foreach (0 .. $openmax); + umask 0; # clear the file creation mask - open(STDIN, "+>/dev/null"); + unless ( $self->dont_close_all_files ) { + # get the max number of possible file descriptors + my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX ); + $openmax = 64 if !defined($openmax) || $openmax < 0; + + # close them all + POSIX::close($_) foreach (0 .. $openmax); + } + + # fixup STDIN ... + + open(STDIN, "+>/dev/null") + or confess "Could not redirect STDOUT to /dev/null"; + + # fixup STDOUT ... if (my $stdout_file = $ENV{MX_DAEMON_STDOUT}) { - open STDOUT, ">", $stdout_file + open STDOUT, ">", $stdout_file or confess "Could not redirect STDOUT to $stdout_file : $!"; } else { - open(STDOUT, "+>&STDIN"); + open(STDOUT, "+>&STDIN") + or confess "Could not redirect STDOUT to /dev/null"; } - if (my $stderr_file = $ENV{MX_DAEMON_STDERR}) { - open STDERR, ">", "ERR.txt" - or confess "Could not redirect STDERR to $stderr_file : $!"; + # fixup STDERR ... + + if (my $stderr_file = $ENV{MX_DAEMON_STDERR}) { + open STDERR, ">", $stderr_file + or confess "Could not redirect STDERR to $stderr_file : $!"; } - else { - open(STDERR, "+>&STDIN"); + else { + open(STDERR, "+>&STDIN") + or confess "Could not redirect STDERR to /dev/null"; ; } + + # do a little house cleaning ... + + # Avoid 'stdin reopened for output' + # warning with newer perls + open( NULL, '/dev/null' ); + if (0); + + # return success + return 1; } sub daemonize { - my ($self) = @_; - $self->daemon_fork; - $self->daemon_detach; + my ($self, %options) = @_; + $self->daemon_fork(%options); + $self->daemon_detach(%options); } 1; + __END__ -=head1 NAME +=pod -MooseX::Daemonize::Core - provides a Role the core daemonization features +=head1 NAME -=head1 VERSION +MooseX::Daemonize::Core - A Role with the core daemonization features =head1 SYNOPSIS - + + package My::Daemon; + use Moose; + + with 'MooseX::Daemonize::Core'; + + sub start { + my $self = shift; + # daemonize me ... + $self->daemonize; + # return from the parent,... + return unless $self->is_daemon; + # but continue on in the child (daemon) + } + =head1 DESCRIPTION -=head2 Important Note +This is the basic daemonization Role, it provides a few methods (see +below) and the minimum features needed to properly daemonize your code. + +=head2 Important Notes + +None of the methods in this role will exit the parent process for you, +it only forks and detaches your child (daemon) process. It is your +responsibility to exit the parent process in some way. -This method with not exit the parent process for you, it only forks -and detaches your child (daemon) process. It is your responsibility -to exit the parent process in some way. +There is no PID or PID file management in this role, that is your +responsibility (see some of the other roles in this distro for that). =head1 ATTRIBUTES @@ -89,64 +181,170 @@ to exit the parent process in some way. =item I rw, isa => Bool)> -This attribute is used to signal if we are within the -daemon process or not. +This attribute is used to signal if we are within the +daemon process or not. + +=item I rw, isa => Bool)> + +Setting this attribute to true will cause this method to not perform the +typical double-fork, which is extra added protection from your process +accidentally acquiring a controlling terminal. More information can be +found above, and by Googling "double fork daemonize". + +If you the double-fork behavior off, you might want to enable the +I. + +=item I rw, isa => Bool)> + +Setting this attribute to a true value will result in setting the C<$SIG{CHLD}> +handler to C. This tells perl to clean up zombie processes. By +default, and for the most part you don't I it, only when you turn off +the double fork behavior (with the I attribute) +do you sometimes want this behavior. + +=item I rw, isa => Bool)> + +Setting this attribute to true will cause it to skip closing all the +filehandles. This is useful if you are opening things like sockets +and such in the pre-fork. =back -=head1 METHODS +=head1 METHODS =over -=item B +=item B -This forks off the child process to be daemonized. Just as with -the built in fork, it returns the child pid to the parent process, -0 to the child process. It will also set the is_daemon flag +This forks off the child process to be daemonized. Just as with +the built in fork, it returns the child pid to the parent process, +0 to the child process. It will also set the is_daemon flag appropriately. -=item B +The C<%options> argument remains for backwards compatibility, but +it is suggested that you use the attributes listed above instead. + +=item B -This detaches the new child process from the terminal by doing -the following things. If called from within the parent process -(the is_daemon flag is set to false), then it will simply return -and do nothing. +This detaches the new child process from the terminal by doing +the following things. + +The C<%options> argument remains for backwards compatibility, but +it is suggested that you use the attributes listed above instead. =over 4 -=item Becomes a session leader +=item Becomes a session leader -This detaches the program from the controlling terminal, it is +This detaches the program from the controlling terminal, it is accomplished by calling POSIX::setsid. +=item Performing the double-fork + +See below for information on how to change this part of the process. + =item Changes the current working directory to "/" -This is standard daemon behavior, if you want a different working -directory then simply change it later in your daemons code. +This is standard daemon behavior, if you want a different working +directory then simply change it later in your daemons code. =item Clears the file creation mask. =item Closes all open file descriptors. +See the I attribute for information on how to +change this part of the process. + =item Reopen STDERR, STDOUT & STDIN to /dev/null -This behavior can be controlled slightly though the MX_DAEMON_STDERR +This behavior can be controlled slightly though the MX_DAEMON_STDERR and MX_DAEMON_STDOUT environment variables. It will look for a filename in either of these variables and redirect STDOUT and/or STDERR to those files. This is useful for debugging and/or testing purposes. --back +B + +If called from within the parent process (the is_daemon flag is set to +false), this method will simply return and do nothing. -=item B +=item B This will simply call C followed by C. +The C<%options> argument remains for backwards compatibility, but +it is suggested that you use the attributes listed above instead. + =item meta() The C method from L =back +=back + +=head1 STUFF YOU SHOULD READ + +=over 4 + +=item Note about double fork + +Taken from L +in a comment entitled I, +it is not the definitive statement on the issue, but it's clear and well +written enough so I decided to reproduce it here. + + The first fork accomplishes two things - allow the shell to return, + and allow you to do a setsid(). + + The setsid() removes yourself from your controlling terminal. You + see, before, you were still listed as a job of your previous process, + and therefore the user might accidentally send you a signal. setsid() + gives you a new session, and removes the existing controlling terminal. + + The problem is, you are now a session leader. As a session leader, if + you open a file descriptor that is a terminal, it will become your + controlling terminal (oops!). Therefore, the second fork makes you NOT + be a session leader. Only session leaders can acquire a controlling + terminal, so you can open up any file you wish without worrying that + it will make you a controlling terminal. + + So - first fork - allow shell to return, and permit you to call setsid() + + Second fork - prevent you from accidentally reacquiring a controlling + terminal. + +That said, you don't always want this to be the behavior, so you are +free to specify otherwise using the I attribute. + +=item Note about zombies + +Doing the double fork (see above) tends to get rid of your zombies since +by the time you have double forked your daemon process is then owned by +the init process. However, sometimes the double-fork is more than you +really need, and you want to keep your daemon processes a little closer +to you. In this case you have to watch out for zombies, you can avoid then +by just setting the I attribute (see above). + +=back + +=head1 ENVIRONMENT VARIABLES + +These variables are best just used for debugging and/or testing, but +not used for actual logging. For that, you should reopen STDOUT/ERR on +your own. + +=over 4 + +=item B + +A filename to redirect the daemon STDOUT to. + +=item B + +A filename to redirect the daemon STDERR to. + +=back + =head1 DEPENDENCIES L, L @@ -167,19 +365,17 @@ L. L -This code is based B on L, we originally +This code is based B on L, we originally depended on it, but we needed some more flexibility, so instead -we just stole the code. +we just stole the code. =head1 AUTHOR Stevan Little C<< >> -=head1 THANKS - =head1 LICENCE AND COPYRIGHT -Copyright (c) 2007, Chris Prather C<< >>. All rights +Copyright (c) 2007-2011, Chris Prather C<< >>. All rights reserved. Portions heavily borrowed from L which is copyright Earl Hood. @@ -209,3 +405,5 @@ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +=cut