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