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.5 2000/11/18 07:06:09 muaddib Exp $
11 use vars qw(@valid_states);
13 $FCGI::ProcManager::VERSION = '0.10';
14 @valid_states = qw(managing handling idle);
19 FCGI::ProcManager - functions for managing FastCGI applications.
23 # In its simplest form.
25 use FCGI::ProcManager;
26 my $proc_manager = FCGI::ProcManager->new({n_processes=>10});
27 $proc_manager->manage();
28 while (my $cgi = CGI::Fast->new()) {
33 FCGI::ProcManager is used to serve as a FastCGI process manager.
34 The parent uses fork(2) and wait(2) to manage a set of FastCGI application
44 my ($proto,$init) = @_;
47 bless $this, ref($proto)||$proto;
49 $init and %$this = %$init;
50 defined $this->n_processes() or
51 $this->n_processes($ENV{PROCMANAGER_PROCESSES});
61 () manage(int processes_to_spawn)
65 When this is called by a FastCGI script to manage application servers.
72 # initialize state and begin to handle signals.
73 $this->register_sig_handler();
75 # return right away if we are not managing any processes.
76 $this->n_processes() or return 1;
78 # call the (possibly overloaded) pre-manage initialization.
79 $this->state("managing");
80 $this->pre_manage_init();
82 # write out the pid file.
83 $this->write_pid_file();
88 # do things that we only do when we're not already managing processes..
89 if (! %{$this->{PIDS}}) {
90 if (!$this->n_processes()) {
91 $this->remove_pid_file();
93 } elsif ($this->want_to_die()) {
94 $this->remove_pid_file();
95 $this->exit("Manager $$ dying from death request.\n");
96 } elsif ($this->n_processes() < 0) {
97 $this->remove_pid_file();
98 $this->abort("Manager $$ dying from number of processes exception: ".
99 $this->n_processes(), -( 1 + $this->n_processes()));
103 # if we have fewer children than we want..
104 PIDS: while (keys(%{$this->{PIDS}}) < $this->n_processes()) {
108 # the parent notes the child.
109 $this->warn("started process $pid\n");
110 $this->{PIDS}->{$pid} = { pid=>$pid };
112 } elsif (! defined $pid) {
113 # handle errors um gracefully.
114 $this->abort("fork: $!\n");
117 # the child returns to the calling application.
118 print "$$ lasting..\n";
123 # wait on the next child to die.
124 $this->abort("wait: $!\n") if ($pid = wait()) < 0;
125 $this->warn("Child process $pid died with exit status $?\n");
126 delete $this->{PIDS}->{$pid}
127 or $this->abort("Internal error: ".
128 "wait() returned non-existent pid=$pid??\n");
132 # call the (possibly overloaded) post-manage initialization.
133 $this->post_manage_init();
135 $this->state("idle");
137 print "$$ returning..\n";
138 # children and parent with n_processes == 0 return to calling app.
142 =head2 pre_manage_init
146 sub pre_manage_init {
150 =head2 post_manage_init
154 sub post_manage_init {
164 $this->state("handling");
173 $this->want_to_die() and
174 $this->exit("Process $$ responding to death request.");
175 $this->state("idle");
178 =head2 write_pid_file
183 my ($this,$fname) = @_;
184 $fname ||= $this->pid_fname() or return;
185 if (!open PIDFILE, ">$fname") {
186 $this->warn("open: $fname: $!\n");
189 print PIDFILE "$$\n";
193 =head2 remove_pid_file
197 sub remove_pid_file {
198 my ($this,$fname) = @_;
199 $fname ||= $this->pid_fname() or return;
200 my $ret = unlink($fname) or $this->warn("unlink: $fname: $!\n");
209 my ($this,$key,$value) = @_;
210 defined $value and $this->{$key} = $value;
211 return $this->{$key};
224 sub n_processes { shift->gen_mutator("n_processes",@_); }
225 sub want_to_die { shift->gen_mutator("want_to_die",@_); }
226 sub no_signals { shift->gen_mutator("no_signals",@_); }
227 sub pid_fname { shift->gen_mutator("pid_fname",@_); }
234 my ($this,$new_state) = @_;
235 if (defined $new_state) {
236 if (!grep {$new_state eq $_} @valid_states) {
237 $this->abort("Invalid state: $new_state\n");
239 $this->{state} = $new_state;
241 defined $this->{state} or $this->{state} = "idle";
242 return $this->{state};
245 =head2 register_sig_handler
249 sub register_sig_handler {
251 return if $this->no_signals();
252 $SIG{TERM} = sub { $this->sig_method(@_) };
253 $SIG{HUP} = sub { $this->sig_method(@_) };
256 =head2 unregister_sig_handler
260 sub unregister_sig_handler {
262 return if $this->no_signals();
272 my ($this,$name) = @_;
273 if ($name eq "TERM") {
274 if ($this->state() eq "idle") {
275 $this->exit("Process $$ dying after receiving SIG$name.\n");
277 $this->warn("Process $$ received SIG$name. Cleaning up.\n");
278 $this->want_to_die(1);
279 $this->n_processes(-1);
280 # is the following necessary?
281 kill $name, keys %{$this->{PIDS}};
284 $this->warn("I don't know what to do with $name yet.. ignoring?\n");
293 my ($this,$msg) = @_;
302 my ($this,$msg,$n) = @_;
314 my ($this,$msg,$n) = @_;
324 No known bugs, but this does not mean no bugs exist.
332 FCGI-ProcManager - A Perl FCGI Process Manager
333 Copyright (c) 2000, FundsXpress Financial Network, Inc.
335 This library is free software; you can redistribute it and/or
336 modify it under the terms of the GNU Lesser General Public
337 License as published by the Free Software Foundation; either
338 version 2 of the License, or (at your option) any later version.
340 BECAUSE THIS LIBRARY IS LICENSED FREE OF CHARGE, THIS LIBRARY IS
341 BEING PROVIDED "AS IS WITH ALL FAULTS," WITHOUT ANY WARRANTIES
342 OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT
343 LIMITATION, ANY IMPLIED WARRANTIES OF TITLE, NONINFRINGEMENT,
344 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, AND THE
345 ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY,
346 AND EFFORT IS WITH THE YOU. See the GNU Lesser General Public
347 License for more details.
349 You should have received a copy of the GNU Lesser General Public
350 License along with this library; if not, write to the Free Software
351 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA