Since you mention fork... never thought what kind of awful code this particular high-maintenance design causes?
As a first example, think about the ugly hack that vfork is. It still survives because it does perform better than fork. Luckily posix_spawn hides this ugliness and chooses fork over vfork. On the other hand, it means that popen can be _much_ more expensive than system just because one will use fork and the other will use vfork.
And one thing I realized today: you can hardly retrieve the errno of a failed exec system call in a forked process. Everything you will do is going to be racy, except possibly using ptrace on the child.
(FWIW, the most clever way I thought about it is to use a FD_CLOEXEC pipe and write the return code of exec there; if the parent cannot read anything, exec succeeded... or the forked child died of a signal after exec returned but before it wrote to a pipe... and if you wanna use waitpid, you get it wrong in case the execed process was signaled before the parent started waitpid...).