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