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