summaryrefslogtreecommitdiff
path: root/src/libstddjb/cspawn_workaround.c
blob: da9bd893d311cc3bd2de15268709773f273d6dda (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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