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
#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]