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

Hi,

am trying to run a perl script which will internally start a perl process on background and with nohup. which am doing as below in my perl script

$cmd = qq(nohup perl.pl &); system($cmd);
My problem is it starts with the parent process group.

I have tried to do the below which is not working

$cmd = qq{nohup perl.pl &}; my $pid = fork(); if($pid == 0) { print STDERR "parent pid=$$ child pid=$pid\n"; }elsif (defined $pid) { print STDERR "child pid=$$ pid=$pid\n"; # Must do setpgrp as child for nohup and job control stuff... setpgrp(0, $$) || die "Cannot do SetGroup\n"; exec "$cmd" || die "Bad exec $!"; }
which is not working as expected. Can someone help me in solving this problem or give an idea on how i can proceed.

Update:
In the output we are getting PGRP different from PID 31852 1 31851 18307 /usr/bin/perl ./perl.pl 31854 31852 31851 18307 \_ sleep 1 31852 is different from 31851... i am trying to start the perl.pl in background with PID = PGRP.. if i start the job without Background.. it works as expected but when i start the job on Background(&).

Replies are listed 'Best First'.
Re: Run Perl script in perl program with new process group
by almut (Canon) on Dec 18, 2009 at 01:48 UTC
    ...which is not working as expected.

    So what do you expect?  Basically, the approach is fine — except that you got it the wrong way round wrt what is child and parent (if ($pid == 0) {...} is the child).

    Here's a slightly modified version which does what I would expect, i.e. have perl.pl (and its children, if any) run under a new process group:

    #!/usr/bin/perl use strict; use warnings; my $cmd = qq{nohup ./perl.pl &}; my $pid = fork; die 'fork failed' unless defined $pid; if ($pid) { print STDERR "parent pid=$$ child pid=$pid\n"; wait; my $sid = getppid; system "ps f -s $sid -o pid,ppid,pgrp,sid,cmd"; } else { print STDERR "child pid=$$ \$pid=$pid\n"; setpgrp; exec $cmd or die "Bad exec: $!"; }

    With perl.pl being

    #!/usr/bin/perl system 'sleep 1';

    the output is something like

    $ ./813283.pl child pid=31851 $pid=0 parent pid=31850 child pid=31851 PID PPID PGRP SID CMD 18307 18305 18307 18307 bash -rcfile .bashrc 31850 18307 31850 18307 \_ /usr/bin/perl ./813283.pl 31853 31850 31850 18307 \_ ps f -s 18307 -o pid,ppid,pgrp,sid,cmd 31852 1 31851 18307 /usr/bin/perl ./perl.pl 31854 31852 31851 18307 \_ sleep 1

    As you can see in the PGRP column, perl.pl and its child process sleep 1 is running under a different process group (31851 in the sample output).

      in the output we are getting PGRP different from PID 31852 1 31851 18307 /usr/bin/perl ./perl.pl 31854 31852 31851 18307 \_ sleep 1 31852 is different from 31851... i am trying to start the perl.pl in background with PID = PGRP.. if i start the job without Background.. it works as expected but when i start the job on Background(&).

        If you start a job in the foreground (my $cmd = qq{nohup ./perl.pl};), then the process id (PID) and process group (PGRP) will match, because perl will exec the program directly.

        If you start a job in the background (my $cmd = qq{nohup ./perl.pl &};), then the process id (PID) and process group (PGRP) will NOT match, because perl notices the shell meta-character '&' and execs a shell directly, and the shell will exec the program. The PGRP you are seeing is the PID of the shell that was started solely to process the backgrounding character.

        Since you're already forking, why do you need the implied fork of the '&' character too?

Re: Run Perl script in perl program with new process group
by gmargo (Hermit) on Dec 18, 2009 at 18:51 UTC

    Here's some code to daemonize a program. With the test daemon I've provided, it is easy to confirm that the code runs even if you log off.

    Here is the daemonizing code:

    #!/usr/bin/perl -w use strict; use warnings; use diagnostics; use POSIX qw(setsid); my $cmd = qq{./perl_delayer.pl}; fork_daemon($cmd); exit 0; ################################################# # Daemonize! ################################################# sub fork_daemon { my ($cmd) = @_; # Pass a string or an array reference my $pid_c = fork(); # Parent spawns Child die "Cannot fork: $!" if !defined $pid_c; if ($pid_c) { # ==== Parent ==== waitpid($pid_c, 0); # Zombies not allowed return; # No attachment to grand-child } # ==== Child ==== my $pid_gc = fork(); # Child spawns Grand-Child die "Cannot fork: $!" if !defined $pid_gc; exit (0) if $pid_gc; # Child exits immediately # ==== Grand-Child ==== # Grand-Child continues, now parented by init. # Detach from controlling terminal and create new process group. setsid() || die "Can't start a new session: $!"; # Close std* streams. close STDIN; close STDOUT; close STDERR; open STDIN , "< /dev/null"; open STDOUT, "> /dev/null"; # could use "> nohup.out" open STDERR, "> /dev/null"; # could use ">> nohup.out" # There is no reason to ignore SIGHUP. # Since we've detached from the terminal, we won't # be getting one when the user logs out. # $SIG{HUP} = 'IGNORE'; exec(ref($cmd) eq "ARRAY" ? @$cmd : $cmd); # Run command exit(0); }

    Here is the test daemon code, "perl_delayer.pl". You will observe that the file created ("delayer.out") gets the full countdown and is not affected by a logout.

    #!/usr/bin/perl -w use strict; use warnings; my $fname = "delayer.out"; open(OUT,">",$fname) || die("Cannot open $fname: $!"); select OUT; $| = 1; # make unbuffered select STDOUT; $| = 1; # make unbuffered my $count = 60; while ($count--) { my $str = "$count: The time is now ".localtime()."\n"; print $str; print OUT $str; sleep 1; }
      Thanks for All who have helped me in here. Everything is working now as expected. Was using system() instead of exec and there was some confusion created due to lot of debug print statements. After removing all print statements it is working now as expected. $cmd = qq{nohup ./perl.pl}; my $pid = fork; if ($pid) { } else{ # Must do setpgrp as child for nohup and job control stuff... setpgrp || die "Cannot do SetGroup\n"; exec "$cmd" || die "Bad exec $!"; }