posix_spawn.c 5.0 KB

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