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$"); 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 __sys_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 (__sys_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, saved_errno; 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 saved_errno = errno; 154 (void)_close(fd); 155 return (saved_errno); 156 } 157 if (_close(fd) != 0) { 158 if (errno == EBADF) 159 return (EBADF); 160 } 161 } 162 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1) 163 return (errno); 164 break; 165 case FAE_DUP2: 166 /* Perform a dup2() */ 167 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1) 168 return (errno); 169 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1) 170 return (errno); 171 break; 172 case FAE_CLOSE: 173 /* Perform a close(), do not fail if already closed */ 174 (void)_close(fae->fae_fildes); 175 break; 176 } 177 return (0); 178} 179 180static int 181process_file_actions(const posix_spawn_file_actions_t fa) 182{ 183 posix_spawn_file_actions_entry_t *fae; 184 int error; 185 186 /* Replay all file descriptor modifications */ 187 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) { 188 error = process_file_actions_entry(fae); 189 if (error) 190 return (error); 191 } 192 return (0); 193} 194 195static int 196do_posix_spawn(pid_t *pid, const char *path, 197 const posix_spawn_file_actions_t *fa, 198 const posix_spawnattr_t *sa, 199 char * const argv[], char * const envp[], int use_env_path) 200{ 201 pid_t p; 202 volatile int error = 0; 203 204 p = vfork(); 205 switch (p) { 206 case -1: 207 return (errno); 208 case 0: 209 if (sa != NULL) { 210 error = process_spawnattr(*sa); 211 if (error) 212 _exit(127); 213 } 214 if (fa != NULL) { 215 error = process_file_actions(*fa); 216 if (error) 217 _exit(127); 218 } 219 if (use_env_path) 220 _execvpe(path, argv, envp != NULL ? envp : environ); 221 else 222 _execve(path, argv, envp != NULL ? envp : environ); 223 error = errno; 224 _exit(127); 225 default: 226 if (error != 0) 227 _waitpid(p, NULL, WNOHANG); 228 else if (pid != NULL) 229 *pid = p; 230 return (error); 231 } 232} 233 234int 235posix_spawn(pid_t *pid, const char *path, 236 const posix_spawn_file_actions_t *fa, 237 const posix_spawnattr_t *sa, 238 char * const argv[], char * const envp[]) 239{ 240 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 241} 242 243int 244posix_spawnp(pid_t *pid, const char *path, 245 const posix_spawn_file_actions_t *fa, 246 const posix_spawnattr_t *sa, 247 char * const argv[], char * const envp[]) 248{ 249 return do_posix_spawn(pid, path, fa, sa, argv, envp, 1); 250} 251 252/* 253 * File descriptor actions 254 */ 255 256int 257posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) 258{ 259 posix_spawn_file_actions_t fa; 260 261 fa = malloc(sizeof(struct __posix_spawn_file_actions)); 262 if (fa == NULL) 263 return (-1); 264 265 STAILQ_INIT(&fa->fa_list); 266 *ret = fa; 267 return (0); 268} 269 270int 271posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) 272{ 273 posix_spawn_file_actions_entry_t *fae; 274 275 while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) { 276 /* Remove file action entry from the queue */ 277 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); 278 279 /* Deallocate file action entry */ 280 if (fae->fae_action == FAE_OPEN) 281 free(fae->fae_path); 282 free(fae); 283 } 284 285 free(*fa); 286 return (0); 287} 288 289int 290posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa, 291 int fildes, const char * __restrict path, int oflag, mode_t mode) 292{ 293 posix_spawn_file_actions_entry_t *fae; 294 int error; 295 296 if (fildes < 0) 297 return (EBADF); 298 299 /* Allocate object */ 300 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 301 if (fae == NULL) 302 return (errno); 303 304 /* Set values and store in queue */ 305 fae->fae_action = FAE_OPEN; 306 fae->fae_path = strdup(path); 307 if (fae->fae_path == NULL) { 308 error = errno; 309 free(fae); 310 return (error); 311 } 312 fae->fae_fildes = fildes; 313 fae->fae_oflag = oflag; 314 fae->fae_mode = mode; 315 316 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 317 return (0); 318} 319 320int 321posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, 322 int fildes, int newfildes) 323{ 324 posix_spawn_file_actions_entry_t *fae; 325 326 if (fildes < 0 || newfildes < 0) 327 return (EBADF); 328 329 /* Allocate object */ 330 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 331 if (fae == NULL) 332 return (errno); 333 334 /* Set values and store in queue */ 335 fae->fae_action = FAE_DUP2; 336 fae->fae_fildes = fildes; 337 fae->fae_newfildes = newfildes; 338 339 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 340 return (0); 341} 342 343int 344posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, 345 int fildes) 346{ 347 posix_spawn_file_actions_entry_t *fae; 348 349 if (fildes < 0) 350 return (EBADF); 351 352 /* Allocate object */ 353 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 354 if (fae == NULL) 355 return (errno); 356 357 /* Set values and store in queue */ 358 fae->fae_action = FAE_CLOSE; 359 fae->fae_fildes = fildes; 360 361 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 362 return (0); 363} 364 365/* 366 * Spawn attributes 367 */ 368 369int 370posix_spawnattr_init(posix_spawnattr_t *ret) 371{ 372 posix_spawnattr_t sa; 373 374 sa = calloc(1, sizeof(struct __posix_spawnattr)); 375 if (sa == NULL) 376 return (errno); 377 378 /* Set defaults as specified by POSIX, cleared above */ 379 *ret = sa; 380 return (0); 381} 382 383int 384posix_spawnattr_destroy(posix_spawnattr_t *sa) 385{ 386 free(*sa); 387 return (0); 388} 389 390int 391posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 392 short * __restrict flags) 393{ 394 *flags = (*sa)->sa_flags; 395 return (0); 396} 397 398int 399posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 400 pid_t * __restrict pgroup) 401{ 402 *pgroup = (*sa)->sa_pgroup; 403 return (0); 404} 405 406int 407posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa, 408 struct sched_param * __restrict schedparam) 409{ 410 *schedparam = (*sa)->sa_schedparam; 411 return (0); 412} 413 414int 415posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 416 int * __restrict schedpolicy) 417{ 418 *schedpolicy = (*sa)->sa_schedpolicy; 419 return (0); 420} 421 422int 423posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 424 sigset_t * __restrict sigdefault) 425{ 426 *sigdefault = (*sa)->sa_sigdefault; 427 return (0); 428} 429 430int 431posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 432 sigset_t * __restrict sigmask) 433{ 434 *sigmask = (*sa)->sa_sigmask; 435 return (0); 436} 437 438int 439posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 440{ 441 (*sa)->sa_flags = flags; 442 return (0); 443} 444 445int 446posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 447{ 448 (*sa)->sa_pgroup = pgroup; 449 return (0); 450} 451 452int 453posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa, 454 const struct sched_param * __restrict schedparam) 455{ 456 (*sa)->sa_schedparam = *schedparam; 457 return (0); 458} 459 460int 461posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 462{ 463 (*sa)->sa_schedpolicy = schedpolicy; 464 return (0); 465} 466 467int 468posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 469 const sigset_t * __restrict sigdefault) 470{ 471 (*sa)->sa_sigdefault = *sigdefault; 472 return (0); 473} 474 475int 476posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 477 const sigset_t * __restrict sigmask) 478{ 479 (*sa)->sa_sigmask = *sigmask; 480 return (0); 481} 482