import FCGI-ProcManager 0.14 from CPAN
[catagits/FCGI-ProcManager.git] / ProcManager.pm
CommitLineData
0baf5fac 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
c146b63d 8# $Id: ProcManager.pm,v 1.12 2001/01/13 06:44:34 muaddib Exp $
0baf5fac 9
10use strict;
4ceac1a1 11use Exporter;
4ceac1a1 12
13use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS $Q @valid_states);
0baf5fac 14BEGIN {
c146b63d 15 $VERSION = '0.14';
4ceac1a1 16 @ISA = qw(Exporter);
17 @EXPORT_OK = qw(pm_manage pm_parameter pm_state pm_warn pm_abort pm_exit
18 pm_write_pid_file pm_remove_pid_file
c146b63d 19 pm_pre_dispatch pm_post_dispatch
4ceac1a1 20 pm_register_sig_handler pm_unregister_sig_handler);
21 $EXPORT_TAGS{all} = \@EXPORT_OK;
22 $FCGI::ProcManager::Default = 'FCGI::ProcManager';
23
0baf5fac 24 @valid_states = qw(managing handling idle);
25}
26
27=head1 NAME
28
29 FCGI::ProcManager - functions for managing FastCGI applications.
30
31=head1 SYNOPSIS
32
33 # In its simplest form.
34 use CGI::Fast;
35 use FCGI::ProcManager;
36 my $proc_manager = FCGI::ProcManager->new({n_processes=>10});
37 $proc_manager->manage();
38 while (my $cgi = CGI::Fast->new()) {
39 #...
40
41=head1 DESCRIPTION
42
43FCGI::ProcManager is used to serve as a FastCGI process manager.
44The parent uses fork(2) and wait(2) to manage a set of FastCGI application
45servers. more later.
46
47=head1 METHODS
48
49=head2 new
50
51=cut
52
53sub new {
54 my ($proto,$init) = @_;
55
56 my $this = {};
0baf5fac 57 $init and %$this = %$init;
4ceac1a1 58
59 bless $this, ref($proto)||$proto;
0baf5fac 60
61 $this->{PIDS} = {};
62
63 return $this;
64}
65
4ceac1a1 66=head2 self_or_default
67
68 private global
69 (ProcManager, @args) self_or_default([ ProcManager, ] @args);
70
71DESCRIPTION:
72
73This is a helper subroutine to acquire or otherwise create a singleton
74default object if one is not passed in, e.g., a method call.
75
76=cut
77
78sub self_or_default {
79 return @_ if defined $_[0] and !ref $_[0] and $_[0] eq 'FCGI::ProcManager';
c146b63d 80 if (!defined $_[0] or (ref($_[0]) ne 'FCGI::ProcManager' and
4ceac1a1 81 !UNIVERSAL::isa($_[0],'FCGI::ProcManager'))) {
82 $Q or $Q = $FCGI::ProcManager::Default->new;
83 unshift @_, $Q;
84 }
85 return wantarray ? @_ : $Q;
86}
87
88=head2 pm_manage
0baf5fac 89
90 global
4ceac1a1 91 () pm_manage(int processes_to_spawn)
0baf5fac 92
93DESCRIPTION:
94
95When this is called by a FastCGI script to manage application servers.
96
97=cut
98
4ceac1a1 99sub pm_manage {
100 my ($this) = self_or_default(@_);
0baf5fac 101
102 # initialize state and begin to handle signals.
4ceac1a1 103 $this->pm_register_sig_handler();
0baf5fac 104
c146b63d 105 # switch to handling state right away if we are not managing any processes.
106 $this->n_processes() or goto HANDLING;
0baf5fac 107
c146b63d 108 # begin the managing sequence.
4ceac1a1 109 $this->pm_state("managing");
c146b63d 110
111 # call the (possibly overloaded) managing initialization.
112 $this->managing_init();
0baf5fac 113
114 # write out the pid file.
c146b63d 115 $this->pm_write_pid_file();
0baf5fac 116
117 my ($pid);
118 MANAGE: while (1) {
119
120 # do things that we only do when we're not already managing processes..
121 if (! %{$this->{PIDS}}) {
c146b63d 122 if ($this->received_signal()) {
123 $this->pm_remove_pid_file();
4ceac1a1 124 $this->pm_exit("Manager $$ dying from death request.\n");
0baf5fac 125 } elsif ($this->n_processes() < 0) {
c146b63d 126 $this->pm_remove_pid_file();
4ceac1a1 127 $this->pm_abort("Manager $$ dying from processes number exception: ".
128 $this->n_processes(), -( 1 + $this->n_processes()));
0baf5fac 129 }
130 }
131
132 # if we have fewer children than we want..
133 PIDS: while (keys(%{$this->{PIDS}}) < $this->n_processes()) {
134
135 # fork.
136 if ($pid = fork()) {
137 # the parent notes the child.
4ceac1a1 138 $this->pm_warn("started process $pid\n");
0baf5fac 139 $this->{PIDS}->{$pid} = { pid=>$pid };
140
141 } elsif (! defined $pid) {
142 # handle errors um gracefully.
4ceac1a1 143 $this->pm_abort("fork: $!\n");
0baf5fac 144
145 } else {
146 # the child returns to the calling application.
0baf5fac 147 last MANAGE;
148 }
149 }
150
151 # wait on the next child to die.
4ceac1a1 152 $this->pm_abort("wait: $!\n") if ($pid = wait()) < 0;
c146b63d 153
154 # notify when one of our children have died.
155 delete $this->{PIDS}->{$pid} and
156 $this->pm_warn("Child process $pid died with exit status $?\n");
0baf5fac 157
158 }# while 1
159
c146b63d 160 HANDLING:
161 $this->pm_state("handling");
0baf5fac 162
c146b63d 163 # call the (possibly overloaded) handling initialization.
164 $this->handling_init();
0baf5fac 165
0baf5fac 166 # children and parent with n_processes == 0 return to calling app.
167 return 1;
168}
169
c146b63d 170=head2 managing_init
0baf5fac 171
172=cut
173
c146b63d 174sub managing_init {
4ceac1a1 175 my ($this) = self_or_default(@_);
0baf5fac 176}
177
c146b63d 178=head2 handling_init
0baf5fac 179
180=cut
181
c146b63d 182sub handling_init {
4ceac1a1 183 my ($this) = self_or_default(@_);
0baf5fac 184}
185
c146b63d 186=head2 pm_pre_dispatch
0baf5fac 187
188=cut
189
c146b63d 190sub pm_pre_dispatch {
4ceac1a1 191 my ($this) = self_or_default(@_);
0baf5fac 192}
193
c146b63d 194=head2 pm_post_dispatch
0baf5fac 195
196=cut
197
c146b63d 198sub pm_post_dispatch {
4ceac1a1 199 my ($this) = self_or_default(@_);
c146b63d 200 if (my $name = $this->received_signal()) {
201 if ($name eq "HUP" or $name eq "TERM") {
202 $this->pm_exit("Process $$ responding to $name death request.\n");
203 }
204 }
0baf5fac 205}
206
c146b63d 207=head2 pm_write_pid_file
0baf5fac 208
209=cut
210
4ceac1a1 211sub pm_write_pid_file {
212 my ($this,$fname) = self_or_default(@_);
0baf5fac 213 $fname ||= $this->pid_fname() or return;
214 if (!open PIDFILE, ">$fname") {
4ceac1a1 215 $this->pm_warn("open: $fname: $!\n");
0baf5fac 216 return;
217 }
218 print PIDFILE "$$\n";
219 close PIDFILE;
220}
221
c146b63d 222=head2 pm_remove_pid_file
0baf5fac 223
224=cut
225
4ceac1a1 226sub pm_remove_pid_file {
227 my ($this,$fname) = self_or_default(@_);
0baf5fac 228 $fname ||= $this->pid_fname() or return;
4ceac1a1 229 my $ret = unlink($fname) or $this->pm_warn("unlink: $fname: $!\n");
0baf5fac 230 return $ret;
231}
232
4ceac1a1 233=head2 pm_parameter
0baf5fac 234
235=cut
236
4ceac1a1 237sub pm_parameter {
238 my ($this,$key,$value) = self_or_default(@_);
0baf5fac 239 defined $value and $this->{$key} = $value;
240 return $this->{$key};
241}
242
243=head2 n_processes
244
0baf5fac 245=head2 no_signals
246
247=head2 pid_fname
248
249=cut
250
c146b63d 251sub n_processes { shift->pm_parameter("n_processes", @_); }
252sub pid_fname { shift->pm_parameter("pid_fname", @_); }
253sub received_signal { shift->pm_parameter("received_signal", @_); }
254sub no_signals { shift->pm_parameter("no_signals", @_); }
0baf5fac 255
4ceac1a1 256=head2 pm_state
0baf5fac 257
258=cut
259
4ceac1a1 260sub pm_state {
261 my ($this,$new_state) = self_or_default(@_);
0baf5fac 262 if (defined $new_state) {
263 if (!grep {$new_state eq $_} @valid_states) {
4ceac1a1 264 $this->pm_abort("Invalid state: $new_state\n");
0baf5fac 265 }
266 $this->{state} = $new_state;
267 }
268 defined $this->{state} or $this->{state} = "idle";
269 return $this->{state};
270}
271
c146b63d 272=head2 pm_register_sig_handler
0baf5fac 273
274=cut
275
4ceac1a1 276sub pm_register_sig_handler {
277 my ($this) = self_or_default(@_);
0baf5fac 278 return if $this->no_signals();
279 $SIG{TERM} = sub { $this->sig_method(@_) };
c146b63d 280 $SIG{HUP} = sub { $this->sig_method(@_) };
0baf5fac 281}
282
c146b63d 283=head2 pm_unregister_sig_handler
0baf5fac 284
285=cut
286
4ceac1a1 287sub pm_unregister_sig_handler {
288 my ($this) = self_or_default(@_);
0baf5fac 289 return if $this->no_signals();
290 undef $SIG{TERM};
291 undef $SIG{HUP};
292}
293
294=head2 sig_method
295
296=cut
297
298sub sig_method {
c146b63d 299 my ($this,$name) = @_;
300 # note which signal we've received.
301 $this->{received_signal} = $name;
302 $this->n_processes(0);
303 # propagate this signal to children. (is this necessary?)
304 if (%{$this->{PIDS}}) {
305 kill $name, keys %{$this->{PIDS}};
0baf5fac 306 }
307}
308
c146b63d 309=head2 pm_warn
0baf5fac 310
311=cut
312
4ceac1a1 313sub pm_warn {
314 my ($this,$msg) = self_or_default(@_);
0baf5fac 315 print STDERR $msg;
316}
317
318=head2 exit
319
320=cut
321
4ceac1a1 322sub pm_exit {
323 my ($this,$msg,$n) = self_or_default(@_);
0baf5fac 324 $n ||= 0;
4ceac1a1 325 $this->pm_warn($msg);
0baf5fac 326 $@ = $msg;
327 exit $n;
328}
329
c146b63d 330=head2 pm_abort
0baf5fac 331
332=cut
333
4ceac1a1 334sub pm_abort {
335 my ($this,$msg,$n) = self_or_default(@_);
0baf5fac 336 $n ||= 1;
4ceac1a1 337 $this->pm_exit($msg,1);
0baf5fac 338}
339
3401;
341__END__
342
343=head1 BUGS
344
345No known bugs, but this does not mean no bugs exist.
346
347=head1 SEE ALSO
348
349L<FCGI>.
350
351=head1 COPYRIGHT
352
353 FCGI-ProcManager - A Perl FCGI Process Manager
354 Copyright (c) 2000, FundsXpress Financial Network, Inc.
355
356 This library is free software; you can redistribute it and/or
357 modify it under the terms of the GNU Lesser General Public
358 License as published by the Free Software Foundation; either
359 version 2 of the License, or (at your option) any later version.
360
361 BECAUSE THIS LIBRARY IS LICENSED FREE OF CHARGE, THIS LIBRARY IS
362 BEING PROVIDED "AS IS WITH ALL FAULTS," WITHOUT ANY WARRANTIES
363 OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT
364 LIMITATION, ANY IMPLIED WARRANTIES OF TITLE, NONINFRINGEMENT,
365 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, AND THE
366 ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY,
367 AND EFFORT IS WITH THE YOU. See the GNU Lesser General Public
368 License for more details.
369
370 You should have received a copy of the GNU Lesser General Public
371 License along with this library; if not, write to the Free Software
372 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
373
374=cut