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