bronto has asked for the wisdom of the Perl Monks concerning the following question:

Hello all

I am writing a small script using HTTP::Daemon, which I want to use to manage another service. Connecting to the daemon with a browser shows a form from which I can choose to start, stop or restart a certain service.

When I choose start, the script forks and immediately execs another program; for testing purposes, I am launching a shell scripts that runs ping 127.0.0.1; The call is as follows:/p>

my @start_command = ($service_start,'>',"${service}.out",'2>&1') + ; exec @start_command or print STDERR "ERROR: Cannot exec $service: $!\n" ;

When I push stop I would like the forked child and all subchildren to be killed. I am trying to do it with:

my ($status,$pid) = status() ; if ($pid > 0) { my $signaled ; # Try breaking my $signaled = kill "SIGINT",$pid ; sleep 1 ; ($status,$pid) = status() ; if ($pid > 0) { # Try killing gently $signaled = kill "SIGTERM",$pid ; sleep 1 ; ($status,$pid) = status() ; } # If it did not die, try killing it harder if ($pid > 0) { $signaled = kill "SIGKILL",$pid ; sleep 1 ; ($status,$pid) = status() ; } $message = $pid > 0 ? "$service_name could not be stopped, pid is $pid" : "$service_name killed" ;

Unfortunately, only the child process gets killed, all subchildren keep on running

Possible solutions:

...you have a better solution, don't you? ;-)

Ciao!
--bronto


The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
--John M. Dlugosz

Replies are listed 'Best First'.
Update: Killing a process and, recursively, all its children
by bronto (Priest) on Aug 12, 2003 at 11:53 UTC

    Just installed Proc::ProcessTable and discovered Proc::Killfam in the distribution. Very good!

    Any better solution?

    Ciao!
    --bronto


    The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
    --John M. Dlugosz

      A better solution that doesn't involve using the module? Copypaste its source. Obviously it works.

      Is there any reason you'd want to roll your own?

      Makeshifts last the longest.

        Is there any reason you'd want to roll your own?

        None, actually; Proc::Killfam does just what I was looking for. But there are so many modules on CPAN and so many experts Perl programmers here that I could well have missed a better module for the task that somebody here knew about. That was what I was asking for. But it seems that I already found the right one.

        Ciao!
        --bronto


        The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
        --John M. Dlugosz
Re: Killing a process and, recursively, all its children
by bsb (Priest) on Aug 12, 2003 at 13:37 UTC
    I've not tried this but "man kill" says:

    Negative PID values may be used to choose whole process groups; see the PGID column in ps command output

    perldoc -f kill:

    ...
    Unlike in the shell, if SIGNAL is negative, it kills process groups instead of processes. (On System V, a negative PROCESS number will also kill process groups, but that's not portable.)...

Re: Killing a process and, recursively, all its children
by zentara (Cardinal) on Aug 12, 2003 at 17:05 UTC
    Here is something which I have found useful in the past by robau.
    #!/usr/bin/perl use warnings; use strict; #robau #This subroutine takes two arguments, the parent process ID #and the numeric signal to pass to the processes #(which would be 9 if you wanted to issue a -TERM). #Using Proc::Process you could find the process ID #of the process login -- zentara with something similar #to the following : #my $proc = Proc::ProcessTable->new; #my @ps = map { $_->pid if ($_->cmndline =~ /login -- zentara/) } @{$p +roc->table}; #&killchd($_, 9) foreach @ps; &killchd(9895, 9); #kill -9 process 9895 sub killchd ($;$) { use Proc::ProcessTable; my $sig = ($_[1] =~ /^\-?\d+$/) ? $_[1] : 0; my $proc = Proc::ProcessTable->new; my %fields = map { $_ => 1 } $proc->fields; return undef unless exists $fields{'ppid'}; foreach (@{$proc->table}) { kill $sig, $_->pid if ($_->ppid == $_[0]); }; kill $sig, $_[0]; };
Re: Killing a process and, recursively, all its children
by Kageneko (Scribe) on Aug 12, 2003 at 13:11 UTC
    Another idea is to have each child keep track of its children. In the END block of each child, have it kill its children. Theoretically, if the parent is killed, it will kill all of its children, who will kill all of their children, etc.

      Unfortunately, the child process could be everything: a perl script, a shell script, a binary executable, a rocket... :-), so your approach is not suitable.

      Anyway, thanks for your suggestion.

      Ciao!
      --bronto


      The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
      --John M. Dlugosz
Re: Killing a process and, recursively, all its children
by bobn (Chaplain) on Aug 12, 2003 at 13:50 UTC

    I think killing the negative of a process kills it and all it's children, as a 'process group'.

    --Bob Niederman, http://bob-n.com
      bsb> I've not tried this but "man kill" says: ...

      bobn> ...I think killing the negative of a process kills it and all it's children, as a 'process group'.

      I tried the "process group" approach before posting, even if I had many doubts it could work. And in fact, it didn't: killing the process group means killing the daemon, too, since it is part of the process group of its children.

      Ciao!
      --bronto


      The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
      --John M. Dlugosz

        Assuming you have some sort of access to the POSIX layer (via XS, libraries, etc.) you can put your child processes in their own process group with setpgid:

        The setpgid() and setpgrp2() system calls cause the process specified by pid to join an existing process group or create a new process group within the session of the calling process. The process group ID of the process whose process ID is pid is set to pgid. If pid is zero, the process ID of the calling process is used. If pgid is zero, the process ID of the indicated process is used. The process group ID of a session leader does not change.

        You could have your parent proces set the process group of the children when it makes them and it should automatically propogate to any sub-children they make, etc. I think.