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() */ 167 if (_close(fae->fae_fildes) != 0) { 168 if (errno == EBADF) 169 return (EBADF); 170 } 171 break; 172 } 173 return (0); 174 } 175 176 static int 177 process_file_actions(const posix_spawn_file_actions_t fa) 178 { 179 posix_spawn_file_actions_entry_t *fae; 180 int error; 181 182 /* Replay all file descriptor modifications */ 183 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) { 184 error = process_file_actions_entry(fae); 185 if (error) 186 return (error); 187 } 188 return (0); 189 } 190 191 static int 192 do_posix_spawn(pid_t *pid, const char *path, 193 const posix_spawn_file_actions_t *fa, 194 const posix_spawnattr_t *sa, 195 char * const argv[], char * const envp[], int use_env_path) 196 { 197 pid_t p; 198 volatile int error = 0; 199 200 p = vfork(); 201 switch (p) { 202 case -1: 203 return (errno); 204 case 0: 205 if (sa != NULL) { 206 error = process_spawnattr(*sa); 207 if (error) 208 _exit(127); 209 } 210 if (fa != NULL) { 211 error = process_file_actions(*fa); 212 if (error) 213 _exit(127); 214 } 215 if (use_env_path) 216 _execvpe(path, argv, envp != NULL ? envp : environ); 217 else 218 _execve(path, argv, envp != NULL ? envp : environ); 219 error = errno; 220 _exit(127); 221 default: 222 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