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 45 extern char **environ; 46 47 struct __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 56 struct __posix_spawn_file_actions { 57 STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; 58 }; 59 60 typedef 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 85 static int 86 process_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 140 static int 141 process_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 177 static int 178 process_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 192 static int 193 do_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 231 int 232 posix_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 240 int 241 posix_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 253 int 254 posix_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 267 int 268 posix_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 286 int 287 posix_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 317 int 318 posix_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 340 int 341 posix_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 366 int 367 posix_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 380 int 381 posix_spawnattr_destroy(posix_spawnattr_t *sa) 382 { 383 free(*sa); 384 return (0); 385 } 386 387 int 388 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 389 short * __restrict flags) 390 { 391 *flags = (*sa)->sa_flags; 392 return (0); 393 } 394 395 int 396 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 397 pid_t * __restrict pgroup) 398 { 399 *pgroup = (*sa)->sa_pgroup; 400 return (0); 401 } 402 403 int 404 posix_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 411 int 412 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 413 int * __restrict schedpolicy) 414 { 415 *schedpolicy = (*sa)->sa_schedpolicy; 416 return (0); 417 } 418 419 int 420 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 421 sigset_t * __restrict sigdefault) 422 { 423 *sigdefault = (*sa)->sa_sigdefault; 424 return (0); 425 } 426 427 int 428 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 429 sigset_t * __restrict sigmask) 430 { 431 *sigmask = (*sa)->sa_sigmask; 432 return (0); 433 } 434 435 int 436 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 437 { 438 (*sa)->sa_flags = flags; 439 return (0); 440 } 441 442 int 443 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 444 { 445 (*sa)->sa_pgroup = pgroup; 446 return (0); 447 } 448 449 int 450 posix_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 457 int 458 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 459 { 460 (*sa)->sa_schedpolicy = schedpolicy; 461 return (0); 462 } 463 464 int 465 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 466 const sigset_t * __restrict sigdefault) 467 { 468 (*sa)->sa_sigdefault = *sigdefault; 469 return (0); 470 } 471 472 int 473 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 474 const sigset_t * __restrict sigmask) 475 { 476 (*sa)->sa_sigmask = *sigmask; 477 return (0); 478 } 479