1
0

posix_spawn.c 5.2 KB

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