kubo39's blog

ただの雑記です。

孫プロセスの終了通知を親が待つ

親プロセスが孫プロセスの終了を待ち受けたいときもあるわけです。 unshare(2)でCLONE_NEWUSER使いたいけど、マルチスレッドで動いてる場合とかにdouble-forkで制限突破するしかないのだけれど、孫の終了状態はチェックしたいとか。

prctl(2) の PR_SET_CHILD_SUBREAPER を使うとこういうことができます。

#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

pid_t spawnGrandchild()
{
  int fds[2];
  if (pipe(fds) == -1) {
    perror("pipe(2) failed.");
    exit(EXIT_FAILURE);
  }
  // we can wait on our grandchild.
  if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) {
    perror("prctl(2) failed.");
    exit(EXIT_FAILURE);
  }

  pid_t pid = fork();
  if (pid == -1) {
    close(fds[0]);
    close(fds[1]);
    perror("fork(2) failed.");
    exit(EXIT_FAILURE);
  }

  // child
  if (pid == 0) {
    close(fds[0]);
    pid = fork();
    if (pid == -1) {
      close(fds[1]);
      perror("fork(2) failed.");
      exit(EXIT_FAILURE);
    }
    // grandchild.
    if (pid == 0) {
      sleep(5);
      puts("hello");
      abort();
    }
    // child.
    size_t ret = write(fds[1], &pid, sizeof(pid_t));
    assert(ret == sizeof(pid_t));
    exit(EXIT_SUCCESS);
  }

  // parent.
  close(fds[1]);
  pid_t grandchild_pid;
  size_t ret = read(fds[0], &grandchild_pid, sizeof(pid_t));
  assert(ret == sizeof(pid_t));
  return grandchild_pid;
}

int main()
{
  int status;
  pid_t grandchild_pid = spawnGrandchild();

  for (;;) {
    pid_t check = waitpid(-1, &status, 0);
    if (check == -1) {
      perror("waitpid(2) failed.");
      exit(EXIT_FAILURE);
    }
    if (check == grandchild_pid) {
      break;
    }
  }
  exit(status);
}