1 /*
2 * Copyright 2017-2019, Jérôme Duval, jerome.duval@gmail.com
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <spawn.h>
8
9 #include <errno.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include <libroot_private.h>
17 #include <signal_defs.h>
18 #include <syscalls.h>
19
20
21 enum action_type {
22 file_action_open,
23 file_action_close,
24 file_action_dup2,
25 file_action_chdir,
26 file_action_fchdir
27 };
28
29 struct _file_action {
30 enum action_type type;
31 int fd;
32 union {
33 struct {
34 char* path;
35 int oflag;
36 mode_t mode;
37 } open_action;
38 struct {
39 int srcfd;
40 } dup2_action;
41 struct {
42 char* path;
43 } chdir_action;
44 } action;
45 };
46
47 struct _posix_spawnattr {
48 short flags;
49 pid_t pgroup;
50 sigset_t sigdefault;
51 sigset_t sigmask;
52 };
53
54 struct _posix_spawn_file_actions {
55 int size;
56 int count;
57 _file_action *actions;
58 };
59
60
61 static int
posix_spawn_file_actions_extend(struct _posix_spawn_file_actions * actions)62 posix_spawn_file_actions_extend(struct _posix_spawn_file_actions *actions)
63 {
64 int newsize = actions->size + 4;
65 void *newactions = realloc(actions->actions,
66 newsize * sizeof(struct _file_action));
67 if (newactions == NULL)
68 return ENOMEM;
69 actions->actions = (struct _file_action*)newactions;
70 actions->size = newsize;
71 return 0;
72 }
73
74
75 int
posix_spawn_file_actions_init(posix_spawn_file_actions_t * _file_actions)76 posix_spawn_file_actions_init(posix_spawn_file_actions_t *_file_actions)
77 {
78 posix_spawn_file_actions_t actions = (posix_spawn_file_actions_t)malloc(
79 sizeof(struct _posix_spawn_file_actions));
80
81 if (actions == NULL)
82 return ENOMEM;
83
84 memset(actions, 0, sizeof(*actions));
85 *_file_actions = actions;
86
87 return 0;
88 }
89
90
91 int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t * _actions)92 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *_actions)
93 {
94 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
95
96 if (actions == NULL)
97 return EINVAL;
98
99 for (int i = 0; i < actions->count; i++) {
100 struct _file_action *action = &actions->actions[i];
101
102 if (action->type == file_action_open)
103 free(action->action.open_action.path);
104 else if (action->type == file_action_chdir)
105 free(action->action.chdir_action.path);
106 }
107
108 free(actions);
109
110 return 0;
111 }
112
113
114 int
posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * _actions,int fildes,const char * path,int oflag,mode_t mode)115 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *_actions,
116 int fildes, const char *path, int oflag, mode_t mode)
117 {
118 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
119
120 if (actions == NULL)
121 return EINVAL;
122
123 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
124 return EBADF;
125
126 char* npath = strdup(path);
127 if (npath == NULL)
128 return ENOMEM;
129 if (actions->count == actions->size
130 && posix_spawn_file_actions_extend(actions) != 0) {
131 free(npath);
132 return ENOMEM;
133 }
134
135 struct _file_action *action = &actions->actions[actions->count];
136 action->type = file_action_open;
137 action->fd = fildes;
138 action->action.open_action.path = npath;
139 action->action.open_action.oflag = oflag;
140 action->action.open_action.mode = mode;
141 actions->count++;
142 return 0;
143 }
144
145
146 int
posix_spawn_file_actions_addclose(posix_spawn_file_actions_t * _actions,int fildes)147 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *_actions,
148 int fildes)
149 {
150 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
151
152 if (actions == NULL)
153 return EINVAL;
154
155 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
156 return EBADF;
157
158 if (actions->count == actions->size
159 && posix_spawn_file_actions_extend(actions) != 0) {
160 return ENOMEM;
161 }
162
163 struct _file_action *action = &actions->actions[actions->count];
164 action->type = file_action_close;
165 action->fd = fildes;
166 actions->count++;
167 return 0;
168 }
169
170
171 int
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t * _actions,int fildes,int newfildes)172 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *_actions,
173 int fildes, int newfildes)
174 {
175 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
176
177 if (actions == NULL)
178 return EINVAL;
179
180 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
181 return EBADF;
182
183 if (actions->count == actions->size
184 && posix_spawn_file_actions_extend(actions) != 0) {
185 return ENOMEM;
186 }
187
188 struct _file_action *action = &actions->actions[actions->count];
189 action->type = file_action_dup2;
190 action->fd = newfildes;
191 action->action.dup2_action.srcfd = fildes;
192 actions->count++;
193 return 0;
194 }
195
196
197 extern "C" int
posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t * _actions,const char * path)198 posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *_actions,
199 const char *path)
200 {
201 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
202
203 if (actions == NULL)
204 return EINVAL;
205
206 char* npath = strdup(path);
207 if (npath == NULL)
208 return ENOMEM;
209 if (actions->count == actions->size
210 && posix_spawn_file_actions_extend(actions) != 0) {
211 free(npath);
212 return ENOMEM;
213 }
214
215 struct _file_action *action = &actions->actions[actions->count];
216 action->type = file_action_chdir;
217 action->fd = -1;
218 action->action.chdir_action.path = npath;
219 actions->count++;
220 return 0;
221 }
222
223
224 extern "C" int
posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t * _actions,int fildes)225 posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *_actions,
226 int fildes)
227 {
228 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
229
230 if (actions == NULL)
231 return EINVAL;
232
233 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
234 return EBADF;
235
236 if (actions->count == actions->size
237 && posix_spawn_file_actions_extend(actions) != 0) {
238 return ENOMEM;
239 }
240
241 struct _file_action *action = &actions->actions[actions->count];
242 action->type = file_action_fchdir;
243 action->fd = fildes;
244 actions->count++;
245 return 0;
246 }
247
248
249 int
posix_spawnattr_init(posix_spawnattr_t * _attr)250 posix_spawnattr_init(posix_spawnattr_t *_attr)
251 {
252 posix_spawnattr_t attr = (posix_spawnattr_t)malloc(
253 sizeof(struct _posix_spawnattr));
254
255 if (attr == NULL)
256 return ENOMEM;
257
258 memset(attr, 0, sizeof(*attr));
259 *_attr = attr;
260
261 return 0;
262 }
263
264
265 int
posix_spawnattr_destroy(posix_spawnattr_t * _attr)266 posix_spawnattr_destroy(posix_spawnattr_t *_attr)
267 {
268 struct _posix_spawnattr* attr = _attr != NULL ? *_attr : NULL;
269
270 if (attr == NULL)
271 return EINVAL;
272
273 free(attr);
274
275 return 0;
276 }
277
278
279 int
posix_spawnattr_getflags(const posix_spawnattr_t * _attr,short * flags)280 posix_spawnattr_getflags(const posix_spawnattr_t *_attr, short *flags)
281 {
282 struct _posix_spawnattr *attr;
283
284 if (_attr == NULL || (attr = *_attr) == NULL || flags == NULL)
285 return EINVAL;
286
287 *flags = attr->flags;
288
289 return 0;
290 }
291
292
293 int
posix_spawnattr_setflags(posix_spawnattr_t * _attr,short flags)294 posix_spawnattr_setflags(posix_spawnattr_t *_attr, short flags)
295 {
296 struct _posix_spawnattr *attr;
297
298 if (_attr == NULL || (attr = *_attr) == NULL)
299 return EINVAL;
300
301 if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP
302 | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK
303 | POSIX_SPAWN_SETSID)) != 0) {
304 return EINVAL;
305 }
306
307 attr->flags = flags;
308
309 return 0;
310 }
311
312
313 int
posix_spawnattr_getpgroup(const posix_spawnattr_t * _attr,pid_t * pgroup)314 posix_spawnattr_getpgroup(const posix_spawnattr_t *_attr, pid_t *pgroup)
315 {
316 struct _posix_spawnattr *attr;
317
318 if (_attr == NULL || (attr = *_attr) == NULL || pgroup == NULL)
319 return EINVAL;
320
321 *pgroup = attr->pgroup;
322
323 return 0;
324 }
325
326
327 int
posix_spawnattr_setpgroup(posix_spawnattr_t * _attr,pid_t pgroup)328 posix_spawnattr_setpgroup(posix_spawnattr_t *_attr, pid_t pgroup)
329 {
330 struct _posix_spawnattr *attr;
331
332 if (_attr == NULL || (attr = *_attr) == NULL)
333 return EINVAL;
334
335 attr->pgroup = pgroup;
336
337 return 0;
338 }
339
340
341 int
posix_spawnattr_getsigdefault(const posix_spawnattr_t * _attr,sigset_t * sigdefault)342 posix_spawnattr_getsigdefault(const posix_spawnattr_t *_attr, sigset_t *sigdefault)
343 {
344 struct _posix_spawnattr *attr;
345
346 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL)
347 return EINVAL;
348
349 memcpy(sigdefault, &attr->sigdefault, sizeof(sigset_t));
350
351 return 0;
352 }
353
354
355 int
posix_spawnattr_setsigdefault(posix_spawnattr_t * _attr,const sigset_t * sigdefault)356 posix_spawnattr_setsigdefault(posix_spawnattr_t *_attr,
357 const sigset_t *sigdefault)
358 {
359 struct _posix_spawnattr *attr;
360
361 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL)
362 return EINVAL;
363
364 memcpy(&attr->sigdefault, sigdefault, sizeof(sigset_t));
365
366 return 0;
367 }
368
369
370 int
posix_spawnattr_getsigmask(const posix_spawnattr_t * _attr,sigset_t * sigmask)371 posix_spawnattr_getsigmask(const posix_spawnattr_t *_attr, sigset_t *sigmask)
372 {
373 struct _posix_spawnattr *attr;
374
375 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL)
376 return EINVAL;
377
378 memcpy(sigmask, &attr->sigmask, sizeof(sigset_t));
379
380 return 0;
381 }
382
383
384 int
posix_spawnattr_setsigmask(posix_spawnattr_t * _attr,const sigset_t * sigmask)385 posix_spawnattr_setsigmask(posix_spawnattr_t *_attr, const sigset_t *sigmask)
386 {
387 struct _posix_spawnattr *attr;
388
389 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL)
390 return EINVAL;
391
392 memcpy(&attr->sigmask, sigmask, sizeof(sigset_t));
393
394 return 0;
395 }
396
397
398 static int
process_spawnattr(const posix_spawnattr_t * _attr)399 process_spawnattr(const posix_spawnattr_t *_attr)
400 {
401 if (_attr == NULL)
402 return 0;
403
404 struct _posix_spawnattr *attr = *_attr;
405 if (attr == NULL)
406 return EINVAL;
407
408 if ((attr->flags & POSIX_SPAWN_SETSIGMASK) != 0)
409 sigprocmask(SIG_SETMASK, &attr->sigmask, NULL);
410
411 if ((attr->flags & POSIX_SPAWN_SETSIGDEF) != 0) {
412 struct sigaction action;
413 action.sa_handler = SIG_DFL;
414 action.sa_flags = 0;
415 action.sa_userdata = NULL;
416 sigemptyset(&action.sa_mask);
417 for (int i = 1; i <= MAX_SIGNAL_NUMBER; i++) {
418 if (sigismember(&attr->sigdefault, i) == 1
419 && sigaction(i, &action, NULL) != 0) {
420 return errno;
421 }
422 }
423 }
424
425 if ((attr->flags & POSIX_SPAWN_RESETIDS) != 0) {
426 if (setegid(getgid()) != 0)
427 return errno;
428 if (seteuid(getuid()) != 0)
429 return errno;
430 }
431
432 if ((attr->flags & POSIX_SPAWN_SETSID) != 0) {
433 if (setsid() != 0)
434 return errno;
435 }
436
437 if ((attr->flags & POSIX_SPAWN_SETPGROUP) != 0) {
438 if (setpgid(0, attr->pgroup) != 0)
439 return errno;
440 }
441
442 return 0;
443 }
444
445
446 static int
process_file_actions(const posix_spawn_file_actions_t * _actions,int * errfd)447 process_file_actions(const posix_spawn_file_actions_t *_actions, int *errfd)
448 {
449 if (_actions == NULL)
450 return 0;
451
452 struct _posix_spawn_file_actions* actions = *_actions;
453 if (actions == NULL)
454 return EINVAL;
455
456 for (int i = 0; i < actions->count; i++) {
457 struct _file_action *action = &actions->actions[i];
458
459 if (action->fd == *errfd) {
460 int newfd = dup(action->fd);
461 if (newfd == -1)
462 return errno;
463 close(action->fd);
464 fcntl(newfd, F_SETFD, FD_CLOEXEC);
465 *errfd = newfd;
466 }
467
468 if (action->type == file_action_close) {
469 if (close(action->fd) != 0)
470 return errno;
471 } else if (action->type == file_action_open) {
472 int fd = open(action->action.open_action.path,
473 action->action.open_action.oflag,
474 action->action.open_action.mode);
475 if (fd == -1)
476 return errno;
477 if (fd != action->fd) {
478 if (dup2(fd, action->fd) == -1)
479 return errno;
480 if (close(fd) != 0)
481 return errno;
482 }
483 } else if (action->type == file_action_dup2) {
484 if (dup2(action->action.dup2_action.srcfd, action->fd) == -1)
485 return errno;
486 } else if (action->type == file_action_chdir) {
487 if (chdir(action->action.chdir_action.path) == -1)
488 return errno;
489 } else if (action->type == file_action_fchdir) {
490 if (fchdir(action->fd) == -1)
491 return errno;
492 }
493 }
494
495 return 0;
496 }
497
498
499 static int
spawn_using_fork(pid_t * _pid,const char * path,const posix_spawn_file_actions_t * actions,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[],bool envpath)500 spawn_using_fork(pid_t *_pid, const char *path,
501 const posix_spawn_file_actions_t *actions,
502 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[],
503 bool envpath)
504 {
505 int err = 0;
506 int fds[2];
507 pid_t pid;
508
509 if (pipe(fds) != 0)
510 return errno;
511 if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0
512 || fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) {
513 goto fail;
514 }
515 pid = vfork();
516 if (pid == -1)
517 goto fail;
518
519 if (pid != 0) {
520 // parent
521 if (_pid != NULL)
522 *_pid = pid;
523 close(fds[1]);
524 read(fds[0], &err, sizeof(err));
525 if (err != 0)
526 waitpid(pid, NULL, WNOHANG);
527 close(fds[0]);
528 return err;
529 }
530
531 // child
532 close(fds[0]);
533
534 err = process_spawnattr(attrp);
535 if (err == 0)
536 err = process_file_actions(actions, &fds[1]);
537 if (err != 0)
538 goto fail_child;
539
540 if (envpath)
541 execvpe(path, argv, envp != NULL ? envp : environ);
542 else
543 execve(path, argv, envp != NULL ? envp : environ);
544
545 err = errno;
546
547 fail_child:
548 write(fds[1], &err, sizeof(err));
549 close(fds[1]);
550 _exit(127);
551
552 fail:
553 err = errno;
554 close(fds[0]);
555 close(fds[1]);
556 return err;
557 }
558
559
560 static int
spawn_using_load_image(pid_t * _pid,const char * _path,char * const argv[],char * const envp[],bool envpath)561 spawn_using_load_image(pid_t *_pid, const char *_path,
562 char *const argv[], char *const envp[], bool envpath)
563 {
564 const char* path;
565 // if envpath is specified but the path contains '/', don't search PATH
566 if (!envpath || strchr(_path, '/') != NULL) {
567 path = _path;
568 } else {
569 char* buffer = (char*)alloca(B_PATH_NAME_LENGTH);
570 status_t status = __look_up_in_path(_path, buffer);
571 if (status != B_OK)
572 return status;
573 path = buffer;
574 }
575
576 // count arguments
577 int32 argCount = 0;
578 while (argv[argCount] != NULL)
579 argCount++;
580
581 thread_id thread = __load_image_at_path(path, argCount, (const char**)argv,
582 (const char**)(envp != NULL ? envp : environ));
583 if (thread < 0)
584 return thread;
585
586 *_pid = thread;
587 return resume_thread(thread);
588 }
589
590
591 static int
do_posix_spawn(pid_t * _pid,const char * path,const posix_spawn_file_actions_t * actions,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[],bool envpath)592 do_posix_spawn(pid_t *_pid, const char *path,
593 const posix_spawn_file_actions_t *actions,
594 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[],
595 bool envpath)
596 {
597 if ((actions == NULL || (*actions)->count == 0)
598 && (attrp == NULL || (*attrp)->flags == 0)) {
599 return spawn_using_load_image(_pid, path, argv, envp, envpath);
600 } else {
601 return spawn_using_fork(_pid, path, actions, attrp, argv, envp,
602 envpath);
603 }
604 }
605
606
607 int
posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])608 posix_spawn(pid_t *pid, const char *path,
609 const posix_spawn_file_actions_t *file_actions,
610 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[])
611 {
612 return do_posix_spawn(pid, path, file_actions, attrp, argv, envp, false);
613 }
614
615
616 int
posix_spawnp(pid_t * pid,const char * file,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])617 posix_spawnp(pid_t *pid, const char *file,
618 const posix_spawn_file_actions_t *file_actions,
619 const posix_spawnattr_t *attrp, char *const argv[],
620 char *const envp[])
621 {
622 return do_posix_spawn(pid, file, file_actions, attrp, argv, envp, true);
623 }
624
625
626 B_DEFINE_WEAK_ALIAS(posix_spawn_file_actions_addchdir_np, posix_spawn_file_actions_addchdir);
627 B_DEFINE_WEAK_ALIAS(posix_spawn_file_actions_addfchdir_np, posix_spawn_file_actions_addfchdir);
628