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