#!/usr/bin/perl # runEmbeddedScript: # This script illustrates how to run an embedded Perl script (source embedded # in the current script) as a separate process. # The new process (running the embedded Perl script) is completely independent # of the parent process. It will continue to run even after the parent dies. # # Try the following tests: # - doing Control-C on the parent # - sending a TERM signal to the parent (with 'kill') # - sending a USR1 signal to the parent (with 'kill -USR1') # - let the parent finish normally # # If you discover any problems with this script or have a better way to do this, # please send me email about it. # # Cameron Hayne (macdev@hayne.net) Nov 2006 use strict; use warnings; use POSIX qw(setsid); sub runPerlScriptInSeparateProcess($@) { my ($script, @args) = @_; $SIG{CHLD} = 'IGNORE'; # to avoid zombies pipe(READER, WRITER); my $pid; if ($pid = fork()) { # parent process close(READER); # not needed print WRITER $script; close(WRITER); } elsif (defined($pid)) { # child process close(WRITER); # not needed # close the stdio file descriptors inherited from the parent close(STDIN); close(STDOUT); close(STDERR); # we dup READER to STDIN so that the child gets the script from STDIN open(STDIN, "<&READER"); close(READER); # start a new process session so as to disassociate # from the parent process and its controlling terminal setsid() or die "Can't start a new session: $!\n"; my $command = "/usr/bin/perl"; exec($command, "-", @args) or die "Failed to exec \"$command\": $!\n"; } else { warn "Can't fork: $!\n"; $pid = 0; } # return the pid of the child process so the parent can kill it later return $pid; } sub terminateProcess($) { my ($pid) = @_; print "Terminating process $pid\n"; if (kill('TERM', $pid) < 1) { print "Failed to terminate process $pid\n"; } } my $scriptText = <<'EOT'; $0 = "scriptFromRunEmbeddedScript"; my $logfile = "foo"; $SIG{INT} = sub { print FILE "rec'd INT signal\n"; exit; }; $SIG{TERM} = sub { print FILE "rec'd TERM signal\n"; exit; }; open(FILE, ">$logfile") or die "Can't open file \"$logfile\": $!\n"; select((select(FILE), $| = 1)[0]); # auto-flush so we can see progress foreach my $arg (@ARGV) { print FILE "$arg\n"; } print FILE "pid is $$\n"; foreach my $i (0..10000) { print FILE time(), "\n"; sleep 1; } close(FILE); exit; EOT MAIN: { print "parent pid is $$\n"; my @args = ("hello world", "glad to be here"); my $childPid = runPerlScriptInSeparateProcess($scriptText, @args); print "childPid is $childPid\n"; $SIG{INT} = sub { print "parent rec'd INT signal\n"; exit; }; $SIG{TERM} = sub { print "parent rec'd TERM signal\n"; exit; }; $SIG{USR1} = sub { print "parent rec'd USR1 signal\n"; terminateProcess($childPid); }; print "Press 'Return' to continue\n"; my $input = ; my $n = 5; for (my $i = 0; $i < $n; $i++) { print "This is the parent ($i)\n"; sleep 4; } print "parent is finished\n"; }