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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * lsvcrun - run an rc?.d script, modifying appropriate data in the 27 * repository to reflect legacy behavior. 28 * 29 * We try to keep track of what we can for the legacy scripts via 30 * property groups under the smf/legacy_run service. Each property 31 * group identifies a service, named in the form 'rc2_d_S10foo'. 32 * 33 * Each group has the following properties: name, the script name 34 * displayed by svcs(1); state_timestamp; contract, contract ID; 35 * inode, the inode of the script; and suffix, the suffix of the 36 * script name, e.g. 'foo'. 37 * 38 * In order to support rc scripts which delete themselves upon invocation we 39 * collect inode before running the script. 40 * 41 * When we run a K script, we try to identify and remove the 42 * property group by means of examining the inode and script 43 * suffix. The inode check means more than one script with the 44 * same suffix will still work as intended in the common case. 45 * 46 * If we cannot find a property group, or one already exists 47 * when we try to add one, then we print a suitable warning. These 48 * are warnings because there was no strict requirement that K 49 * and S scripts be matched up. 50 * 51 * In the face of these assumptions being proved wrong, we always 52 * make sure to execute the script anyway in an attempt to keep 53 * things working as they used to. If we can't execute the script, 54 * we try to leave the repository in the state it was before. 55 */ 56 57 #include <sys/ctfs.h> 58 #include <sys/types.h> 59 #include <sys/wait.h> 60 #include <sys/stat.h> 61 #include <assert.h> 62 #include <ctype.h> 63 #include <errno.h> 64 #include <fcntl.h> 65 #include <fnmatch.h> 66 #include <libcontract.h> 67 #include <libcontract_priv.h> 68 #include <libintl.h> 69 #include <libscf.h> 70 #include <libscf_priv.h> 71 #include <libuutil.h> 72 #include <signal.h> 73 #include <stdio.h> 74 #include <stdlib.h> 75 #include <string.h> 76 #include <strings.h> 77 #include <time.h> 78 #include <unistd.h> 79 #include <limits.h> 80 81 82 /* Environment variables to pass on. See clean_environment(). */ 83 static char *evars_to_pass[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE", 84 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "PATH", "TZ" 85 }; 86 87 #define EVARS_TO_PASS_NUM \ 88 (sizeof (evars_to_pass) / sizeof (*evars_to_pass)) 89 90 91 static void 92 usage() 93 { 94 (void) fprintf(stderr, 95 gettext("Usage: %s [-s] script {start | stop}\n"), uu_getpname()); 96 exit(UU_EXIT_USAGE); 97 } 98 99 /* 100 * Pick out the script name and convert it for use as an SMF property 101 * group name. 102 */ 103 static char * 104 start_pg_name(const char *path) 105 { 106 char *out, *cp; 107 108 if (fnmatch("/etc/rc[0-6S].d/S*", path, FNM_PATHNAME) != 0) { 109 uu_warn(gettext("couldn't parse name %s.\n"), path); 110 return (NULL); 111 } 112 113 out = strdup(path + sizeof ("/etc/") - 1); 114 115 if (out == NULL) { 116 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno)); 117 return (NULL); 118 } 119 120 /* Convert illegal characters to _. */ 121 for (cp = out; *cp != '\0'; ++cp) { 122 /* locale problem? */ 123 if (!isalnum(*cp) && *cp != '-') 124 *cp = '_'; 125 } 126 127 return (out); 128 } 129 130 static char * 131 script_suffix(const char *path) 132 { 133 const char *cp; 134 char *out; 135 136 if (fnmatch("/etc/rc[0-6S].d/[SK]*", path, FNM_PATHNAME) != 0) { 137 uu_warn(gettext("couldn't parse name %s.\n"), path); 138 return (NULL); 139 } 140 141 cp = path + sizeof ("/etc/rc0.d/S") - 1; 142 143 while (isdigit(*cp)) 144 cp++; 145 146 if (*cp == '\0') { 147 uu_warn(gettext("couldn't parse name %s.\n"), path); 148 return (NULL); 149 } 150 151 out = strdup(cp); 152 if (out == NULL) 153 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno)); 154 155 return (out); 156 } 157 158 /* 159 * Convert a path to an acceptable SMF (service) name. 160 */ 161 static char * 162 path_to_svc_name(const char *path) 163 { 164 char *out, *cp; 165 166 out = strdup(path); 167 if (out == NULL) { 168 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno)); 169 return (NULL); 170 } 171 172 /* Convert illegal characters to _. */ 173 for (cp = out; *cp != '\0'; ++cp) { 174 /* locale problem? */ 175 if (!isalnum(*cp) && *cp != '-' && *cp != '/') 176 *cp = '_'; 177 } 178 179 /* If the first character is _, use a instead. */ 180 if (*out == '_') 181 *out = 'a'; 182 183 return (out); 184 } 185 186 static void 187 scferr(const char *func) 188 { 189 uu_warn(gettext("%s failed (%s). Repository will not be modified.\n"), 190 func, scf_strerror(scf_error())); 191 } 192 193 static scf_propertygroup_t * 194 get_start_pg(const char *script, scf_handle_t *h, scf_service_t *svc, 195 boolean_t *ok) 196 { 197 char *pg_name = NULL; 198 scf_propertygroup_t *pg = NULL; 199 scf_property_t *prop = NULL; 200 201 if ((pg_name = start_pg_name(script)) == NULL) 202 return (NULL); 203 204 if ((pg = scf_pg_create(h)) == NULL) { 205 scferr("scf_pg_create()"); 206 goto out; 207 } 208 209 add: 210 if (scf_service_add_pg(svc, pg_name, SCF_GROUP_FRAMEWORK, 211 SCF_PG_FLAG_NONPERSISTENT, pg) == 0) { 212 *ok = 1; 213 free(pg_name); 214 return (pg); 215 } 216 217 switch (scf_error()) { 218 case SCF_ERROR_INVALID_ARGUMENT: 219 assert(0); 220 abort(); 221 /* NOTREACHED */ 222 223 case SCF_ERROR_EXISTS: 224 break; 225 226 case SCF_ERROR_PERMISSION_DENIED: 227 uu_die(gettext( 228 "Insufficient privilege to add repository properties; " 229 "not launching \"%s\".\n"), script); 230 /* NOTREACHED */ 231 232 default: 233 scferr("scf_service_add_pg()"); 234 scf_pg_destroy(pg); 235 pg = NULL; 236 goto out; 237 } 238 239 if (scf_service_get_pg(svc, pg_name, pg) != 0) { 240 switch (scf_error()) { 241 case SCF_ERROR_INVALID_ARGUMENT: 242 assert(0); 243 abort(); 244 /* NOTREACHED */ 245 246 case SCF_ERROR_NOT_FOUND: 247 goto add; 248 249 default: 250 scferr("scf_service_get_pg()"); 251 scf_pg_destroy(pg); 252 pg = NULL; 253 goto out; 254 } 255 } 256 257 if ((prop = scf_property_create(h)) == NULL) { 258 scferr("scf_property_create()"); 259 scf_pg_destroy(pg); 260 pg = NULL; 261 goto out; 262 } 263 264 /* 265 * See if the pg has the name property. If it has, that 266 * implies we successfully ran the same script before. We 267 * should re-run it anyway, but not modify the existing pg; 268 * this might lose contract-control but there's not much we 269 * can do. 270 * 271 * If there's no name property, then we probably couldn't 272 * remove the pg fully after a script failed to run. 273 */ 274 275 if (scf_pg_get_property(pg, SCF_LEGACY_PROPERTY_NAME, prop) == 0) { 276 uu_warn(gettext("Service matching \"%s\" " 277 "seems to be running.\n"), script); 278 scf_pg_destroy(pg); 279 pg = NULL; 280 } else if (scf_error() != SCF_ERROR_NOT_FOUND) { 281 scferr("scf_pg_get_property()"); 282 scf_pg_destroy(pg); 283 pg = NULL; 284 } else { 285 uu_warn(gettext("Service \"%s\" has an invalid property " 286 "group.\n"), script); 287 } 288 289 out: 290 free(pg_name); 291 scf_property_destroy(prop); 292 return (pg); 293 } 294 295 static scf_propertygroup_t * 296 pg_match(scf_handle_t *h, scf_service_t *svc, ino_t ino, const char *suffix) 297 { 298 char buf[PATH_MAX]; 299 scf_iter_t *iter = NULL; 300 scf_propertygroup_t *pg = NULL; 301 scf_property_t *prop = NULL; 302 scf_value_t *val = NULL; 303 304 if ((pg = scf_pg_create(h)) == NULL) { 305 scferr("scf_pg_create()"); 306 goto err; 307 } 308 309 if ((iter = scf_iter_create(h)) == NULL) { 310 scferr("scf_iter_create()"); 311 goto err; 312 } 313 314 if ((prop = scf_property_create(h)) == NULL) { 315 scferr("scf_property_create()"); 316 goto err; 317 } 318 319 if ((val = scf_value_create(h)) == NULL) { 320 scferr("scf_value_create()"); 321 goto err; 322 } 323 324 if (scf_iter_service_pgs_typed(iter, svc, SCF_GROUP_FRAMEWORK) != 325 0) { 326 scferr("scf_iter_service_pgs_typed()"); 327 goto err; 328 } 329 330 while (scf_iter_next_pg(iter, pg) > 0) { 331 int match = 1; 332 333 if (suffix != NULL) { 334 ssize_t len; 335 336 if (scf_pg_get_property(pg, SCF_LEGACY_PROPERTY_SUFFIX, 337 prop) != 0) 338 continue; 339 340 if (scf_property_get_value(prop, val) != 0) 341 continue; 342 343 len = scf_value_get_astring(val, buf, sizeof (buf)); 344 if (len < 0) { 345 scferr("scf_value_get_astring()"); 346 goto err; 347 } 348 if (len >= sizeof (buf)) 349 continue; 350 351 match = (strcmp(buf, suffix) == 0); 352 } 353 354 if (ino != 0) { 355 uint64_t pval; 356 357 if (scf_pg_get_property(pg, SCF_LEGACY_PROPERTY_INODE, 358 prop) != 0) 359 continue; 360 361 if (scf_property_get_value(prop, val) != 0) 362 continue; 363 364 if (scf_value_get_count(val, &pval) != 0) 365 continue; 366 367 match = (ino == pval) && match; 368 } 369 370 if (match) 371 goto out; 372 } 373 374 err: 375 scf_pg_destroy(pg); 376 pg = NULL; 377 378 out: 379 scf_value_destroy(val); 380 scf_iter_destroy(iter); 381 scf_property_destroy(prop); 382 return (pg); 383 } 384 385 /* 386 * Try and find the property group matching the service this script 387 * stops. First we look for a matching inode plus a matching suffix. 388 * This commonly succeeds, but if not, we just search for inode. 389 * Finally, we try for just the script suffix. 390 */ 391 static scf_propertygroup_t * 392 get_stop_pg(const char *script, scf_handle_t *h, scf_service_t *svc, 393 boolean_t *ok) 394 { 395 struct stat st; 396 char *suffix; 397 scf_propertygroup_t *pg; 398 399 if (stat(script, &st) != 0) { 400 uu_warn(gettext("Couldn't stat %s (%s).\n"), script, 401 strerror(errno)); 402 return (NULL); 403 } 404 405 if ((suffix = script_suffix(script)) == NULL) { 406 pg = pg_match(h, svc, st.st_ino, NULL); 407 if (pg != NULL) 408 goto out; 409 return (NULL); 410 } 411 412 if ((pg = pg_match(h, svc, st.st_ino, suffix)) != NULL) 413 goto out; 414 415 if ((pg = pg_match(h, svc, st.st_ino, NULL)) != NULL) 416 goto out; 417 418 if ((pg = pg_match(h, svc, 0, suffix)) == NULL) { 419 uu_warn(gettext("Service matching \"%s\" " 420 "doesn't seem to be running.\n"), script); 421 free(suffix); 422 return (NULL); 423 } 424 425 out: 426 *ok = 1; 427 free(suffix); 428 return (pg); 429 } 430 431 static scf_propertygroup_t * 432 get_script_pg(const char *script, boolean_t start_flag, boolean_t *ok) 433 { 434 scf_handle_t *h = NULL; 435 scf_scope_t *scope = NULL; 436 scf_service_t *svc = NULL; 437 scf_propertygroup_t *pg = NULL; 438 439 *ok = 0; 440 441 h = scf_handle_create(SCF_VERSION); 442 if (h == NULL) { 443 scferr("scf_handle_create()"); 444 goto out; 445 } 446 447 if (scf_handle_bind(h) != 0) { 448 if (scf_error() != SCF_ERROR_NO_SERVER) { 449 scferr("scf_handle_bind()"); 450 } else { 451 uu_warn(gettext( 452 "Could not connect to svc.configd.\n")); 453 } 454 goto out; 455 } 456 457 if ((scope = scf_scope_create(h)) == NULL) { 458 scferr("scf_scope_create()"); 459 goto out; 460 } 461 462 if ((svc = scf_service_create(h)) == NULL) { 463 scferr("scf_service_create()"); 464 goto out; 465 } 466 467 if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) { 468 scferr("scf_handle_get_local_scope()"); 469 goto out; 470 } 471 472 if (scf_scope_get_service(scope, SCF_LEGACY_SERVICE, svc) != 0) { 473 if (scf_error() != SCF_ERROR_NOT_FOUND) { 474 scferr("scf_scope_get_service()"); 475 goto out; 476 } 477 478 if (scf_scope_add_service(scope, SCF_LEGACY_SERVICE, svc) != 479 0) { 480 scferr("scf_scope_add_service()"); 481 goto out; 482 } 483 } 484 485 if (start_flag) 486 pg = get_start_pg(script, h, svc, ok); 487 else 488 pg = get_stop_pg(script, h, svc, ok); 489 490 out: 491 scf_service_destroy(svc); 492 scf_scope_destroy(scope); 493 return (pg); 494 } 495 496 static int 497 prepare_contract(const char *script, const char *action) 498 { 499 int fd; 500 char *svc_name; 501 char *svc_strbuf; 502 int err = 0; 503 504 do { 505 fd = open64(CTFS_ROOT "/process/template", O_RDWR); 506 } while (fd < 0 && errno == EINTR); 507 if (fd < 0) { 508 uu_warn(gettext("Can not create contract")); 509 return (-1); 510 } 511 512 svc_strbuf = malloc(CT_PARAM_MAX_SIZE); 513 if (svc_strbuf == NULL) { 514 uu_warn(gettext("Can not allocate memory")); 515 err = -1; 516 goto cleanup; 517 } 518 519 (void) strlcpy(svc_strbuf, SCF_FMRI_LEGACY_PREFIX, CT_PARAM_MAX_SIZE); 520 svc_name = path_to_svc_name(script); 521 (void) strlcat(svc_strbuf, svc_name ? svc_name : script, 522 CT_PARAM_MAX_SIZE); 523 if (svc_name != NULL) { 524 free(svc_name); 525 } 526 527 if ((errno = ct_pr_tmpl_set_svc_fmri(fd, svc_strbuf)) != 0) { 528 uu_warn(gettext("Can not set svc_fmri")); 529 err = -1; 530 goto cleanup; 531 } 532 533 (void) strlcpy(svc_strbuf, action, CT_PARAM_MAX_SIZE); 534 if ((errno = ct_pr_tmpl_set_svc_aux(fd, svc_strbuf)) != 0) { 535 uu_warn(gettext("Can not set svc_aux")); 536 err = -1; 537 goto cleanup; 538 } 539 540 /* Leave HWERR in fatal set. */ 541 542 errno = ct_tmpl_activate(fd); 543 if (errno != 0) { 544 assert(errno == EPERM); 545 uu_warn(gettext("Can not activate contract template")); 546 err = -1; 547 goto cleanup; 548 } 549 550 cleanup: 551 if (svc_strbuf != NULL) 552 free(svc_strbuf); 553 (void) close(fd); 554 555 return (err); 556 } 557 558 static void 559 cleanup_pg(scf_propertygroup_t *pg) 560 { 561 scf_error_t err; 562 char buf[80]; 563 564 if (scf_pg_delete(pg) == 0) 565 return; 566 567 err = scf_error(); 568 569 if (scf_pg_to_fmri(pg, buf, sizeof (buf)) != 0) 570 (void) strcpy(buf, "?"); 571 572 uu_warn(gettext("Could not remove property group %s: %s.\n"), buf, 573 scf_strerror(err)); 574 } 575 576 /* 577 * Create a duplicate environment which only contains approved 578 * variables---those in evars_to_pass and those beginning with "_INIT_". 579 */ 580 static char ** 581 approved_env(char **env) 582 { 583 char **newenv; 584 int i, i_new, j; 585 586 for (i = 0; env[i] != NULL; ++i) 587 ; 588 589 newenv = malloc(sizeof (*newenv) * (i + 1)); 590 if (newenv == NULL) 591 return (NULL); 592 593 i_new = 0; 594 595 for (i = 0; env[i] != NULL; ++i) { 596 if (strncmp(env[i], "_INIT_", sizeof ("_INIT_") - 1) == 0) { 597 newenv[i_new++] = env[i]; 598 continue; 599 } 600 601 for (j = 0; j < EVARS_TO_PASS_NUM; ++j) { 602 size_t l = strlen(evars_to_pass[j]); 603 604 if (env[i][l] == '=' && 605 strncmp(env[i], evars_to_pass[j], l) == 0) 606 newenv[i_new++] = env[i]; 607 } 608 } 609 610 newenv[i_new] = NULL; 611 612 return (newenv); 613 } 614 615 /* 616 * Create a duplicate environment which does not contain any SMF_ variables. 617 */ 618 static char ** 619 env_without_smf(char **env) 620 { 621 char **newenv; 622 int i, i_new; 623 624 for (i = 0; env[i] != NULL; ++i) 625 ; 626 627 newenv = malloc(sizeof (*newenv) * (i + 1)); 628 if (newenv == NULL) 629 return (NULL); 630 631 i_new = 0; 632 633 for (i = 0; env[i] != NULL; ++i) { 634 if (strncmp(env[i], "SMF_", sizeof ("SMF_") - 1) == 0) 635 continue; 636 637 newenv[i_new++] = env[i]; 638 } 639 640 newenv[i_new] = NULL; 641 642 return (newenv); 643 } 644 645 static int 646 add_new_property(scf_handle_t *h, scf_transaction_t *tx, const char *name, 647 scf_type_t ty, const void *val) 648 { 649 scf_transaction_entry_t *e; 650 scf_value_t *v; 651 const char *func; 652 const struct timeval *t; 653 int r; 654 655 if ((e = scf_entry_create(h)) == NULL) { 656 func = "scf_entry_create()"; 657 goto err; 658 } 659 660 if ((v = scf_value_create(h)) == NULL) { 661 func = "scf_value_create()"; 662 goto err; 663 } 664 665 r = scf_transaction_property_new(tx, e, name, ty); 666 if (r != 0) { 667 func = "scf_transaction_property_new()"; 668 goto err; 669 } 670 671 switch (ty) { 672 case SCF_TYPE_COUNT: 673 scf_value_set_count(v, (uint64_t)(uintptr_t)val); 674 break; 675 676 case SCF_TYPE_TIME: 677 t = val; 678 r = scf_value_set_time(v, t->tv_sec, 1000 * t->tv_usec); 679 assert(r == 0); 680 break; 681 682 case SCF_TYPE_ASTRING: 683 r = scf_value_set_astring(v, val); 684 assert(r == 0); 685 break; 686 687 default: 688 assert(0); 689 abort(); 690 } 691 692 if (scf_entry_add_value(e, v) == 0) 693 return (0); 694 695 func = "scf_entry_add_value()"; 696 697 err: 698 uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error())); 699 return (-1); 700 } 701 702 static void 703 set_legacy_service(scf_propertygroup_t *pg, const char *script, ino_t inode) 704 { 705 scf_handle_t *h; 706 const char *func; 707 char *suffix; 708 scf_transaction_t *tx; 709 struct timeval tstamp; 710 ctid_t ctid; 711 char *svc_name = NULL; 712 int ret; 713 714 h = scf_pg_handle(pg); 715 if (h == NULL) { 716 func = "scf_pg_handle()"; 717 goto scferr; 718 } 719 720 ret = gettimeofday(&tstamp, NULL); 721 assert(ret == 0); 722 723 if (errno = contract_latest(&ctid)) { 724 uu_warn(gettext("Could not get contract")); 725 goto err; 726 } 727 728 tx = scf_transaction_create(h); 729 if (tx == NULL) { 730 func = "scf_transaction_create()"; 731 goto scferr; 732 } 733 734 if (scf_transaction_start(tx, pg) != 0) { 735 func = "scf_transaction_start()"; 736 goto scferr; 737 } 738 739 /* 740 * We'd like to use the prettier svc_name, but if path_to_svc_name() 741 * fails, we can use the script name anyway. 742 */ 743 svc_name = path_to_svc_name(script); 744 745 if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_NAME, SCF_TYPE_ASTRING, 746 (void *)(svc_name ? svc_name : script)) != 0) 747 goto err; 748 749 if (add_new_property(h, tx, SCF_PROPERTY_STATE_TIMESTAMP, 750 SCF_TYPE_TIME, &tstamp) != 0) 751 goto err; 752 753 if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_INODE, 754 SCF_TYPE_COUNT, (void *)inode) != 0) 755 goto err; 756 757 if ((suffix = script_suffix(script)) != NULL) { 758 if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_SUFFIX, 759 SCF_TYPE_ASTRING, (void *)suffix) != 0) 760 goto err; 761 762 free(suffix); 763 } 764 765 if (add_new_property(h, tx, SCF_PROPERTY_CONTRACT, SCF_TYPE_COUNT, 766 (void *)ctid) != 0) 767 goto err; 768 769 for (;;) { 770 switch (scf_transaction_commit(tx)) { 771 case 1: 772 free(svc_name); 773 return; 774 775 case 0: 776 if (scf_pg_update(pg) == -1) { 777 func = "scf_pg_update()"; 778 goto scferr; 779 } 780 continue; 781 782 case -1: 783 func = "scf_transaction_commit()"; 784 goto scferr; 785 786 default: 787 assert(0); 788 abort(); 789 } 790 } 791 792 scferr: 793 uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error())); 794 err: 795 uu_die(gettext("Could not commit property values to repository.\n")); 796 } 797 798 int 799 main(int argc, char *argv[], char *envp[]) 800 { 801 const char *restarter, *script, *action; 802 boolean_t source = 0; 803 int o; 804 boolean_t start_flag; 805 char **newenv; 806 pid_t pid; 807 int pipefds[2]; 808 char c; 809 int exitstatus; 810 struct stat st; 811 812 scf_propertygroup_t *pg; 813 boolean_t pg_ok; 814 815 (void) uu_setpname(argv[0]); 816 uu_alt_exit(UU_PROFILE_LAUNCHER); 817 818 /* Make sure we were run by svc.startd. */ 819 if ((restarter = getenv("SMF_RESTARTER")) == NULL || 820 strcmp(restarter, SCF_SERVICE_STARTD) != 0) 821 uu_die(gettext("invocation outside smf(7) inappropriate\n")); 822 823 while ((o = getopt(argc, argv, "s")) != -1) { 824 switch (o) { 825 case 's': 826 source = 1; 827 break; 828 829 default: 830 usage(); 831 } 832 } 833 834 if (argc - optind != 2) 835 usage(); 836 837 script = argv[optind]; 838 action = argv[optind + 1]; 839 840 if (strcmp(action, "start") == 0) 841 start_flag = 1; 842 else if (strcmp(action, "stop") == 0) 843 start_flag = 0; 844 else 845 usage(); 846 847 /* 848 * Look for the pg & exit if appropriate. Also, if we're starting, 849 * add the pg now so we can exit before launching the script if we 850 * have insufficient repository privilege. 851 * 852 * If any other problem occurs, we carry on anyway. 853 */ 854 pg = get_script_pg(script, start_flag, &pg_ok); 855 856 /* Clean the environment. Now so we can fail early. */ 857 if (!source) 858 newenv = approved_env(envp); 859 else 860 newenv = env_without_smf(envp); 861 if (newenv == NULL) 862 uu_die(gettext( 863 "Could not create new environment: out of memory.\n")); 864 865 if (prepare_contract(script, action) == -1) { 866 if (start_flag && pg != NULL) 867 cleanup_pg(pg); 868 869 exit(UU_EXIT_FATAL); 870 } 871 872 /* pipe to communicate exec success or failure */ 873 if (pipe(pipefds) != 0) { 874 uu_warn(gettext("Could not create pipe")); 875 876 if (start_flag && pg != NULL) 877 cleanup_pg(pg); 878 879 exit(UU_EXIT_FATAL); 880 } 881 882 if (!pg_ok) 883 (void) printf(gettext("Executing legacy init script \"%s\" " 884 "despite previous errors.\n"), script); 885 else 886 (void) printf(gettext("Executing legacy init script \"%s\".\n"), 887 script); 888 (void) fflush(stdout); 889 890 if (stat(script, &st) != 0) { 891 uu_warn(gettext("Couldn't stat %s (%s).\n"), script, 892 strerror(errno)); 893 st.st_ino = (ino_t)0; 894 } 895 896 pid = fork(); 897 if (pid < 0) { 898 uu_warn(gettext("Could not fork")); 899 900 if (start_flag && pg != NULL) 901 cleanup_pg(pg); 902 903 exit(UU_EXIT_FATAL); 904 } 905 906 if (pid == 0) { 907 /* child */ 908 909 const char *arg1, *arg2, *arg3; 910 911 (void) close(pipefds[0]); 912 (void) fcntl(pipefds[1], F_SETFD, FD_CLOEXEC); 913 914 if (!source) { 915 arg1 = "/bin/sh"; 916 arg2 = script; 917 arg3 = action; 918 } else { 919 arg1 = "/bin/sh"; 920 arg2 = "-c"; 921 arg3 = script; 922 } 923 924 (void) execle(arg1, arg1, arg2, arg3, NULL, newenv); 925 926 uu_warn(gettext("Could not exec \"%s %s %s\""), arg1, 927 arg2, arg3); 928 929 930 /* Notify parent of the failure. */ 931 while (write(pipefds[1], &c, 1) != 1) { 932 switch (errno) { 933 case EAGAIN: 934 (void) sleep(1); 935 936 /* FALLTHROUGH */ 937 938 case EINTR: 939 continue; 940 } 941 942 uu_warn(gettext("Could not inform parent of error")); 943 break; 944 } 945 946 exit(UU_EXIT_FATAL); 947 } 948 949 (void) close(pipefds[1]); 950 951 if (read(pipefds[0], &c, sizeof (c)) > 0) { 952 if (!start_flag) 953 uu_die(gettext("exec() failed; leaving properties.\n")); 954 else { 955 uu_warn(gettext("exec() failed.\n")); 956 if (pg != NULL) 957 cleanup_pg(pg); 958 exit(UU_EXIT_FATAL); 959 } 960 } 961 962 while (waitpid(pid, &exitstatus, 0) == -1) { 963 assert(errno == EINTR); 964 } 965 966 if (WIFSIGNALED(exitstatus)) { 967 char buf[SIG2STR_MAX]; 968 (void) sig2str(WTERMSIG(exitstatus), buf); 969 (void) printf(gettext("Legacy init script \"%s\" failed due " 970 "to signal %s.\n"), script, buf); 971 } else { 972 (void) printf(gettext("Legacy init script \"%s\" exited with " 973 "return code %d.\n"), script, WEXITSTATUS(exitstatus)); 974 } 975 976 if (pg != NULL) { 977 if (start_flag) 978 set_legacy_service(pg, script, st.st_ino); 979 else 980 cleanup_pg(pg); 981 scf_pg_destroy(pg); 982 } 983 984 return (UU_EXIT_OK); 985 } 986