1 package FCGI::ProcManager;
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.
8 # $Id: ProcManager.pm,v 1.10 2000/12/16 01:34:22 muaddib Exp $
14 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS $Q @valid_states);
18 @EXPORT_OK = qw(pm_manage pm_parameter pm_state pm_warn pm_abort pm_exit
19 pm_write_pid_file pm_remove_pid_file
20 pm_register_sig_handler pm_unregister_sig_handler);
21 $EXPORT_TAGS{all} = \@EXPORT_OK;
22 $FCGI::ProcManager::Default = 'FCGI::ProcManager';
24 @valid_states = qw(managing handling idle);
29 FCGI::ProcManager - functions for managing FastCGI applications.
33 # In its simplest form.
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()) {
43 FCGI::ProcManager is used to serve as a FastCGI process manager.
44 The parent uses fork(2) and wait(2) to manage a set of FastCGI application
54 my ($proto,$init) = @_;
57 $init and %$this = %$init;
59 bless $this, ref($proto)||$proto;
66 =head2 self_or_default
69 (ProcManager, @args) self_or_default([ ProcManager, ] @args);
73 This is a helper subroutine to acquire or otherwise create a singleton
74 default object if one is not passed in, e.g., a method call.
79 return @_ if defined $_[0] and !ref $_[0] and $_[0] eq 'FCGI::ProcManager';
80 if (!defined $_[0] or (ref($_[0]) ne 'FCGI::ProcManager' or
81 !UNIVERSAL::isa($_[0],'FCGI::ProcManager'))) {
82 $Q or $Q = $FCGI::ProcManager::Default->new;
85 return wantarray ? @_ : $Q;
91 () pm_manage(int processes_to_spawn)
95 When this is called by a FastCGI script to manage application servers.
100 my ($this) = self_or_default(@_);
102 # initialize state and begin to handle signals.
103 $this->pm_register_sig_handler();
105 # return right away if we are not managing any processes.
106 $this->n_processes() or return 1;
108 # call the (possibly overloaded) pre-manage initialization.
109 $this->pm_state("managing");
110 $this->pre_manage_init();
112 # write out the pid file.
113 $this->write_pid_file();
118 # do things that we only do when we're not already managing processes..
119 if (! %{$this->{PIDS}}) {
120 if (!$this->n_processes()) {
121 $this->remove_pid_file();
123 } elsif ($this->want_to_die()) {
124 $this->remove_pid_file();
125 $this->pm_exit("Manager $$ dying from death request.\n");
126 } elsif ($this->n_processes() < 0) {
127 $this->remove_pid_file();
128 $this->pm_abort("Manager $$ dying from processes number exception: ".
129 $this->n_processes(), -( 1 + $this->n_processes()));
133 # if we have fewer children than we want..
134 PIDS: while (keys(%{$this->{PIDS}}) < $this->n_processes()) {
138 # the parent notes the child.
139 $this->pm_warn("started process $pid\n");
140 $this->{PIDS}->{$pid} = { pid=>$pid };
142 } elsif (! defined $pid) {
143 # handle errors um gracefully.
144 $this->pm_abort("fork: $!\n");
147 # the child returns to the calling application.
148 print "$$ lasting..\n";
153 # wait on the next child to die.
154 $this->pm_abort("wait: $!\n") if ($pid = wait()) < 0;
155 $this->pm_warn("Child process $pid died with exit status $?\n");
156 delete $this->{PIDS}->{$pid}
157 or $this->pm_abort("Internal error: ".
158 "wait() returned non-existent pid=$pid??\n");
162 # call the (possibly overloaded) post-manage initialization.
163 $this->post_manage_init();
165 $this->pm_state("idle");
167 print "$$ returning..\n";
168 # children and parent with n_processes == 0 return to calling app.
172 =head2 pre_manage_init
176 sub pre_manage_init {
177 my ($this) = self_or_default(@_);
180 =head2 post_manage_init
184 sub post_manage_init {
185 my ($this) = self_or_default(@_);
193 my ($this) = self_or_default(@_);
194 $this->pm_state("handling");
202 my ($this) = self_or_default(@_);
203 $this->want_to_die() and
204 $this->pm_exit("Process $$ responding to death request.");
205 $this->pm_state("idle");
208 =head2 write_pid_file
212 sub pm_write_pid_file {
213 my ($this,$fname) = self_or_default(@_);
214 $fname ||= $this->pid_fname() or return;
215 if (!open PIDFILE, ">$fname") {
216 $this->pm_warn("open: $fname: $!\n");
219 print PIDFILE "$$\n";
223 =head2 remove_pid_file
227 sub pm_remove_pid_file {
228 my ($this,$fname) = self_or_default(@_);
229 $fname ||= $this->pid_fname() or return;
230 my $ret = unlink($fname) or $this->pm_warn("unlink: $fname: $!\n");
239 my ($this,$key,$value) = self_or_default(@_);
240 defined $value and $this->{$key} = $value;
241 return $this->{$key};
254 sub n_processes { shift->pm_parameter("n_processes",@_); }
255 sub want_to_die { shift->pm_parameter("want_to_die",@_); }
256 sub no_signals { shift->pm_parameter("no_signals",@_); }
257 sub pid_fname { shift->pm_parameter("pid_fname",@_); }
264 my ($this,$new_state) = self_or_default(@_);
265 if (defined $new_state) {
266 if (!grep {$new_state eq $_} @valid_states) {
267 $this->pm_abort("Invalid state: $new_state\n");
269 $this->{state} = $new_state;
271 defined $this->{state} or $this->{state} = "idle";
272 return $this->{state};
275 =head2 register_sig_handler
279 sub pm_register_sig_handler {
280 my ($this) = self_or_default(@_);
281 return if $this->no_signals();
282 $SIG{TERM} = sub { $this->sig_method(@_) };
283 $SIG{HUP} = sub { $this->sig_method(@_) };
286 =head2 unregister_sig_handler
290 sub pm_unregister_sig_handler {
291 my ($this) = self_or_default(@_);
292 return if $this->no_signals();
302 my ($this,$name) = self_or_default(@_);
303 if ($name eq "TERM" or $name eq "HUP") {
304 if ($this->pm_state() eq "idle") {
305 $this->pm_exit("Process $$ dying after receiving SIG$name.\n");
307 $this->pm_warn("Process $$ received SIG$name. Cleaning up.\n");
308 $this->want_to_die(1);
309 $this->n_processes(-1);
310 # is the following necessary?
311 kill $name, keys %{$this->{PIDS}};
314 $this->pm_warn("I don't know what to do with $name yet.. ignoring?\n");
323 my ($this,$msg) = self_or_default(@_);
332 my ($this,$msg,$n) = self_or_default(@_);
334 $this->pm_warn($msg);
344 my ($this,$msg,$n) = self_or_default(@_);
346 $this->pm_exit($msg,1);
354 No known bugs, but this does not mean no bugs exist.
362 FCGI-ProcManager - A Perl FCGI Process Manager
363 Copyright (c) 2000, FundsXpress Financial Network, Inc.
365 This library is free software; you can redistribute it and/or
366 modify it under the terms of the GNU Lesser General Public
367 License as published by the Free Software Foundation; either
368 version 2 of the License, or (at your option) any later version.
370 BECAUSE THIS LIBRARY IS LICENSED FREE OF CHARGE, THIS LIBRARY IS
371 BEING PROVIDED "AS IS WITH ALL FAULTS," WITHOUT ANY WARRANTIES
372 OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT
373 LIMITATION, ANY IMPLIED WARRANTIES OF TITLE, NONINFRINGEMENT,
374 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, AND THE
375 ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY,
376 AND EFFORT IS WITH THE YOU. See the GNU Lesser General Public
377 License for more details.
379 You should have received a copy of the GNU Lesser General Public
380 License along with this library; if not, write to the Free Software
381 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA