blob: 7015ad923e2c997baa70df49ae3a914700071f2a [file] [log] [blame]
Elliott Hughes14e3ff92017-10-06 16:58:36 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <spawn.h>
30
31#include <fcntl.h>
32#include <signal.h>
33#include <stdlib.h>
34#include <unistd.h>
35
36#include "private/ScopedSignalBlocker.h"
37
38enum Action {
39 kOpen,
40 kClose,
41 kDup2
42};
43
44struct __posix_spawn_file_action {
45 __posix_spawn_file_action* next;
46
47 Action what;
48 int fd;
49 int new_fd;
50 char* path;
51 int flags;
52 mode_t mode;
53
54 void Do() {
55 if (what == kOpen) {
56 fd = open(path, flags, mode);
57 if (fd == -1) _exit(127);
58 // If it didn't land where we wanted it, move it.
59 if (fd != new_fd) {
60 if (dup2(fd, new_fd) == -1) _exit(127);
61 close(fd);
62 }
63 } else if (what == kClose) {
64 // Failure to close is ignored.
65 close(fd);
66 } else {
67 if (dup2(fd, new_fd) == -1) _exit(127);
68 }
69 }
70};
71
72struct __posix_spawn_file_actions {
73 __posix_spawn_file_action* head;
74 __posix_spawn_file_action* last;
75
76 void Do() {
77 for (__posix_spawn_file_action* action = head; action != nullptr; action = action->next) {
78 action->Do();
79 }
80 }
81};
82
83struct __posix_spawnattr {
84 short flags;
85 pid_t pgroup;
86 sched_param schedparam;
87 int schedpolicy;
88 sigset_t sigmask;
89 sigset_t sigdefault;
90
91 void Do() {
92 bool use_sigdefault = ((flags & POSIX_SPAWN_SETSIGDEF) != 0);
93
94 for (int s = 1; s < _NSIG; ++s) {
95 struct sigaction sa;
96 if (sigaction(s, nullptr, &sa) == -1) _exit(127);
97 if (sa.sa_handler == SIG_DFL) continue;
98 // POSIX: "Signals set to be caught by the calling process shall be set to the default
99 // action in the child process."
100 // POSIX: "If POSIX_SPAWN_SETSIGDEF is set ... signals in sigdefault ... shall be set to
101 // their default actions in the child process."
102 if (sa.sa_handler != SIG_IGN || (use_sigdefault && sigismember(&sigdefault, s))) {
103 sa.sa_handler = SIG_DFL;
104 if (sigaction(s, &sa, nullptr) == -1) _exit(127);
105 }
106 }
107
108 if ((flags & POSIX_SPAWN_SETPGROUP) != 0 && setpgid(0, pgroup) == -1) _exit(127);
109 if ((flags & POSIX_SPAWN_SETSID) != 0 && setsid() == -1) _exit(127);
110
111 // POSIX_SPAWN_SETSCHEDULER overrides POSIX_SPAWN_SETSCHEDPARAM, but it is not an error
112 // to set both.
113 if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) {
114 if (sched_setscheduler(0, schedpolicy, &schedparam) == -1) _exit(127);
115 } else if ((flags & POSIX_SPAWN_SETSCHEDPARAM) != 0) {
116 if (sched_setparam(0, &schedparam) == -1) _exit(127);
117 }
118
119 if ((flags & POSIX_SPAWN_RESETIDS) != 0) {
120 if (seteuid(getuid()) == -1 || setegid(getgid()) == -1) _exit(127);
121 }
122
123 if ((flags & POSIX_SPAWN_SETSIGMASK) != 0) {
124 if (sigprocmask(SIG_SETMASK, &sigmask, nullptr)) _exit(127);
125 }
126 }
127};
128
129static int posix_spawn(pid_t* pid_ptr,
130 const char* path,
131 const posix_spawn_file_actions_t* actions,
132 const posix_spawnattr_t* attr,
133 char* const argv[],
134 char* const env[],
135 int exec_fn(const char* path, char* const argv[], char* const env[])) {
136 // See http://man7.org/linux/man-pages/man3/posix_spawn.3.html
137 // and http://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html
138
139 ScopedSignalBlocker ssb;
140
141 short flags = attr ? (*attr)->flags : 0;
142 bool use_vfork = ((flags & POSIX_SPAWN_USEVFORK) != 0) || (actions == nullptr && flags == 0);
143
144 pid_t pid = use_vfork ? vfork() : fork();
145 if (pid == -1) return errno;
146
147 if (pid == 0) {
148 // Child.
149 if (attr) (*attr)->Do();
150 if (actions) (*actions)->Do();
151 if ((flags & POSIX_SPAWN_SETSIGMASK) == 0) ssb.reset();
152 exec_fn(path, argv, env ? env : environ);
153 _exit(127);
154 }
155
156 // Parent.
157 if (pid_ptr) *pid_ptr = pid;
158 return 0;
159}
160
161int posix_spawn(pid_t* pid, const char* path, const posix_spawn_file_actions_t* actions,
162 const posix_spawnattr_t* attr, char* const argv[], char* const env[]) {
163 return posix_spawn(pid, path, actions, attr, argv, env, execve);
164}
165
166int posix_spawnp(pid_t* pid, const char* file, const posix_spawn_file_actions_t* actions,
167 const posix_spawnattr_t* attr, char* const argv[], char* const env[]) {
168 return posix_spawn(pid, file, actions, attr, argv, env, execvpe);
169}
170
171int posix_spawnattr_init(posix_spawnattr_t* attr) {
172 *attr = reinterpret_cast<__posix_spawnattr*>(calloc(1, sizeof(__posix_spawnattr)));
173 return (*attr == nullptr) ? errno : 0;
174}
175
176int posix_spawnattr_destroy(posix_spawnattr_t* attr) {
177 free(*attr);
178 *attr = nullptr;
179 return 0;
180}
181
182int posix_spawnattr_setflags(posix_spawnattr_t* attr, short flags) {
183 if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF |
184 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER |
185 POSIX_SPAWN_USEVFORK | POSIX_SPAWN_SETSID)) != 0) {
186 return EINVAL;
187 }
188 (*attr)->flags = flags;
189 return 0;
190}
191
192int posix_spawnattr_getflags(const posix_spawnattr_t* attr, short* flags) {
193 *flags = (*attr)->flags;
194 return 0;
195}
196
197int posix_spawnattr_setpgroup(posix_spawnattr_t* attr, pid_t pgroup) {
198 (*attr)->pgroup = pgroup;
199 return 0;
200}
201
202int posix_spawnattr_getpgroup(const posix_spawnattr_t* attr, pid_t* pgroup) {
203 *pgroup = (*attr)->pgroup;
204 return 0;
205}
206
207int posix_spawnattr_setsigmask(posix_spawnattr_t* attr, const sigset_t* mask) {
208 (*attr)->sigmask = *mask;
209 return 0;
210}
211
212int posix_spawnattr_getsigmask(const posix_spawnattr_t* attr, sigset_t* mask) {
213 *mask = (*attr)->sigmask;
214 return 0;
215}
216
217int posix_spawnattr_setsigdefault(posix_spawnattr_t* attr, const sigset_t* mask) {
218 (*attr)->sigdefault = *mask;
219 return 0;
220}
221
222int posix_spawnattr_getsigdefault(const posix_spawnattr_t* attr, sigset_t* mask) {
223 *mask = (*attr)->sigdefault;
224 return 0;
225}
226
227int posix_spawnattr_setschedparam(posix_spawnattr_t* attr, const struct sched_param* param) {
228 (*attr)->schedparam = *param;
229 return 0;
230}
231
232int posix_spawnattr_getschedparam(const posix_spawnattr_t* attr, struct sched_param* param) {
233 *param = (*attr)->schedparam;
234 return 0;
235}
236
237int posix_spawnattr_setschedpolicy(posix_spawnattr_t* attr, int policy) {
238 (*attr)->schedpolicy = policy;
239 return 0;
240}
241
242int posix_spawnattr_getschedpolicy(const posix_spawnattr_t* attr, int* policy) {
243 *policy = (*attr)->schedpolicy;
244 return 0;
245}
246
247int posix_spawn_file_actions_init(posix_spawn_file_actions_t* actions) {
248 *actions = reinterpret_cast<__posix_spawn_file_actions*>(calloc(1, sizeof(**actions)));
249 return (*actions == nullptr) ? errno : 0;
250}
251
252int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* actions) {
253 __posix_spawn_file_action* a = (*actions)->head;
254 while (a) {
255 __posix_spawn_file_action* last = a;
256 a = a->next;
257 free(last->path);
258 free(last);
259 }
260 free(*actions);
261 *actions = nullptr;
262 return 0;
263}
264
265static int posix_spawn_add_file_action(posix_spawn_file_actions_t* actions,
266 Action what,
267 int fd,
268 int new_fd,
269 const char* path,
270 int flags,
271 mode_t mode) {
272 __posix_spawn_file_action* action =
273 reinterpret_cast<__posix_spawn_file_action*>(malloc(sizeof(*action)));
274 if (action == nullptr) return errno;
275
276 action->next = nullptr;
277 if (path != nullptr) {
278 action->path = strdup(path);
279 if (action->path == nullptr) {
280 free(action);
281 return errno;
282 }
283 } else {
284 action->path = nullptr;
285 }
286 action->what = what;
287 action->fd = fd;
288 action->new_fd = new_fd;
289 action->flags = flags;
290 action->mode = mode;
291
292 if ((*actions)->head == nullptr) {
293 (*actions)->head = (*actions)->last = action;
294 } else {
295 (*actions)->last->next = action;
296 (*actions)->last = action;
297 }
298
299 return 0;
300}
301
302int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t* actions,
303 int fd, const char* path, int flags, mode_t mode) {
304 if (fd < 0) return EBADF;
305 return posix_spawn_add_file_action(actions, kOpen, -1, fd, path, flags, mode);
306}
307
308int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t* actions, int fd) {
309 if (fd < 0) return EBADF;
310 return posix_spawn_add_file_action(actions, kClose, fd, -1, nullptr, 0, 0);
311}
312
313int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t* actions, int fd, int new_fd) {
314 if (fd < 0 || new_fd < 0) return EBADF;
315 return posix_spawn_add_file_action(actions, kDup2, fd, new_fd, nullptr, 0, 0);
316}