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
20enum action_type {
21	file_action_open,
22	file_action_close,
23	file_action_dup2,
24	file_action_chdir,
25	file_action_fchdir
26};
27
28struct _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
46struct _posix_spawnattr {
47	short	flags;
48	pid_t	pgroup;
49	sigset_t	sigdefault;
50	sigset_t	sigmask;
51};
52
53struct _posix_spawn_file_actions {
54	int		size;
55	int		count;
56	_file_action *actions;
57};
58
59
60static int
61posix_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
74int
75posix_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
90int
91posix_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
113int
114posix_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
145int
146posix_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
170int
171posix_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
196int
197posix_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
223int
224posix_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
248int
249posix_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
264int
265posix_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
278int
279posix_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
292int
293posix_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
311int
312posix_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
325int
326posix_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
339int
340posix_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
353int
354posix_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
368int
369posix_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
382int
383posix_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
396static int
397process_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
444static int
445process_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
497static int
498do_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
545fail_child:
546	write(fds[1], &err, sizeof(err));
547	close(fds[1]);
548	_exit(127);
549
550fail:
551	err = errno;
552	close(fds[0]);
553	close(fds[1]);
554	return err;
555}
556
557
558int
559posix_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
567int
568posix_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