xref: /minix/minix/lib/libc/sys/posix_spawn.c (revision 1bb466dd)
1 /*
2  * Taken from newlib/libc/posix/posix_spawn.c
3  */
4 
5 /*-
6  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 
33 #include <sys/queue.h>
34 #include <sys/wait.h>
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <sched.h>
39 #include <spawn.h>
40 #include <signal.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 extern char **environ;
46 
47 /* Only deal with a pointer to environ, to work around subtle bugs with shared
48    libraries and/or small data systems where the user declares his own
49    'environ'.  */
50 static char ***p_environ = &environ;
51 
52 /*
53  * Spawn routines
54  */
55 
56 static int
process_spawnattr(const posix_spawnattr_t * sa)57 process_spawnattr(const posix_spawnattr_t * sa)
58 {
59 	struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
60 	int i;
61 
62 	/*
63 	 * POSIX doesn't really describe in which order everything
64 	 * should be set. We'll just set them in the order in which they
65 	 * are mentioned.
66 	 */
67 
68 	/* Set process group */
69 	if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
70 		if (setpgid(0, sa->sa_pgroup) != 0)
71 			return errno;
72 	}
73 
74 	/* Set scheduler policy */
75 	/* XXX: We don't have scheduler policy for now */
76 #if 0
77 	if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
78 		if (sched_setscheduler(0, sa->sa_schedpolicy,
79 		    &sa->sa_schedparam) != 0)
80 			return errno;
81 	} else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
82 		if (sched_setparam(0, &sa->sa_schedparam) != 0)
83 			return errno;
84 	}
85 #endif
86 
87 	/* Reset user ID's */
88 	if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
89 		if (setegid(getgid()) != 0)
90 			return errno;
91 		if (seteuid(getuid()) != 0)
92 			return errno;
93 	}
94 
95 	/* Set signal masks/defaults */
96 	if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
97 		sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
98 	}
99 
100 	if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
101 		for (i = 1; i < NSIG; i++) {
102 			if (sigismember(&sa->sa_sigdefault, i))
103 				if (sigaction(i, &sigact, NULL) != 0)
104 					return errno;
105 		}
106 	}
107 
108 	return 0;
109 }
110 
111 static int
move_fd_up(int * statusfd)112 move_fd_up(int * statusfd)
113 {
114 	/*
115 	 * Move given file descriptor on a higher fd number.
116 	 *
117 	 * This is used to hide the status file descriptor from the application
118 	 * by pushing it out of the way if it tries to use its number.
119 	 */
120 	int newstatusfd;
121 
122 	newstatusfd = fcntl(*statusfd, F_DUPFD, *statusfd+1);
123 	if (newstatusfd == -1)
124 		return -1;
125 
126 	close(*statusfd);
127 	*statusfd = newstatusfd;
128 	return 0;
129 }
130 
131 static int
process_file_actions_entry(posix_spawn_file_actions_entry_t * fae,int * statusfd)132 process_file_actions_entry(posix_spawn_file_actions_entry_t * fae,
133 	int * statusfd)
134 {
135 	int fd;
136 
137 	switch (fae->fae_action) {
138 	case FAE_OPEN:
139 		/* Perform an open(), make it use the right fd */
140 		fd = open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
141 		if (fd < 0)
142 			return errno;
143 		if (fd != fae->fae_fildes) {
144 			if (fae->fae_fildes == *statusfd) {
145 				/* Move the status fd out of the way */
146 				if (move_fd_up(statusfd) == -1)
147 					return errno;
148 			}
149 			if (dup2(fd, fae->fae_fildes) == -1)
150 				return errno;
151 			if (close(fd) != 0) {
152 				if (errno == EBADF)
153 					return EBADF;
154 			}
155 		}
156 		if (fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
157 			return errno;
158 		break;
159 
160 	case FAE_DUP2:
161 		if (fae->fae_fildes == *statusfd) {
162 			/* Nice try */
163 			return EBADF;
164 		}
165 		if (fae->fae_newfildes == *statusfd) {
166 			/* Move the status file descriptor out of the way */
167 			if (move_fd_up(statusfd) == -1)
168 				return errno;
169 		}
170 		/* Perform a dup2() */
171 		if (dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
172 			return errno;
173 		if (fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
174 			return errno;
175 		break;
176 
177 	case FAE_CLOSE:
178 		/* Perform a close(), do not fail if already closed */
179 		if (fae->fae_fildes != *statusfd)
180 			(void)close(fae->fae_fildes);
181 		break;
182 	}
183 	return 0;
184 }
185 
186 static int
process_file_actions(const posix_spawn_file_actions_t * fa,int * statusfd)187 process_file_actions(const posix_spawn_file_actions_t * fa, int * statusfd)
188 {
189 	posix_spawn_file_actions_entry_t *fae;
190 	int error;
191 
192 	/* Replay all file descriptor modifications */
193 	for (unsigned i = 0; i < fa->len; i++) {
194 		fae = &fa->fae[i];
195 		error = process_file_actions_entry(fae, statusfd);
196 		if (error)
197 			return error;
198 	}
199 	return 0;
200 }
201 
202 int
posix_spawn(pid_t * __restrict pid,const char * __restrict path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * __restrict sa,char * const * __restrict argv,char * const * __restrict envp)203 posix_spawn(pid_t * __restrict pid, const char * __restrict path,
204 	const posix_spawn_file_actions_t * fa,
205 	const posix_spawnattr_t * __restrict sa,
206 	char * const * __restrict argv, char * const * __restrict envp)
207 {
208 	pid_t p;
209 	int r, error, pfd[2];
210 
211 	/*
212 	 * Due to the lack of vfork() in Minix, an alternative solution with
213 	 * pipes is used. The writing end is set to close on exec() and the
214 	 * parent performs a read() on it.
215 	 *
216 	 * On success, a successful 0-length read happens.
217 	 * On failure, the child writes the errno to the pipe before exiting,
218 	 * the error is thus transmitted to the parent.
219 	 *
220 	 * This solution was taken from stackoverflow.com question 3703013.
221 	 */
222 	if (pipe(pfd) == -1)
223 		return errno;
224 
225 	p = fork();
226 	switch (p) {
227 	case -1:
228 		close(pfd[0]);
229 		close(pfd[1]);
230 
231 		return errno;
232 
233 	case 0:
234 		close(pfd[0]);
235 
236 		if (fcntl(pfd[1], F_SETFD, FD_CLOEXEC) != 0) {
237 			error = errno;
238 			break;
239 		}
240 
241 		if (sa != NULL) {
242 			error = process_spawnattr(sa);
243 			if (error)
244 				break;
245 		}
246 		if (fa != NULL) {
247 			error = process_file_actions(fa, &pfd[1]);
248 			if (error)
249 				break;
250 		}
251 
252 		(void)execve(path, argv, envp != NULL ? envp : *p_environ);
253 
254 		error = errno;
255 		break;
256 
257 	default:
258 		close(pfd[1]);
259 
260 		/* Retrieve child process status through pipe. */
261 		r = read(pfd[0], &error, sizeof(error));
262 		if (r == 0)
263 			error = 0;
264 		else if (r == -1)
265 			error = errno;
266 		close(pfd[0]);
267 
268 		if (error != 0)
269 			(void)waitpid(p, NULL, 0);
270 
271 		if (pid != NULL)
272 			*pid = p;
273 		return error;
274 	}
275 
276 	/* Child failed somewhere, propagate error through pipe and exit. */
277 	write(pfd[1], &error, sizeof(error));
278 	close(pfd[1]);
279 	_exit(127);
280 }
281