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 *pid, const char *path, const posix_spawn_file_actions_t *fa, 229 const posix_spawnattr_t *sa, char * const argv[], 230 char * const envp[]) 231 { 232 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 233 } 234 235 int 236 posix_spawnp(pid_t *pid, const char *path, const posix_spawn_file_actions_t *fa, 237 const posix_spawnattr_t *sa, char * const argv[], 238 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, 283 int oflag, mode_t mode) 284 { 285 posix_spawn_file_actions_entry_t *fae; 286 int error; 287 288 if (fildes < 0) 289 return (EBADF); 290 291 /* Allocate object */ 292 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 293 if (fae == NULL) 294 return (errno); 295 296 /* Set values and store in queue */ 297 fae->fae_action = FAE_OPEN; 298 fae->fae_path = strdup(path); 299 if (fae->fae_path == NULL) { 300 error = errno; 301 free(fae); 302 return (error); 303 } 304 fae->fae_fildes = fildes; 305 fae->fae_oflag = oflag; 306 fae->fae_mode = mode; 307 308 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 309 return (0); 310 } 311 312 int 313 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, 314 int fildes, int newfildes) 315 { 316 posix_spawn_file_actions_entry_t *fae; 317 318 if (fildes < 0 || newfildes < 0) 319 return (EBADF); 320 321 /* Allocate object */ 322 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 323 if (fae == NULL) 324 return (errno); 325 326 /* Set values and store in queue */ 327 fae->fae_action = FAE_DUP2; 328 fae->fae_fildes = fildes; 329 fae->fae_newfildes = newfildes; 330 331 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 332 return (0); 333 } 334 335 int 336 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, 337 int fildes) 338 { 339 posix_spawn_file_actions_entry_t *fae; 340 341 if (fildes < 0) 342 return (EBADF); 343 344 /* Allocate object */ 345 fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 346 if (fae == NULL) 347 return (errno); 348 349 /* Set values and store in queue */ 350 fae->fae_action = FAE_CLOSE; 351 fae->fae_fildes = fildes; 352 353 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 354 return (0); 355 } 356 357 /* 358 * Spawn attributes 359 */ 360 361 int 362 posix_spawnattr_init(posix_spawnattr_t *ret) 363 { 364 posix_spawnattr_t sa; 365 366 sa = calloc(1, sizeof(struct __posix_spawnattr)); 367 if (sa == NULL) 368 return (errno); 369 370 /* Set defaults as specified by POSIX, cleared above */ 371 *ret = sa; 372 return (0); 373 } 374 375 int 376 posix_spawnattr_destroy(posix_spawnattr_t *sa) 377 { 378 free(*sa); 379 return (0); 380 } 381 382 int 383 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 384 short * __restrict flags) 385 { 386 *flags = (*sa)->sa_flags; 387 return (0); 388 } 389 390 int 391 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 392 pid_t * __restrict pgroup) 393 { 394 *pgroup = (*sa)->sa_pgroup; 395 return (0); 396 } 397 398 int 399 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa, 400 struct sched_param * __restrict schedparam) 401 { 402 *schedparam = (*sa)->sa_schedparam; 403 return (0); 404 } 405 406 int 407 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 408 int * __restrict schedpolicy) 409 { 410 *schedpolicy = (*sa)->sa_schedpolicy; 411 return (0); 412 } 413 414 int 415 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 416 sigset_t * __restrict sigdefault) 417 { 418 *sigdefault = (*sa)->sa_sigdefault; 419 return (0); 420 } 421 422 int 423 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 424 sigset_t * __restrict sigmask) 425 { 426 *sigmask = (*sa)->sa_sigmask; 427 return (0); 428 } 429 430 int 431 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 432 { 433 (*sa)->sa_flags = flags; 434 return (0); 435 } 436 437 int 438 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 439 { 440 (*sa)->sa_pgroup = pgroup; 441 return (0); 442 } 443 444 int 445 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa, 446 const struct sched_param * __restrict schedparam) 447 { 448 (*sa)->sa_schedparam = *schedparam; 449 return (0); 450 } 451 452 int 453 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 454 { 455 (*sa)->sa_schedpolicy = schedpolicy; 456 return (0); 457 } 458 459 int 460 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 461 const sigset_t * __restrict sigdefault) 462 { 463 (*sa)->sa_sigdefault = *sigdefault; 464 return (0); 465 } 466 467 int 468 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 469 const sigset_t * __restrict sigmask) 470 { 471 (*sa)->sa_sigmask = *sigmask; 472 return (0); 473 } 474