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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/wait.h> 28 #include <sys/ctfs.h> 29 #include <sys/contract.h> 30 #include <sys/contract/process.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <signal.h> 38 #include <limits.h> 39 #include <libuutil.h> 40 #include <libcontract.h> 41 #include <libcontract_priv.h> 42 43 #include <locale.h> 44 #include <langinfo.h> 45 46 static int opt_verbose; 47 static int opt_Verbose; 48 49 #define OPT_NORMAL 0x1 50 #define OPT_FATAL 0x2 51 52 typedef struct optvect { 53 const char *opt_name; 54 uint_t opt_value; 55 uint_t opt_flags; 56 } optvect_t; 57 58 static optvect_t option_params[] = { 59 { "noorphan", CT_PR_NOORPHAN }, 60 { "pgrponly", CT_PR_PGRPONLY }, 61 { "regent", CT_PR_REGENT }, 62 { "inherit", CT_PR_INHERIT }, 63 { NULL } 64 }; 65 66 static optvect_t option_events[] = { 67 { "core", CT_PR_EV_CORE, OPT_NORMAL | OPT_FATAL }, 68 { "signal", CT_PR_EV_SIGNAL, OPT_NORMAL | OPT_FATAL }, 69 { "hwerr", CT_PR_EV_HWERR, OPT_NORMAL | OPT_FATAL }, 70 { "empty", CT_PR_EV_EMPTY, OPT_NORMAL }, 71 { "fork", CT_PR_EV_FORK, OPT_NORMAL }, 72 { "exit", CT_PR_EV_EXIT, OPT_NORMAL }, 73 { NULL } 74 }; 75 76 typedef enum lifetime { 77 LT_NONE, 78 LT_CHILD, 79 LT_CONTRACT 80 } lifetime_t; 81 82 /* 83 * Exit code to use when the child exited abnormally (i.e. exited with 84 * a status we are unable to emulate). 85 */ 86 #define EXIT_BADCHILD 123 87 88 #define USAGESTR \ 89 "Usage: %s [-i eventlist] [-f eventlist] [-l lifetime] \n" \ 90 "\t[-o optionlist] [-r count [-t]] [-v]\n" \ 91 "\t[-F fmri] [-A aux] command\n" 92 93 /* 94 * usage 95 * 96 * Educate the user. 97 */ 98 static void 99 usage(void) 100 { 101 (void) fprintf(stderr, gettext(USAGESTR), uu_getpname()); 102 exit(UU_EXIT_USAGE); 103 } 104 105 /* 106 * bit2str 107 * 108 * Convert a bit into its string representation. 109 */ 110 static const char * 111 bit2str(optvect_t *options, uint_t bit) 112 { 113 for (; options->opt_name; options++) 114 if (options->opt_value == bit) 115 return (options->opt_name); 116 return (NULL); 117 } 118 119 /* 120 * str2bit 121 * 122 * Convert a string into its bit representation. If match is set, only 123 * look at those options with the match bit set in its opt_flags 124 * field. 125 */ 126 static uint_t 127 str2bit(optvect_t *options, int match, const char *str, int len) 128 { 129 for (; options->opt_name; options++) { 130 if (match && (options->opt_flags & match) == 0) 131 continue; 132 if (strncmp(str, options->opt_name, len) == 0) 133 return (options->opt_value); 134 } 135 return (0); 136 } 137 138 /* 139 * opt2bits 140 * 141 * Given a set of textual options separated by commas or spaces, 142 * convert them to a set of bits. Errors are fatal, except for empty 143 * options (which are ignored) and duplicate options (which are 144 * idempotent). 145 */ 146 static void 147 opt2bits(optvect_t *options, int match, const char *str, uint_t *bits, char c) 148 { 149 const char *ptr, *next = str; 150 uint_t result = 0; 151 uint_t bit; 152 int none = 0; 153 154 while (*str) { 155 int len; 156 157 ptr = strpbrk(str, ", "); 158 if (ptr != NULL) { 159 len = ptr - str; 160 next = ptr + 1; 161 } else { 162 len = strlen(str); 163 next = str + len; 164 } 165 if (len == 0) { 166 uu_warn(gettext("empty option\n")); 167 bit = 0; 168 } else { 169 bit = str2bit(options, match, str, len); 170 if (bit == 0 && strncmp(str, "none", len) == 0) { 171 none = 1; 172 if (result) 173 goto noneerr; 174 } else if (bit == 0) { 175 uu_warn(gettext("unrecognized option '%.*s'\n"), 176 len, str); 177 uu_warn(gettext("error parsing '-%c' option\n"), 178 c); 179 usage(); 180 } else if (none) { 181 goto noneerr; 182 } 183 if (result & bit) 184 uu_warn(gettext("option '%.*s' " 185 "specified twice\n"), len, str); 186 } 187 result |= bit; 188 str = next; 189 } 190 191 *bits = result; 192 return; 193 194 noneerr: 195 uu_warn(gettext("option is incompatible with others: '%s'\n"), "none"); 196 usage(); 197 } 198 199 /* 200 * close_on_exec 201 * 202 * Given a fd, marks it close-on-exec. 203 */ 204 static int 205 close_on_exec(int fd) 206 { 207 int flags = fcntl(fd, F_GETFD, 0); 208 if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1)) 209 return (0); 210 return (-1); 211 } 212 213 /* 214 * v_printf 215 * 216 * Output routine for messages printed only when -v is specified. 217 */ 218 /* PRINTFLIKE1 */ 219 static void 220 v_printf(const char *format, ...) 221 { 222 va_list va; 223 224 if (opt_verbose) { 225 (void) printf("%s(%ld): ", uu_getpname(), getpid()); 226 va_start(va, format); 227 (void) vprintf(format, va); 228 va_end(va); 229 } 230 } 231 232 /* 233 * get_event 234 * 235 * Reads and acknowledges an event. Returns the event type. 236 */ 237 static uint_t 238 get_event(int fd, int ctfd, ctid_t ctid) 239 { 240 ct_evthdl_t ev; 241 uint_t result; 242 ctevid_t evid; 243 244 for (;;) { 245 int efd; 246 247 /* 248 * Normally we only need to look at critical messages. 249 * If we are displaying contract events, however, we 250 * have to read them all. 251 */ 252 errno = opt_verbose ? ct_event_read(fd, &ev) : 253 ct_event_read_critical(fd, &ev); 254 if (errno != 0) 255 uu_die(gettext("failed to listen to contract events")); 256 257 /* 258 * If requested, display the event. 259 */ 260 if (opt_verbose) { 261 v_printf(gettext("event from contract %ld: "), 262 ct_event_get_ctid(ev)); 263 contract_event_dump(stdout, ev, opt_Verbose); 264 if ((ct_event_get_flags(ev) & CTE_INFO) != 0) { 265 ct_event_free(ev); 266 continue; 267 } 268 } 269 270 /* 271 * We're done if this event is one of ours. 272 */ 273 evid = ct_event_get_evid(ev); 274 if (ct_event_get_ctid(ev) == ctid) 275 break; 276 277 /* 278 * ACK events from other contracts. 279 * This shouldn't happen, but it could. 280 */ 281 efd = contract_open(ct_event_get_ctid(ev), "process", "ctl", 282 O_WRONLY); 283 if (efd != -1) { 284 (void) ct_ctl_ack(efd, evid); 285 (void) close(efd); 286 } 287 ct_event_free(ev); 288 } 289 290 /* 291 * Note that if we want to use ctrun as a simple restarter, we 292 * need persistently keep track of fatal events so we can 293 * properly handle the death of the contract. Rather than keep 294 * a file or somesuch lying around, it might make more sense to 295 * leave the significant fatal event sitting in the queue so 296 * that a restarted instance of ctrun can pick it up. For now 297 * we'll just ACK all events. 298 */ 299 (void) ct_ctl_ack(ctfd, evid); 300 301 result = ct_event_get_type(ev); 302 ct_event_free(ev); 303 304 return (result); 305 } 306 307 /* 308 * abandon 309 * 310 * Given an fd for a contract's ctl file, abandon the contract and 311 * close the file. 312 */ 313 static void 314 abandon(int ctfd) 315 { 316 if (ct_ctl_abandon(ctfd) == -1) 317 uu_die(gettext("failed to abandon contract %d"), ctfd); 318 319 (void) close(ctfd); 320 } 321 322 static int chldstat; 323 static int chldexited; 324 325 /* 326 * sigchld 327 * 328 * Our SIGCHLD handler. Sets chldstat and chldexited so the 329 * interrupted code knows what happened. 330 */ 331 /*ARGSUSED*/ 332 static void 333 sigchld(int sig, struct siginfo *si, void *ucp) 334 { 335 int err = errno; 336 337 if (si->si_code == CLD_EXITED) 338 chldstat = si->si_status; 339 else 340 chldstat = EXIT_BADCHILD; 341 chldexited = 1; 342 while (waitpid(si->si_pid, NULL, 0) == -1 && errno == EINTR) 343 ; 344 errno = err; 345 } 346 347 /* 348 * dowait 349 * 350 * Waits for the specified child to exit. Returns the exit code ctrun 351 * should return. 352 */ 353 static int 354 dowait(int pid) 355 { 356 pid_t wpid; 357 int wstatus; 358 359 do 360 wpid = waitpid(pid, &wstatus, 0); 361 while (wpid == -1 && errno == EINTR); 362 363 if (wpid == -1) 364 uu_die(gettext("wait failed")); 365 366 if (WIFEXITED(wstatus)) 367 return (WEXITSTATUS(wstatus)); 368 else 369 return (EXIT_BADCHILD); 370 } 371 372 int 373 main(int argc, char **argv) 374 { 375 int fd, efd; 376 pid_t pid; 377 ctid_t ctid = 0; 378 int ctfd; 379 int pipefds[2]; 380 struct sigaction osact; 381 382 int s; 383 ctid_t opt_adopt = 0; 384 int opt_transfer = 0; 385 int opt_count = -1; 386 uint_t opt_info = CT_PR_EV_CORE; 387 uint_t opt_crit = 0; 388 uint_t eff_fatal, opt_fatal = CT_PR_EV_HWERR; 389 uint_t eff_param, opt_param = 0; 390 lifetime_t opt_life = LT_CONTRACT; 391 392 char *svc_fmri = NULL; 393 char *svc_aux = NULL; 394 395 (void) setlocale(LC_ALL, ""); 396 (void) textdomain(TEXT_DOMAIN); 397 uu_alt_exit(UU_PROFILE_LAUNCHER); 398 399 (void) uu_setpname(argv[0]); 400 401 while ((s = getopt(argc, argv, "a:A:l:o:i:c:f:F:r:tvV")) != EOF) { 402 switch (s) { 403 case 'a': 404 if (uu_strtoint(optarg, &opt_adopt, sizeof (opt_adopt), 405 0, 0, INT32_MAX) == -1) { 406 uu_warn(gettext("invalid contract ID '%s'\n"), 407 optarg); 408 usage(); 409 } 410 break; 411 case 'v': 412 opt_verbose = 1; 413 break; 414 case 'V': 415 opt_Verbose = 1; 416 opt_verbose = 1; 417 break; 418 case 't': 419 opt_transfer = 1; 420 break; 421 case 'r': 422 if (uu_strtoint(optarg, &opt_count, sizeof (opt_adopt), 423 0, 0, INT32_MAX) == -1) { 424 uu_warn(gettext("invalid count '%s'\n"), 425 optarg); 426 usage(); 427 } 428 break; 429 case 'l': 430 if (strcmp(optarg, "none") == 0) { 431 opt_life = LT_NONE; 432 } else if (strcmp(optarg, "child") == 0) { 433 opt_life = LT_CHILD; 434 } else if (strcmp(optarg, "contract") == 0) { 435 opt_life = LT_CONTRACT; 436 } else { 437 uu_warn(gettext("invalid lifetime '%s'\n"), 438 optarg); 439 usage(); 440 } 441 442 break; 443 case 'o': 444 opt2bits(option_params, 0, optarg, &opt_param, 445 optopt); 446 break; 447 case 'i': 448 opt2bits(option_events, OPT_NORMAL, optarg, &opt_info, 449 optopt); 450 break; 451 case 'c': 452 opt2bits(option_events, OPT_NORMAL, optarg, &opt_crit, 453 optopt); 454 break; 455 case 'f': 456 opt2bits(option_events, OPT_FATAL, optarg, &opt_fatal, 457 optopt); 458 break; 459 case 'F': 460 svc_fmri = optarg; 461 break; 462 case 'A': 463 svc_aux = optarg; 464 break; 465 default: 466 usage(); 467 } 468 } 469 argc -= optind; 470 argv += optind; 471 472 /* 473 * Basic argument sanity checks. 474 */ 475 if ((opt_life == LT_NONE) && (opt_param & CT_PR_NOORPHAN)) { 476 uu_warn(gettext("cannot use option '%s' with lifetime '%s'\n"), 477 bit2str(option_params, CT_PR_NOORPHAN), "none"); 478 usage(); 479 } 480 481 if ((opt_life != LT_CONTRACT) && (opt_count >= 0)) { 482 uu_warn(gettext("cannot restart with lifetime '%s'\n"), 483 opt_life == LT_NONE ? "none" : "child"); 484 usage(); 485 } 486 487 if ((opt_param & CT_PR_PGRPONLY) && (opt_count >= 0)) { 488 uu_warn(gettext("cannot restart with option '%s'\n"), 489 bit2str(option_params, CT_PR_PGRPONLY)); 490 usage(); 491 } 492 493 if (opt_transfer && (opt_count == -1)) { 494 uu_warn(gettext("cannot transfer when not restarting\n")); 495 usage(); 496 } 497 498 if (argc <= 0) 499 usage(); 500 501 /* 502 * Create a process contract template and our process's process 503 * contract bundle endpoint. Mark them close-on-exec so we 504 * don't have to worry about closing them in our child. 505 */ 506 fd = open64(CTFS_ROOT "/process/template", O_RDWR); 507 if (fd == -1) 508 uu_die(gettext("template open failed")); 509 510 efd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY); 511 if (efd == -1) 512 uu_die(gettext("process bundle open failed")); 513 514 if (close_on_exec(fd) || close_on_exec(efd)) 515 uu_die(gettext("could not set FD_CLOEXEC")); 516 517 /* 518 * Set the process contract's terms based on our arguments. 519 */ 520 if (errno = ct_pr_tmpl_set_param(fd, opt_param)) 521 uu_die(gettext("set param failed")); 522 523 if (errno = ct_tmpl_set_informative(fd, opt_info)) 524 uu_die(gettext("set notify failed")); 525 526 if (errno = ct_pr_tmpl_set_fatal(fd, opt_fatal)) 527 uu_die(gettext("set fatal failed")); 528 529 if (opt_param & CT_PR_PGRPONLY) 530 opt_crit = CT_PR_EV_EMPTY; 531 else 532 opt_crit |= opt_fatal | CT_PR_EV_EMPTY; 533 if (errno = ct_tmpl_set_critical(fd, opt_crit)) 534 uu_die(gettext("set critical failed")); 535 if (svc_fmri && (errno = ct_pr_tmpl_set_svc_fmri(fd, svc_fmri))) 536 uu_die(gettext("set fmri failed: " 537 "insufficient privileges\n")); 538 if (svc_aux && (errno = ct_pr_tmpl_set_svc_aux(fd, svc_aux))) 539 uu_die(gettext("set aux failed")); 540 541 /* 542 * Activate the template. 543 */ 544 if (errno = ct_tmpl_activate(fd)) 545 uu_die(gettext("template activate failed")); 546 547 restart: 548 if (opt_adopt) { 549 /* 550 * Adopt a specific contract. 551 */ 552 ct_stathdl_t st; 553 int stfd; 554 555 if ((ctfd = contract_open(opt_adopt, "process", "ctl", 556 O_WRONLY)) == -1) 557 uu_die(gettext("could not open contract %ld"), 558 opt_adopt); 559 560 /* 561 * Read the contract's terms so that we interpret its 562 * events properly. 563 */ 564 if (((stfd = contract_open(opt_adopt, "process", "status", 565 O_RDONLY)) == -1) || 566 (errno = ct_status_read(stfd, CTD_FIXED, &st)) || 567 (errno = ct_pr_status_get_fatal(st, &eff_fatal)) || 568 (errno = ct_pr_status_get_param(st, &eff_param))) 569 uu_die(gettext("could not stat contract %ld"), 570 opt_adopt); 571 ct_status_free(st); 572 (void) close(stfd); 573 574 if (errno = ct_ctl_adopt(ctfd)) 575 uu_die(gettext("could not adopt contract %ld"), 576 opt_adopt); 577 578 ctid = opt_adopt; 579 opt_adopt = 0; 580 v_printf(gettext("adopted contract id %ld\n"), ctid); 581 } else { 582 /* 583 * Create a new process. 584 */ 585 if (opt_life == LT_CONTRACT) { 586 struct sigaction sact; 587 588 /* 589 * Since we are going to be waiting for and 590 * reacting to contract events, install a 591 * signal handler so we capture the exit status 592 * of our child. 593 */ 594 chldstat = UU_EXIT_OK; 595 chldexited = 0; 596 sact.sa_sigaction = sigchld; 597 sact.sa_flags = SA_SIGINFO | SA_RESTART | 598 SA_NOCLDSTOP; 599 (void) sigemptyset(&sact.sa_mask); 600 if (sigaction(SIGCHLD, &sact, &osact) == -1) 601 uu_die(gettext("failed to install " 602 "sigchld handler")); 603 } else if (opt_life == LT_NONE) { 604 /* 605 * Though we aren't waiting for our child to 606 * exit, as a well-behaved command launcher we 607 * must wait for it to exec. On success the 608 * pipe will simply close, and on failure the 609 * proper exit status will be sent. 610 */ 611 if (pipe(pipefds) == -1 || 612 close_on_exec(pipefds[0]) == -1 || 613 close_on_exec(pipefds[1]) == -1) 614 uu_die(gettext("failed to create pipe")); 615 } 616 617 if ((pid = fork()) == -1) { 618 uu_die(gettext("fork failed")); 619 } else if (pid == 0) { 620 int result = execvp(argv[0], argv); 621 if (opt_life == LT_NONE) { 622 char a = 1; 623 int err = errno; 624 625 (void) write(pipefds[1], &a, sizeof (a)); 626 errno = err; 627 } 628 if (result == -1) 629 uu_xdie(errno == ENOENT ? 127 : 126, 630 gettext("exec failed")); 631 uu_die(gettext("exec returned!\n")); 632 } 633 634 /* 635 * Get the newly-created contract's id and ctl fd. 636 */ 637 if (errno = contract_latest(&ctid)) 638 uu_die(gettext("could not get new contract's id")); 639 if ((ctfd = contract_open(ctid, "process", "ctl", 640 O_WRONLY)) == -1) 641 uu_die(gettext("could not open contract")); 642 643 /* 644 * Clear the transfer parameter so that the contract 645 * will be freed sooner and admins won't get nervous. 646 */ 647 if (opt_transfer) { 648 (void) ct_pr_tmpl_set_transfer(fd, 0); 649 (void) ct_tmpl_activate(fd); 650 } 651 652 v_printf(gettext("created contract id %ld\n"), ctid); 653 eff_param = opt_param; 654 eff_fatal = opt_fatal; 655 } 656 657 if (opt_life == LT_CONTRACT) { 658 uint_t event, errevent = 0; 659 660 /* 661 * Wait until the contract empties out. 662 */ 663 do { 664 event = get_event(efd, ctfd, ctid); 665 if (event & eff_fatal) { 666 if ((eff_param & CT_PR_PGRPONLY) == 0) 667 errevent = event; 668 v_printf(gettext( 669 "fatal \"%s\" event from contract %ld\n"), 670 bit2str(option_events, event), ctid); 671 } 672 } while ((event & CT_PR_EV_EMPTY) == 0); 673 674 /* 675 * If we encountered a fatal error event, and we 676 * haven't expended our maximum loop count, restart. 677 */ 678 if ((errevent != 0) && 679 ((opt_count == 0) || (opt_count-- > 1))) { 680 v_printf(gettext("failure in contract %ld, " 681 "restarting command\n"), ctid); 682 if (opt_transfer) { 683 /* 684 * Add the failed contract to the new 685 * contract's terms so that its 686 * inherited subcontracts can be 687 * adopted by the new process. 688 */ 689 if (errno = ct_pr_tmpl_set_transfer(fd, ctid)) 690 uu_die(gettext("set transfer failed")); 691 if (errno = ct_tmpl_activate(fd)) 692 uu_die(gettext( 693 "template activate failed")); 694 (void) close(ctfd); 695 } else { 696 abandon(ctfd); 697 } 698 goto restart; 699 } 700 701 /* 702 * At this point we are done with the contract; we 703 * don't want it to be inherited when we exit. 704 */ 705 abandon(ctfd); 706 707 /* 708 * In case there was a race between SIGCHLD delivery 709 * and contract event delivery, disable the signal 710 * handler and look for the child. 711 */ 712 (void) sigaction(SIGCHLD, &osact, NULL); 713 if (chldexited == 0) 714 chldstat = dowait(pid); 715 } else if (opt_life == LT_NONE) { 716 char a; 717 int result; 718 719 chldstat = UU_EXIT_OK; 720 (void) close(pipefds[1]); 721 do { 722 result = read(pipefds[0], &a, sizeof (a)); 723 if (result == -1 && errno != EINTR) 724 uu_die(gettext("read failed")); 725 if (result == 1) 726 chldstat = dowait(pid); 727 } while (result == -1); 728 } else { 729 chldstat = dowait(pid); 730 } 731 732 return (chldstat); 733 } 734