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 |