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 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 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 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 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 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