1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "lint.h" 28 #include "thr_uberdata.h" 29 #include <sys/libc_kernel.h> 30 #include <sys/procset.h> 31 #include <sys/fork.h> 32 #include <alloca.h> 33 #include <spawn.h> 34 35 #define ALL_POSIX_SPAWN_FLAGS \ 36 (POSIX_SPAWN_RESETIDS | \ 37 POSIX_SPAWN_SETPGROUP | \ 38 POSIX_SPAWN_SETSIGDEF | \ 39 POSIX_SPAWN_SETSIGMASK | \ 40 POSIX_SPAWN_SETSCHEDPARAM | \ 41 POSIX_SPAWN_SETSCHEDULER | \ 42 POSIX_SPAWN_SETSIGIGN_NP | \ 43 POSIX_SPAWN_NOSIGCHLD_NP | \ 44 POSIX_SPAWN_WAITPID_NP | \ 45 POSIX_SPAWN_NOEXECERR_NP) 46 47 typedef struct { 48 int sa_psflags; /* POSIX_SPAWN_* flags */ 49 int sa_priority; 50 int sa_schedpolicy; 51 pid_t sa_pgroup; 52 sigset_t sa_sigdefault; 53 sigset_t sa_sigignore; 54 sigset_t sa_sigmask; 55 } spawn_attr_t; 56 57 typedef struct file_attr { 58 struct file_attr *fa_next; /* circular list of file actions */ 59 struct file_attr *fa_prev; 60 enum {FA_OPEN, FA_CLOSE, FA_DUP2} fa_type; 61 uint_t fa_pathsize; /* size of fa_path[] array */ 62 char *fa_path; /* copied pathname for open() */ 63 int fa_oflag; /* oflag for open() */ 64 mode_t fa_mode; /* mode for open() */ 65 int fa_filedes; /* file descriptor for open()/close() */ 66 int fa_newfiledes; /* new file descriptor for dup2() */ 67 } file_attr_t; 68 69 extern int __lwp_sigmask(int, const sigset_t *, sigset_t *); 70 extern int __sigaction(int, const struct sigaction *, struct sigaction *); 71 72 static int 73 perform_flag_actions(spawn_attr_t *sap) 74 { 75 int sig; 76 struct sigaction action; 77 78 if (sap->sa_psflags & POSIX_SPAWN_SETSIGMASK) { 79 (void) __lwp_sigmask(SIG_SETMASK, &sap->sa_sigmask, NULL); 80 } 81 82 if (sap->sa_psflags & POSIX_SPAWN_SETSIGIGN_NP) { 83 (void) memset(&action, 0, sizeof (action)); 84 action.sa_handler = SIG_IGN; 85 for (sig = 1; sig < NSIG; sig++) { 86 if (sigismember(&sap->sa_sigignore, sig)) 87 (void) __sigaction(sig, &action, NULL); 88 } 89 } 90 91 if (sap->sa_psflags & POSIX_SPAWN_SETSIGDEF) { 92 (void) memset(&action, 0, sizeof (action)); 93 action.sa_handler = SIG_DFL; 94 for (sig = 1; sig < NSIG; sig++) { 95 if (sigismember(&sap->sa_sigdefault, sig)) 96 (void) __sigaction(sig, &action, NULL); 97 } 98 } 99 100 if (sap->sa_psflags & POSIX_SPAWN_RESETIDS) { 101 if (setgid(getgid()) != 0 || setuid(getuid()) != 0) 102 return (errno); 103 } 104 105 if (sap->sa_psflags & POSIX_SPAWN_SETPGROUP) { 106 if (setpgid(0, sap->sa_pgroup) != 0) 107 return (errno); 108 } 109 110 if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDULER) { 111 if (setparam(P_LWPID, P_MYID, 112 sap->sa_schedpolicy, sap->sa_priority) == -1) 113 return (errno); 114 } else if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDPARAM) { 115 if (setprio(P_LWPID, P_MYID, sap->sa_priority, NULL) == -1) 116 return (errno); 117 } 118 119 return (0); 120 } 121 122 static int 123 perform_file_actions(file_attr_t *fap) 124 { 125 file_attr_t *froot = fap; 126 int fd; 127 128 do { 129 switch (fap->fa_type) { 130 case FA_OPEN: 131 fd = __open(fap->fa_path, 132 fap->fa_oflag, fap->fa_mode); 133 if (fd < 0) 134 return (errno); 135 if (fd != fap->fa_filedes) { 136 if (__fcntl(fd, F_DUP2FD, fap->fa_filedes) < 0) 137 return (errno); 138 (void) __close(fd); 139 } 140 break; 141 case FA_CLOSE: 142 if (__close(fap->fa_filedes) == -1) 143 return (errno); 144 break; 145 case FA_DUP2: 146 fd = __fcntl(fap->fa_filedes, F_DUP2FD, 147 fap->fa_newfiledes); 148 if (fd < 0) 149 return (errno); 150 break; 151 } 152 } while ((fap = fap->fa_next) != froot); 153 154 return (0); 155 } 156 157 static int 158 forkflags(spawn_attr_t *sap) 159 { 160 int flags = 0; 161 162 if (sap != NULL) { 163 if (sap->sa_psflags & POSIX_SPAWN_NOSIGCHLD_NP) 164 flags |= FORK_NOSIGCHLD; 165 if (sap->sa_psflags & POSIX_SPAWN_WAITPID_NP) 166 flags |= FORK_WAITPID; 167 } 168 169 return (flags); 170 } 171 172 /* 173 * set_error() / get_error() are used to guarantee that the local variable 174 * 'error' is set correctly in memory on return from vfork() in the parent. 175 */ 176 177 static int 178 set_error(int *errp, int err) 179 { 180 return (*errp = err); 181 } 182 183 static int 184 get_error(int *errp) 185 { 186 return (*errp); 187 } 188 189 /* 190 * For MT safety, do not invoke the dynamic linker after calling vfork(). 191 * If some other thread was in the dynamic linker when this thread's parent 192 * called vfork() then the dynamic linker's lock would still be held here 193 * (with a defunct owner) and we would deadlock ourself if we invoked it. 194 * 195 * Therefore, all of the functions we call here after returning from 196 * vforkx() in the child are not and must never be exported from libc 197 * as global symbols. To do so would risk invoking the dynamic linker. 198 */ 199 200 int 201 posix_spawn( 202 pid_t *pidp, 203 const char *path, 204 const posix_spawn_file_actions_t *file_actions, 205 const posix_spawnattr_t *attrp, 206 char *const argv[], 207 char *const envp[]) 208 { 209 spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL; 210 file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL; 211 int error; /* this will be set by the child */ 212 pid_t pid; 213 214 if (attrp != NULL && sap == NULL) 215 return (EINVAL); 216 217 switch (pid = vforkx(forkflags(sap))) { 218 case 0: /* child */ 219 break; 220 case -1: /* parent, failure */ 221 return (errno); 222 default: /* parent, success */ 223 /* 224 * We don't get here until the child exec()s or exit()s 225 */ 226 if (pidp != NULL && get_error(&error) == 0) 227 *pidp = pid; 228 return (get_error(&error)); 229 } 230 231 if (sap != NULL) 232 if (set_error(&error, perform_flag_actions(sap)) != 0) 233 _exit(_EVAPORATE); 234 235 if (fap != NULL) 236 if (set_error(&error, perform_file_actions(fap)) != 0) 237 _exit(_EVAPORATE); 238 239 (void) set_error(&error, 0); 240 (void) execve(path, argv, envp); 241 if (sap != NULL && (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP)) 242 _exit(127); 243 (void) set_error(&error, errno); 244 _exit(_EVAPORATE); 245 return (0); /* not reached */ 246 } 247 248 /* 249 * Much of posix_spawnp() blatently stolen from execvp() (port/gen/execvp.c). 250 */ 251 252 extern int libc__xpg4; 253 254 static const char * 255 execat(const char *s1, const char *s2, char *si) 256 { 257 int cnt = PATH_MAX + 1; 258 char *s; 259 char c; 260 261 for (s = si; (c = *s1) != '\0' && c != ':'; s1++) { 262 if (cnt > 0) { 263 *s++ = c; 264 cnt--; 265 } 266 } 267 if (si != s && cnt > 0) { 268 *s++ = '/'; 269 cnt--; 270 } 271 for (; (c = *s2) != '\0' && cnt > 0; s2++) { 272 *s++ = c; 273 cnt--; 274 } 275 *s = '\0'; 276 return (*s1? ++s1: NULL); 277 } 278 279 /* ARGSUSED */ 280 int 281 posix_spawnp( 282 pid_t *pidp, 283 const char *file, 284 const posix_spawn_file_actions_t *file_actions, 285 const posix_spawnattr_t *attrp, 286 char *const argv[], 287 char *const envp[]) 288 { 289 spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL; 290 file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL; 291 const char *pathstr = (strchr(file, '/') == NULL)? getenv("PATH") : ""; 292 int xpg4 = libc__xpg4; 293 int error = 0; /* this will be set by the child */ 294 char path[PATH_MAX+4]; 295 const char *cp; 296 pid_t pid; 297 char **newargs; 298 int argc; 299 int i; 300 static const char *sun_path = "/bin/sh"; 301 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 302 static const char *shell = "sh"; 303 304 if (attrp != NULL && sap == NULL) 305 return (EINVAL); 306 307 if (*file == '\0') 308 return (EACCES); 309 310 /* 311 * We may need to invoke the shell with a slightly modified 312 * argv[] array. To do this we need to preallocate the array. 313 * We must call alloca() before calling vfork() because doing 314 * it after vfork() (in the child) would corrupt the parent. 315 */ 316 for (argc = 0; argv[argc] != NULL; argc++) 317 continue; 318 newargs = alloca((argc + 2) * sizeof (char *)); 319 320 switch (pid = vforkx(forkflags(sap))) { 321 case 0: /* child */ 322 break; 323 case -1: /* parent, failure */ 324 return (errno); 325 default: /* parent, success */ 326 /* 327 * We don't get here until the child exec()s or exit()s 328 */ 329 if (pidp != NULL && get_error(&error) == 0) 330 *pidp = pid; 331 return (get_error(&error)); 332 } 333 334 if (sap != NULL) 335 if (set_error(&error, perform_flag_actions(sap)) != 0) 336 _exit(_EVAPORATE); 337 338 if (fap != NULL) 339 if (set_error(&error, perform_file_actions(fap)) != 0) 340 _exit(_EVAPORATE); 341 342 if (pathstr == NULL) { 343 /* 344 * XPG4: pathstr is equivalent to _CS_PATH, except that 345 * :/usr/sbin is appended when root, and pathstr must end 346 * with a colon when not root. Keep these paths in sync 347 * with _CS_PATH in confstr.c. Note that pathstr must end 348 * with a colon when not root so that when file doesn't 349 * contain '/', the last call to execat() will result in an 350 * attempt to execv file from the current directory. 351 */ 352 if (geteuid() == 0 || getuid() == 0) { 353 if (!xpg4) 354 pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin"; 355 else 356 pathstr = "/usr/xpg4/bin:/usr/ccs/bin:" 357 "/usr/bin:/opt/SUNWspro/bin:/usr/sbin"; 358 } else { 359 if (!xpg4) 360 pathstr = "/usr/ccs/bin:/usr/bin:"; 361 else 362 pathstr = "/usr/xpg4/bin:/usr/ccs/bin:" 363 "/usr/bin:/opt/SUNWspro/bin:"; 364 } 365 } 366 367 cp = pathstr; 368 do { 369 cp = execat(cp, file, path); 370 /* 371 * 4025035 and 4038378 372 * if a filename begins with a "-" prepend "./" so that 373 * the shell can't interpret it as an option 374 */ 375 if (*path == '-') { 376 char *s; 377 378 for (s = path; *s != '\0'; s++) 379 continue; 380 for (; s >= path; s--) 381 *(s + 2) = *s; 382 path[0] = '.'; 383 path[1] = '/'; 384 } 385 (void) set_error(&error, 0); 386 (void) execve(path, argv, envp); 387 if (set_error(&error, errno) == ENOEXEC) { 388 newargs[0] = (char *)shell; 389 newargs[1] = path; 390 for (i = 1; i <= argc; i++) 391 newargs[i + 1] = argv[i]; 392 (void) set_error(&error, 0); 393 (void) execve(xpg4? xpg4_path : sun_path, 394 newargs, envp); 395 if (sap != NULL && 396 (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP)) 397 _exit(127); 398 (void) set_error(&error, errno); 399 _exit(_EVAPORATE); 400 } 401 } while (cp); 402 403 if (sap != NULL && 404 (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP)) { 405 (void) set_error(&error, 0); 406 _exit(127); 407 } 408 _exit(_EVAPORATE); 409 return (0); /* not reached */ 410 } 411 412 int 413 posix_spawn_file_actions_init( 414 posix_spawn_file_actions_t *file_actions) 415 { 416 file_actions->__file_attrp = NULL; 417 return (0); 418 } 419 420 int 421 posix_spawn_file_actions_destroy( 422 posix_spawn_file_actions_t *file_actions) 423 { 424 file_attr_t *froot = file_actions->__file_attrp; 425 file_attr_t *fap; 426 file_attr_t *next; 427 428 if ((fap = froot) != NULL) { 429 do { 430 next = fap->fa_next; 431 if (fap-> fa_type == FA_OPEN) 432 lfree(fap->fa_path, fap->fa_pathsize); 433 lfree(fap, sizeof (*fap)); 434 } while ((fap = next) != froot); 435 } 436 file_actions->__file_attrp = NULL; 437 return (0); 438 } 439 440 static void 441 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap) 442 { 443 file_attr_t *froot = file_actions->__file_attrp; 444 445 if (froot == NULL) { 446 fap->fa_next = fap->fa_prev = fap; 447 file_actions->__file_attrp = fap; 448 } else { 449 fap->fa_next = froot; 450 fap->fa_prev = froot->fa_prev; 451 froot->fa_prev->fa_next = fap; 452 froot->fa_prev = fap; 453 } 454 } 455 456 int 457 posix_spawn_file_actions_addopen( 458 posix_spawn_file_actions_t *file_actions, 459 int filedes, 460 const char *path, 461 int oflag, 462 mode_t mode) 463 { 464 file_attr_t *fap; 465 466 if (filedes < 0) 467 return (EBADF); 468 if ((fap = lmalloc(sizeof (*fap))) == NULL) 469 return (ENOMEM); 470 471 fap->fa_pathsize = strlen(path) + 1; 472 if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) { 473 lfree(fap, sizeof (*fap)); 474 return (ENOMEM); 475 } 476 (void) strcpy(fap->fa_path, path); 477 478 fap->fa_type = FA_OPEN; 479 fap->fa_oflag = oflag; 480 fap->fa_mode = mode; 481 fap->fa_filedes = filedes; 482 add_file_attr(file_actions, fap); 483 484 return (0); 485 } 486 487 int 488 posix_spawn_file_actions_addclose( 489 posix_spawn_file_actions_t *file_actions, 490 int filedes) 491 { 492 file_attr_t *fap; 493 494 if (filedes < 0) 495 return (EBADF); 496 if ((fap = lmalloc(sizeof (*fap))) == NULL) 497 return (ENOMEM); 498 499 fap->fa_type = FA_CLOSE; 500 fap->fa_filedes = filedes; 501 add_file_attr(file_actions, fap); 502 503 return (0); 504 } 505 506 int 507 posix_spawn_file_actions_adddup2( 508 posix_spawn_file_actions_t *file_actions, 509 int filedes, 510 int newfiledes) 511 { 512 file_attr_t *fap; 513 514 if (filedes < 0 || newfiledes < 0) 515 return (EBADF); 516 if ((fap = lmalloc(sizeof (*fap))) == NULL) 517 return (ENOMEM); 518 519 fap->fa_type = FA_DUP2; 520 fap->fa_filedes = filedes; 521 fap->fa_newfiledes = newfiledes; 522 add_file_attr(file_actions, fap); 523 524 return (0); 525 } 526 527 int 528 posix_spawnattr_init( 529 posix_spawnattr_t *attr) 530 { 531 if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL) 532 return (ENOMEM); 533 /* 534 * Add default stuff here? 535 */ 536 return (0); 537 } 538 539 int 540 posix_spawnattr_destroy( 541 posix_spawnattr_t *attr) 542 { 543 spawn_attr_t *sap = attr->__spawn_attrp; 544 545 if (sap == NULL) 546 return (EINVAL); 547 548 /* 549 * deallocate stuff here? 550 */ 551 lfree(sap, sizeof (*sap)); 552 attr->__spawn_attrp = NULL; 553 return (0); 554 } 555 556 int 557 posix_spawnattr_setflags( 558 posix_spawnattr_t *attr, 559 short flags) 560 { 561 spawn_attr_t *sap = attr->__spawn_attrp; 562 563 if (sap == NULL || 564 (flags & ~ALL_POSIX_SPAWN_FLAGS)) 565 return (EINVAL); 566 567 sap->sa_psflags = flags; 568 return (0); 569 } 570 571 int 572 posix_spawnattr_getflags( 573 const posix_spawnattr_t *attr, 574 short *flags) 575 { 576 spawn_attr_t *sap = attr->__spawn_attrp; 577 578 if (sap == NULL) 579 return (EINVAL); 580 581 *flags = sap->sa_psflags; 582 return (0); 583 } 584 585 int 586 posix_spawnattr_setpgroup( 587 posix_spawnattr_t *attr, 588 pid_t pgroup) 589 { 590 spawn_attr_t *sap = attr->__spawn_attrp; 591 592 if (sap == NULL) 593 return (EINVAL); 594 595 sap->sa_pgroup = pgroup; 596 return (0); 597 } 598 599 int 600 posix_spawnattr_getpgroup( 601 const posix_spawnattr_t *attr, 602 pid_t *pgroup) 603 { 604 spawn_attr_t *sap = attr->__spawn_attrp; 605 606 if (sap == NULL) 607 return (EINVAL); 608 609 *pgroup = sap->sa_pgroup; 610 return (0); 611 } 612 613 int 614 posix_spawnattr_setschedparam( 615 posix_spawnattr_t *attr, 616 const struct sched_param *schedparam) 617 { 618 spawn_attr_t *sap = attr->__spawn_attrp; 619 620 if (sap == NULL) 621 return (EINVAL); 622 623 /* 624 * Check validity? 625 */ 626 sap->sa_priority = schedparam->sched_priority; 627 return (0); 628 } 629 630 int 631 posix_spawnattr_getschedparam( 632 const posix_spawnattr_t *attr, 633 struct sched_param *schedparam) 634 { 635 spawn_attr_t *sap = attr->__spawn_attrp; 636 637 if (sap == NULL) 638 return (EINVAL); 639 640 schedparam->sched_priority = sap->sa_priority; 641 return (0); 642 } 643 644 int 645 posix_spawnattr_setschedpolicy( 646 posix_spawnattr_t *attr, 647 int schedpolicy) 648 { 649 spawn_attr_t *sap = attr->__spawn_attrp; 650 651 if (sap == NULL || schedpolicy == SCHED_SYS) 652 return (EINVAL); 653 654 /* 655 * Cache the policy information for later use 656 * by the vfork() child of posix_spawn(). 657 */ 658 if (get_info_by_policy(schedpolicy) == NULL) 659 return (errno); 660 661 sap->sa_schedpolicy = schedpolicy; 662 return (0); 663 } 664 665 int 666 posix_spawnattr_getschedpolicy( 667 const posix_spawnattr_t *attr, 668 int *schedpolicy) 669 { 670 spawn_attr_t *sap = attr->__spawn_attrp; 671 672 if (sap == NULL) 673 return (EINVAL); 674 675 *schedpolicy = sap->sa_schedpolicy; 676 return (0); 677 } 678 679 int 680 posix_spawnattr_setsigdefault( 681 posix_spawnattr_t *attr, 682 const sigset_t *sigdefault) 683 { 684 spawn_attr_t *sap = attr->__spawn_attrp; 685 686 if (sap == NULL) 687 return (EINVAL); 688 689 sap->sa_sigdefault = *sigdefault; 690 return (0); 691 } 692 693 int 694 posix_spawnattr_getsigdefault( 695 const posix_spawnattr_t *attr, 696 sigset_t *sigdefault) 697 { 698 spawn_attr_t *sap = attr->__spawn_attrp; 699 700 if (sap == NULL) 701 return (EINVAL); 702 703 *sigdefault = sap->sa_sigdefault; 704 return (0); 705 } 706 707 int 708 posix_spawnattr_setsigignore_np( 709 posix_spawnattr_t *attr, 710 const sigset_t *sigignore) 711 { 712 spawn_attr_t *sap = attr->__spawn_attrp; 713 714 if (sap == NULL) 715 return (EINVAL); 716 717 sap->sa_sigignore = *sigignore; 718 return (0); 719 } 720 721 int 722 posix_spawnattr_getsigignore_np( 723 const posix_spawnattr_t *attr, 724 sigset_t *sigignore) 725 { 726 spawn_attr_t *sap = attr->__spawn_attrp; 727 728 if (sap == NULL) 729 return (EINVAL); 730 731 *sigignore = sap->sa_sigignore; 732 return (0); 733 } 734 735 int 736 posix_spawnattr_setsigmask( 737 posix_spawnattr_t *attr, 738 const sigset_t *sigmask) 739 { 740 spawn_attr_t *sap = attr->__spawn_attrp; 741 742 if (sap == NULL) 743 return (EINVAL); 744 745 sap->sa_sigmask = *sigmask; 746 return (0); 747 } 748 749 int 750 posix_spawnattr_getsigmask( 751 const posix_spawnattr_t *attr, 752 sigset_t *sigmask) 753 { 754 spawn_attr_t *sap = attr->__spawn_attrp; 755 756 if (sap == NULL) 757 return (EINVAL); 758 759 *sigmask = sap->sa_sigmask; 760 return (0); 761 } 762