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