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; /* 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 _exit(_EVAPORATE); 392 return (0); /* not reached */ 393 } 394 395 int 396 posix_spawn_file_actions_init( 397 posix_spawn_file_actions_t *file_actions) 398 { 399 file_actions->__file_attrp = NULL; 400 return (0); 401 } 402 403 int 404 posix_spawn_file_actions_destroy( 405 posix_spawn_file_actions_t *file_actions) 406 { 407 file_attr_t *froot = file_actions->__file_attrp; 408 file_attr_t *fap; 409 file_attr_t *next; 410 411 if ((fap = froot) != NULL) { 412 do { 413 next = fap->fa_next; 414 if (fap-> fa_type == FA_OPEN) 415 lfree(fap->fa_path, fap->fa_pathsize); 416 lfree(fap, sizeof (*fap)); 417 } while ((fap = next) != froot); 418 } 419 file_actions->__file_attrp = NULL; 420 return (0); 421 } 422 423 static void 424 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap) 425 { 426 file_attr_t *froot = file_actions->__file_attrp; 427 428 if (froot == NULL) { 429 fap->fa_next = fap->fa_prev = fap; 430 file_actions->__file_attrp = fap; 431 } else { 432 fap->fa_next = froot; 433 fap->fa_prev = froot->fa_prev; 434 froot->fa_prev->fa_next = fap; 435 froot->fa_prev = fap; 436 } 437 } 438 439 int 440 posix_spawn_file_actions_addopen( 441 posix_spawn_file_actions_t *file_actions, 442 int filedes, 443 const char *path, 444 int oflag, 445 mode_t mode) 446 { 447 file_attr_t *fap; 448 449 if (filedes < 0) 450 return (EBADF); 451 if ((fap = lmalloc(sizeof (*fap))) == NULL) 452 return (ENOMEM); 453 454 fap->fa_pathsize = strlen(path) + 1; 455 if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) { 456 lfree(fap, sizeof (*fap)); 457 return (ENOMEM); 458 } 459 (void) strcpy(fap->fa_path, path); 460 461 fap->fa_type = FA_OPEN; 462 fap->fa_oflag = oflag; 463 fap->fa_mode = mode; 464 fap->fa_filedes = filedes; 465 add_file_attr(file_actions, fap); 466 467 return (0); 468 } 469 470 int 471 posix_spawn_file_actions_addclose( 472 posix_spawn_file_actions_t *file_actions, 473 int filedes) 474 { 475 file_attr_t *fap; 476 477 if (filedes < 0) 478 return (EBADF); 479 if ((fap = lmalloc(sizeof (*fap))) == NULL) 480 return (ENOMEM); 481 482 fap->fa_type = FA_CLOSE; 483 fap->fa_filedes = filedes; 484 add_file_attr(file_actions, fap); 485 486 return (0); 487 } 488 489 int 490 posix_spawn_file_actions_adddup2( 491 posix_spawn_file_actions_t *file_actions, 492 int filedes, 493 int newfiledes) 494 { 495 file_attr_t *fap; 496 497 if (filedes < 0 || newfiledes < 0) 498 return (EBADF); 499 if ((fap = lmalloc(sizeof (*fap))) == NULL) 500 return (ENOMEM); 501 502 fap->fa_type = FA_DUP2; 503 fap->fa_filedes = filedes; 504 fap->fa_newfiledes = newfiledes; 505 add_file_attr(file_actions, fap); 506 507 return (0); 508 } 509 510 int 511 posix_spawnattr_init( 512 posix_spawnattr_t *attr) 513 { 514 if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL) 515 return (ENOMEM); 516 /* 517 * Add default stuff here? 518 */ 519 return (0); 520 } 521 522 int 523 posix_spawnattr_destroy( 524 posix_spawnattr_t *attr) 525 { 526 spawn_attr_t *sap = attr->__spawn_attrp; 527 528 if (sap == NULL) 529 return (EINVAL); 530 531 /* 532 * deallocate stuff here? 533 */ 534 lfree(sap, sizeof (*sap)); 535 attr->__spawn_attrp = NULL; 536 return (0); 537 } 538 539 int 540 posix_spawnattr_setflags( 541 posix_spawnattr_t *attr, 542 short flags) 543 { 544 spawn_attr_t *sap = attr->__spawn_attrp; 545 546 if (sap == NULL || 547 (flags & ~ALL_POSIX_SPAWN_FLAGS)) 548 return (EINVAL); 549 550 sap->sa_psflags = flags; 551 return (0); 552 } 553 554 int 555 posix_spawnattr_getflags( 556 const posix_spawnattr_t *attr, 557 short *flags) 558 { 559 spawn_attr_t *sap = attr->__spawn_attrp; 560 561 if (sap == NULL) 562 return (EINVAL); 563 564 *flags = sap->sa_psflags; 565 return (0); 566 } 567 568 int 569 posix_spawnattr_setpgroup( 570 posix_spawnattr_t *attr, 571 pid_t pgroup) 572 { 573 spawn_attr_t *sap = attr->__spawn_attrp; 574 575 if (sap == NULL) 576 return (EINVAL); 577 578 sap->sa_pgroup = pgroup; 579 return (0); 580 } 581 582 int 583 posix_spawnattr_getpgroup( 584 const posix_spawnattr_t *attr, 585 pid_t *pgroup) 586 { 587 spawn_attr_t *sap = attr->__spawn_attrp; 588 589 if (sap == NULL) 590 return (EINVAL); 591 592 *pgroup = sap->sa_pgroup; 593 return (0); 594 } 595 596 int 597 posix_spawnattr_setschedparam( 598 posix_spawnattr_t *attr, 599 const struct sched_param *schedparam) 600 { 601 spawn_attr_t *sap = attr->__spawn_attrp; 602 603 if (sap == NULL) 604 return (EINVAL); 605 606 /* 607 * Check validity? 608 */ 609 sap->sa_priority = schedparam->sched_priority; 610 return (0); 611 } 612 613 int 614 posix_spawnattr_getschedparam( 615 const posix_spawnattr_t *attr, 616 struct sched_param *schedparam) 617 { 618 spawn_attr_t *sap = attr->__spawn_attrp; 619 620 if (sap == NULL) 621 return (EINVAL); 622 623 schedparam->sched_priority = sap->sa_priority; 624 return (0); 625 } 626 627 int 628 posix_spawnattr_setschedpolicy( 629 posix_spawnattr_t *attr, 630 int schedpolicy) 631 { 632 spawn_attr_t *sap = attr->__spawn_attrp; 633 634 if (sap == NULL || schedpolicy == SCHED_SYS) 635 return (EINVAL); 636 637 /* 638 * Cache the policy information for later use 639 * by the vfork() child of posix_spawn(). 640 */ 641 if (get_info_by_policy(schedpolicy) == NULL) 642 return (errno); 643 644 sap->sa_schedpolicy = schedpolicy; 645 return (0); 646 } 647 648 int 649 posix_spawnattr_getschedpolicy( 650 const posix_spawnattr_t *attr, 651 int *schedpolicy) 652 { 653 spawn_attr_t *sap = attr->__spawn_attrp; 654 655 if (sap == NULL) 656 return (EINVAL); 657 658 *schedpolicy = sap->sa_schedpolicy; 659 return (0); 660 } 661 662 int 663 posix_spawnattr_setsigdefault( 664 posix_spawnattr_t *attr, 665 const sigset_t *sigdefault) 666 { 667 spawn_attr_t *sap = attr->__spawn_attrp; 668 669 if (sap == NULL) 670 return (EINVAL); 671 672 sap->sa_sigdefault = *sigdefault; 673 return (0); 674 } 675 676 int 677 posix_spawnattr_getsigdefault( 678 const posix_spawnattr_t *attr, 679 sigset_t *sigdefault) 680 { 681 spawn_attr_t *sap = attr->__spawn_attrp; 682 683 if (sap == NULL) 684 return (EINVAL); 685 686 *sigdefault = sap->sa_sigdefault; 687 return (0); 688 } 689 690 int 691 posix_spawnattr_setsigmask( 692 posix_spawnattr_t *attr, 693 const sigset_t *sigmask) 694 { 695 spawn_attr_t *sap = attr->__spawn_attrp; 696 697 if (sap == NULL) 698 return (EINVAL); 699 700 sap->sa_sigmask = *sigmask; 701 return (0); 702 } 703 704 int 705 posix_spawnattr_getsigmask( 706 const posix_spawnattr_t *attr, 707 sigset_t *sigmask) 708 { 709 spawn_attr_t *sap = attr->__spawn_attrp; 710 711 if (sap == NULL) 712 return (EINVAL); 713 714 *sigmask = sap->sa_sigmask; 715 return (0); 716 } 717