Fix Pod
[catagits/FCGI-ProcManager.git] / lib / FCGI / ProcManager.pm
1 package FCGI::ProcManager;
2
3 # Copyright (c) 2000, FundsXpress Financial Network, Inc.
4 # This library is free software released under the GNU Lesser General
5 # Public License, Version 2.1.  Please read the important licensing and
6 # disclaimer information included below.
7
8 # $Id: ProcManager.pm,v 1.23 2001/04/23 16:10:11 muaddie Exp $
9
10 use strict;
11 use Exporter;
12 use POSIX qw(:signal_h);
13
14 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS $Q $SIG_CODEREF);
15 BEGIN {
16   $VERSION = '0.19'; 
17   @ISA = qw(Exporter);
18   @EXPORT_OK = qw(pm_manage pm_die pm_wait
19                   pm_write_pid_file pm_remove_pid_file
20                   pm_pre_dispatch pm_post_dispatch
21                   pm_change_process_name pm_received_signal pm_parameter 
22                   pm_warn pm_notify pm_abort pm_exit
23                   $SIG_CODEREF);
24   $EXPORT_TAGS{all} = \@EXPORT_OK;
25   $FCGI::ProcManager::Default = 'FCGI::ProcManager';
26 }
27
28 =head1 NAME
29
30  FCGI::ProcManager - functions for managing FastCGI applications.
31
32 =head1 SYNOPSIS
33
34  # In Object-oriented style.
35  use CGI::Fast;
36  use FCGI::ProcManager;
37  my $proc_manager = FCGI::ProcManager->new({
38         n_processes => 10
39  });
40  $proc_manager->pm_manage();
41  while (my $cgi = CGI::Fast->new()) {
42    $proc_manager->pm_pre_dispatch();
43    # ... handle the request here ...
44    $proc_manager->pm_post_dispatch();
45  }
46
47  # This style is also supported:
48  use CGI::Fast;
49  use FCGI::ProcManager qw(pm_manage pm_pre_dispatch
50                           pm_post_dispatch);
51  pm_manage( n_processes => 10 );
52  while (my $cgi = CGI::Fast->new()) {
53    pm_pre_dispatch();
54    #...
55    pm_post_dispatch();
56  }
57
58 =head1 DESCRIPTION
59
60 FCGI::ProcManager is used to serve as a FastCGI process manager.  By
61 re-implementing it in perl, developers can more finely tune performance in
62 their web applications, and can take advantage of copy-on-write semantics
63 prevalent in UNIX kernel process management.  The process manager should
64 be invoked before the caller''s request loop
65
66 The primary routine, C<pm_manage>, enters a loop in which it maintains a
67 number of FastCGI servers (via fork(2)), and which reaps those servers
68 when they die (via wait(2)).
69
70 C<pm_manage> provides too hooks:
71
72  C<managing_init> - called just before the manager enters the manager loop.
73  C<handling_init> - called just before a server is returns from C<pm_manage>
74
75 It is necessary for the caller, when implementing its request loop, to
76 insert a call to C<pm_pre_dispatch> at the top of the loop, and then
77 7C<pm_post_dispatch> at the end of the loop.
78
79 =head2 Signal Handling
80
81 FCGI::ProcManager attempts to do the right thing for proper shutdowns now.
82
83 When it receives a SIGHUP, it sends a SIGTERM to each of its children, and
84 then resumes its normal operations.   
85
86 When it receives a SIGTERM, it sends a SIGTERM to each of its children, sets
87 an alarm(3) "die timeout" handler, and waits for each of its children to
88 die.  If all children die before this timeout, process manager exits with
89 return status 0.  If all children do not die by the time the "die timeout"
90 occurs, the process manager sends a SIGKILL to each of the remaining
91 children, and exists with return status 1.
92
93 In order to get FastCGI servers to exit upon receiving a signal, it is
94 necessary to use its FAIL_ACCEPT_ON_INTR.  See FCGI.pm's description of
95 FAIL_ACCEPT_ON_INTR.  Unfortunately, if you want/need to use CGI::Fast, it
96 appears currently necessary to modify your installation of FCGI.pm, with
97 something like the following:
98
99  -*- patch -*-
100  --- FCGI.pm     2001/03/09 01:44:00     1.1.1.3
101  +++ FCGI.pm     2001/03/09 01:47:32     1.2
102  @@ -24,7 +24,7 @@
103   *FAIL_ACCEPT_ON_INTR = sub() { 1 };
104   
105   sub Request(;***$$$) {
106  -    my @defaults = (\*STDIN, \*STDOUT, \*STDERR, \%ENV, 0, 0);
107  +    my @defaults = (\*STDIN, \*STDOUT, \*STDERR, \%ENV, 0, FAIL_ACCEPT_ON_INTR());
108       splice @defaults,0,@_,@_;
109       RequestX(@defaults);
110   }   
111  -*- end patch -*-
112
113 Otherwise, if you don't, there is a loop around accept(2) which prevents
114 os_unix.c OS_Accept() from returning the necessary error when FastCGI
115 servers blocking on accept(2) receive the SIGTERM or SIGHUP.
116
117 FCGI::ProcManager uses POSIX::sigaction() to override the default SA_RESTART
118 policy used for perl's %SIG behavior.  Specifically, the process manager
119 never uses SA_RESTART, while the child FastCGI servers turn off SA_RESTART
120 around the accept(2) loop, but re-enstate it otherwise.
121
122 The desired (and implemented) effect is to give a request as big a chance as
123 possible to succeed and to delay their exits until after their request,
124 while allowing the FastCGI servers waiting for new requests to die right
125 away. 
126
127 =head1 METHODS
128
129 =head2 new
130
131  class or instance
132  (ProcManager) new([hash parameters])
133
134 Constructs a new process manager.  Takes an option has of initial parameter
135 values, and assigns these to the constructed object HASH, overriding any
136 default values.  The default parameter values currently are:
137
138  role         => manager
139  start_delay  => 0
140  die_timeout  => 60
141  pm_title => 'perl-fcgi-pm'
142
143 =cut
144
145 sub new {
146   my ($proto,$init) = @_;
147   $init ||= {};
148
149   my $this = { 
150               role => "manager",
151               start_delay => 0,
152               die_timeout => 60,
153         pm_title => 'perl-fcgi-pm',
154               %$init
155              };
156   bless $this, ref($proto)||$proto;
157
158   $this->{PIDS} = {};
159
160   # initialize signal constructions.
161   unless ($this->no_signals()) {
162     $this->{sigaction_no_sa_restart} =
163         POSIX::SigAction->new('FCGI::ProcManager::sig_sub');
164     $this->{sigaction_sa_restart} =
165         POSIX::SigAction->new('FCGI::ProcManager::sig_sub',undef,POSIX::SA_RESTART);
166   }
167
168   return $this;
169 }
170
171 =head1 Manager methods
172
173 =head2 pm_manage
174
175  instance or export
176  (int) pm_manage([hash parameters])
177
178 DESCRIPTION:
179
180 When this is called by a FastCGI script to manage application servers.  It
181 defines a sequence of instructions for a process to enter this method and
182 begin forking off and managing those handlers, and it defines a sequence of
183 instructions to intialize those handlers.
184
185 If n_processes < 1, the managing section is subverted, and only the
186 handling sequence is executed.
187
188 Either returns the return value of pm_die() and/or pm_abort() (which will
189 not ever return in general), or returns 1 to the calling script to begin
190 handling requests.
191
192 =cut
193
194 sub pm_manage {
195   my ($this,%values) = self_or_default(@_);
196   map { $this->pm_parameter($_,$values{$_}) } keys %values;
197
198   # skip to handling now if we won't be managing any processes.
199   $this->n_processes() or return;
200
201   # call the (possibly overloaded) management initialization hook.
202   $this->role("manager");
203   $this->managing_init();
204   $this->pm_notify("initialized");
205
206   my $manager_pid = $$;
207
208  MANAGING_LOOP: while (1) {
209
210     $this->n_processes() > 0 or
211       return $this->pm_die();
212
213     # while we have fewer servers than we want.
214   PIDS: while (keys(%{$this->{PIDS}}) < $this->n_processes()) {
215
216       if (my $pid = fork()) {
217         # the manager remembers the server.
218         $this->{PIDS}->{$pid} = { pid=>$pid };
219         $this->pm_notify("server (pid $pid) started");
220
221       } elsif (! defined $pid) {
222         return $this->pm_abort("fork: $!");
223
224       } else {
225         $this->{MANAGER_PID} = $manager_pid;
226         # the server exits the managing loop.
227         last MANAGING_LOOP;
228       }
229
230       for (my $s = $this->start_delay(); $s; $s = sleep $s) {};
231     }
232
233     # this should block until the next server dies.
234     $this->pm_wait();
235
236   }# while 1
237
238 HANDLING:
239
240   # forget any children we had been collecting.
241   delete $this->{PIDS};
242
243   # call the (possibly overloaded) handling init hook
244   $this->role("server");
245   $this->handling_init();
246   $this->pm_notify("initialized");
247
248   # server returns 
249   return 1;
250 }
251
252 =head2 managing_init
253
254  instance
255  () managing_init()
256
257 DESCRIPTION:
258
259 Overrideable method which initializes a process manager.  In order to
260 handle signals, manage the PID file, and change the process name properly,
261 any method which overrides this should call SUPER::managing_init().
262
263 =cut
264
265 sub managing_init {
266   my ($this) = @_;
267
268   # begin to handle signals.
269   # We do NOT want SA_RESTART in the process manager.
270   # -- we want start the shutdown sequence immediately upon SIGTERM.
271   unless ($this->no_signals()) {
272     sigaction(SIGTERM, $this->{sigaction_no_sa_restart}) or
273         $this->pm_warn("sigaction: SIGTERM: $!");
274     sigaction(SIGHUP,  $this->{sigaction_no_sa_restart}) or
275         $this->pm_warn("sigaction: SIGHUP: $!");
276     $SIG_CODEREF = sub { $this->sig_manager(@_) };
277   }
278
279   # change the name of this process as it appears in ps(1) output.
280   $this->pm_change_process_name($this->pm_parameter('pm_title'));
281
282   $this->pm_write_pid_file();
283 }
284
285 =head2 pm_die
286
287  instance or export
288  () pm_die(string msg[, int exit_status])
289
290 DESCRIPTION:
291
292 This method is called when a process manager receives a notification to
293 shut itself down.  pm_die() attempts to shutdown the process manager
294 gently, sending a SIGTERM to each managed process, waiting die_timeout()
295 seconds to reap each process, and then exit gracefully once all children
296 are reaped, or to abort if all children are not reaped.
297
298 =cut
299
300 sub pm_die {
301   my ($this,$msg,$n) = self_or_default(@_);
302
303   # stop handling signals.
304   undef $SIG_CODEREF;
305   $SIG{HUP}  = 'DEFAULT';
306   $SIG{TERM} = 'DEFAULT';
307
308   $this->pm_remove_pid_file();
309
310   # prepare to die no matter what.
311   if (defined $this->die_timeout()) {
312     $SIG{ALRM} = sub { $this->pm_abort("wait timeout") };
313     alarm $this->die_timeout();
314   }
315
316   # send a TERM to each of the servers.
317   if (my @pids = keys %{$this->{PIDS}}) {
318     $this->pm_notify("sending TERM to PIDs, @pids");
319     kill "TERM", @pids;
320   }
321
322   # wait for the servers to die.
323   while (%{$this->{PIDS}}) {
324     $this->pm_wait();
325   }
326
327   # die already.
328   $this->pm_exit("dying: ".$msg,$n);
329 }
330
331 =head2 pm_wait
332
333  instance or export
334  (int pid) pm_wait()
335
336 DESCRIPTION:
337
338 This calls wait() which suspends execution until a child has exited.
339 If the process ID returned by wait corresponds to a managed process,
340 pm_notify() is called with the exit status of that process.
341 pm_wait() returns with the return value of wait().
342
343 =cut
344
345 sub pm_wait {
346   my ($this) = self_or_default(@_);
347
348   # wait for the next server to die.
349   next if (my $pid = wait()) < 0;
350
351   # notify when one of our servers have died.
352   delete $this->{PIDS}->{$pid} and
353     $this->pm_notify("server (pid $pid) exited with status $?");
354
355   return $pid;
356 }
357
358 =head2 pm_write_pid_file
359
360  instance or export
361  () pm_write_pid_file([string filename])
362
363 DESCRIPTION:
364
365 Writes current process ID to optionally specified file.  If no filename is
366 specified, it uses the value of the C<pid_fname> parameter.
367
368 =cut
369
370 sub pm_write_pid_file {
371   my ($this,$fname) = self_or_default(@_);
372   $fname ||= $this->pid_fname() or return;
373   if (!open PIDFILE, ">$fname") {
374     $this->pm_warn("open: $fname: $!");
375     return;
376   }
377   print PIDFILE "$$\n";
378   close PIDFILE;
379 }
380
381 =head2 pm_remove_pid_file
382
383  instance or export
384  () pm_remove_pid_file()
385
386 DESCRIPTION:
387
388 Removes optionally specified file.  If no filename is specified, it uses
389 the value of the C<pid_fname> parameter.
390
391 =cut
392
393 sub pm_remove_pid_file {
394   my ($this,$fname) = self_or_default(@_);
395   $fname ||= $this->pid_fname() or return;
396   my $ret = unlink($fname) or $this->pm_warn("unlink: $fname: $!");
397   return $ret;
398 }
399
400 =head2 sig_sub
401
402  instance
403  () sig_sub(string name)
404
405 DESCRIPTION:
406
407 The name of this method is passed to POSIX::sigaction(), and handles signals
408 for the process manager.  If $SIG_CODEREF is set, then the input arguments
409 to this are passed to a call to that.
410
411 =cut
412
413 sub sig_sub {
414   $SIG_CODEREF->(@_) if ref $SIG_CODEREF;
415 }
416
417 =head2 sig_manager
418
419  instance
420  () sig_manager(string name)
421
422 DESCRIPTION:
423
424 Handles signals of the process manager.  Takes as input the name of signal
425 being handled.
426
427 =cut
428
429 sub sig_manager {
430   my ($this,$name) = @_;
431   if ($name eq "TERM") {
432     $this->pm_notify("received signal $name");
433     $this->pm_die("safe exit from signal $name");
434   } elsif ($name eq "HUP") {
435     # send a TERM to each of the servers, and pretend like nothing happened..
436     if (my @pids = keys %{$this->{PIDS}}) {
437       $this->pm_notify("sending TERM to PIDs, @pids");
438       kill "TERM", @pids;
439     }
440   } else {
441     $this->pm_notify("ignoring signal $name");
442   }
443 }
444
445 =head1 Handler methods
446
447 =head2 handling_init
448
449  instance or export
450  () handling_init()
451
452 DESCRIPTION:
453
454 =cut
455
456 sub handling_init {
457   my ($this) = @_;
458
459   # begin to handle signals.
460   # We'll want accept(2) to return -1(EINTR) on caught signal..
461   unless ($this->no_signals()) {
462     sigaction(SIGTERM, $this->{sigaction_no_sa_restart}) or $this->pm_warn("sigaction: SIGTERM: $!");
463     sigaction(SIGHUP,  $this->{sigaction_no_sa_restart}) or $this->pm_warn("sigaction: SIGHUP: $!");
464     $SIG_CODEREF = sub { $this->sig_handler(@_) };
465   }
466
467   # change the name of this process as it appears in ps(1) output.
468   $this->pm_change_process_name("perl-fcgi");
469 }
470
471 =head2 pm_pre_dispatch
472
473  instance or export
474  () pm_pre_dispatch()
475
476 DESCRIPTION:
477
478 =cut
479
480 sub pm_pre_dispatch {
481   my ($this) = self_or_default(@_);
482
483   # Now, we want the request to continue unhindered..
484   unless ($this->no_signals()) {
485     sigaction(SIGTERM, $this->{sigaction_sa_restart}) or $this->pm_warn("sigaction: SIGTERM: $!");
486     sigaction(SIGHUP,  $this->{sigaction_sa_restart}) or $this->pm_warn("sigaction: SIGHUP: $!");
487   }
488 }
489
490 =head2 pm_post_dispatch
491
492  instance or export
493  () pm_post_dispatch()
494
495 DESCRIPTION:
496
497 =cut
498
499 sub pm_post_dispatch {
500   my ($this) = self_or_default(@_);
501   if ($this->pm_received_signal("TERM")) {
502     $this->pm_exit("safe exit after SIGTERM");
503   }
504   if ($this->pm_received_signal("HUP")) {
505     $this->pm_exit("safe exit after SIGHUP");
506   }
507   if ($this->{MANAGER_PID} and getppid() != $this->{MANAGER_PID}) {
508     $this->pm_exit("safe exit: manager has died");
509   }
510   # We'll want accept(2) to return -1(EINTR) on caught signal..
511   unless ($this->no_signals()) {
512     sigaction(SIGTERM, $this->{sigaction_no_sa_restart}) or $this->pm_warn("sigaction: SIGTERM: $!");
513     sigaction(SIGHUP,  $this->{sigaction_no_sa_restart}) or $this->pm_warn("sigaction: SIGHUP: $!");
514   }
515 }
516
517 =head2 sig_handler
518
519  instance or export
520  () sig_handler()
521
522 DESCRIPTION:
523
524 =cut
525
526 sub sig_handler {
527   my ($this,$name) = @_;
528   $this->pm_received_signal($name,1);
529 }
530
531 =head1 Common methods and routines
532
533 =head2 self_or_default
534
535  private global
536  (ProcManager, @args) self_or_default([ ProcManager, ] @args);
537
538 DESCRIPTION:
539
540 This is a helper subroutine to acquire or otherwise create a singleton
541 default object if one is not passed in, e.g., a method call.
542
543 =cut
544
545 sub self_or_default {
546   return @_ if defined $_[0] and !ref $_[0] and $_[0] eq 'FCGI::ProcManager';
547   if (!defined $_[0] or (ref($_[0]) ne 'FCGI::ProcManager' and
548                          !UNIVERSAL::isa($_[0],'FCGI::ProcManager'))) {
549     $Q or $Q = $FCGI::ProcManager::Default->new;
550     unshift @_, $Q;
551   }
552   return wantarray ? @_ : $Q;
553 }
554
555 =head2 pm_change_process_name
556
557  instance or export
558  () pm_change_process_name()
559
560 DESCRIPTION:
561
562 =cut
563
564 sub pm_change_process_name {
565   my ($this,$name) = self_or_default(@_);
566   $0 = $name;
567 }
568
569 =head2 pm_received_signal
570
571  instance or export
572  () pm_received signal()
573
574 DESCRIPTION:
575
576 =cut
577
578 sub pm_received_signal {
579   my ($this,$sig,$received) = self_or_default(@_);
580   $sig or return $this->{SIG_RECEIVED};
581   $received and $this->{SIG_RECEIVED}->{$sig}++;
582   return $this->{SIG_RECEIVED}->{$sig};
583 }
584
585 =head1 parameters
586
587 =head2 pm_parameter
588
589  instance or export
590  () pm_parameter()
591
592 DESCRIPTION:
593
594 =cut
595
596 sub pm_parameter {
597   my ($this,$key,$value) = self_or_default(@_);
598   defined $value and $this->{$key} = $value;
599   return $this->{$key};
600 }
601
602 =head2 n_processes
603
604 =head2 no_signals
605
606 =head2 pid_fname
607
608 =head2 die_timeout
609
610 =head2 role
611
612 =head2 start_delay
613
614 DESCRIPTION:
615
616 =cut
617
618 sub n_processes     { shift->pm_parameter("n_processes",     @_); }
619 sub pid_fname       { shift->pm_parameter("pid_fname",       @_); }
620 sub no_signals      { shift->pm_parameter("no_signals",      @_); }
621 sub die_timeout     { shift->pm_parameter("die_timeout",     @_); }
622 sub role            { shift->pm_parameter("role",            @_); }
623 sub start_delay     { shift->pm_parameter("start_delay",     @_); }
624
625 =head1 notification and death
626
627 =head2 pm_warn
628
629  instance or export
630  () pm_warn()
631
632 DESCRIPTION:
633
634 =cut
635
636 sub pm_warn {
637   my ($this,$msg) = self_or_default(@_);
638   $this->pm_notify($msg);
639 }
640
641 =head2 pm_notify
642
643  instance or export
644  () pm_notify()
645
646 DESCRIPTION:
647
648 =cut
649
650 sub pm_notify {
651   my ($this,$msg) = self_or_default(@_);
652   $msg =~ s/\s*$/\n/;
653   print STDERR "FastCGI: ".$this->role()." (pid $$): ".$msg;
654 }
655
656 =head2 pm_exit
657
658  instance or export
659  () pm_exit(string msg[, int exit_status])
660
661 DESCRIPTION:
662
663 =cut
664
665 sub pm_exit {
666   my ($this,$msg,$n) = self_or_default(@_);
667   $n ||= 0;
668
669   # if we still have children at this point, something went wrong.
670   # SIGKILL them now.
671   kill "KILL", keys %{$this->{PIDS}} if $this->{PIDS};
672
673   $this->pm_warn($msg);
674   $@ = $msg;
675   exit $n;
676 }
677
678 =head2 pm_abort
679
680  instance or export
681  () pm_abort(string msg[, int exit_status])
682
683 DESCRIPTION:
684
685 =cut
686
687 sub pm_abort {
688   my ($this,$msg,$n) = self_or_default(@_);
689   $n ||= 1;
690   $this->pm_exit($msg,1);
691 }
692
693 1;
694 __END__
695
696 =head1 BUGS
697
698 No known bugs, but this does not mean no bugs exist.
699
700 =head1 SEE ALSO
701
702 L<FCGI>.
703
704 =head1 MAINTAINER
705
706 Gareth Kirwan <gbjk@thermeon.com>
707
708 =head1 AUTHOR
709
710 James E Jurach Jr.
711
712 =head1 COPYRIGHT
713
714  FCGI-ProcManager - A Perl FCGI Process Manager
715  Copyright (c) 2000, FundsXpress Financial Network, Inc.
716
717  This library is free software; you can redistribute it and/or
718  modify it under the terms of the GNU Lesser General Public
719  License as published by the Free Software Foundation; either
720  version 2 of the License, or (at your option) any later version.
721
722  BECAUSE THIS LIBRARY IS LICENSED FREE OF CHARGE, THIS LIBRARY IS
723  BEING PROVIDED "AS IS WITH ALL FAULTS," WITHOUT ANY WARRANTIES
724  OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT
725  LIMITATION, ANY IMPLIED WARRANTIES OF TITLE, NONINFRINGEMENT,
726  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, AND THE
727  ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY,
728  AND EFFORT IS WITH THE YOU.  See the GNU Lesser General Public
729  License for more details.
730
731  You should have received a copy of the GNU Lesser General Public
732  License along with this library; if not, write to the Free Software
733  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
734
735 =cut