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 * $FreeBSD: src/lib/libc/gen/posix_spawn.c,v 1.6 2008/10/23 15:32:06 rdivacky Exp $ 27 */ 28 29 #include "namespace.h" 30 #include <sys/queue.h> 31 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <sched.h> 35 #include <spawn.h> 36 #include <signal.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include "un-namespace.h" 41 #include "libc_private.h" 42 43 extern char **environ; 44 45 struct __posix_spawnattr { 46 short sa_flags; 47 pid_t sa_pgroup; 48 struct sched_param sa_schedparam; 49 int sa_schedpolicy; 50 sigset_t sa_sigdefault; 51 sigset_t sa_sigmask; 52 }; 53 54 struct __posix_spawn_file_actions { 55 STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; 56 }; 57 58 typedef struct __posix_spawn_file_actions_entry { 59 STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; 60 enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action; 61 62 int fae_fildes; 63 union { 64 struct { 65 char *path; 66 #define fae_path fae_data.open.path 67 int oflag; 68 #define fae_oflag fae_data.open.oflag 69 mode_t mode; 70 #define fae_mode fae_data.open.mode 71 } open; 72 struct { 73 int newfildes; 74 #define fae_newfildes fae_data.dup2.newfildes 75 } dup2; 76 } fae_data; 77 } posix_spawn_file_actions_entry_t; 78 79 /* 80 * Spawn routines 81 */ 82 83 static int 84 process_spawnattr(const posix_spawnattr_t sa) 85 { 86 struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL }; 87 int i; 88 89 /* 90 * POSIX doesn't really describe in which order everything 91 * should be set. We'll just set them in the order in which they 92 * are mentioned. 93 */ 94 95 /* Set process group */ 96 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) { 97 if (setpgid(0, sa->sa_pgroup) != 0) 98 return (errno); 99 } 100 101 /* Set scheduler policy */ 102 if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) { 103 if (sched_setscheduler(0, sa->sa_schedpolicy, 104 &sa->sa_schedparam) != 0) 105 return (errno); 106 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) { 107 if (sched_setparam(0, &sa->sa_schedparam) != 0) 108 return (errno); 109 } 110 111 /* Reset user ID's */ 112 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) { 113 if (setegid(getgid()) != 0) 114 return (errno); 115 if (seteuid(getuid()) != 0) 116 return (errno); 117 } 118 119 /* Set signal masks/defaults */ 120 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) { 121 _sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL); 122 } 123 124 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) { 125 for (i = 1; i <= _SIG_MAXSIG; i++) { 126 if (sigismember(&sa->sa_sigdefault, i)) 127 if (_sigaction(i, &sigact, NULL) != 0) 128 return (errno); 129 } 130 } 131 132 return (0); 133 } 134 135 static int 136 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae) 137 { 138 int fd; 139 140 switch (fae->fae_action) { 141 case FAE_OPEN: 142 /* Perform an open(), make it use the right fd */ 143 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode); 144 if (fd < 0) 145 return (errno); 146 if (fd != fae->fae_fildes) { 147 if (_dup2(fd, fae->fae_fildes) == -1) 148 return (errno); 149 if (_close(fd) != 0) { 150 if (errno == EBADF) 151 return (EBADF); 152 } 153 } 154 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1) 155 return (errno); 156 break; 157 case FAE_DUP2: 158 /* Perform a dup2() */ 159 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1) 160 return (errno); 161 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1) 162 return (errno); 163 break; 164 case FAE_CLOSE: 165 /* Perform a close() */ 166 if (_close(fae->fae_fildes) != 0) { 167 if (errno == EBADF) 168 return (EBADF); 169 } 170 break; 171 } 172 return (0); 173 } 174 175 static int 176 process_file_actions(const posix_spawn_file_actions_t fa) 177 { 178 posix_spawn_file_actions_entry_t *fae; 179 int error; 180 181 /* Replay all file descriptor modifications */ 182 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) { 183 error = process_file_actions_entry(fae); 184 if (error) 185 return (error); 186 } 187 return (0); 188 } 189 190 static int 191 do_posix_spawn(pid_t *pid, const char *path, 192 const posix_spawn_file_actions_t *fa, 193 const posix_spawnattr_t *sa, char * const argv[], 194 char * const envp[], int use_env_path) 195 { 196 pid_t p; 197 volatile int error = 0; 198 199 p = vfork(); 200 switch (p) { 201 case -1: 202 return (errno); 203 case 0: 204 if (sa != NULL) { 205 error = process_spawnattr(*sa); 206 if (error) 207 _exit(127); 208 } 209 if (fa != NULL) { 210 error = process_file_actions(*fa); 211 if (error) 212 _exit(127); 213 } 214 if (use_env_path) 215 _execvpe(path, argv, envp != NULL ? envp : environ); 216 else 217 _execve(path, argv, envp != NULL ? envp : environ); 218 error = errno; 219 _exit(127); 220 default: 221 if (pid != NULL) 222 *pid = p; 223 return (error); 224 } 225 } 226 227 int 228 posix_spawn(pid_t * __restrict pid, const char * __restrict path, 229 const posix_spawn_file_actions_t *fa, 230 const posix_spawnattr_t * __restrict sa, char * const argv[__restrict_arr], 231 char * const envp[__restrict_arr]) 232 { 233 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 234 } 235 236 int 237 posix_spawnp(pid_t * __restrict pid, const char * __restrict path, 238 const posix_spawn_file_actions_t *fa, 239 const posix_spawnattr_t * __restrict sa, char * const argv[__restrict_arr], 240 char * const envp[__restrict_arr]) 241 { 242 return do_posix_spawn(pid, path, fa, sa, argv, envp, 1); 243 } 244 245 /* 246 * File descriptor actions 247 */ 248 249 int 250 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) 251 { 252 posix_spawn_file_actions_t fa; 253 254 fa = malloc(sizeof(struct __posix_spawn_file_actions)); 255 if (fa == NULL) 256 return (-1); 257 258 STAILQ_INIT(&fa->fa_list); 259 *ret = fa; 260 return (0); 261 } 262 263 int 264 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) 265 { 266 posix_spawn_file_actions_entry_t *fae; 267 268 while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) { 269 /* Remove file action entry from the queue */ 270 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); 271 272 /* Deallocate file action entry */ 273 if (fae->fae_action == FAE_OPEN) 274 free(fae->fae_path); 275 free(fae); 276 } 277 278 free(*fa); 279 return (0); 280 } 281 282 int 283 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa, 284 int fildes, const char * __restrict path, 285 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