headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / posix / spawn.cpp
blob7206dbc9a49706d3b35e82196e0e92653d398cff
1 /*
2 * Copyright 2017, Jérôme Duval, jerome.Duval@gmail.com
3 * Distributed under the terms of the MIT license.
4 */
7 #include <spawn.h>
9 #include <errno.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
16 #include <signal_defs.h>
17 #include <syscalls.h>
20 enum action_type {
21 file_action_open,
22 file_action_close,
23 file_action_dup2
26 struct _file_action {
27 enum action_type type;
28 int fd;
29 union {
30 struct {
31 char* path;
32 int oflag;
33 mode_t mode;
34 } open_action;
35 struct {
36 int srcfd;
37 } dup2_action;
38 } action;
41 struct _posix_spawnattr {
42 short flags;
43 pid_t pgroup;
44 sigset_t sigdefault;
45 sigset_t sigmask;
48 struct _posix_spawn_file_actions {
49 int size;
50 int count;
51 _file_action *actions;
55 static int
56 posix_spawn_file_actions_extend(struct _posix_spawn_file_actions *actions)
58 int newsize = actions->size + 4;
59 void *newactions = realloc(actions->actions,
60 newsize * sizeof(struct _file_action));
61 if (newactions == NULL)
62 return ENOMEM;
63 actions->actions = (struct _file_action*)newactions;
64 actions->size = newsize;
65 return 0;
69 int
70 posix_spawn_file_actions_init(posix_spawn_file_actions_t *_file_actions)
72 posix_spawn_file_actions_t actions = (posix_spawn_file_actions_t)malloc(
73 sizeof(struct _posix_spawn_file_actions));
75 if (actions == NULL)
76 return ENOMEM;
78 memset(actions, 0, sizeof(*actions));
79 *_file_actions = actions;
81 return 0;
85 int
86 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *_actions)
88 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
90 if (actions == NULL)
91 return EINVAL;
93 free(actions);
95 return 0;
99 int
100 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *_actions,
101 int fildes, const char *path, int oflag, mode_t mode)
103 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
105 if (actions == NULL)
106 return EINVAL;
108 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
109 return EBADF;
111 char* npath = strdup(path);
112 if (npath == NULL)
113 return ENOMEM;
114 if (actions->count == actions->size
115 && posix_spawn_file_actions_extend(actions) != 0) {
116 free(npath);
117 return ENOMEM;
120 struct _file_action *action = &actions->actions[actions->count];
121 action->type = file_action_open;
122 action->fd = fildes;
123 action->action.open_action.path = npath;
124 action->action.open_action.oflag = oflag;
125 action->action.open_action.mode = mode;
126 actions->count++;
127 return 0;
132 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *_actions,
133 int fildes)
135 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
137 if (actions == NULL)
138 return EINVAL;
140 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
141 return EBADF;
143 if (actions->count == actions->size
144 && posix_spawn_file_actions_extend(actions) != 0) {
145 return ENOMEM;
148 struct _file_action *action = &actions->actions[actions->count];
149 action->type = file_action_close;
150 action->fd = fildes;
151 actions->count++;
152 return 0;
157 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *_actions,
158 int fildes, int newfildes)
160 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL;
162 if (actions == NULL)
163 return EINVAL;
165 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX))
166 return EBADF;
168 if (actions->count == actions->size
169 && posix_spawn_file_actions_extend(actions) != 0) {
170 return ENOMEM;
173 struct _file_action *action = &actions->actions[actions->count];
174 action->type = file_action_dup2;
175 action->fd = newfildes;
176 action->action.dup2_action.srcfd = fildes;
177 actions->count++;
178 return 0;
183 posix_spawnattr_init(posix_spawnattr_t *_attr)
185 posix_spawnattr_t attr = (posix_spawnattr_t)malloc(
186 sizeof(struct _posix_spawnattr));
188 if (attr == NULL)
189 return ENOMEM;
191 memset(attr, 0, sizeof(*attr));
192 *_attr = attr;
194 return 0;
199 posix_spawnattr_destroy(posix_spawnattr_t *_attr)
201 struct _posix_spawnattr* attr = _attr != NULL ? *_attr : NULL;
203 if (attr == NULL)
204 return EINVAL;
206 free(attr);
208 return 0;
213 posix_spawnattr_getflags(const posix_spawnattr_t *_attr, short *flags)
215 struct _posix_spawnattr *attr;
217 if (_attr == NULL || (attr = *_attr) == NULL || flags == NULL)
218 return EINVAL;
220 *flags = attr->flags;
222 return 0;
227 posix_spawnattr_setflags(posix_spawnattr_t *_attr, short flags)
229 struct _posix_spawnattr *attr;
231 if (_attr == NULL || (attr = *_attr) == NULL)
232 return EINVAL;
234 if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP
235 | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK)) != 0) {
236 return EINVAL;
239 attr->flags = flags;
241 return 0;
246 posix_spawnattr_getpgroup(const posix_spawnattr_t *_attr, pid_t *pgroup)
248 struct _posix_spawnattr *attr;
250 if (_attr == NULL || (attr = *_attr) == NULL || pgroup == NULL)
251 return EINVAL;
253 *pgroup = attr->pgroup;
255 return 0;
260 posix_spawnattr_setpgroup(posix_spawnattr_t *_attr, pid_t pgroup)
262 struct _posix_spawnattr *attr;
264 if (_attr == NULL || (attr = *_attr) == NULL)
265 return EINVAL;
267 attr->pgroup = pgroup;
269 return 0;
274 posix_spawnattr_getsigdefault(const posix_spawnattr_t *_attr, sigset_t *sigdefault)
276 struct _posix_spawnattr *attr;
278 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL)
279 return EINVAL;
281 memcpy(sigdefault, &attr->sigdefault, sizeof(sigset_t));
283 return 0;
288 posix_spawnattr_setsigdefault(posix_spawnattr_t *_attr,
289 const sigset_t *sigdefault)
291 struct _posix_spawnattr *attr;
293 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL)
294 return EINVAL;
296 memcpy(&attr->sigdefault, sigdefault, sizeof(sigset_t));
298 return 0;
303 posix_spawnattr_getsigmask(const posix_spawnattr_t *_attr, sigset_t *sigmask)
305 struct _posix_spawnattr *attr;
307 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL)
308 return EINVAL;
310 memcpy(sigmask, &attr->sigmask, sizeof(sigset_t));
312 return 0;
317 posix_spawnattr_setsigmask(posix_spawnattr_t *_attr, const sigset_t *sigmask)
319 struct _posix_spawnattr *attr;
321 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL)
322 return EINVAL;
324 memcpy(&attr->sigmask, sigmask, sizeof(sigset_t));
326 return 0;
330 static int
331 process_spawnattr(const posix_spawnattr_t *_attr)
333 if (_attr == NULL)
334 return 0;
336 struct _posix_spawnattr *attr = *_attr;
337 if (attr == NULL)
338 return EINVAL;
340 if ((attr->flags & POSIX_SPAWN_SETSIGMASK) != 0)
341 sigprocmask(SIG_SETMASK, &attr->sigmask, NULL);
343 if ((attr->flags & POSIX_SPAWN_SETSIGDEF) != 0) {
344 struct sigaction action;
345 action.sa_handler = SIG_DFL;
346 action.sa_flags = 0;
347 action.sa_userdata = NULL;
348 sigemptyset(&action.sa_mask);
349 for (int i = 1; i <= MAX_SIGNAL_NUMBER; i++) {
350 if (sigismember(&attr->sigdefault, i) == 1
351 && sigaction(i, &action, NULL) != 0) {
352 return errno;
357 if ((attr->flags & POSIX_SPAWN_RESETIDS) != 0) {
358 if (setegid(getgid()) != 0)
359 return errno;
360 if (seteuid(getuid()) != 0)
361 return errno;
364 if ((attr->flags & POSIX_SPAWN_SETPGROUP) != 0) {
365 if (setpgid(0, attr->pgroup) != 0)
366 return errno;
369 return 0;
373 static int
374 process_file_actions(const posix_spawn_file_actions_t *_actions, int *errfd)
376 if (_actions == NULL)
377 return 0;
379 struct _posix_spawn_file_actions* actions = *_actions;
380 if (actions == NULL)
381 return EINVAL;
383 for (int i = 0; i < actions->count; i++) {
384 struct _file_action *action = &actions->actions[i];
386 if (action->fd == *errfd) {
387 int newfd = dup(action->fd);
388 if (newfd == -1)
389 return errno;
390 close(action->fd);
391 fcntl(newfd, F_SETFD, FD_CLOEXEC);
392 *errfd = newfd;
395 if (action->type == file_action_close) {
396 if (close(action->fd) != 0)
397 return errno;
398 } else if (action->type == file_action_open) {
399 int fd = open(action->action.open_action.path,
400 action->action.open_action.oflag,
401 action->action.open_action.mode);
402 if (fd == -1)
403 return errno;
404 if (fd != action->fd) {
405 if (dup2(action->fd, fd) != 0)
406 return errno;
407 if (close(fd) != 0)
408 return errno;
410 } else if (action->type == file_action_dup2) {
411 if (dup2(action->action.dup2_action.srcfd, action->fd) != 0)
412 return errno;
416 return 0;
420 static int
421 do_posix_spawn(pid_t *_pid, const char *path,
422 const posix_spawn_file_actions_t *actions,
423 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[],
424 bool envpath)
426 int err = 0;
427 int fds[2];
428 pid_t pid;
430 if (pipe(fds) != 0)
431 return errno;
432 if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0
433 || fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) {
434 goto fail;
436 pid = vfork();
437 if (pid == -1)
438 goto fail;
440 if (pid != 0) {
441 // parent
442 if (_pid != NULL)
443 *_pid = pid;
444 close(fds[1]);
445 read(fds[0], &err, sizeof(err));
446 if (err != 0)
447 waitpid(pid, NULL, WNOHANG);
448 close(fds[0]);
449 return err;
452 // child
453 close(fds[0]);
455 err = process_spawnattr(attrp);
456 if (err == 0)
457 err = process_file_actions(actions, &fds[1]);
458 if (err != 0)
459 goto fail_child;
461 if (envpath)
462 execvpe(path, argv, envp != NULL ? envp : environ);
463 else
464 execve(path, argv, envp != NULL ? envp : environ);
466 err = errno;
468 fail_child:
469 write(fds[1], &err, sizeof(err));
470 close(fds[1]);
471 _exit(127);
473 fail:
474 err = errno;
475 close(fds[0]);
476 close(fds[1]);
477 return err;
482 posix_spawn(pid_t *pid, const char *path,
483 const posix_spawn_file_actions_t *file_actions,
484 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[])
486 return do_posix_spawn(pid, path, file_actions, attrp, argv, envp, false);
491 posix_spawnp(pid_t *pid, const char *file,
492 const posix_spawn_file_actions_t *file_actions,
493 const posix_spawnattr_t *attrp, char *const argv[],
494 char *const envp[])
496 return do_posix_spawn(pid, file, file_actions, attrp, argv, envp, true);