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.12 2001/01/13 06:44:34 muaddib Exp $
13 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS $Q @valid_states);
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
19 pm_pre_dispatch pm_post_dispatch
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' and
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 # switch to handling state right away if we are not managing any processes.
106 $this->n_processes() or goto HANDLING;
108 # begin the managing sequence.
109 $this->pm_state("managing");
111 # call the (possibly overloaded) managing initialization.
112 $this->managing_init();
114 # write out the pid file.
115 $this->pm_write_pid_file();
120 # do things that we only do when we're not already managing processes..
121 if (! %{$this->{PIDS}}) {
122 if ($this->received_signal()) {
123 $this->pm_remove_pid_file();
124 $this->pm_exit("Manager $$ dying from death request.\n");
125 } elsif ($this->n_processes() < 0) {
126 $this->pm_remove_pid_file();
127 $this->pm_abort("Manager $$ dying from processes number exception: ".
128 $this->n_processes(), -( 1 + $this->n_processes()));
132 # if we have fewer children than we want..
133 PIDS: while (keys(%{$this->{PIDS}}) < $this->n_processes()) {
137 # the parent notes the child.
138 $this->pm_warn("started process $pid\n");
139 $this->{PIDS}->{$pid} = { pid=>$pid };
141 } elsif (! defined $pid) {
142 # handle errors um gracefully.
143 $this->pm_abort("fork: $!\n");
146 # the child returns to the calling application.
151 # wait on the next child to die.
152 $this->pm_abort("wait: $!\n") if ($pid = wait()) < 0;
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");
161 $this->pm_state("handling");
163 # call the (possibly overloaded) handling initialization.
164 $this->handling_init();
166 # children and parent with n_processes == 0 return to calling app.
175 my ($this) = self_or_default(@_);
183 my ($this) = self_or_default(@_);
186 =head2 pm_pre_dispatch
190 sub pm_pre_dispatch {
191 my ($this) = self_or_default(@_);
194 =head2 pm_post_dispatch
198 sub pm_post_dispatch {
199 my ($this) = self_or_default(@_);
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");
207 =head2 pm_write_pid_file
211 sub pm_write_pid_file {
212 my ($this,$fname) = self_or_default(@_);
213 $fname ||= $this->pid_fname() or return;
214 if (!open PIDFILE, ">$fname") {
215 $this->pm_warn("open: $fname: $!\n");
218 print PIDFILE "$$\n";
222 =head2 pm_remove_pid_file
226 sub pm_remove_pid_file {
227 my ($this,$fname) = self_or_default(@_);
228 $fname ||= $this->pid_fname() or return;
229 my $ret = unlink($fname) or $this->pm_warn("unlink: $fname: $!\n");
238 my ($this,$key,$value) = self_or_default(@_);
239 defined $value and $this->{$key} = $value;
240 return $this->{$key};
251 sub n_processes { shift->pm_parameter("n_processes", @_); }
252 sub pid_fname { shift->pm_parameter("pid_fname", @_); }
253 sub received_signal { shift->pm_parameter("received_signal", @_); }
254 sub no_signals { shift->pm_parameter("no_signals", @_); }
261 my ($this,$new_state) = self_or_default(@_);
262 if (defined $new_state) {
263 if (!grep {$new_state eq $_} @valid_states) {
264 $this->pm_abort("Invalid state: $new_state\n");
266 $this->{state} = $new_state;
268 defined $this->{state} or $this->{state} = "idle";
269 return $this->{state};
272 =head2 pm_register_sig_handler
276 sub pm_register_sig_handler {
277 my ($this) = self_or_default(@_);
278 return if $this->no_signals();
279 $SIG{TERM} = sub { $this->sig_method(@_) };
280 $SIG{HUP} = sub { $this->sig_method(@_) };
283 =head2 pm_unregister_sig_handler
287 sub pm_unregister_sig_handler {
288 my ($this) = self_or_default(@_);
289 return if $this->no_signals();
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}};
314 my ($this,$msg) = self_or_default(@_);
323 my ($this,$msg,$n) = self_or_default(@_);
325 $this->pm_warn($msg);
335 my ($this,$msg,$n) = self_or_default(@_);
337 $this->pm_exit($msg,1);
345 No known bugs, but this does not mean no bugs exist.
353 FCGI-ProcManager - A Perl FCGI Process Manager
354 Copyright (c) 2000, FundsXpress Financial Network, Inc.
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.
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.
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