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