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 /* Set signal masks/defaults */ 122 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) { 123 _sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL); 124 } 125 126 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) { 127 for (i = 1; i <= _SIG_MAXSIG; i++) { 128 if (sigismember(&sa->sa_sigdefault, i)) 129 if (_sigaction(i, &sigact, NULL) != 0) 130 return (errno); 131 } 132 } 133 134 return (0); 135 } 136 137 static int 138 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae) 139 { 140 int fd; 141 142 switch (fae->fae_action) { 143 case FAE_OPEN: 144 /* Perform an open(), make it use the right fd */ 145 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode); 146 if (fd < 0) 147 return (errno); 148 if (fd != fae->fae_fildes) { 149 if (_dup2(fd, fae->fae_fildes) == -1) 150 return (errno); 151 if (_close(fd) != 0) { 152 if (errno == EBADF) 153 return (EBADF); 154 } 155 } 156 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1) 157 return (errno); 158 break; 159 case FAE_DUP2: 160 /* Perform a dup2() */ 161 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1) 162 return (errno); 163 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1) 164 return (errno); 165 break; 166 case FAE_CLOSE: 167 /* Perform a close(), do not fail if already closed */ 168 (void)_close(fae->fae_fildes); 169 break; 170 } 171 return (0); 172 } 173 174 static int 175 process_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 189 static int 190 do_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 (error != 0) 221 _waitpid(p, NULL, WNOHANG); 222 else if (pid != NULL) 223 *pid = p; 224 return (error); 225 } 226 } 227 228 int 229 posix_spawn(pid_t *pid, const char *path, 230 const posix_spawn_file_actions_t *fa, 231 const posix_spawnattr_t *sa, 232 char * const argv[], char * const envp[]) 233 { 234 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 235 } 236 237 int 238 posix_spawnp(pid_t *pid, const char *path, 239 const posix_spawn_file_actions_t *fa, 240 const posix_spawnattr_t *sa, 241 char * const argv[], char * const envp[]) 242 { 243 return do_posix_spawn(pid, path, fa, sa, argv, envp, 1); 244 } 245 246 /* 247 * File descriptor actions 248 */ 249 250 int 251 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) 252 { 253 posix_spawn_file_actions_t fa; 254 255 fa = malloc(sizeof(struct __posix_spawn_file_actions)); 256 if (fa == NULL) 257 return (-1); 258 259 STAILQ_INIT(&fa->fa_list); 260 *ret = fa; 261 return (0); 262 } 263 264 int 265 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) 266 { 267 posix_spawn_file_actions_entry_t *fae; 268 269 while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) { 270 /* Remove file action entry from the queue */ 271 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); 272 273 /* Deallocate file action entry */ 274 if (fae->fae_action == FAE_OPEN) 275 free(fae->fae_path); 276 free(fae); 277 } 278 279 free(*fa); 280 return (0); 281 } 282 283 int 284 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa, 285 int fildes, const char * __restrict path, int oflag, mode_t mode) 286 { 287 posix_spawn_file_actions_entry_t *fae; 288 int error; 289 290 if (fildes < 0) 291 return (EBADF); 292 293 /* Allocate object */ 294 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 295 if (fae == NULL) 296 return (errno); 297 298 /* Set values and store in queue */ 299 fae->fae_action = FAE_OPEN; 300 fae->fae_path = strdup(path); 301 if (fae->fae_path == NULL) { 302 error = errno; 303 free(fae); 304 return (error); 305 } 306 fae->fae_fildes = fildes; 307 fae->fae_oflag = oflag; 308 fae->fae_mode = mode; 309 310 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 311 return (0); 312 } 313 314 int 315 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, 316 int fildes, int newfildes) 317 { 318 posix_spawn_file_actions_entry_t *fae; 319 320 if (fildes < 0 || newfildes < 0) 321 return (EBADF); 322 323 /* Allocate object */ 324 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 325 if (fae == NULL) 326 return (errno); 327 328 /* Set values and store in queue */ 329 fae->fae_action = FAE_DUP2; 330 fae->fae_fildes = fildes; 331 fae->fae_newfildes = newfildes; 332 333 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 334 return (0); 335 } 336 337 int 338 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, 339 int fildes) 340 { 341 posix_spawn_file_actions_entry_t *fae; 342 343 if (fildes < 0) 344 return (EBADF); 345 346 /* Allocate object */ 347 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 348 if (fae == NULL) 349 return (errno); 350 351 /* Set values and store in queue */ 352 fae->fae_action = FAE_CLOSE; 353 fae->fae_fildes = fildes; 354 355 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 356 return (0); 357 } 358 359 /* 360 * Spawn attributes 361 */ 362 363 int 364 posix_spawnattr_init(posix_spawnattr_t *ret) 365 { 366 posix_spawnattr_t sa; 367 368 sa = calloc(1, sizeof(struct __posix_spawnattr)); 369 if (sa == NULL) 370 return (errno); 371 372 /* Set defaults as specified by POSIX, cleared above */ 373 *ret = sa; 374 return (0); 375 } 376 377 int 378 posix_spawnattr_destroy(posix_spawnattr_t *sa) 379 { 380 free(*sa); 381 return (0); 382 } 383 384 int 385 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 386 short * __restrict flags) 387 { 388 *flags = (*sa)->sa_flags; 389 return (0); 390 } 391 392 int 393 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 394 pid_t * __restrict pgroup) 395 { 396 *pgroup = (*sa)->sa_pgroup; 397 return (0); 398 } 399 400 int 401 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa, 402 struct sched_param * __restrict schedparam) 403 { 404 *schedparam = (*sa)->sa_schedparam; 405 return (0); 406 } 407 408 int 409 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 410 int * __restrict schedpolicy) 411 { 412 *schedpolicy = (*sa)->sa_schedpolicy; 413 return (0); 414 } 415 416 int 417 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 418 sigset_t * __restrict sigdefault) 419 { 420 *sigdefault = (*sa)->sa_sigdefault; 421 return (0); 422 } 423 424 int 425 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 426 sigset_t * __restrict sigmask) 427 { 428 *sigmask = (*sa)->sa_sigmask; 429 return (0); 430 } 431 432 int 433 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 434 { 435 (*sa)->sa_flags = flags; 436 return (0); 437 } 438 439 int 440 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 441 { 442 (*sa)->sa_pgroup = pgroup; 443 return (0); 444 } 445 446 int 447 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa, 448 const struct sched_param * __restrict schedparam) 449 { 450 (*sa)->sa_schedparam = *schedparam; 451 return (0); 452 } 453 454 int 455 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 456 { 457 (*sa)->sa_schedpolicy = schedpolicy; 458 return (0); 459 } 460 461 int 462 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 463 const sigset_t * __restrict sigdefault) 464 { 465 (*sa)->sa_sigdefault = *sigdefault; 466 return (0); 467 } 468 469 int 470 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 471 const sigset_t * __restrict sigmask) 472 { 473 (*sa)->sa_sigmask = *sigmask; 474 return (0); 475 } 476