The standard also later says: > > ---- > These functions may fail if: > > [EINVAL] The value specified by file_actions or attrp is invalid. > > If this error occurs after the calling process successfully returns from > the posix_spawn() or posix_spawnp() function, the child process may exit > with exit status 127. >
This indicates that if the file actions parameters passed are invalid, the child process internally can call _exit(127). > > If posix_spawn() or posix_spawnp() fail for any of the reasons that > would cause fork() or one of the exec family of functions to fail, > *an error value shall be returned as described by fork() and exec*, > This indicates failure to exec (such as ENOENT) should be captured, and returned via the return value from posix_spawn(). > respectively (or, if the error occurs after the calling process > successfully returns, the child process shall exit with exit status 127). > This indicates that if for some reason, the parent *had* to return first, the child should call _exit(127). > > The permission there to have the child process exit with exit status 127 > without successful execution of the target executable directly contradicts > the apparent requirement of the RETURN VALUES section. > Based on my reading, I don't see a contradiction. You should be passing failures from exec*() as the return from posix_spawn(), unless you're *forced* to have the parent return first (on limited systems that have no other choice, or perhaps an especially crazy scenario with the various options passed). > > The rationale for the posix_spawn interface explicitly calls out that it > was intended and desired for the interface to be implementable without > kernel changes, so we believe this is an error in the RETURN VALUES > section of the standard and do not feel this is a bug in our > implementation. > It can be implemented without kernel changes, as was done in the libraries I mentioned earlier. You just need to have the parent wait and monitor the child until it executes, and if not, return what went wrong. FreeBSD uses vfork() in the parent to accomplish this. A more elegant solution may be to use IPC. I've implemented this by having the function create a pipe which has close on exec set. After fork(), the parent blocks on reading from the pipe. If the child successfully executes, the write end of the pipe dies, and the parent reads nothing, indicating success. If the child fails to exec, have it return the errno via the pipe for the parent to use. An alternate approach which I haven't tried but should work is shared memory with a cross process mutex. But in any case, there are many solutions to this which can be done purely in C with existing functions without additional kernel support. Right now as it stands, OpenBSD's implementation is inferior in various ways to other implementations, and the function cannot be used reliably. I don't see why it needs to remain that way.
