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