diff options
Diffstat (limited to 'src/libstddjb/cspawn_workaround.c')
-rw-r--r-- | src/libstddjb/cspawn_workaround.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/src/libstddjb/cspawn_workaround.c b/src/libstddjb/cspawn_workaround.c new file mode 100644 index 0000000..da9bd89 --- /dev/null +++ b/src/libstddjb/cspawn_workaround.c @@ -0,0 +1,55 @@ +/* ISC license. */ + +#include <skalibs/sysdeps.h> +#include <skalibs/djbunix.h> + +#if defined(SKALIBS_HASPOSIXSPAWN) && defined(SKALIBS_HASPOSIXSPAWNEARLYRETURN) + +#include <errno.h> +#include <signal.h> +#include <sys/wait.h> + +#include <skalibs/allreadwrite.h> + + /* when posix_spawn returns too early, you need this */ + +pid_t cspawn_workaround (pid_t pid, int const *p) +{ + siginfo_t si ; + int e ; + ssize_t r ; + char c ; + + fd_close(p[1]) ; + r = fd_read(p[0], &c, 1) ; + fd_close(p[0]) ; + if (r == -1) return 0 ; + if (r) return (errno = EILSEQ, 0) ; /* child wrote, wtf */ + + do e = waitid(P_PID, pid, &si, WEXITED | WNOHANG | WNOWAIT) ; + while (e == -1 && errno == EINTR) ; + if (e == -1) return pid ; /* we're in trouble, but don't leak a child */ + if (!si.si_pid) return pid ; /* child is running */ + if (si.si_code != CLD_EXITED || si.si_status != 127) return pid ; /* child died after execve(), let caller handle it */ + /* + child exited 127, so either execve() failed, which is what we want to catch, + or it raced like a mofo, execve()d and then exited 127 on its own, in which + case, tough luck, it never existed. + */ + wait_pid(pid, 0) ; + return (errno = 0, 0) ; +} + +#else + +pid_t cspawn_workaround (pid_t pid, int const *p) +{ + if (p) + { + fd_close(p[1]) ; + fd_close(p[0]) ; + } + return pid ; +} + +#endif |