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.

Reply via email to