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