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