10

Suppose pid X is a process group leader and X terminates, but other processes in the process group remain running (with X as their pgid). Will Linux prevent the value X from being assigned as a pid to a new process?

I ask this because of a failure condition POSIX allows for setsid:

[EPERM] The calling process is already a process group leader, or the process group ID of a process other than the calling process matches the process ID of the calling process.

This error seems to be an unrecoverable condition for code using process groups (i.e. shells) that would be triggered "at random", making it even more odious. I would assume any implementation aiming at sane levels of quality would avoid reassigning X as a pid while it's still in use as a pgid, but I can't find this documented anywhere.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711

2 Answers2

6

Not a problem, because fork guarantees:

The child process ID also shall not match any active process group ID.

And fork is the only way to create new processes.

Nemo
  • 70,042
  • 10
  • 116
  • 153
  • This seems to settle it. Any idea why that bogus language was put in the specification for `setsid` if it can never apply? – R.. GitHub STOP HELPING ICE Jul 22 '11 at 03:36
  • Strictly speaking, `fork` is not the only way to create new processes. There's `posix_spawn`, `posix_spawnp`, `popen`, and `system` too. – R.. GitHub STOP HELPING ICE Jul 20 '13 at 01:36
  • Oh, and I figured out why the "bogus" language is in the specification. It's not bogus. If the process calling `setsid` is a process group **leader**, then other processes in its process group will have its process id as their process group id. In this case, `setsid` must be forbidden because a new session needs a new session id and process group id, which would be equal to the caller's pid. But this id cannot be used as a new process group id because a process group with that id already exists. BTW, this is the reason `daemon(3)` traditionally double-`fork`s. – R.. GitHub STOP HELPING ICE Jul 20 '13 at 01:39
  • @R: Sorry, but I don't get it. The spec says `EPERM` happens if (1) "the calling process is already a process group leader" **or** (2) "the process group ID of a process other than the calling process matches the process ID of the calling process". Your explanation makes sense for (1), but (2) still looks redundant/impossible. What am I missing? – Nemo Jul 21 '13 at 19:14
5

Nemo is correct that POSIX guarantees that fork() will not re-use an existing PGID as a PID; however, there is more to the story.

Process groups, and process group leaders, can also be changed using setpgid(). The following example code causes the existence of a process group equal to the PID of the current process, which the current process is not in:

#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

int main()
{
    pid_t pgrp_orig;
    pid_t child;
    int status;

    /* Fork so that we are not a process group leader */
    if (fork()) {
        /* Grandparent process */
        wait(&status);
        return 0;
    }

    /* Record our original process group, then start a new one */
    pgrp_orig = getpgrp();
    if (setpgid(0, 0))
        perror("setpgid");

    child = fork();

    if (!child) {
        /* Child process */
        pause();
        return 0;
    }

    /* Switch back to original process group.  Child remains in the new one */
    if (setpgid(0, pgrp_orig))
        perror("setpgid");

    printf("Parent pid=%ld, pgid=%ld\n", (long)getpid(), (long)getpgrp());
    printf("Child pid=%ld, pgid=%ld\n", (long)child, (long)getpgid(child));

    /* Wake child up to finish up */
    kill(child, SIGUSR1);
    wait(&status);

    return 0;
}

Note that if the parent process tries to call setsid() here before the child exits, the failure condition you asked about will be triggered.

However, due to the restrictions on the allowable transitions that setpgid() can cause, this can't cause the kind of random failures you're worried about. The breakage is confined to a single session.

caf
  • 233,326
  • 40
  • 323
  • 462
  • In particular, `setsid` cannot fail for this reason if called immediately after `fork` in the child process, right? – R.. GitHub STOP HELPING ICE Jul 22 '11 at 04:45
  • 1
    @R.: That *is* possible, but only if the parent process mucks about to deliberately cause it (after the fork, the parent uses `setpgid()` to put the child into its own process group, then uses `setpgid()` to put another process into that process group too, then uses `setpgid()` a third time to put the child back into its original process group). Obviously it's racy too. – caf Jul 22 '11 at 05:07