1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "namespace.h" 33 #include <sys/queue.h> 34 #include <sys/wait.h> 35 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <sched.h> 39 #include <spawn.h> 40 #include <signal.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include "un-namespace.h" 45 #include "libc_private.h" 46 47 extern char **environ; 48 49 struct __posix_spawnattr { 50 short sa_flags; 51 pid_t sa_pgroup; 52 struct sched_param sa_schedparam; 53 int sa_schedpolicy; 54 sigset_t sa_sigdefault; 55 sigset_t sa_sigmask; 56 }; 57 58 struct __posix_spawn_file_actions { 59 STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; 60 }; 61 62 typedef struct __posix_spawn_file_actions_entry { 63 STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; 64 enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action; 65 66 int fae_fildes; 67 union { 68 struct { 69 char *path; 70 #define fae_path fae_data.open.path 71 int oflag; 72 #define fae_oflag fae_data.open.oflag 73 mode_t mode; 74 #define fae_mode fae_data.open.mode 75 } open; 76 struct { 77 int newfildes; 78 #define fae_newfildes fae_data.dup2.newfildes 79 } dup2; 80 } fae_data; 81 } posix_spawn_file_actions_entry_t; 82 83 /* 84 * Spawn routines 85 */ 86 87 static int 88 process_spawnattr(const posix_spawnattr_t sa) 89 { 90 struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL }; 91 int i; 92 93 /* 94 * POSIX doesn't really describe in which order everything 95 * should be set. We'll just set them in the order in which they 96 * are mentioned. 97 */ 98 99 /* Set process group */ 100 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) { 101 if (setpgid(0, sa->sa_pgroup) != 0) 102 return (errno); 103 } 104 105 /* Set scheduler policy */ 106 if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) { 107 if (sched_setscheduler(0, sa->sa_schedpolicy, 108 &sa->sa_schedparam) != 0) 109 return (errno); 110 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) { 111 if (sched_setparam(0, &sa->sa_schedparam) != 0) 112 return (errno); 113 } 114 115 /* Reset user ID's */ 116 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) { 117 if (setegid(getgid()) != 0) 118 return (errno); 119 if (seteuid(getuid()) != 0) 120 return (errno); 121 } 122 123 /* 124 * Set signal masks/defaults. 125 * Use unwrapped syscall, libthr is in undefined state after vfork(). 126 */ 127 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) { 128 __sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL); 129 } 130 131 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) { 132 for (i = 1; i <= _SIG_MAXSIG; i++) { 133 if (sigismember(&sa->sa_sigdefault, i)) 134 if (__sys_sigaction(i, &sigact, NULL) != 0) 135 return (errno); 136 } 137 } 138 139 return (0); 140 } 141 142 static int 143 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae) 144 { 145 int fd, saved_errno; 146 147 switch (fae->fae_action) { 148 case FAE_OPEN: 149 /* Perform an open(), make it use the right fd */ 150 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode); 151 if (fd < 0) 152 return (errno); 153 if (fd != fae->fae_fildes) { 154 if (_dup2(fd, fae->fae_fildes) == -1) { 155 saved_errno = errno; 156 (void)_close(fd); 157 return (saved_errno); 158 } 159 if (_close(fd) != 0) { 160 if (errno == EBADF) 161 return (EBADF); 162 } 163 } 164 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1) 165 return (errno); 166 break; 167 case FAE_DUP2: 168 /* Perform a dup2() */ 169 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1) 170 return (errno); 171 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1) 172 return (errno); 173 break; 174 case FAE_CLOSE: 175 /* Perform a close(), do not fail if already closed */ 176 (void)_close(fae->fae_fildes); 177 break; 178 } 179 return (0); 180 } 181 182 static int 183 process_file_actions(const posix_spawn_file_actions_t fa) 184 { 185 posix_spawn_file_actions_entry_t *fae; 186 int error; 187 188 /* Replay all file descriptor modifications */ 189 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) { 190 error = process_file_actions_entry(fae); 191 if (error) 192 return (error); 193 } 194 return (0); 195 } 196 197 static int 198 do_posix_spawn(pid_t *pid, const char *path, 199 const posix_spawn_file_actions_t *fa, 200 const posix_spawnattr_t *sa, 201 char * const argv[], char * const envp[], int use_env_path) 202 { 203 pid_t p; 204 volatile int error = 0; 205 206 p = vfork(); 207 switch (p) { 208 case -1: 209 return (errno); 210 case 0: 211 if (sa != NULL) { 212 error = process_spawnattr(*sa); 213 if (error) 214 _exit(127); 215 } 216 if (fa != NULL) { 217 error = process_file_actions(*fa); 218 if (error) 219 _exit(127); 220 } 221 if (use_env_path) 222 _execvpe(path, argv, envp != NULL ? envp : environ); 223 else 224 _execve(path, argv, envp != NULL ? envp : environ); 225 error = errno; 226 _exit(127); 227 default: 228 if (error != 0) 229 _waitpid(p, NULL, WNOHANG); 230 else if (pid != NULL) 231 *pid = p; 232 return (error); 233 } 234 } 235 236 int 237 posix_spawn(pid_t *pid, const char *path, 238 const posix_spawn_file_actions_t *fa, 239 const posix_spawnattr_t *sa, 240 char * const argv[], char * const envp[]) 241 { 242 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 243 } 244 245 int 246 posix_spawnp(pid_t *pid, const char *path, 247 const posix_spawn_file_actions_t *fa, 248 const posix_spawnattr_t *sa, 249 char * const argv[], char * const envp[]) 250 { 251 return do_posix_spawn(pid, path, fa, sa, argv, envp, 1); 252 } 253 254 /* 255 * File descriptor actions 256 */ 257 258 int 259 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) 260 { 261 posix_spawn_file_actions_t fa; 262 263 fa = malloc(sizeof(struct __posix_spawn_file_actions)); 264 if (fa == NULL) 265 return (-1); 266 267 STAILQ_INIT(&fa->fa_list); 268 *ret = fa; 269 return (0); 270 } 271 272 int 273 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) 274 { 275 posix_spawn_file_actions_entry_t *fae; 276 277 while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) { 278 /* Remove file action entry from the queue */ 279 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); 280 281 /* Deallocate file action entry */ 282 if (fae->fae_action == FAE_OPEN) 283 free(fae->fae_path); 284 free(fae); 285 } 286 287 free(*fa); 288 return (0); 289 } 290 291 int 292 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa, 293 int fildes, const char * __restrict path, int oflag, mode_t mode) 294 { 295 posix_spawn_file_actions_entry_t *fae; 296 int error; 297 298 if (fildes < 0) 299 return (EBADF); 300 301 /* Allocate object */ 302 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 303 if (fae == NULL) 304 return (errno); 305 306 /* Set values and store in queue */ 307 fae->fae_action = FAE_OPEN; 308 fae->fae_path = strdup(path); 309 if (fae->fae_path == NULL) { 310 error = errno; 311 free(fae); 312 return (error); 313 } 314 fae->fae_fildes = fildes; 315 fae->fae_oflag = oflag; 316 fae->fae_mode = mode; 317 318 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 319 return (0); 320 } 321 322 int 323 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, 324 int fildes, int newfildes) 325 { 326 posix_spawn_file_actions_entry_t *fae; 327 328 if (fildes < 0 || newfildes < 0) 329 return (EBADF); 330 331 /* Allocate object */ 332 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 333 if (fae == NULL) 334 return (errno); 335 336 /* Set values and store in queue */ 337 fae->fae_action = FAE_DUP2; 338 fae->fae_fildes = fildes; 339 fae->fae_newfildes = newfildes; 340 341 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 342 return (0); 343 } 344 345 int 346 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, 347 int fildes) 348 { 349 posix_spawn_file_actions_entry_t *fae; 350 351 if (fildes < 0) 352 return (EBADF); 353 354 /* Allocate object */ 355 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 356 if (fae == NULL) 357 return (errno); 358 359 /* Set values and store in queue */ 360 fae->fae_action = FAE_CLOSE; 361 fae->fae_fildes = fildes; 362 363 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 364 return (0); 365 } 366 367 /* 368 * Spawn attributes 369 */ 370 371 int 372 posix_spawnattr_init(posix_spawnattr_t *ret) 373 { 374 posix_spawnattr_t sa; 375 376 sa = calloc(1, sizeof(struct __posix_spawnattr)); 377 if (sa == NULL) 378 return (errno); 379 380 /* Set defaults as specified by POSIX, cleared above */ 381 *ret = sa; 382 return (0); 383 } 384 385 int 386 posix_spawnattr_destroy(posix_spawnattr_t *sa) 387 { 388 free(*sa); 389 return (0); 390 } 391 392 int 393 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 394 short * __restrict flags) 395 { 396 *flags = (*sa)->sa_flags; 397 return (0); 398 } 399 400 int 401 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 402 pid_t * __restrict pgroup) 403 { 404 *pgroup = (*sa)->sa_pgroup; 405 return (0); 406 } 407 408 int 409 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa, 410 struct sched_param * __restrict schedparam) 411 { 412 *schedparam = (*sa)->sa_schedparam; 413 return (0); 414 } 415 416 int 417 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 418 int * __restrict schedpolicy) 419 { 420 *schedpolicy = (*sa)->sa_schedpolicy; 421 return (0); 422 } 423 424 int 425 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 426 sigset_t * __restrict sigdefault) 427 { 428 *sigdefault = (*sa)->sa_sigdefault; 429 return (0); 430 } 431 432 int 433 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 434 sigset_t * __restrict sigmask) 435 { 436 *sigmask = (*sa)->sa_sigmask; 437 return (0); 438 } 439 440 int 441 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 442 { 443 (*sa)->sa_flags = flags; 444 return (0); 445 } 446 447 int 448 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 449 { 450 (*sa)->sa_pgroup = pgroup; 451 return (0); 452 } 453 454 int 455 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa, 456 const struct sched_param * __restrict schedparam) 457 { 458 (*sa)->sa_schedparam = *schedparam; 459 return (0); 460 } 461 462 int 463 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 464 { 465 (*sa)->sa_schedpolicy = schedpolicy; 466 return (0); 467 } 468 469 int 470 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 471 const sigset_t * __restrict sigdefault) 472 { 473 (*sa)->sa_sigdefault = *sigdefault; 474 return (0); 475 } 476 477 int 478 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 479 const sigset_t * __restrict sigmask) 480 { 481 (*sa)->sa_sigmask = *sigmask; 482 return (0); 483 } 484