X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMooseX%2FDaemonize%2FCore.pm;h=d8512f1081993305731ea920f25648b069f8e965;hb=61d0473c7a2d30f0862ec6d2c63f686c4d761990;hp=dbf892ea30ad4e27ec1d6b4ec3a0b67ffd67a13e;hpb=18cc5c89142d5f39decc3098fd26077654d05c6d;p=gitmo%2FMooseX-Daemonize.git diff --git a/lib/MooseX/Daemonize/Core.pm b/lib/MooseX/Daemonize/Core.pm index dbf892e..d8512f1 100644 --- a/lib/MooseX/Daemonize/Core.pm +++ b/lib/MooseX/Daemonize/Core.pm @@ -1,74 +1,280 @@ package MooseX::Daemonize::Core; -use strict; # because Kwalitee is pedantic +use strict; # cause Perl::Critic errors are annoying +use MooseX::Getopt; # to load the NoGetopt metaclass use Moose::Role; -our $VERSION = 0.01; +our $VERSION = '0.16'; -use Proc::Daemon; +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 }, ); -sub daemon_fork { Proc::Daemon::Fork } -sub daemon_detach { Proc::Daemon::Init } +has ignore_zombies => ( + metaclass => 'Getopt', + isa => 'Bool', + is => 'rw', + default => sub { 0 }, +); + +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 compability.. 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; + } + else { + $self->is_daemon(1); + return; + } +} + +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"; + + unless ( $self->no_double_fork ) { + $SIG{'HUP'} = 'IGNORE'; + fork && exit; + } + + chdir '/'; # change to root directory + umask 0; # clear the file creation mask + + unless ( $self->dont_close_all_files ) { + # 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); + } + + # 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 + or confess "Could not redirect STDOUT to $stdout_file : $!"; + } + else { + open(STDOUT, "+>&STDIN") + or confess "Could not redirect STDOUT to /dev/null"; + } + + # 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") + 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) = @_; - return if $self->daemon_fork; - $self->daemon_detach; - $self->is_daemon(1); + 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 +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. + +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 =over -=item is_daemon Bool +=item I rw, isa => Bool)> + +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 aquiring 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. -If true, the process is the backgrounded process. This is useful for example -in an after 'start' => sub { } block +=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 daemon_fork() +=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 +appropriately. + +The C<%options> argument remains for backwards compatability, 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. + +The C<%options> argument remains for backwards compatability, but +it is suggested that you use the attributes listed above instead. + +=over 4 + +=item Becomes a session leader + +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 "/" -=item daemon_detach() +This is standard daemon behavior, if you want a different working +directory then simply change it later in your daemons code. -=item daemonize() +=item Clears the file creation mask. -Calls C to daemonize this process. +=item Closes all open file descriptors. -=item setup_signals() +See the I attribute for information on how to +change this part of the process. -Setup the signal handlers, by default it only sets up handlers for SIGINT and SIGHUP +=item Reopen STDERR, STDOUT & STDIN to /dev/null -=item handle_sigint() +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. -Handle a INT signal, by default calls C<$self->stop()> +B -=item handle_sighup() +If called from within the parent process (the is_daemon flag is set to +false), this method will simply return and do nothing. -Handle a HUP signal. By default calls C<$self->restart()> +=item B + +This will simply call C followed by C. + +The C<%options> argument remains for backwards compatability, but +it is suggested that you use the attributes listed above instead. =item meta() @@ -76,38 +282,78 @@ The C method from L =back -=head1 DEPENDENCIES +=head1 STUFF YOU SHOULD READ -=for author to fill in: - A list of all the other modules that this module relies upon, - including any restrictions on versions, and an indication whether - the module is part of the standard Perl distribution, part of the - module's distribution, or must be installed separately. ] +=over 4 -Obviously L, and L +=item Note about double fork -=head1 INCOMPATIBILITIES +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. -=for author to fill in: - A list of any modules that this module cannot be used in conjunction - with. This may be due to name conflicts in the interface, or - competition for system or program resources, or due to internal - limitations of Perl (for example, many modules that use source code - filters are mutually incompatible). + The first fork accomplishes two things - allow the shell to return, + and allow you to do a setsid(). -None reported. + 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. -=head1 BUGS AND LIMITATIONS + 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 -=for author to fill in: - A list of known problems with the module, together with some - indication Whether they are likely to be fixed in an upcoming - release. Also a list of restrictions on the features the module - does provide: data types that cannot be handled, performance issues - and the circumstances in which they may arise, practical - limitations on the size of data sets, special cases that are not - (yet) handled, etc. +=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 + +=head1 INCOMPATIBILITIES + +None reported. + +=head1 BUGS AND LIMITATIONS No bugs have been reported. @@ -117,28 +363,26 @@ L. =head1 SEE ALSO -L, L, L - -=head1 AUTHOR - -Chris Prather C<< >> +L -=head1 THANKS +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. -Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the -#moose denzians +=head1 AUTHOR -Some bug fixes sponsored by Takkle Inc. +Stevan Little C<< >> =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. + This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. - =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY @@ -161,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