This code example will allow you to verify that indeed the spawned
child process has its own life, and its parent is free as well. Simply
issue a request that will run this script, watch that the warnings are
started to be written into the /tmp/log file and issue a complete
server stop and start. If everything is correct, the server will
successfully restart and the long term process will still be
running. You will know that it’s still running, if the warnings are
still printed into the /tmp/log file. You may need to raise the
number of warnings to do above 20, to make sure that you don’t miss
the end of the run.
If there are only 5 warnings to be printed, you should see the
following output in this file:
started 1 2 3 4 5 completed
Starting a Long Running External Program
But what happens if we cannot just run a Perl code from the spawned
process and we have a compiled utility, i.e. a program written in C.
Or we have a Perl program which cannot be easily converted into a
module, and thus called as a function. Of course in this case we have
to use system(), exec(), qx()
or (back ticks) to start it.
When using any of these methods and when the Taint mode is enabled,
we must at least add the following code to untaint the PATH
environment variable and delete a few other insecure environment
variables. This information can be found in the perlsec manpage.
{'PATH'} = '/bin:/usr/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};Now all we have to do is to reuse the code from the previous section.
First we move the core program into the external.pl file, add the
shebang first line so the program will be executed by Perl, tell the
program to run under Taint mode (-T) and possibly enable the
warnings mode (-w) and make it executable:external.pl ----------- #!/usr/bin/perl -Twopen STDIN, '/dev/null' or die "Can't read /dev/null: "; open STDOUT, '>/dev/null' or die "Can't write to /dev/null: "; open STDERR, '>/tmp/log' or die "Can't write to /tmp/log: ";select STDERR; local $|=1; warn "startedn"; # do something time-consuming sleep 1, warn "shn" for 1..20; warn "completedn";Now we replace the code that moved into the external program with
exec()
to call it:proper_fork_exec.pl ------------------- use strict; use POSIX 'setsid'; use Apache::SubProcess;{'PATH'} = '/bin:/usr/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};my = shift; ->send_http_header("text/html");{CHLD} = 'IGNORE';defined (my = fork) or die "Cannot fork: n"; if () { print "Parent has finished, kid's PID: n"; } else { ->cleanup_for_exec(); # untie the socket chdir '/' or die "Can't chdir to /: "; open STDIN, '/dev/null' or die "Can't read /dev/null: "; open STDOUT, '>/dev/null'or die "Can't write to /dev/null: "; open STDERR, '>&STDOUT' or die "Can't dup stdout: "; setsid or die "Can't start a new session: ";exec "/home/httpd/perl/external.pl" or die "Cannot execute exec: "; }