fork, exec, and signal handling

Discussion in 'Perl Misc' started by Todd Pytel, Dec 31, 2003.

  1. Todd Pytel

    Todd Pytel Guest

    Hi all,

    An infrequent perl coder here, having some issues with signal handling.
    I'm not understanding something about how forking works with signals, I
    think. The script is supposed to loop, running tcpdump until it exits
    (after a certain number of packets are captured), rotating the dump output
    and analyzing the output with tcpstat. Since the tcpdump can be rather
    lengthy, I'd like to catch TERM signals and have the output analyzed so as
    not to lose data. Here's some of what I've come up with - the use of pid
    files is rather ghetto, I think, so suggestions are welcome:

    $SIG{'TERM'} = \&term_handler;

    sub main {
    while ( 1 ) {
    ( $stop ) && exit;
    defined (my $pid = fork) or die "Cannot fork: $!";
    unless ($pid) {
    exec "tcpdump -c $packet_count -i $interface -w $output > /dev/null 2>&1";
    }
    open PIDFILE, "> $dumppid";
    print PIDFILE "$pid\n";
    close PIDFILE;
    waitpid($pid, 0);
    unlink $dumppid;
    &rotate;
    &analyze; # Should fork this, too - don't want to wait on analysis
    }
    }

    sub term_handler {
    print "Running term_handler...\n";
    $stop = 1;
    if ( -e $dumppid ) {
    open PIDFILE, "$dumppid";
    while (<PIDFILE>) {
    chomp;
    kill 15, $_;
    }
    close PIDFILE;
    }
    }

    The problem is that the signal never gets caught, so the tcpdump keeps
    going, and the cleanup at the end of main never occurs. From what I've
    seen searching, this has something to do with the parent waiting on the
    child process, but I'm not experienced enough to see exactly what the
    problem is. Is there a solution or alternate approach that I can use here?

    Thanks,
    Todd Pytel
     
    Todd Pytel, Dec 31, 2003
    #1
    1. Advertising

  2. Todd Pytel

    Anno Siegel Guest

    Todd Pytel <> wrote in comp.lang.perl.misc:
    > Hi all,
    >
    > An infrequent perl coder here, having some issues with signal handling.
    > I'm not understanding something about how forking works with signals, I
    > think. The script is supposed to loop, running tcpdump until it exits
    > (after a certain number of packets are captured), rotating the dump output
    > and analyzing the output with tcpstat. Since the tcpdump can be rather
    > lengthy, I'd like to catch TERM signals and have the output analyzed so as
    > not to lose data. Here's some of what I've come up with - the use of pid
    > files is rather ghetto, I think, so suggestions are welcome:


    Why do you think you need to store the pid in a file? Just declare
    the $pid variable outside the loop and use it in term_handler().

    > $SIG{'TERM'} = \&term_handler;
    >
    > sub main {
    > while ( 1 ) {
    > ( $stop ) && exit;


    Why the parentheses around $stop? Also, $stop is undeclared.

    > defined (my $pid = fork) or die "Cannot fork: $!";
    > unless ($pid) {
    > exec "tcpdump -c $packet_count -i $interface -w $output > /dev/null 2>&1";
    > }
    > open PIDFILE, "> $dumppid";
    > print PIDFILE "$pid\n";
    > close PIDFILE;
    > waitpid($pid, 0);
    > unlink $dumppid;
    > &rotate;
    > &analyze; # Should fork this, too - don't want to wait on analysis


    Don't call subs with a leading "&" unless you want the effects associated
    with that form.

    > }
    > }


    [more code snipped]

    Try something along these lines (untested):

    $SIG{'TERM'} = \&term_handler;

    main();

    my $stop = 0;
    my $pid;
    sub main {
    while ( 1 ) {
    $stop && do { print "main exiting\n"; exit };
    defined ($pid = fork) or die "Cannot fork: $!";
    unless ($pid) {
    exec "tcpdump -c $packet_count -i $interface " .
    "-w $output > /dev/null 2>&1";
    }
    waitpid($pid, 0);
    &rotate;
    &analyze; # Should fork this, too - don't
    # want to wait on analysis
    }
    }

    sub term_handler {
    print "Running term_handler...\n";
    $stop = 1;
    return unless $pid;
    kill 15, $pid;
    }

    Anno
     
    Anno Siegel, Jan 2, 2004
    #2
    1. Advertising

  3. Todd Pytel <> writes:

    > An infrequent perl coder here, having some issues with signal handling.
    > I'm not understanding something about how forking works with signals, I
    > think.


    No, you are not understanding what exec() does.

    exec() completely replaces the current process with a new executable.

    If you set up a signal handler then exec() the signal handler no
    longer exists in the new process.

    This has nothing to do with Perl.

    --
    \\ ( )
    . _\\__[oo
    .__/ \\ /\@
    . l___\\
    # ll l\\
    ###LL LL\\
     
    Brian McCauley, Jan 2, 2004
    #3
  4. Todd Pytel

    Anno Siegel Guest

    Brian McCauley <> wrote in comp.lang.perl.misc:
    > Todd Pytel <> writes:
    >
    > > An infrequent perl coder here, having some issues with signal handling.
    > > I'm not understanding something about how forking works with signals, I
    > > think.

    >
    > No, you are not understanding what exec() does.
    >
    > exec() completely replaces the current process with a new executable.
    >
    > If you set up a signal handler then exec() the signal handler no
    > longer exists in the new process.


    Well, the OP didn't explain the problem very well, but I think exec is fine
    where it is. The Idea was, if I understand, to make a TERM signal to the
    parent kill the child process (instead of the parent).

    To the OP: Perl's system() already does something very similar -- to INTR
    and TERM, as I recall, but look it up.

    Anno
     
    Anno Siegel, Jan 2, 2004
    #4
  5. In article <>,
    Todd Pytel <> wrote:
    >...
    >lengthy, I'd like to catch TERM signals and have the output analyzed so as
    >not to lose data. Here's some of what I've come up with - the use of pid
    >files is rather ghetto, I think, so suggestions are welcome:
    >
    >$SIG{'TERM'} = \&term_handler;
    >
    >sub main {
    > while ( 1 ) {
    > ( $stop ) && exit;
    > defined (my $pid = fork) or die "Cannot fork: $!";
    > unless ($pid) {
    > exec "tcpdump -c $packet_count -i $interface -w $output > /dev/null 2>&1";
    > }
    > open PIDFILE, "> $dumppid";
    > print PIDFILE "$pid\n";
    > close PIDFILE;
    > waitpid($pid, 0);
    > unlink $dumppid;
    > &rotate;
    > &analyze; # Should fork this, too - don't want to wait on analysis
    > }
    >}
    >
    >sub term_handler {
    > print "Running term_handler...\n";
    > $stop = 1;
    > if ( -e $dumppid ) {
    > open PIDFILE, "$dumppid";
    > while (<PIDFILE>) {
    > chomp;
    > kill 15, $_;
    > }
    > close PIDFILE;
    > }
    >}
    >
    >The problem is that the signal never gets caught, so the tcpdump keeps
    >going, and the cleanup at the end of main never occurs. From what I've
    >seen searching, this has something to do with the parent waiting on the
    >child process, but I'm not experienced enough to see exactly what the
    >problem is. Is there a solution or alternate approach that I can use here?
    >


    I'm not sure what your search revealed but I don't see how
    the parent's blocking waitpid could be problematic at all.
    The TERM handler below for instance gets caught:

    my $pid = fork;
    die $! unless defined $pid;

    $SIG{ TERM } = sub { die "caught a term ..."; };
    unless ($pid) {
    exec "sleep 600" or die "exec failed: $!"; }
    else {
    waitpid( $pid,0);
    }

    The only long shots I can think of is that the signal's
    being blocked or ignored somewhere else. You might want
    to trace what's happening in the rest of the code.

    hth,
    --
    Charles DeRykus
     
    Charles DeRykus, Jan 3, 2004
    #5
  6. Todd Pytel

    Todd Pytel Guest

    Thanks for the responses.

    Yes, Brian, I'm aware that exec() replaces the current process. I'm
    somewhat clueless, but not that clueless. As Charles and Anno suggested,
    the problem was elsewhere. Two places, really...

    1) The $SIG{'TERM'} line was defined globally, but *after* the program had
    entered the main loop. Thus the signal wasn't being caught. I haven't done
    signal handling before, and thought that perl "read ahead" of the loop.
    Guess not.

    2) The exec command included the shell redirection - "> /dev/null 2>&1".
    This means the fork's pid is the pid of /bin/sh invoked to run tcpdump,
    not the pid of tcpdump itself. Removing the redirection takes care of
    that, which is fine since I was planning to daemonize the process anyway.

    Thanks for the pushes in the right direction, and sorry if I was unclear.
    All I've read is the llama book, and this is the first substantial script
    I've done from scratch, as opposed to modifying other people's stuff.

    --Todd
     
    Todd Pytel, Jan 4, 2004
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Hoegje
    Replies:
    2
    Views:
    22,552
    Gianni Mariani
    Dec 5, 2003
  2. Benoit Dejean

    fork, exec, and disown

    Benoit Dejean, Feb 8, 2004, in forum: Python
    Replies:
    4
    Views:
    956
    Benoit Dejean
    Feb 9, 2004
  3. Lingyun Yang
    Replies:
    4
    Views:
    11,825
    Keith Dart
    Dec 16, 2004
  4. Eric Snow

    os.fork and pty.fork

    Eric Snow, Jan 8, 2009, in forum: Python
    Replies:
    0
    Views:
    574
    Eric Snow
    Jan 8, 2009
  5. Peter
    Replies:
    34
    Views:
    1,941
    James Kanze
    Oct 17, 2009
Loading...

Share This Page