1 /* $OpenBSD: posix_spawn.c,v 1.10 2019/06/28 13:32:41 deraadt Exp $ */
2 /*-
3 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/queue.h>
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sched.h>
33 #include <spawn.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 struct __posix_spawnattr {
40 short sa_flags;
41 pid_t sa_pgroup;
42 struct sched_param sa_schedparam;
43 int sa_schedpolicy;
44 sigset_t sa_sigdefault;
45 sigset_t sa_sigmask;
46 };
47
48 struct __posix_spawn_file_actions {
49 SIMPLEQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
50 };
51
52 typedef struct __posix_spawn_file_actions_entry {
53 SIMPLEQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
54 enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action;
55
56 int fae_fildes;
57 union {
58 struct {
59 char *path;
60 #define fae_path fae_data.open.path
61 int oflag;
62 #define fae_oflag fae_data.open.oflag
63 mode_t mode;
64 #define fae_mode fae_data.open.mode
65 } open;
66 struct {
67 int newfildes;
68 #define fae_newfildes fae_data.dup2.newfildes
69 } dup2;
70 } fae_data;
71 } posix_spawn_file_actions_entry_t;
72
73 /*
74 * Spawn routines
75 */
76
77 static int
process_spawnattr(const posix_spawnattr_t sa)78 process_spawnattr(const posix_spawnattr_t sa)
79 {
80 struct sigaction sigact;
81 int i;
82
83 memset(&sigact, 0, sizeof(sigact));
84 sigact.sa_handler = SIG_DFL;
85
86 /*
87 * POSIX doesn't really describe in which order everything
88 * should be set. We'll just set them in the order in which they
89 * are mentioned.
90 */
91
92 /* Set process group */
93 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
94 if (setpgid(0, sa->sa_pgroup) != 0)
95 return (errno);
96 }
97
98 #if 0 /* NOT IMPLEMENTED */
99 /* Set scheduler policy */
100 if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
101 if (sched_setscheduler(0, sa->sa_schedpolicy,
102 &sa->sa_schedparam) != 0)
103 return (errno);
104 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
105 if (sched_setparam(0, &sa->sa_schedparam) != 0)
106 return (errno);
107 }
108 #endif
109
110 /* Reset user ID's */
111 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
112 if (setegid(getgid()) != 0)
113 return (errno);
114 if (seteuid(getuid()) != 0)
115 return (errno);
116 }
117
118 /* Set signal masks/defaults */
119 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
120 WRAP(sigprocmask)(SIG_SETMASK, &sa->sa_sigmask, NULL);
121 }
122
123 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
124 for (i = 1; i < _NSIG; i++) {
125 /* silently ignore attempts to alter SIGTHR */
126 if (sigismember(&sa->sa_sigdefault, i) && i != SIGTHR)
127 if (sigaction(i, &sigact, NULL) != 0)
128 return (errno);
129 }
130 }
131
132 return (0);
133 }
134
135 static int
process_file_actions_entry(posix_spawn_file_actions_entry_t * fae)136 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
137 {
138 int fd;
139
140 switch (fae->fae_action) {
141 case FAE_OPEN:
142 /* Perform an open(), make it use the right fd */
143 fd = open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
144 if (fd == -1)
145 return (errno);
146 if (fd != fae->fae_fildes) {
147 if (dup2(fd, fae->fae_fildes) == -1)
148 return (errno);
149 if (close(fd) != 0) {
150 if (errno == EBADF)
151 return (EBADF);
152 }
153 }
154 break;
155 case FAE_DUP2:
156 /*
157 * Perform a dup2(), making sure the FD_CLOEXEC flag is clear
158 */
159 fd = fae->fae_fildes;
160 if (fd == fae->fae_newfildes) {
161 int flags = fcntl(fd, F_GETFD);
162
163 if (flags == -1 ||
164 ((flags & FD_CLOEXEC) &&
165 fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) == -1))
166 return (errno);
167 } else if (dup2(fd, fae->fae_newfildes) == -1)
168 return (errno);
169 break;
170 case FAE_CLOSE:
171 /* Perform a close(), do not fail if already closed */
172 (void)close(fae->fae_fildes);
173 break;
174 }
175 return (0);
176 }
177
178 static int
process_file_actions(const posix_spawn_file_actions_t fa)179 process_file_actions(const posix_spawn_file_actions_t fa)
180 {
181 posix_spawn_file_actions_entry_t *fae;
182 int error;
183
184 /* Replay all file descriptor modifications */
185 SIMPLEQ_FOREACH(fae, &fa->fa_list, fae_list) {
186 error = process_file_actions_entry(fae);
187 if (error)
188 return (error);
189 }
190 return (0);
191 }
192
193 static int
do_posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * sa,char * const argv[],char * const envp[],int use_env_path)194 do_posix_spawn(pid_t *pid, const char *path,
195 const posix_spawn_file_actions_t *fa,
196 const posix_spawnattr_t *sa,
197 char *const argv[], char *const envp[], int use_env_path)
198 {
199 pid_t p;
200
201 p = fork();
202 switch (p) {
203 case -1:
204 return (errno);
205 case 0: {
206 int error;
207 if (sa != NULL) {
208 error = process_spawnattr(*sa);
209 if (error)
210 _exit(127);
211 }
212 if (fa != NULL) {
213 error = process_file_actions(*fa);
214 if (error)
215 _exit(127);
216 }
217 if (use_env_path)
218 execvpe(path, argv, envp != NULL ? envp : environ);
219 else
220 execve(path, argv, envp != NULL ? envp : environ);
221 _exit(127);
222 }
223 default:
224 if (pid != NULL)
225 *pid = p;
226 return (0);
227 }
228 }
229
230 int
posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * sa,char * const argv[],char * const envp[])231 posix_spawn(pid_t *pid, const char *path,
232 const posix_spawn_file_actions_t *fa,
233 const posix_spawnattr_t *sa,
234 char *const argv[], char *const envp[])
235 {
236 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0);
237 }
238
239 int
posix_spawnp(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * sa,char * const argv[],char * const envp[])240 posix_spawnp(pid_t *pid, const char *path,
241 const posix_spawn_file_actions_t *fa,
242 const posix_spawnattr_t *sa,
243 char *const argv[], char *const envp[])
244 {
245 return do_posix_spawn(pid, path, fa, sa, argv, envp, 1);
246 }
247
248 /*
249 * File descriptor actions
250 */
251
252 int
posix_spawn_file_actions_init(posix_spawn_file_actions_t * ret)253 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
254 {
255 posix_spawn_file_actions_t fa;
256
257 fa = malloc(sizeof(struct __posix_spawn_file_actions));
258 if (fa == NULL)
259 return (errno);
260
261 SIMPLEQ_INIT(&fa->fa_list);
262 *ret = fa;
263 return (0);
264 }
265
266 int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t * fa)267 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
268 {
269 posix_spawn_file_actions_entry_t *fae;
270
271 while ((fae = SIMPLEQ_FIRST(&(*fa)->fa_list)) != NULL) {
272 /* Remove file action entry from the queue */
273 SIMPLEQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
274
275 /* Deallocate file action entry */
276 if (fae->fae_action == FAE_OPEN)
277 free(fae->fae_path);
278 free(fae);
279 }
280
281 free(*fa);
282 return (0);
283 }
284
285 int
posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,int fildes,const char * __restrict path,int oflag,mode_t mode)286 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict fa,
287 int fildes, const char *__restrict path, int oflag, mode_t mode)
288 {
289 posix_spawn_file_actions_entry_t *fae;
290 int error;
291
292 if (fildes < 0)
293 return (EBADF);
294
295 /* Allocate object */
296 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
297 if (fae == NULL)
298 return (errno);
299
300 /* Set values and store in queue */
301 fae->fae_action = FAE_OPEN;
302 fae->fae_path = strdup(path);
303 if (fae->fae_path == NULL) {
304 error = errno;
305 free(fae);
306 return (error);
307 }
308 fae->fae_fildes = fildes;
309 fae->fae_oflag = oflag;
310 fae->fae_mode = mode;
311
312 SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
313 return (0);
314 }
315
316 int
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t * fa,int fildes,int newfildes)317 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
318 int fildes, int newfildes)
319 {
320 posix_spawn_file_actions_entry_t *fae;
321
322 if (fildes < 0 || newfildes < 0)
323 return (EBADF);
324
325 /* Allocate object */
326 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
327 if (fae == NULL)
328 return (errno);
329
330 /* Set values and store in queue */
331 fae->fae_action = FAE_DUP2;
332 fae->fae_fildes = fildes;
333 fae->fae_newfildes = newfildes;
334
335 SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
336 return (0);
337 }
338
339 int
posix_spawn_file_actions_addclose(posix_spawn_file_actions_t * fa,int fildes)340 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
341 int fildes)
342 {
343 posix_spawn_file_actions_entry_t *fae;
344
345 if (fildes < 0)
346 return (EBADF);
347
348 /* Allocate object */
349 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
350 if (fae == NULL)
351 return (errno);
352
353 /* Set values and store in queue */
354 fae->fae_action = FAE_CLOSE;
355 fae->fae_fildes = fildes;
356
357 SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
358 return (0);
359 }
360
361 /*
362 * Spawn attributes
363 */
364
365 int
posix_spawnattr_init(posix_spawnattr_t * ret)366 posix_spawnattr_init(posix_spawnattr_t *ret)
367 {
368 posix_spawnattr_t sa;
369
370 sa = calloc(1, sizeof(struct __posix_spawnattr));
371 if (sa == NULL)
372 return (errno);
373
374 /* Set defaults as specified by POSIX, cleared above */
375 *ret = sa;
376 return (0);
377 }
378
379 int
posix_spawnattr_destroy(posix_spawnattr_t * sa)380 posix_spawnattr_destroy(posix_spawnattr_t *sa)
381 {
382 free(*sa);
383 return (0);
384 }
385
386 int
posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,short * __restrict flags)387 posix_spawnattr_getflags(const posix_spawnattr_t *__restrict sa,
388 short *__restrict flags)
389 {
390 *flags = (*sa)->sa_flags;
391 return (0);
392 }
393
394 int
posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,pid_t * __restrict pgroup)395 posix_spawnattr_getpgroup(const posix_spawnattr_t *__restrict sa,
396 pid_t *__restrict pgroup)
397 {
398 *pgroup = (*sa)->sa_pgroup;
399 return (0);
400 }
401
402 int
posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,struct sched_param * __restrict schedparam)403 posix_spawnattr_getschedparam(const posix_spawnattr_t *__restrict sa,
404 struct sched_param *__restrict schedparam)
405 {
406 *schedparam = (*sa)->sa_schedparam;
407 return (0);
408 }
409
410 int
posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,int * __restrict schedpolicy)411 posix_spawnattr_getschedpolicy(const posix_spawnattr_t *__restrict sa,
412 int *__restrict schedpolicy)
413 {
414 *schedpolicy = (*sa)->sa_schedpolicy;
415 return (0);
416 }
417
418 int
posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,sigset_t * __restrict sigdefault)419 posix_spawnattr_getsigdefault(const posix_spawnattr_t *__restrict sa,
420 sigset_t *__restrict sigdefault)
421 {
422 *sigdefault = (*sa)->sa_sigdefault;
423 return (0);
424 }
425
426 int
posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,sigset_t * __restrict sigmask)427 posix_spawnattr_getsigmask(const posix_spawnattr_t *__restrict sa,
428 sigset_t *__restrict sigmask)
429 {
430 *sigmask = (*sa)->sa_sigmask;
431 return (0);
432 }
433
434 int
posix_spawnattr_setflags(posix_spawnattr_t * sa,short flags)435 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
436 {
437 (*sa)->sa_flags = flags;
438 return (0);
439 }
440
441 int
posix_spawnattr_setpgroup(posix_spawnattr_t * sa,pid_t pgroup)442 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
443 {
444 (*sa)->sa_pgroup = pgroup;
445 return (0);
446 }
447
448 int
posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,const struct sched_param * __restrict schedparam)449 posix_spawnattr_setschedparam(posix_spawnattr_t *__restrict sa,
450 const struct sched_param *__restrict schedparam)
451 {
452 (*sa)->sa_schedparam = *schedparam;
453 return (0);
454 }
455
456 int
posix_spawnattr_setschedpolicy(posix_spawnattr_t * sa,int schedpolicy)457 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
458 {
459 (*sa)->sa_schedpolicy = schedpolicy;
460 return (0);
461 }
462
463 int
posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,const sigset_t * __restrict sigdefault)464 posix_spawnattr_setsigdefault(posix_spawnattr_t *__restrict sa,
465 const sigset_t *__restrict sigdefault)
466 {
467 (*sa)->sa_sigdefault = *sigdefault;
468 return (0);
469 }
470
471 int
posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,const sigset_t * __restrict sigmask)472 posix_spawnattr_setsigmask(posix_spawnattr_t *__restrict sa,
473 const sigset_t *__restrict sigmask)
474 {
475 (*sa)->sa_sigmask = *sigmask;
476 return (0);
477 }
478