6

I have situation where one parent process may spawn many child processes. What I want to achieve is that if parent process is killed or if it exits, then all it's children should terminate together with parent.

In the post (link below) I have found suggestion to archive this by making parent process a group leader. If I understand it right this is also the main purpose of process groups. Am I right?
Post also mentions prctl(PR_SET_PDEATHSIG, SIGHUP); and some other methods, but they are ether OS specific or otherwise don't seam so elegant.

I have written a small demo to try to understand things better, but it doesn't work the way I expect. What am I doing wrong?

//https://www.andrew.cmu.edu/course/15-310/applications/homework/homework4/terminalgroups1.html
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/termios.h>

int main()
{
    int status;
    int cpid;
    int ppid;

    ppid = getpid();
    printf("parent: %d\n", ppid);

    if (!(cpid=fork()))
    {
        printf("child: %d\n", getpid());
        if(setpgid(0,ppid) == -1)
           printf("child setpgid errno %s\n", strerror(errno));
        else
           printf("child gid %d\n", getpgid(0));
        pause();
        printf("child exited\n");
        exit (-1);
     }

     if (cpid < 0)
         exit(-1);

     setpgid(0, ppid);
     if(setpgid(0,0) == -1)
         printf("parent setpgid erno %s\n", strerror(errno));
     else
         printf("parrent gid %d\n", getpgid(0));


     sleep(7);
     printf("parent exit\n");
     exit(0);
}

This post relates to suggestion made in : * How to make child process die after parent exits?

Community
  • 1
  • 1
  • *How* doesn't it work the way you expect? What *do* you expect? – Some programmer dude May 13 '14 at 07:04
  • Parent process terminates after 7 seconds, but child remains and is orphaned by init process. I expect a child to receive some signal and to be terminated with parent. – user2979375 May 13 '14 at 07:15
  • http://man7.org/linux/man-pages/man3/exit.3.html says but i don't know if this is actual in this situation: If the process is a session leader and its controlling terminal is the controlling terminal of the session, then each process in the foreground process group of this controlling terminal is sent a SIGHUP signal, and the terminal is disassociated from this session, allowing it to be acquired by a new controlling process. – user2979375 May 13 '14 at 07:23
  • Have you thought about calling [`setsid`](http://man7.org/linux/man-pages/man2/setsid.2.html) before forking, to make sure the parent process is process group leader and you have a fresh session? – Some programmer dude May 13 '14 at 07:24
  • setsid returns "Operation not permitted" error – user2979375 May 13 '14 at 07:43
  • That should mean that the parent process already is the group leader. – Some programmer dude May 13 '14 at 07:48
  • I have called setsid before setpgid right at the bigening. So I don't know what to do to prevent this. – user2979375 May 13 '14 at 08:07

2 Answers2

6

Note that a signal is sent to child processes only in a very limited set of circumstances. POSIX says:

  • If the process is a controlling process, the SIGHUP signal shall be sent to each process in the foreground process group of the controlling terminal belonging to the calling process.

  • If the process is a controlling process, the controlling terminal associated with the session shall be disassociated from the session, allowing it to be acquired by a new controlling process.

  • If the exit of the process causes a process group to become orphaned, and if any member of the newly-orphaned process group is stopped, then a SIGHUP signal followed by a SIGCONT signal shall be sent to each process in the newly-orphaned process group.

The definition of controlling process is:

The session leader that established the connection to the controlling terminal. If the terminal subsequently ceases to be a controlling terminal for this session, the session leader ceases to be the controlling process.

In general, your process is not going to be the session leader that established the connection to the controlling terminal (that will normally be your shell).

If there's another part of POSIX that applies, please inform me.

I did some testing with this adaptation of your code (termkids.c):

#include "posixver.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

static void sigcatcher(int signum)
{

  printf("%d: Signal caught: %d\n", (int)getpid(), signum);
  exit(1);
}

int main(void)
{
    int cpid;
    int ppid;

    ppid = getpid();
    printf("Parent  PID:  %d\n", ppid);
    printf("Initial PGID: %d\n", (int)getpgid(0));
    if (setpgid(0, 0) != 0)
    {
      fprintf(stderr, "setpgid() failed (%d: %s)\n", errno, strerror(errno));
      return 1;
    }
    printf("Revised PGID: %d\n", (int)getpgid(0));

    if ((cpid=fork()) < 0)
    {
      fprintf(stderr, "fork() failed (%d: %s)\n", errno, strerror(errno));
      return 1;
    }
    else if (cpid == 0)
    {
        cpid = getpid();
        printf("Child PID:  %d\n", cpid);
        printf("Child PGID: %d\n", (int)getpgid(0));
        (void)signal(SIGTERM, sigcatcher);
        (void)signal(SIGHUP,  sigcatcher);

        pause();
        printf("%d: child exited\n", cpid);
        return(-1);
     }

     printf("Parent - sleeping\n");
     sleep(7);
     printf("Parent exits\n");
     return(0);
}

Sample output:

$ ./termkids
Parent  PID:  17701
Initial PGID: 17701
Revised PGID: 17701
Parent - sleeping
Child PID:  17702
Child PGID: 17701
Parent exits
$ ps
  PID TTY          TIME CMD
  388 pts/5    00:00:00 bash
17702 pts/5    00:00:00 termkids
17707 pts/5    00:00:00 ps
$ kill 17702
17702: Signal caught: 15
$

Note that the kill 17702 was sent some minutes after the parent process completed.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

You can use atexit to register a function that sends a SIGHUP signal to all the processes with the same process group id. That would have the desired effect of sending a signal to all the children when the parent exits. However, note that the SIGHUP signal handler in the children would cause the child to exit immediately, without returning from pause() and printing the child exited message.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
#include <sys/types.h>

void killall() 
{
    kill(0, SIGHUP);
}

int main()
{
    int status;
    int cpid;
    int ppid;

    if (atexit(killall) != 0)
    {
        fprintf(stderr, "atexit failed with %d", errno);
        exit(-1);
    }

    ppid = getpid();
    printf("parent: %d\n", ppid);

    if (!(cpid=fork()))
    {
       printf("child: %d\n", getpid());
       if(setpgid(0,ppid) == -1)
           printf("child setpgid errno %s\n", strerror(errno));
       else
           printf("child gid %d\n", getpgid(0));
       pause();
       printf("child exited\n");
       exit (-1);
     }

     if (cpid < 0)
         exit(-1);

     setpgid(0, ppid);
     if(setpgid(0,0) == -1)
         printf("parent setpgid erno %s\n", strerror(errno));
     else
         printf("parent gid %d\n", getpgid(0));


     sleep(7);
     printf("parent exit\n");
     exit(0);
}