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, 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 180 static int 181 process_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 195 static int 196 do_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 234 int 235 posix_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 243 int 244 posix_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 256 int 257 posix_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 270 int 271 posix_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 289 int 290 posix_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 320 int 321 posix_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 343 int 344 posix_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 369 int 370 posix_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 383 int 384 posix_spawnattr_destroy(posix_spawnattr_t *sa) 385 { 386 free(*sa); 387 return (0); 388 } 389 390 int 391 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 392 short * __restrict flags) 393 { 394 *flags = (*sa)->sa_flags; 395 return (0); 396 } 397 398 int 399 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 400 pid_t * __restrict pgroup) 401 { 402 *pgroup = (*sa)->sa_pgroup; 403 return (0); 404 } 405 406 int 407 posix_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 414 int 415 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 416 int * __restrict schedpolicy) 417 { 418 *schedpolicy = (*sa)->sa_schedpolicy; 419 return (0); 420 } 421 422 int 423 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 424 sigset_t * __restrict sigdefault) 425 { 426 *sigdefault = (*sa)->sa_sigdefault; 427 return (0); 428 } 429 430 int 431 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 432 sigset_t * __restrict sigmask) 433 { 434 *sigmask = (*sa)->sa_sigmask; 435 return (0); 436 } 437 438 int 439 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 440 { 441 (*sa)->sa_flags = flags; 442 return (0); 443 } 444 445 int 446 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 447 { 448 (*sa)->sa_pgroup = pgroup; 449 return (0); 450 } 451 452 int 453 posix_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 460 int 461 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 462 { 463 (*sa)->sa_schedpolicy = schedpolicy; 464 return (0); 465 } 466 467 int 468 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 469 const sigset_t * __restrict sigdefault) 470 { 471 (*sa)->sa_sigdefault = *sigdefault; 472 return (0); 473 } 474 475 int 476 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 477 const sigset_t * __restrict sigmask) 478 { 479 (*sa)->sa_sigmask = *sigmask; 480 return (0); 481 } 482