Re: trying to understand fork and wait
From: Ben Morrow (usenet_at_morrow.me.uk)
Date: 11/20/03
- Next message: Ben Morrow: "Re: use perl script interactive with a program like mplayer"
- Previous message: James Willmore: "Re: Protecting Source code of a perl script"
- In reply to: John: "trying to understand fork and wait"
- Next in thread: John: "Re: trying to understand fork and wait"
- Reply: John: "Re: trying to understand fork and wait"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Thu, 20 Nov 2003 18:41:39 +0000 (UTC)
jguad98@hotmail.com (John) wrote:
> #!/usr/local/bin/perl
> #=================================================================#
You don't need all these: they really don't make things any clearer.
You do need
use warnings;
use strict;
> #-----------------------------------------------------------------
> # some global vars
> #-----------------------------------------------------------------
> $mypid=$$;
my $mypid = $$;
and so on throughout.
> $basedir="/paf/rpas";
> @domainlist=`ls $basedir | egrep '^(top|key)..$'`;
my @domainlist = <$basedir/{top,key}??>;
see perldoc -f glob
> $patternfile="/path/to/patterns.list";
> open(PATTERNS,$patternfile);
open my $PATTERNS, $patternfile or die "can't open pattern file: $!";
> @patternlist=<PATTERNS>; # this is my store of error messages to
> watch for
> close(PATTERNS);
> #-----------------------------------------------------------------
> # scan the directories
> #-----------------------------------------------------------------
> foreach $domain (@domainlist) {
for my $domain (@domainlist) {
> chomp($domain);
> $userdirbase="$basedir/$domain/users";
> opendir(USERS, "$userdirbase");
> #-------------------------------------------------------------
> # read the dir
> #-------------------------------------------------------------
> while ($file = readdir(USERS)) {
> #---------------------------------------------------------
> # only select files matching the username pattern
> #---------------------------------------------------------
> next unless $file =~ /^[a-z][a-z]\d\d\d\d\d$/;
/^[a-z][a-z]\d{5}$/
> #---------------------------------------------------------
> # check if we have a user dir
> #---------------------------------------------------------
> $currentuserdir="$userdirbase/$file";
> if (-d "$currentuserdir") {
> #-----------------------------------------------------
> # check if there is an applog in the user dir
> #-----------------------------------------------------
> $logfile="$currentuserdir/applog";
> if ( -e "$logfile") {
> #-------------------------------------------------
> # is the log open or closed?
> #-------------------------------------------------
> $return=`grep 'known end of session message'
> $logfile`;
Better than this would be to delete the log when it finishes; unless
you need to keep them, in which case I would lock the logfile while
the child reads it.
> #-------------------------------------------------
> # if open, fire the monitor
> #-------------------------------------------------
> if (! $return) { # the grep failed, ergo the file is
> active
grep exits with 0 if it succeeds, so this test is precisely backwards
:).
> #-------------------------------------------------
> # line below is because everything I've read about
> fork
> # says I gotta wait on the child pid (?) or it will
> # become a zombie which is bad. I don't understand
> # what this does or how it works.
> #-------------------------------------------------
> $SIG{'CHLD'} = sub { wait(); };
When a process exits, it returns a status code (like grep exitted with
0 or 1 above). The parent process can collect this by calling wait or
waitpid. Until it does, the process has to sit around occupying a slot
in the process table, just to keep a hold of the exit code. So, for
instance, the system() call above does (NB this is simplified C)
pid = fork();
if(pid)
return waitpid(pid);
else
exec("grep", ...);
internally.
The easy way to solve this is $SIG{CHLD} = 'IGNORE';, which says you
don't care about exit codes. That fails on some systems, though; if it
does you need to read the discussion under 'Signals' in perldoc perlipc.
> #-------------------------------------------------
> # now we fork if and only if we are the original
> program
> # we consider ourselves too young to have
> grandchildren
> #-------------------------------------------------
> if ($$ == $mypid) {
> $kidpid = fork() or die "cannot fork: $!";
This is wrong. fork returns 0 to the child, <pid> > 0 to the parent and
undef if it fails. Children will always die, with this... You want
my $kidpid = fork;
defined $kidpid or die "cannot fork: $!";
# From this point on we have two almost identical copies of the
# program running. The only difference is that one has $kidpid set to
# 0, the other to some positive number.
if($kidpid) {
# parent
} else {
# child
}
which is why you need two blocks in general. In your case, the parent
doesn't want to do anything but loop round again, and the child loops
over a file and exits. So you want <untested>
unless($kidpid) { # if we are the parent, just loop back
open my $LOG, $file or die "can't open logfile $file: $!";
while ( (my $line = <LOG>) !~ /known EOS/ ) {
my $now = localtime;
system "logger $now $domain $user $line"
for grep { $line =~ /$_/ } @patternlist;
# or you would probably be better off using Sys::Syslog
}
close $LOG; #
unlink $file; # if you decide to delete them when you're done
exit 0; # so we won't get out into the parent's code
}
and ditch all this...
> } else {
> #-------------------------------------------------
> # are we in the child process?
> #-------------------------------------------------
> if ($kidpid == 0) {
> #---------------------------------------------
> # read the user log
> #---------------------------------------------
> open(LOG,"$file");
> while ($line = <LOG>) {
> close(LOG), exit if ($line =~ /known EOS
> message/);
> foreach $patt (@patternlist) {
> if ($line =~ /$patt/) {
> $now=localtime(time);
> system("logger $now $domain $user $line");
> }#end child's if pattern match
> }#end child's foreach loop
> }#end child's while loop
> }# this is the end of the child block
> }#end if mypid
> }#end if $return
> }#end if logfile exists
> }#end if directory (go to top of parent while loop)
> } #end parent while loop (go to top of parent foreach loop)
...down to here.
> closedir(USERS);
> }#end parent foreach loop
> #-----------------------------------------------------------------
> exit 0;
No need for this: 'falling off the end' is a perfectly valid way to
end a Perl program.
> #== EOF ==========================================================#
<snip>
> Will "wait" make me wait for a return or not?
Yes. waitpid() will wait for the specified child process to exit and
return its exitcode; wait() (or waitpid with a pid of -1) will wait
for *any* child to exit and return its exitcode. Depending on your
system, if you are ignoring SIGCHLD both may return -1.
See wait(2).
> I have not actually run the above program mostly because I don't
> understand how the wait & fork will work and I fear unintended
> consequences.
Heh. :) When learning things like this, you need a test box to try
stuff on. Remember, you can always kill the parent process, and then
mop up the childen, if it starts doing things you don't expect.
Ben
-- If I were a butterfly I'd live for a day, / I would be free, just blowing away. This cruel country has driven me down / Teased me and lied, teased me and lied. I've only sad stories to tell to this town: / My dreams have withered and died. ben@morrow.me.uk <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=> (Kate Rusby)
- Next message: Ben Morrow: "Re: use perl script interactive with a program like mplayer"
- Previous message: James Willmore: "Re: Protecting Source code of a perl script"
- In reply to: John: "trying to understand fork and wait"
- Next in thread: John: "Re: trying to understand fork and wait"
- Reply: John: "Re: trying to understand fork and wait"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]