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