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