File Coverage

blib/lib/Acme/Ghost.pm
Criterion Covered Total %
statement 56 253 22.1
branch 12 116 10.3
condition 9 64 14.0
subroutine 16 43 37.2
pod 23 23 100.0
total 116 499 23.2


line stmt bran cond sub pod time code
1             package Acme::Ghost;
2 2     2   247071 use warnings;
  2         5  
  2         120  
3 2     2   10 use strict;
  2         7  
  2         52  
4 2     2   1009 use utf8;
  2         565  
  2         12  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             Acme::Ghost - An yet another view to daemon processes
11              
12             =head1 SYNOPSIS
13              
14             use Acme::Ghost
15              
16             my $g = Acme::Ghost->new(
17             logfile => '/tmp/daemon.log',
18             pidfile => '/tmp/daemon.pid',
19             user => 'nobody',
20             group => 'nogroup',
21             );
22              
23             $g->daemonize;
24              
25             $g->log->info('Oops! I am Your Ghost');
26              
27             =head1 DESCRIPTION
28              
29             An yet another view to daemon processes
30              
31             =head2 new
32              
33             my $g = Acme::Ghost->new(
34             name => 'myDaemon',
35             user => 'nobody',
36             group => 'nogroup',
37             pidfile => '/var/run/myDaemon.pid',
38             logfile => '/var/log/myDaemon.log',
39             ident => 'myDaemon',
40             logopt => 'ndelay,pid',
41             facility => 'user',
42             logger => Mojo::Log->new,
43             loglevel => 'debug',
44             loghandle => IO::Handler->new,
45             );
46              
47             =head1 ATTRIBUTES
48              
49             This class implements the following attributes
50              
51             =head2 facility
52              
53             facility => 'user',
54              
55             This attribute sets facility for logging
56              
57             See L
58              
59             =head2 group
60              
61             group => 'nogroup',
62             group => 65534,
63              
64             This attribute sets group/gid for spawned process
65              
66             =head2 ident
67              
68             ident => 'myDaemon',
69              
70             This attribute sets ident string for system log (syslog)
71              
72             =head2 logfile
73              
74             logfile => '/var/log/myDaemon.log',
75              
76             This attribute sets log file path. By default all log entries will be printed to syslog
77              
78             See L
79              
80             =head2 logger
81              
82             logger => Mojo::Log->new,
83              
84             This attribute perfoms to set predefined logger, eg. Mojo::Log.
85             If you set this attribute, the specified logger will be used as the preferred logger
86              
87             =head2 loghandle
88              
89             Log filehandle, defaults to opening "file" or uses syslog if file not specified
90              
91             See L
92              
93             =head2 loglevel
94              
95             loglevel => 'debug',
96              
97             This attribute sets the log level
98              
99             See L
100              
101             =head2 logopt
102              
103             logopt => 'ndelay,pid',
104              
105             This attribute contains zero or more of the options
106              
107             See L
108              
109             =head2 name
110              
111             name => 'myDaemon',
112              
113             This attribute sets name of daemon. Default: script name C
114              
115             =head2 pidfile
116              
117             pidfile => '/var/run/myDaemon.pid',
118              
119             This attribute sets PID file path. Default: ./.pid
120              
121             =head2 user
122              
123             user => 'nobody',
124             user => 65534,
125              
126             This attribute sets user/uid for spawned process
127              
128             =head1 METHODS
129              
130             This class implements the following methods
131              
132             =head2 again
133              
134             This method is called immediately after creating the instance and returns it
135              
136             B Internal use only for subclasses!
137              
138             =head2 daemonize
139              
140             $g = $g->daemonize;
141              
142             Main routine for just daemonize.
143             This routine will check on the pid file, safely fork, create the pid file (storing the pid in the file),
144             become another user and group, close STDIN, STDOUT and STDERR, separate from the process group (become session leader),
145             and install $SIG{INT} to remove the pid file. In otherwords - daemonize.
146             All errors result in a die
147              
148             =head2 filepid
149              
150             my $filepid = $g->filepid;
151              
152             This method returns L object
153              
154             =head2 flush
155              
156             $self = $self->flush;
157              
158             This internal method flush (resets) process counters to defaults. Please do not use this method in your inherits
159              
160             =head2 is_daemonized
161              
162             $g->is_daemonized or die "Your ghost process really is not a daemon"
163              
164             This method returns status of daemon:
165              
166             True - the process is an daemon;
167             False - the process is not daemon;
168              
169             =head2 is_spirited
170              
171             my $is_spirited = $g->is_spirited;
172              
173             This method returns status of spirit:
174              
175             True - the process is an spirit;
176             False - the process is not spirit;
177              
178             =head2 log
179              
180             my $log = $g->log;
181              
182             This method returns L object
183              
184             =head2 ok
185              
186             $g->ok or die "Interrupted!";
187              
188             This method checks process state and returns boolean status of healthy.
189             If this status is false, then it is immediately to shut down Your process
190             as soon as possible, otherwise your process will be forcibly destroyed
191             within 7 seconds from the moment your process receives the corresponding signal
192              
193             =head2 pid
194              
195             print $g->pid;
196              
197             This method returns PID of the daemon
198              
199             =head2 set_gid
200              
201             $g = $g->set_gid('1000 10001 10002');
202             $g = $g->set_gid(1000);
203             $g = $g->set_gid('nogroup');
204             $g = $g->set_gid;
205              
206             Become another group. Arguments are groups (or group ids or space delimited list of group ids). All errors die
207              
208             =head2 set_uid
209              
210             $g = $g->set_uid(1000);
211             $g = $g->set_uid('nobody');
212             $g = $g->set_uid;
213              
214             Become another user. Argument is user (or userid). All errors die
215              
216             =head1 CONTROL METHODS
217              
218             List of LSB Daemon Control Methods
219              
220             These methods can be used to control the daemon behavior.
221             Every effort has been made to have these methods DWIM (Do What I Mean),
222             so that you can focus on just writing the code for your daemon.
223              
224             =head2 ctrl
225              
226             exit $g->ctrl( shift @ARGV, 'USR2' );
227             # start, stop, restart, reload, status
228              
229             Daemon Control Dispatcher with using USR2 to reloading
230              
231             exit $g->ctrl( shift @ARGV, 0 );
232              
233             This example shows how to forced suppress reloading (disable send users signals to daemon)
234              
235             =head2 reload
236              
237             $exit_code = $g->reload; # SIGHUP (by default)
238             $exit_code = $g->reload('USR2'); # SIGUSR2
239             $exit_code = $g->reload(12); # SIGUSR2 too
240             say "Reloading ". $g->pid;
241              
242             This method performs sending signal to Your daemon and return C<0> as exit code.
243             This method is primarily intended to perform a daemon reload
244              
245             =head2 restart
246              
247             $exit_code = $g->restart;
248             if ($exit_code) {
249             say STDERR "Restart failed " . $g->pid;
250             } else {
251             say "Restart successful";
252             }
253              
254             This method performs restarting the daemon and returns C<0> as successfully
255             exit code or C<1> in otherwise
256              
257             =head2 start
258              
259             my $exit_code = $g->start;
260             say "Running ". $g->pid;
261             exit $exit_code;
262              
263             This method performs starting the daemon and returns C<0> as exit code.
264             The spawned process calls the startup handler and exits with status C<0>
265             as exit code without anything return
266              
267             =head2 status
268              
269             if (my $runned = $g->status) {
270             say "Running $runned";
271             } else {
272             say "Not running";
273             }
274              
275             This method checks the status of running daemon and returns its PID (alive).
276             The method returns 0 if it is not running (dead).
277              
278             =head2 stop
279              
280             if (my $runned = $g->stop) {
281             if ($runned < 0) {
282             die "Daemon " . $g->pid ." is still running";
283             } else {
284             say "Stopped $runned";
285             }
286             } else {
287             say "Not running";
288             }
289              
290             This method performs stopping the daemon and returns:
291              
292             +PID -- daemon stopped successfully
293             0 -- daemon is not running
294             -PID -- daemon is still running, stop failed
295              
296             =head1 HOOKS
297              
298             This class implements the following user-methods (hooks).
299             Each of the following methods may be implemented (overwriting) in a your class
300              
301             =head2 preinit
302              
303             sub preinit {
304             my $self = shift;
305             # . . .
306             }
307              
308             The preinit() method is called before spawning (forking)
309              
310             =head2 init
311              
312             sub init {
313             my $self = shift;
314             # . . .
315             }
316              
317             The init() method is called after spawning (forking) and after daemonizing
318              
319             =head2 startup
320              
321             sub startup {
322             my $self = shift;
323             # . . .
324             }
325              
326             The startup() method is called after daemonizing in service mode
327              
328             This is your main hook into the service, it will be called at service startup.
329             Meant to be overloaded in a subclass.
330              
331             =head2 cleanup
332              
333             sub cleanup {
334             my $self = shift;
335             my $scope = shift; # 0 or 1
336             # . . .
337             }
338              
339             The cleanup() method is called at before exit
340             This method passes one argument:
341              
342             0 -- called at normal DESTROY;
343             1 -- called at interrupt
344              
345             B On DESTROY phase logging is unpossible.
346             We not recommended to use logging in this method
347              
348             =head2 hangup
349              
350             sub hangup {
351             my $self = shift;
352             # . . .
353             }
354              
355             The hangup() method is called on HUP or USR2 signals
356              
357             For example (on Your inherit subclass):
358              
359             sub init {
360             my $self = shift;
361              
362             # Listen USR2 (reload)
363             $SIG{HUP} = sub { $self->hangup };
364             }
365             sub hangup {
366             my $self = shift;
367             $self->log->debug(">> Hang up!");
368             }
369              
370             =head1 EXAMPLES
371              
372             =over 4
373              
374             =item ghost_simple.pl
375              
376             This is traditional way to start daemons
377              
378             use Acme::Ghost;
379              
380             my $g = Acme::Ghost->new(
381             logfile => 'daemon.log',
382             pidfile => 'daemon.pid',
383             );
384              
385             my $cmd = shift(@ARGV) // 'start';
386             if ($cmd eq 'status') {
387             if (my $runned = $g->status) {
388             print "Running $runned\n";
389             } else {
390             print "Not running\n";
391             }
392             exit 0; # Ok
393             } elsif ($cmd eq 'stop') {
394             if (my $runned = $g->stop) {
395             if ($runned < 0) {
396             print STDERR "Failed to stop " . $g->pid . "\n";
397             exit 1; # Error
398             }
399             print "Stopped $runned\n";
400             } else {
401             print "Not running\n";
402             }
403             exit 0; # Ok
404             } elsif ($cmd ne 'start') {
405             print STDERR "Command incorrect\n";
406             exit 1; # Error
407             }
408              
409             # Daemonize
410             $g->daemonize;
411              
412             my $max = 10;
413             my $i = 0;
414             while (1) {
415             $i++;
416             sleep 3;
417             $g->log->debug(sprintf("> %d/%d", $i, $max));
418             last if $i >= $max;
419             }
420              
421             =item ghost_acme.pl
422              
423             Simple acme example of daemon with reloading demonstration
424              
425             my $g = MyGhost->new(
426             logfile => 'daemon.log',
427             pidfile => 'daemon.pid',
428             );
429              
430             exit $g->ctrl(shift(@ARGV) // 'start'); # start, stop, restart, reload, status
431              
432             1;
433              
434             package MyGhost;
435              
436             use parent 'Acme::Ghost';
437              
438             sub init {
439             my $self = shift;
440             $SIG{HUP} = sub { $self->hangup }; # Listen USR2 (reload)
441             }
442             sub hangup {
443             my $self = shift;
444             $self->log->debug("Hang up!");
445             }
446             sub startup {
447             my $self = shift;
448             my $max = 100;
449             my $i = 0;
450             while ($self->ok) {
451             $i++;
452             sleep 3;
453             $self->log->debug(sprintf("> %d/%d", $i, $max));
454             last if $i >= $max;
455             }
456             }
457              
458             1;
459              
460             =item ghost_ioloop.pl
461              
462             L example
463              
464             my $g = MyGhost->new(
465             logfile => 'daemon.log',
466             pidfile => 'daemon.pid',
467             );
468              
469             exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status
470              
471             1;
472              
473             package MyGhost;
474              
475             use parent 'Acme::Ghost';
476             use Mojo::IOLoop;
477              
478             sub init {
479             my $self = shift;
480             $self->{loop} = Mojo::IOLoop->new;
481             }
482             sub startup {
483             my $self = shift;
484             my $loop = $self->{loop};
485             my $i = 0;
486              
487             # Add a timers
488             my $timer = $loop->timer(5 => sub {
489             my $l = shift; # loop
490             $self->log->info("Timer!");
491             });
492             my $recur = $loop->recurring(1 => sub {
493             my $l = shift; # loop
494             $l->stop unless $self->ok;
495             $self->log->info("Tick! " . ++$i);
496             $l->stop if $i >= 10;
497             });
498              
499             $self->log->debug("Start IOLoop");
500              
501             # Start event loop if necessary
502             $loop->start unless $loop->is_running;
503              
504             $self->log->debug("Finish IOLoop");
505             }
506              
507             1;
508              
509             =item ghost_ae.pl
510              
511             AnyEvent example
512              
513             my $g = MyGhost->new(
514             logfile => 'daemon.log',
515             pidfile => 'daemon.pid',
516             );
517              
518             exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status
519              
520             1;
521              
522             package MyGhost;
523              
524             use parent 'Acme::Ghost';
525             use AnyEvent;
526              
527             sub startup {
528             my $self = shift;
529             my $quit = AnyEvent->condvar;
530             my $i = 0;
531              
532             # Create watcher timer
533             my $watcher = AnyEvent->timer (after => 1, interval => 1, cb => sub {
534             $quit->send unless $self->ok;
535             });
536              
537             # Create process timer
538             my $timer = AnyEvent->timer(after => 3, interval => 3, cb => sub {
539             $self->log->info("Tick! " . ++$i);
540             $quit->send if $i >= 10;
541             });
542              
543             $self->log->debug("Start AnyEvent");
544             $quit->recv; # Run!
545             $self->log->debug("Finish AnyEvent");
546             }
547              
548             1;
549              
550             =item ghost_nobody.pl
551              
552             This example shows how to start daemons over nobody user and logging to syslog (default)
553              
554             my $g = MyGhost->new(
555             pidfile => '/tmp/daemon.pid',
556             user => 'nobody',
557             group => 'nogroup',
558             );
559              
560             exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, status
561              
562             1;
563              
564             package MyGhost;
565              
566             use parent 'Acme::Ghost';
567              
568             sub startup {
569             my $self = shift;
570             my $max = 100;
571             my $i = 0;
572             while ($self->ok) {
573             $i++;
574             sleep 3;
575             $self->log->debug(sprintf("> %d/%d", $i, $max));
576             last if $i >= $max;
577             }
578             }
579              
580             1;
581              
582             =back
583              
584             =head1 DEBUGGING
585              
586             You can set the C environment variable to get some advanced diagnostics information printed to
587             C.
588              
589             ACME_GHOST_DEBUG=1
590              
591             =head1 TO DO
592              
593             See C file
594              
595             =head1 SEE ALSO
596              
597             L, L, L,
598             L, L, L,
599             L
600              
601             =head1 AUTHOR
602              
603             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
604              
605             =head1 COPYRIGHT
606              
607             Copyright (C) 1998-2026 D&D Corporation
608              
609             =head1 LICENSE
610              
611             This program is distributed under the terms of the Artistic License Version 2.0
612              
613             See the C file or L for details
614              
615             =cut
616              
617             our $VERSION = '1.03';
618              
619 2     2   372 use Carp qw/carp croak/;
  2         5  
  2         118  
620 2     2   12 use Cwd qw/getcwd/;
  2         4  
  2         75  
621 2     2   9 use File::Basename qw//;
  2         4  
  2         35  
622 2     2   8 use File::Spec qw//;
  2         3  
  2         52  
623 2     2   1054 use POSIX qw/ :sys_wait_h SIGINT SIGTERM SIGQUIT SIGKILL SIGHUP SIG_BLOCK SIG_UNBLOCK /;
  2         19165  
  2         19  
624              
625 2     2   5077 use Acme::Ghost::FilePid;
  2         8  
  2         85  
626 2     2   1182 use Acme::Ghost::Log;
  2         52  
  2         226  
627              
628             use constant {
629 2 50 50     7900 DEBUG => $ENV{ACME_GHOST_DEBUG} || 0,
      33        
630             IS_ROOT => (($> == 0) || ($< == 0)) ? 1 : 0,
631             SLEEP => 60,
632             INT_TRIES => 3,
633             LSB_COMMANDS=> [qw/start stop reload restart status/],
634 2     2   17 };
  2         4  
635              
636             sub new {
637 1     1 1 246466 my $class = shift;
638 1 50       14 my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {};
  0 50       0  
639 1   33     106 my $name = $args->{name} || File::Basename::basename($0);
640 1   50     10 my $user = $args->{user} // '';
641 1   50     8 my $group = $args->{group} // '';
642              
643             # Get UID by User
644 1         8 my $uid = $>; # Effect. UID
645 1         2 if (IS_ROOT) {
646 1 50       6 if ($user =~ /^(\d+)$/) {
    50          
647 0         0 $uid = $user;
648             } elsif (length($user)) {
649 0   0     0 $uid = getpwnam($user) || croak "getpwnam failed - $!\n";
650             }
651             }
652 1 50 50     371 $user = getpwuid($uid || 0) unless length $user;
653              
654             # Get GID by Group
655 1         15 my $gids = $); # Effect. GIDs
656 1         3 if (IS_ROOT) {
657 1 50       7 if ($group =~ /^(\d+)$/) {
    50          
658 0         0 $gids = $group;
659             } elsif (length($group)) {
660 0   0     0 $gids = getgrnam($group) || croak "getgrnam failed - $!\n";
661             }
662             }
663 1         5 my $gid = (split /\s+/, $gids)[0]; # Get first GID
664 1 50 50     48 $group = getpwuid($gid || 0) unless length $group;
665              
666             # Check name
667 1 50       5 croak "Can't create unnamed daemon\n" unless $name;
668              
669             my $self = bless {
670             name => $name,
671             user => $user,
672             group => $group,
673             uid => $uid,
674             gid => $gid,
675             gids => $gids,
676              
677             # PID
678             pidfile => $args->{pidfile} || File::Spec->catfile(getcwd(), sprintf("%s.pid", $name)),
679             _filepid => undef,
680              
681             # Log
682             facility => $args->{facility},
683             logfile => $args->{logfile},
684             ident => $args->{ident} || $name,
685             logopt => $args->{logopt},
686             logger => $args->{logger},
687             loglevel => $args->{loglevel},
688             loghandle => $args->{loghandle},
689 1   33     39 _log => undef,
      33        
690              
691             # Runtime
692             initpid => $$, # PID of root process
693             ppid => 0, # PID before daemonize
694             pid => 0, # PID daemonized process
695             daemonized => 0, # 0 - no daemonized; 1 - daemonized
696             spirited => 0, # 0 - is not spirit; 1 - is spirit (See ::Prefork)
697              
698             # Manage
699             ok => 0, # 1 - Ok. Process is healthy (ok)
700             signo => 0, # The caught signal number
701             interrupt => 0, # The interrupt counter
702              
703             }, $class;
704 1         7 return $self->again(%$args);
705             }
706 1     1 1 8 sub again { shift }
707             sub log {
708 0     0 1 0 my $self = shift;
709             return $self->{_log} //= Acme::Ghost::Log->new(
710             facility => $self->{facility},
711             ident => $self->{ident},
712             logopt => $self->{logopt},
713             logger => $self->{logger},
714             level => $self->{loglevel},
715             file => $self->{logfile},
716             handle => $self->{loghandle},
717 0   0     0 );
718             }
719             sub filepid {
720 0     0 1 0 my $self = shift;
721             return $self->{_filepid} //= Acme::Ghost::FilePid->new(
722             file => $self->{pidfile}
723 0   0     0 );
724             }
725             sub set_uid {
726 0     0 1 0 my $self = shift;
727 0   0     0 my $uid = shift // $self->{uid};
728 0         0 return $self unless IS_ROOT; # Skip if no ROOT
729 0 0       0 return $self unless defined $uid; # Skip if no UID
730              
731             # Set UID
732 0 0       0 POSIX::setuid($uid) || die "Setuid $uid failed - $!\n";
733 0 0 0     0 if ($< != $uid || $> != $uid) { # check $> also (rt #21262)
734 0         0 $< = $> = $uid; # try again - needed by some 5.8.0 linux systems (rt #13450)
735 0 0       0 if ($< != $uid) {
736 0         0 die "Detected strange UID. Couldn't become UID \"$uid\": $!\n";
737             }
738             }
739              
740 0         0 return $self;
741             }
742             sub set_gid {
743 0     0 1 0 my $self = shift;
744 0   0     0 my $gids = shift // $self->{gids};
745 0         0 return $self unless IS_ROOT; # Skip if no ROOT
746 0 0       0 return $self unless defined $gids; # Skip if no GIDs
747              
748             # Get GIDs
749 0         0 my $gid = (split /\s+/, $gids)[0]; # Get first GID
750 0         0 $) = "$gid $gids"; # store all the GIDs (calls setgroups)
751 0 0       0 POSIX::setgid($gid) || die "Setgid $gid failed - $!\n"; # Set first GID
752 0 0       0 if (! grep {$gid == $_} split /\s+/, $() { # look for any valid id in the list
  0         0  
753 0         0 die "Detected strange GID. Couldn't become GID \"$gid\": $!\n";
754             }
755              
756 0         0 return $self;
757             }
758             sub daemonize {
759 0     0 1 0 my $self = shift;
760 0         0 my $safe = shift;
761 0 0       0 croak "This process is already daemonized (PID=$$)\n" if $self->{daemonized};
762              
763             # Check PID
764 0         0 my $pid_file = $self->filepid->file; # PID File
765 0 0       0 if ( my $runned = $self->filepid->running ) {
766 0         0 die "Already running $runned\n";
767             }
768              
769             # Store current PID to instance as Parent PID
770 0         0 $self->{ppid} = $$;
771              
772             # Get UID & GID
773 0         0 my $uid = $self->{uid}; # UID
774 0         0 my $gids = $self->{gid}; # returns list of groups (gids)
775 0         0 my $gid = (split /[\s,]+/, $gids)[0]; # First GID
776 0         0 _debug("!! UID=%s; GID=%s; GIDs=\"%s\"", $uid, $gid, $gids);
777              
778             # Pre Init Hook
779 0         0 $self->preinit;
780 0         0 $self->{_log} = undef; # Close log handlers before spawn
781              
782             # Spawn
783 0         0 my $pid = _fork();
784 0 0       0 if ($pid) {
785 0         0 _debug("!! Spawned (PID=%s)", $pid);
786 0 0       0 if ($safe) { # For internal use only
787 0         0 $self->{pid} = $pid; # Store child PID to instance
788 0         0 return $self;
789             }
790 0         0 exit 0; # exit parent process
791             }
792              
793             # Child
794 0         0 $self->{daemonized} = 1; # Set daemonized flag
795 0         0 $self->filepid->pid($$)->save; # Set new PID and Write PID file
796 0 0       0 chown($uid, $gid, $pid_file) if IS_ROOT && -e $pid_file;
797              
798             # Set GID and UID
799 0         0 $self->set_gid->set_uid;
800              
801             # Turn process into session leader, and ensure no controlling terminal
802 0         0 unless (DEBUG) {
803 0 0       0 die "Can't start a new session: $!" if POSIX::setsid() < 0;
804             }
805              
806             # Init logger!
807 0         0 my $log = $self->log;
808              
809             # Close all standart filehandles
810 0         0 unless (DEBUG) {
811 0         0 my $devnull = File::Spec->devnull;
812 0 0       0 open STDIN, '<', $devnull or die "Can't open STDIN from $devnull: $!\n";
813 0 0       0 open STDOUT, '>', $devnull or die "Can't open STDOUT to $devnull: $!\n";
814 0 0       0 open STDERR, '>&', STDOUT or die "Can't open STDERR to $devnull: $!\n";
815             }
816              
817             # Chroot if root
818 0         0 if (IS_ROOT) {
819 0         0 my $rootdir = File::Spec->rootdir;
820 0 0       0 unless (chdir $rootdir) {
821 0         0 $log->fatal("Can't chdir to \"$rootdir\": $!");
822 0         0 die "Can't chdir to \"$rootdir\": $!\n";
823             }
824             }
825              
826             # Clear the file creation mask
827 0         0 umask 0;
828              
829             # Store current PID to instance
830 0         0 $self->{pid} = $$;
831              
832             # Set a signal handler to make sure SIGINT's remove our pid_file
833             $SIG{TERM} = $SIG{INT} = sub {
834 0 0   0   0 POSIX::_exit(1) if $self->is_spirited;
835 0         0 $self->cleanup(1);
836 0         0 $log->fatal("Termination on INT/TERM signal");
837 0         0 $self->filepid->remove;
838 0         0 POSIX::_exit(1);
839 0         0 };
840              
841             # Init Hook
842 0         0 $self->init;
843              
844 0         0 return $self;
845             }
846 1     1 1 19 sub is_daemonized { shift->{daemonized} }
847 0     0 1 0 sub is_spirited { shift->{spirited} }
848 1     1 1 7 sub pid { shift->{pid} }
849              
850             # Hooks
851       0 1   sub preinit { }
852       0 1   sub init { }
853       0 1   sub cleanup { } # 0 -- at destroy; 1 -- at interrupt
854       0 1   sub startup { }
855       0 1   sub hangup { }
856              
857             # Process
858             sub flush { # Flush process counters
859 0     0 1 0 my $self = shift;
860 0         0 $self->{interrupt} = 0;
861 0         0 $self->{signo} = 0;
862 0         0 $self->{ok} = 1;
863 0         0 return $self;
864             }
865             sub ok {
866 0     0 1 0 my $self = shift;
867 0 0       0 return 0 unless defined $self->{ppid}; # No parent pid found (it is not a daemon?)
868 0 0       0 return $self->{ok} ? 1 : 0;
869             }
870              
871             # LSB Daemon Control Methods
872             # These methods can be used to control the daemon behavior.
873             # Every effort has been made to have these methods DWIM (Do What I Mean),
874             # so that you can focus on just writing the code for your daemon
875             sub _term {
876 0     0   0 my $self = shift;
877 0   0     0 my $signo = shift || 0;
878 0         0 $self->{ok} = 0; # Not Ok!
879 0         0 $self->{signo} = $signo;
880 0         0 $self->log->debug(sprintf("Request for terminate of ghost process %s received on signal %s", $self->pid, $signo));
881 0 0       0 if ($self->{interrupt} >= INT_TRIES) { # Forced terminate
882 0 0       0 POSIX::_exit(1) if $self->is_spirited;
883 0         0 $self->cleanup(1);
884 0         0 $self->log->fatal(sprintf("Ghost process %s forcefully terminated on signal %s", $self->pid, $signo));
885 0         0 $self->filepid->remove;
886 0         0 POSIX::_exit(1);
887             }
888 0         0 $self->{interrupt}++;
889             }
890             sub start {
891 0     0 1 0 my $self = shift;
892 0         0 $self->daemonize(1); # First daemonize and switch to child process
893 0 0       0 return 0 unless $self->is_daemonized; # Exit from parent process
894              
895             # Signals Trapping for interruption
896 0     0   0 local $SIG{INT} = sub { $self->_term(SIGINT) }; # 2
  0         0  
897 0     0   0 local $SIG{TERM} = sub { $self->_term(SIGTERM) }; # 15
  0         0  
898 0     0   0 local $SIG{QUIT} = sub { $self->_term(SIGQUIT) }; # 3
  0         0  
899              
900 0         0 $self->flush; # Flush process counters
901 0         0 $self->log->info(sprintf("Ghost process %s started", $self->pid));
902 0         0 $self->startup(); # Master hook
903 0         0 $self->log->info(sprintf("Ghost process %s stopped", $self->pid));
904 0         0 exit 0; # Exit code for child: ok
905             }
906             sub stop {
907 0     0 1 0 my $self = shift;
908 0         0 my $pid = $self->filepid->running;
909 0         0 $self->{pid} = $pid;
910 0 0       0 return 0 unless $pid; # Not running
911              
912             # Try SIGQUIT ... 2s ... SIGTERM ... 4s ... SIGINT ... 3s ... SIGKILL ... 3s ... UNDEAD!
913 0         0 my $tsig = 0;
914 0         0 for ([SIGQUIT, 2], [SIGTERM, 2], [SIGTERM, 2], [SIGINT, 3], [SIGKILL, 3]) {
915 0         0 my ($signo, $timeout) = @$_;
916 0         0 kill $signo, $pid;
917 0         0 for (1 .. $timeout) { # abort early if the process is now stopped
918 0 0       0 unless ($self->filepid->running) {
919 0         0 $tsig = $signo;
920 0         0 last;
921             }
922 0         0 sleep 1;
923             }
924 0 0       0 last if $tsig;
925             }
926 0 0       0 if ($tsig) {
927 0 0       0 if( $tsig == SIGKILL ) {
928 0         0 $self->filepid->remove;
929 0         0 warn "Had to resort to 'kill -9' and it worked, wiping pidfile\n";
930             }
931 0         0 return $pid;
932             }
933              
934             # The ghost process doesn't seem to want to die. It is still running...;
935 0         0 return -1 * $pid;
936             }
937             sub status {
938 0     0 1 0 my $self = shift;
939 0   0     0 return $self->{pid} = $self->filepid->running || 0;
940             }
941             sub restart {
942 0     0 1 0 my $self = shift;
943 0         0 my $runned = $self->stop;
944 0 0 0     0 return 1 if $runned && $runned < 0; # It is still running
945 0         0 _sleep(1); # delay before starting
946 0         0 $self->start;
947             }
948             sub reload {
949 0     0 1 0 my $self = shift;
950 0   0     0 my $signo = shift // SIGHUP;
951 0   0     0 $self->{pid} = $self->filepid->running || 0;
952 0 0       0 return $self->start unless $self->pid; # Not running - start!
953 0         0 kill $signo, $self->pid;
954 0         0 return 0;
955             }
956             sub ctrl { # Dispatching
957 0     0 1 0 my $self = shift;
958 0   0     0 my $cmd = shift || '';
959 0         0 my $sig = shift; # SIGHUP
960 0 0       0 unless (grep {$cmd eq $_} @{(LSB_COMMANDS)}) {
  0         0  
  0         0  
961 0         0 print STDERR "Command incorrect\n";
962 0         0 return 1;
963             }
964 0         0 my $exit_code = 0; # Ok
965 0 0       0 if ($cmd eq 'start') {
    0          
    0          
    0          
    0          
966 0         0 $exit_code = $self->start;
967 0         0 printf "Running %s\n", $self->pid;
968             } elsif ($cmd eq 'status') {
969 0 0       0 if (my $runned = $self->status) {
970 0         0 printf "Running %s\n", $runned;
971             } else {
972 0         0 print "Not running\n";
973             }
974             } elsif ($cmd eq 'stop') {
975 0 0       0 if (my $runned = $self->stop) {
976 0 0       0 if ($runned < 0) {
977 0         0 printf STDERR "The ghost process %s doesn't seem to want to die. It is still running...\n", $self->pid;
978 0         0 $exit_code = 1;
979             } else {
980 0         0 printf "Stopped %s\n", $runned;
981             }
982             } else {
983 0         0 print "Not running\n";
984             }
985             } elsif ($cmd eq 'restart') {
986 0         0 $exit_code = $self->restart;
987 0 0       0 if ($exit_code) {
988 0         0 printf STDERR "Restart failed %s\n", $self->pid;
989             } else {
990 0         0 print "Restart successful\n";
991             }
992             } elsif ($cmd eq 'reload') {
993 0         0 $exit_code = $self->reload($sig);
994 0         0 printf "Reloading %s\n", $self->pid;
995             }
996 0         0 return $exit_code;
997             }
998              
999             sub DESTROY {
1000 1     1   1096 my $self = shift;
1001 1 50       5 return unless $self;
1002 1 50       124 return unless $self->{daemonized};
1003 0 0         return if $self->{spirited}; # Skip cleanup if it is spirit
1004 0           $self->cleanup(0);
1005 0           $self->filepid->remove;
1006             }
1007              
1008             # Utils
1009             sub _sleep {
1010 0   0 0     my $delay = pop || SLEEP;
1011 0           sleep 1 for (1..$delay);
1012 0           return 1
1013             }
1014             sub _fork { # See Proc::Daemon::Fork
1015 0     0     my $lpid;
1016 0           my $loop = 0;
1017              
1018             # Block signal for fork
1019 0           my $sigset = POSIX::SigSet->new(SIGINT);
1020 0 0         POSIX::sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: $!\n";
1021              
1022             MYFORK: {
1023 0           $lpid = fork;
  0            
1024 0 0         if (defined($lpid)) {
1025 0           $SIG{'INT'} = 'DEFAULT'; # make SIGINT kill us as it did before
1026 0 0         POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n";
1027 0           return $lpid;
1028             }
1029 0 0 0       if ( $loop < 6 && ( $! == POSIX::EAGAIN() || $! == POSIX::ENOMEM() ) ) {
      0        
1030 0           $loop++;
1031 0           _sleep(2);
1032 0           redo MYFORK;
1033             }
1034             }
1035              
1036 0           die "Can't fork: $!\n";
1037             }
1038             sub _debug {
1039 0     0     return unless DEBUG;
1040 0 0         my $message = (scalar(@_) == 1) ? shift(@_) : sprintf(shift(@_), @_);
1041 0           print STDERR $message, "\n";
1042             }
1043              
1044             1;
1045              
1046             __END__