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

hi ppl,

after surfing the net for a while in quest for solution a need i came with an answer that it cannot be done. so i figured there must be someone here that thinks differently. the problem is , i am running a perl application that forks itself after a while. when forked the outside process is called and set to run on a different machine through ssh. ok so complications emerge when a user is trying to kill the process (perl application) by issuing a Ctrl-C. so what happens is the main application and its children are killed indeed but since the outside process is getting a new PID when started i have no control over it , meaning i can't automatically kill it (have to do it manually one by one)

i thought of ps-ing every machine and retrieving the PID by process name but then it hit me what if two users started the same outside aplication and one is trying to kill it and the other one is not. in that case my subroutine will identify all outside PID's by process name and kill them all (not the ones started by me)

so my question is (i went through CPAN - 0 points)did anyone encountered this problem and did he/she found the solution.

the solution probably lies in the catching the PID of a process right when the process starts so something like when i start the appl.

$PID = system('./myAPP'); # I'm aware this is something that does not +exist
i should automatically catch the ID of the process into some variable to be dealt with latter. the grid engines have this problem resolved but i can't seam to figure out where the answer lies.

thank you

example:

my @children; for (my $i = 1;$i<=5;$i++){ my $pid = fork(); if ($pid){ push @children, $pid; } elsif ($pid == 0){ system("ssh $mch aplication"); exit; } else{ die "could not fork! \n"; } } foreach (@children){ waitpid($_,0); }

Replies are listed 'Best First'.
Re: how to kill deattached process
by Corion (Patriarch) on Oct 31, 2009 at 15:30 UTC

    The trick is to keep track of the PID as you launch the process. On non-Windows systems, this is easiest done through the magic of exec, which replaces the current process by the other process, keeping the PID ($$) the same. So you can store $$ and then exec the "real" process:

    sub launch { my ($pid_file,@process_and_args) = @_; open my $fh, '>', $pid_file # or print to STDOUT for easier handli +ng by ssh? or die "Couldn't create '$pid_file': $!"; print {$fh} $$; exec(@process_and_args) or die "Couldn't launch [@process_and_args]: $!"; };

    You have to decide whether to output the PID to a file or to directly output it to STDOUT and read it from your ssh launcher and store it in the controlling master.

Re: how to kill deattached process
by BioLion (Curate) on Oct 31, 2009 at 15:39 UTC

    I have never had to deal with infanticide across multiple machines, but when it comes to child management I have found Parallel::Forker very helpful for monitoring, polling and if nessecary (sp.?), brutally murdering processes.

    You can also use it for scheduling launches, for example a cleanup job like the one you describe. It also handles process trees, which might be a way of avoiding the "when one user Ctrl-C's, how do i only kill their processes?" problem, i.e. by keeping a separate tree for each user?

    Anyway, have a play and hopefully it will help and not bee too much of a pain to refactor into your code.

    Just a something something...
Re: how to kill deattached process
by salva (Canon) on Oct 31, 2009 at 17:08 UTC
    A simple workaround is to call the remote commands through a wrapper that writes the PID to some file or to STDERR:
    #!/usr/bin/perl print STDERR "PID: $$\n"; exec @ARGV;
    For example:
    $ ssh my.host.com tellpid echo 'hello world' PID: 15079 hello world
    Also, check this ticket on the OpenSSH bug tracking system: Bug 1424 - Cannot signal a process over a channel (rfc 4254, section 6.9). That feature, scheduled for inclusion in the next release of OpenSSH, would allow to send signals to the remote processes.
Re: how to kill deattached process
by jethro (Monsignor) on Oct 31, 2009 at 15:35 UTC
    Just a quick and dirty idea if nothing better comes along: Give the application on the other machine a parameter with a random id. This id can be seen in the output of ps and so the sshd process identified.
Re: how to kill deattached process
by zentara (Cardinal) on Nov 01, 2009 at 15:02 UTC
    I usually like to stuff the pids of processes, into an array or hash. as I create them, for easy killing later

    You can, as you suggest, give the forked process, a unique name, and kill the process later by searching for the pid of the name

    #!/usr/bin/perl -w use strict; use Proc::ProcessTable; my $t1 = new Proc::ProcessTable; my $pid; my $commandline = shift || $0; foreach my $p (@{$t1->table}){ if($p->cmndline =~ /\Q$commandline\E/){ $pid = $p->pid; print "$pid\n"; } } ###################################################

    The one thing to watch out for, is quite often, you get the pid of a shell (like bash or sh) which runs your external process... so you often need to kill the pid and all it's children, to get the whole shebang killed.

    ...this may be useful, as a guide

    #!/usr/bin/perl -w use strict; use Proc::ProcessTable; #-- #-- we will ignore the child #-- $SIG{CHLD} = 'IGNORE'; #-- #-- create 5 child processes for demo purpose. arrange them so they st +ay #-- long enough so we can find them later #-- for(1..5){ sleep(10) and exit if(fork == 0); } #-- #-- list all process running in your machine and find all #-- child process #-- for my $p (@{new Proc::ProcessTable->table}){ print $p->pid," child of $$\n" if($p->ppid == $$); }
    also look at rsync with tk progressbar for some ideas for tracking down child processes

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku
Re: how to kill deattached process
by baxy77bax (Deacon) on Nov 01, 2009 at 13:13 UTC
    ok ppl,

    i don't think this gonna work . what i did is :

    #!/usr/bin/perl use strict; my @mch = ('compute-0-0','compute-0-1'); my @children; for (my $i = 0;$i<=1;$i++){ my $pid = fork(); if ($pid){ push @children, $pid; } elsif ($pid == 0){ print $$ . "\n"; print "$mch[$i] \n"; exec("ssh $mch[$i] ./sleep.pl") || die "cannot execute process +\n"; exit; } else{ die "could not fork! \n"; } } foreach (@children){ waitpid($_,0); } sleep.pl #!/usr/bin/perl use strict; for (1..10000000){ my $rez = 1; for (1..500){ $rez *=$_; } }
    and i get reported pid 1 = 9776 pid 2 = 9777 but on a compute-0-0 id of the proc is 24356 and on compute-0-1 4443

    so the problem is sending a job to a remote machine and retrieving the pid of the process executed on that machine... any more ideas ??? or this really cannot be done ??

    Update:

    THE WRAPPER !!!! :) hey it's sunday (rough night last night :)) so :

      and i get reported pid 1 = 9776 pid 2 = 9777 but on a compute-0-0 id of the proc is 24356 and on compute-0-1 4443

      The value returned by fork is not the PIDs of the remote processes but the PID of the new local process (the one that will become ssh after the exec call).

      On the remote machine, when the SSH connection is stablished, sshd forks a new process that then forks a new shell that finally forks and executes the requested command.