1 /* $OpenBSD: posix_spawn.c,v 1.3 2012/12/05 23:20:00 deraadt Exp $ */ 2 /*- 3 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include "namespace.h" 29 #include <sys/queue.h> 30 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <sched.h> 34 #include <spawn.h> 35 #include <signal.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 extern char **environ; 41 42 struct __posix_spawnattr { 43 short sa_flags; 44 pid_t sa_pgroup; 45 struct sched_param sa_schedparam; 46 int sa_schedpolicy; 47 sigset_t sa_sigdefault; 48 sigset_t sa_sigmask; 49 }; 50 51 struct __posix_spawn_file_actions { 52 SIMPLEQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; 53 }; 54 55 typedef struct __posix_spawn_file_actions_entry { 56 SIMPLEQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; 57 enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action; 58 59 int fae_fildes; 60 union { 61 struct { 62 char *path; 63 #define fae_path fae_data.open.path 64 int oflag; 65 #define fae_oflag fae_data.open.oflag 66 mode_t mode; 67 #define fae_mode fae_data.open.mode 68 } open; 69 struct { 70 int newfildes; 71 #define fae_newfildes fae_data.dup2.newfildes 72 } dup2; 73 } fae_data; 74 } posix_spawn_file_actions_entry_t; 75 76 /* 77 * Spawn routines 78 */ 79 80 static int 81 process_spawnattr(const posix_spawnattr_t sa) 82 { 83 struct sigaction sigact; 84 int i; 85 86 memset(&sigact, 0, sizeof(sigact)); 87 sigact.sa_handler = SIG_DFL; 88 89 /* 90 * POSIX doesn't really describe in which order everything 91 * should be set. We'll just set them in the order in which they 92 * are mentioned. 93 */ 94 95 /* Set process group */ 96 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) { 97 if (setpgid(0, sa->sa_pgroup) != 0) 98 return (errno); 99 } 100 101 #if 0 /* NOT IMPLEMENTED */ 102 /* Set scheduler policy */ 103 if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) { 104 if (sched_setscheduler(0, sa->sa_schedpolicy, 105 &sa->sa_schedparam) != 0) 106 return (errno); 107 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) { 108 if (sched_setparam(0, &sa->sa_schedparam) != 0) 109 return (errno); 110 } 111 #endif 112 113 /* Reset user ID's */ 114 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) { 115 if (setegid(getgid()) != 0) 116 return (errno); 117 if (seteuid(getuid()) != 0) 118 return (errno); 119 } 120 121 /* Set signal masks/defaults */ 122 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) { 123 sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL); 124 } 125 126 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) { 127 for (i = 1; i <= _NSIG; i++) { 128 if (sigismember(&sa->sa_sigdefault, i)) 129 if (sigaction(i, &sigact, NULL) != 0) 130 return (errno); 131 } 132 } 133 134 return (0); 135 } 136 137 static int 138 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae) 139 { 140 int fd; 141 142 switch (fae->fae_action) { 143 case FAE_OPEN: 144 /* Perform an open(), make it use the right fd */ 145 fd = open(fae->fae_path, fae->fae_oflag, fae->fae_mode); 146 if (fd < 0) 147 return (errno); 148 if (fd != fae->fae_fildes) { 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 break; 157 case FAE_DUP2: 158 /* Perform a dup2() */ 159 if (dup2(fae->fae_fildes, fae->fae_newfildes) == -1) 160 return (errno); 161 break; 162 case FAE_CLOSE: 163 /* Perform a close(), do not fail if already closed */ 164 (void)close(fae->fae_fildes); 165 break; 166 } 167 return (0); 168 } 169 170 static int 171 process_file_actions(const posix_spawn_file_actions_t fa) 172 { 173 posix_spawn_file_actions_entry_t *fae; 174 int error; 175 176 /* Replay all file descriptor modifications */ 177 SIMPLEQ_FOREACH(fae, &fa->fa_list, fae_list) { 178 error = process_file_actions_entry(fae); 179 if (error) 180 return (error); 181 } 182 return (0); 183 } 184 185 static int 186 do_posix_spawn(pid_t *pid, const char *path, 187 const posix_spawn_file_actions_t *fa, 188 const posix_spawnattr_t *sa, 189 char *const argv[], char *const envp[], int use_env_path) 190 { 191 pid_t p; 192 193 p = fork(); 194 switch (p) { 195 case -1: 196 return (errno); 197 case 0: { 198 int error; 199 if (sa != NULL) { 200 error = process_spawnattr(*sa); 201 if (error) 202 _exit(127); 203 } 204 if (fa != NULL) { 205 error = process_file_actions(*fa); 206 if (error) 207 _exit(127); 208 } 209 if (use_env_path) 210 execvpe(path, argv, envp != NULL ? envp : environ); 211 else 212 execve(path, argv, envp != NULL ? envp : environ); 213 _exit(127); 214 } 215 default: 216 if (pid != NULL) 217 *pid = p; 218 return (0); 219 } 220 } 221 222 int 223 posix_spawn(pid_t *pid, const char *path, 224 const posix_spawn_file_actions_t *fa, 225 const posix_spawnattr_t *sa, 226 char *const argv[], char *const envp[]) 227 { 228 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 229 } 230 231 int 232 posix_spawnp(pid_t *pid, const char *path, 233 const posix_spawn_file_actions_t *fa, 234 const posix_spawnattr_t *sa, 235 char *const argv[], char *const envp[]) 236 { 237 return do_posix_spawn(pid, path, fa, sa, argv, envp, 1); 238 } 239 240 /* 241 * File descriptor actions 242 */ 243 244 int 245 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) 246 { 247 posix_spawn_file_actions_t fa; 248 249 fa = malloc(sizeof(struct __posix_spawn_file_actions)); 250 if (fa == NULL) 251 return (errno); 252 253 SIMPLEQ_INIT(&fa->fa_list); 254 *ret = fa; 255 return (0); 256 } 257 258 int 259 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) 260 { 261 posix_spawn_file_actions_entry_t *fae; 262 263 while ((fae = SIMPLEQ_FIRST(&(*fa)->fa_list)) != NULL) { 264 /* Remove file action entry from the queue */ 265 SIMPLEQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); 266 267 /* Deallocate file action entry */ 268 if (fae->fae_action == FAE_OPEN) 269 free(fae->fae_path); 270 free(fae); 271 } 272 273 free(*fa); 274 return (0); 275 } 276 277 int 278 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict fa, 279 int fildes, const char *__restrict path, int oflag, mode_t mode) 280 { 281 posix_spawn_file_actions_entry_t *fae; 282 int error; 283 284 if (fildes < 0) 285 return (EBADF); 286 287 /* Allocate object */ 288 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 289 if (fae == NULL) 290 return (errno); 291 292 /* Set values and store in queue */ 293 fae->fae_action = FAE_OPEN; 294 fae->fae_path = strdup(path); 295 if (fae->fae_path == NULL) { 296 error = errno; 297 free(fae); 298 return (error); 299 } 300 fae->fae_fildes = fildes; 301 fae->fae_oflag = oflag; 302 fae->fae_mode = mode; 303 304 SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 305 return (0); 306 } 307 308 int 309 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, 310 int fildes, int newfildes) 311 { 312 posix_spawn_file_actions_entry_t *fae; 313 314 if (fildes < 0 || newfildes < 0) 315 return (EBADF); 316 317 /* Allocate object */ 318 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 319 if (fae == NULL) 320 return (errno); 321 322 /* Set values and store in queue */ 323 fae->fae_action = FAE_DUP2; 324 fae->fae_fildes = fildes; 325 fae->fae_newfildes = newfildes; 326 327 SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 328 return (0); 329 } 330 331 int 332 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, 333 int fildes) 334 { 335 posix_spawn_file_actions_entry_t *fae; 336 337 if (fildes < 0) 338 return (EBADF); 339 340 /* Allocate object */ 341 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 342 if (fae == NULL) 343 return (errno); 344 345 /* Set values and store in queue */ 346 fae->fae_action = FAE_CLOSE; 347 fae->fae_fildes = fildes; 348 349 SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 350 return (0); 351 } 352 353 /* 354 * Spawn attributes 355 */ 356 357 int 358 posix_spawnattr_init(posix_spawnattr_t *ret) 359 { 360 posix_spawnattr_t sa; 361 362 sa = calloc(1, sizeof(struct __posix_spawnattr)); 363 if (sa == NULL) 364 return (errno); 365 366 /* Set defaults as specified by POSIX, cleared above */ 367 *ret = sa; 368 return (0); 369 } 370 371 int 372 posix_spawnattr_destroy(posix_spawnattr_t *sa) 373 { 374 free(*sa); 375 return (0); 376 } 377 378 int 379 posix_spawnattr_getflags(const posix_spawnattr_t *__restrict sa, 380 short *__restrict flags) 381 { 382 *flags = (*sa)->sa_flags; 383 return (0); 384 } 385 386 int 387 posix_spawnattr_getpgroup(const posix_spawnattr_t *__restrict sa, 388 pid_t *__restrict pgroup) 389 { 390 *pgroup = (*sa)->sa_pgroup; 391 return (0); 392 } 393 394 int 395 posix_spawnattr_getschedparam(const posix_spawnattr_t *__restrict sa, 396 struct sched_param *__restrict schedparam) 397 { 398 *schedparam = (*sa)->sa_schedparam; 399 return (0); 400 } 401 402 int 403 posix_spawnattr_getschedpolicy(const posix_spawnattr_t *__restrict sa, 404 int *__restrict schedpolicy) 405 { 406 *schedpolicy = (*sa)->sa_schedpolicy; 407 return (0); 408 } 409 410 int 411 posix_spawnattr_getsigdefault(const posix_spawnattr_t *__restrict sa, 412 sigset_t *__restrict sigdefault) 413 { 414 *sigdefault = (*sa)->sa_sigdefault; 415 return (0); 416 } 417 418 int 419 posix_spawnattr_getsigmask(const posix_spawnattr_t *__restrict sa, 420 sigset_t *__restrict sigmask) 421 { 422 *sigmask = (*sa)->sa_sigmask; 423 return (0); 424 } 425 426 int 427 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 428 { 429 (*sa)->sa_flags = flags; 430 return (0); 431 } 432 433 int 434 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 435 { 436 (*sa)->sa_pgroup = pgroup; 437 return (0); 438 } 439 440 int 441 posix_spawnattr_setschedparam(posix_spawnattr_t *__restrict sa, 442 const struct sched_param *__restrict schedparam) 443 { 444 (*sa)->sa_schedparam = *schedparam; 445 return (0); 446 } 447 448 int 449 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 450 { 451 (*sa)->sa_schedpolicy = schedpolicy; 452 return (0); 453 } 454 455 int 456 posix_spawnattr_setsigdefault(posix_spawnattr_t *__restrict sa, 457 const sigset_t *__restrict sigdefault) 458 { 459 (*sa)->sa_sigdefault = *sigdefault; 460 return (0); 461 } 462 463 int 464 posix_spawnattr_setsigmask(posix_spawnattr_t *__restrict sa, 465 const sigset_t *__restrict sigmask) 466 { 467 (*sa)->sa_sigmask = *sigmask; 468 return (0); 469 } 470