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