1
0

posix_spawn.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #define _GNU_SOURCE
  2. #include <spawn.h>
  3. #include <sched.h>
  4. #include <unistd.h>
  5. #include <signal.h>
  6. #include <fcntl.h>
  7. #include <sys/wait.h>
  8. #include "syscall.h"
  9. #include "pthread_impl.h"
  10. #include "fdop.h"
  11. #include "libc.h"
  12. struct args {
  13. int p[2];
  14. sigset_t oldmask;
  15. const char *path;
  16. int (*exec)(const char *, char *const *, char *const *);
  17. const posix_spawn_file_actions_t *fa;
  18. const posix_spawnattr_t *restrict attr;
  19. char *const *argv, *const *envp;
  20. };
  21. static int child(void *args_vp)
  22. {
  23. int i, ret;
  24. struct sigaction sa;
  25. struct args *args = args_vp;
  26. int p = args->p[1];
  27. const posix_spawn_file_actions_t *fa = args->fa;
  28. const posix_spawnattr_t *restrict attr = args->attr;
  29. close(args->p[0]);
  30. /* All signal dispositions must be either SIG_DFL or SIG_IGN
  31. * before signals are unblocked. Otherwise a signal handler
  32. * from the parent might get run in the child while sharing
  33. * memory, with unpredictable and dangerous results. */
  34. for (i=1; i<_NSIG; i++) {
  35. __libc_sigaction(i, 0, &sa);
  36. if (sa.sa_handler!=SIG_DFL && (sa.sa_handler!=SIG_IGN ||
  37. ((attr->__flags & POSIX_SPAWN_SETSIGDEF)
  38. && sigismember(&attr->__def, i) ))) {
  39. sa.sa_handler = SIG_DFL;
  40. __libc_sigaction(i, &sa, 0);
  41. }
  42. }
  43. if (attr->__flags & POSIX_SPAWN_SETPGROUP)
  44. if ((ret=__syscall(SYS_setpgid, 0, attr->__pgrp)))
  45. goto fail;
  46. /* Use syscalls directly because pthread state because the
  47. * library functions attempt to do a multi-threaded synchronized
  48. * id-change, which would trash the parent's state. */
  49. if (attr->__flags & POSIX_SPAWN_RESETIDS)
  50. if ((ret=__syscall(SYS_setgid, __syscall(SYS_getgid))) ||
  51. (ret=__syscall(SYS_setuid, __syscall(SYS_getuid))) )
  52. goto fail;
  53. if (fa && fa->__actions) {
  54. struct fdop *op;
  55. int fd;
  56. for (op = fa->__actions; op->next; op = op->next);
  57. for (; op; op = op->prev) {
  58. /* It's possible that a file operation would clobber
  59. * the pipe fd used for synchronizing with the
  60. * parent. To avoid that, we dup the pipe onto
  61. * an unoccupied fd. */
  62. if (op->fd == p) {
  63. ret = __syscall(SYS_dup, p);
  64. if (ret < 0) goto fail;
  65. __syscall(SYS_close, p);
  66. p = ret;
  67. }
  68. switch(op->cmd) {
  69. case FDOP_CLOSE:
  70. if ((ret=__syscall(SYS_close, op->fd)))
  71. goto fail;
  72. break;
  73. case FDOP_DUP2:
  74. if ((ret=__syscall(SYS_dup2, op->srcfd, op->fd))<0)
  75. goto fail;
  76. break;
  77. case FDOP_OPEN:
  78. fd = __syscall(SYS_open, op->path,
  79. op->oflag | O_LARGEFILE, op->mode);
  80. if ((ret=fd) < 0) goto fail;
  81. if (fd != op->fd) {
  82. if ((ret=__syscall(SYS_dup2, fd, op->fd))<0)
  83. goto fail;
  84. __syscall(SYS_close, fd);
  85. }
  86. break;
  87. }
  88. }
  89. }
  90. /* Close-on-exec flag may have been lost if we moved the pipe
  91. * to a different fd. We don't use F_DUPFD_CLOEXEC above because
  92. * it would fail on older kernels and atomicity is not needed --
  93. * in this process there are no threads or signal handlers. */
  94. __syscall(SYS_fcntl, p, F_SETFD, FD_CLOEXEC);
  95. pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
  96. ? &attr->__mask : &args->oldmask, 0);
  97. args->exec(args->path, args->argv, args->envp);
  98. fail:
  99. /* Since sizeof errno < PIPE_BUF, the write is atomic. */
  100. ret = -ret;
  101. if (ret) while (write(p, &ret, sizeof ret) < 0);
  102. _exit(127);
  103. }
  104. int __posix_spawnx(pid_t *restrict res, const char *restrict path,
  105. int (*exec)(const char *, char *const *, char *const *),
  106. const posix_spawn_file_actions_t *fa,
  107. const posix_spawnattr_t *restrict attr,
  108. char *const argv[restrict], char *const envp[restrict])
  109. {
  110. pid_t pid;
  111. char stack[1024];
  112. int ec=0, cs;
  113. struct args args;
  114. if (pipe2(args.p, O_CLOEXEC))
  115. return errno;
  116. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
  117. args.path = path;
  118. args.exec = exec;
  119. args.fa = fa;
  120. args.attr = attr ? attr : &(const posix_spawnattr_t){0};
  121. args.argv = argv;
  122. args.envp = envp;
  123. pthread_sigmask(SIG_BLOCK, SIGALL_SET, &args.oldmask);
  124. pid = __clone(child, stack+sizeof stack, CLONE_VM|SIGCHLD, &args);
  125. close(args.p[1]);
  126. if (pid > 0) {
  127. if (read(args.p[0], &ec, sizeof ec) != sizeof ec) ec = 0;
  128. else waitpid(pid, &(int){0}, 0);
  129. } else {
  130. ec = -pid;
  131. }
  132. close(args.p[0]);
  133. if (!ec) *res = pid;
  134. pthread_sigmask(SIG_SETMASK, &args.oldmask, 0);
  135. pthread_setcancelstate(cs, 0);
  136. return ec;
  137. }
  138. int posix_spawn(pid_t *restrict res, const char *restrict path,
  139. const posix_spawn_file_actions_t *fa,
  140. const posix_spawnattr_t *restrict attr,
  141. char *const argv[restrict], char *const envp[restrict])
  142. {
  143. return __posix_spawnx(res, path, execve, fa, attr, argv, envp);
  144. }