I wrote a program. It starts a process (call it A) that spawns a child process (call it D) that shutdowns and restarts A. Problem is, now I can't kill A nicely from the terminal (ie. CTRL-C isn't getting to it). The pgid's of A and D are the same, but it looks like the terminal drops that process group as its foreground, which is why I can't send it signals now. I suspect this happens when the parent process originally dies. Is there some way to prevent that? Can I change the foreground pgid so it looks like the child (D) is actually the parent and the terminal doesn't drop the process group?
Parent restarted by child doesn’t respond to CTRL-C
processprocess-groupssignalsterminal
Related Solutions
When a process is forked, it inherits its PGID from its parent. The PGID changes when a process becomes a process group leader, then its PGID is copied from its PID. From then on, the new child processes it spawns, and their descendants, inherit that PGID (unless they start new process groups of their own).
In a shell with job control, such as most interactive shells, each job is put in its own process group. If you run a shell script, the shell process running the script will be the group leader, and the PGID will equal its PID.
In a shell without job control, such as most shells used to run scripts, commands are run in the shell's process group.
The syntax kill -- -N
kills all the processes in the group with PGID = N. You can't use it with an arbitrary PID, only the PID of a process group leader, since that's the PGID. This is essentially how the shell's
kill %jobid
syntax works -- it internally translates %jobid
to the PGID of the job and sends the signal to that PGID.
There's no simple way to run a script in its own process group from another shell script. See How to set process group of a shell script for some suggestions, though.
When I saw this question, I was pretty interested because I know I've seen getppid used before..but I couldn't remember where. So, I turned to one of the projects that I figured has probably used every Linux syscall and then some: systemd. One GitHub search later, and I found two uses that portray some more general use cases (there are a few other uses as well, but they're more specific to systemd):
In sd-notify. For some context: systemd needs to know when a service has started so it can proceed to start any that depend on it. This is normally done from a C program via the sd_notify API, which is a way for daemons to tell systemd their status.
Of course, if you're using a shell script as a service...calling C functions isn't exactly doable. Therefore, systemd comes with the systemd-notify command, which is a small wrapper over the sd_notify API. One problem: systemd also needs to know the PID that is sending the message. For systemd-notify, this would be its own PID, which would be a short-lived process ID that immediately goes away. Not useful.
You probably already know where I'm headed: getppid is used by systemd-notify to grab the parent process's PID, since that's usually the actual service process. In short, getppid can be used by a short-lived CLI application to send a message on behalf of the parent process.
Once I found this, another unix tool that might use getppid like this came to mind: polkit, which is a process authentication framework used to gate stuff like sending D-Bus messages or running privileged applications. (At minimum, I'd guess you've seen the GUI password prompts that are displayed by polkit's auth agents.) polkit includes an executable named
pkexec
that can be used a bit like sudo, except now polkit is used for authorization. Now, polkit needs to know the PID of the process asking for authorization...yeah you get the idea, pkexec uses getppid to find that.(While looking at that, I also found out that polkit's TTY auth agent uses it too.)
This one's a bit less interesting but still notable: getppid is used to emulate PR_SET_PDEATHSIG if the parent had died by the time that flag was set. (The flag is just a way for a child to be automatically sent a signal like SIGKILL if the parent dies.)
Best Answer
A simple enough solution:
Have process A exec a second process A first (call it A'). Then let A block forever. A' can start D, and D can restart A', and A sticks around the whole time as the parent.