spawnwithpid.c

Documentation

This spawns a process with the desired PID. There are more elegant ways to do this, but this one doesn't require root (it forkbombs instead).

Why would I want to do this? Well, for reasons, I needed a predictable PID. And even though I ultimately chose a better solution, the challenge was too much fun to pass on.
The main thing I learned was that the Unix APIs are every bit as bad as the web's: Full of outdated syscalls around mostly for historical reasons while the "correct" APIs are often more complicated and unwieldy. Any race condition these syscalls could have, I triggered it during testing.

version history: gist

Code

#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <unistd.h> int forkstatus; int exitstatus = 0; void catch_sigterm () { if (forkstatus != 0) kill(0, SIGTERM); _exit(exitstatus); } int main (int argc, char *const argv[]) { if (argc < 3) { printf("usage: spawnwithpid <pid> <cmd>\n"); return 1; } int wantpid = atoi(argv[1]); if (!(0 < wantpid && wantpid < 32768)) { // ignoring pid_max printf("invalid pid %s.\n", argv[1]); return 2; } // note: if we get lucky on the first try, we actually won't succeed // too lazy to fix this int procstatus = kill(wantpid, 0); if (!(procstatus == -1 && errno == ESRCH)) { printf("pid already taken or other error. (%s)\n", argv[1]); return 3; } signal(SIGTERM, catch_sigterm); // spawn children until pid is hit, then exit // children: wait until parent exits do { forkstatus = fork(); } while (forkstatus > 0 && forkstatus != wantpid); if (forkstatus == -1) { // common problems if (errno == EAGAIN) { // observation: linux seems to stop spawning short of full exhaustion, // making this fail every second call during debugging printf("could not fork anymore, reason: out of pids (EAGAIN). Retrying usually helps.\n"); exitstatus = 5; kill(0, SIGTERM); } else { printf("could not fork for unexpected reason %d\n", errno); exitstatus = 4; kill(0, SIGTERM); } } else if (forkstatus == 0) { // children if (wantpid == getpid()) { // success printf("got pid %d!\n", wantpid); setsid(); kill(getppid(), SIGTERM); execvp(argv[2], &argv[2]); } else { // dud pause(); } } else if (forkstatus == wantpid) { // parent pause(); } return 99; }

[up]