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