Almost every Unix programmer’s done it:
int kid;
if ((kid = fork()) < 0) fail();
if (!kid) {
if (dup2(x, 0) < 0 ||
dup2(y, 1) < 0 ||
dup2(z, 2) < 0)
fail();
close(p); close(q); close(r);
execlp("/bin/foo", "foo", /* args */, (char *)0);
fail();
}
/* and so on... */
And it’s wrong. What if y is zero? You’ve clobbered it too early!
This is fairly easy to deal with if you know where you got these file descriptors from. If you just got them by calling pipe(2), then you’ll know that they’re in ascending order, so you can dup2(2) them in the right order and all will be well. If you’re writing a library function which is meant to do fancy I/O redirection then you’ve got a bigger problem.
I present a glorious new solution to the problem. The mdup(3) function does multiple dup operations in one go. You give it a table explaining which file descriptors you want where, and it makes the mappings happen. It will work correctly even if you ask it to map 0 to 1, 1 to 2, 2 to 3 and 3 to 0. If it fails for some reason (probably you hit the per-process fd limit) it leaves enough information lying around that you can work out what state the world is in and clean up properly.
I won’t claim that mdup always does its job in the most efficient way possible — whatever that might mean anyway (probably the smallest number of calls to dup2(2), or runtime). But it is at least correct. And even so, it’s not a completely simple algorithm.
What? You want real code? OK: from my Git repository.
Leave a comment