Home | History | Annotate | Download | only in lsvcrun
      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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * lsvcrun - run an rc?.d script, modifying appropriate data in the
     30  * repository to reflect legacy behavior.
     31  *
     32  * We try to keep track of what we can for the legacy scripts via
     33  * property groups under the smf/legacy_run service.  Each property
     34  * group identifies a service, named in the form 'rc2_d_S10foo'.
     35  *
     36  * Each group has the following properties: name, the script name
     37  * displayed by svcs(1m); state_timestamp; contract, contract ID;
     38  * inode, the inode of the script; and suffix, the suffix of the
     39  * script name, e.g. 'foo'.
     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)
    704 {
    705 	scf_handle_t *h;
    706 	const char *func;
    707 	char *suffix;
    708 	scf_transaction_t *tx;
    709 	struct timeval tstamp;
    710 	struct stat st;
    711 	ctid_t ctid;
    712 	char *svc_name = NULL;
    713 	int ret;
    714 
    715 	h = scf_pg_handle(pg);
    716 	if (h == NULL) {
    717 		func = "scf_pg_handle()";
    718 		goto scferr;
    719 	}
    720 
    721 	ret = gettimeofday(&tstamp, NULL);
    722 	assert(ret == 0);
    723 
    724 	if (stat(script, &st) != 0) {
    725 		uu_warn(gettext("Couldn't stat %s (%s).\n"), script,
    726 		    strerror(errno));
    727 		goto err;
    728 	}
    729 
    730 	if (errno = contract_latest(&ctid)) {
    731 		uu_warn(gettext("Could not get contract"));
    732 		goto err;
    733 	}
    734 
    735 	tx = scf_transaction_create(h);
    736 	if (tx == NULL) {
    737 		func = "scf_transaction_create()";
    738 		goto scferr;
    739 	}
    740 
    741 	if (scf_transaction_start(tx, pg) != 0) {
    742 		func = "scf_transaction_start()";
    743 		goto scferr;
    744 	}
    745 
    746 	/*
    747 	 * We'd like to use the prettier svc_name, but if path_to_svc_name()
    748 	 * fails, we can use the script name anyway.
    749 	 */
    750 	svc_name = path_to_svc_name(script);
    751 
    752 	if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_NAME, SCF_TYPE_ASTRING,
    753 	    (void *)(svc_name ? svc_name : script)) != 0)
    754 		goto err;
    755 
    756 	if (add_new_property(h, tx, SCF_PROPERTY_STATE_TIMESTAMP,
    757 	    SCF_TYPE_TIME, &tstamp) != 0)
    758 		goto err;
    759 
    760 	if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_INODE,
    761 	    SCF_TYPE_COUNT, (void *)st.st_ino) != 0)
    762 		goto err;
    763 
    764 	if ((suffix = script_suffix(script)) != NULL) {
    765 		if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_SUFFIX,
    766 		    SCF_TYPE_ASTRING, (void *)suffix) != 0)
    767 			goto err;
    768 
    769 		free(suffix);
    770 	}
    771 
    772 	if (add_new_property(h, tx, SCF_PROPERTY_CONTRACT, SCF_TYPE_COUNT,
    773 	    (void *)ctid) != 0)
    774 		goto err;
    775 
    776 	for (;;) {
    777 		switch (scf_transaction_commit(tx)) {
    778 		case 1:
    779 			free(svc_name);
    780 			return;
    781 
    782 		case 0:
    783 			if (scf_pg_update(pg) == -1) {
    784 				func = "scf_pg_update()";
    785 				goto scferr;
    786 			}
    787 			continue;
    788 
    789 		case -1:
    790 			func = "scf_transaction_commit()";
    791 			goto scferr;
    792 
    793 		default:
    794 			assert(0);
    795 			abort();
    796 		}
    797 	}
    798 
    799 scferr:
    800 	uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error()));
    801 err:
    802 	uu_die(gettext("Could not commit property values to repository.\n"));
    803 }
    804 
    805 int
    806 main(int argc, char *argv[], char *envp[])
    807 {
    808 	const char *restarter, *script, *action;
    809 	boolean_t source = 0;
    810 	int o;
    811 	boolean_t start_flag;
    812 	char **newenv;
    813 	pid_t pid;
    814 	int pipefds[2];
    815 	char c;
    816 	int exitstatus;
    817 
    818 	scf_propertygroup_t *pg;
    819 	boolean_t pg_ok;
    820 
    821 	(void) uu_setpname(argv[0]);
    822 	uu_alt_exit(UU_PROFILE_LAUNCHER);
    823 
    824 	/* Make sure we were run by svc.startd. */
    825 	if ((restarter = getenv("SMF_RESTARTER")) == NULL ||
    826 	    strcmp(restarter, SCF_SERVICE_STARTD) != 0)
    827 		uu_die(gettext("invocation outside smf(5) inappropriate\n"));
    828 
    829 	while ((o = getopt(argc, argv, "s")) != -1) {
    830 		switch (o) {
    831 		case 's':
    832 			source = 1;
    833 			break;
    834 
    835 		default:
    836 			usage();
    837 		}
    838 	}
    839 
    840 	if (argc - optind != 2)
    841 		usage();
    842 
    843 	script = argv[optind];
    844 	action = argv[optind + 1];
    845 
    846 	if (strcmp(action, "start") == 0)
    847 		start_flag = 1;
    848 	else if (strcmp(action, "stop") == 0)
    849 		start_flag = 0;
    850 	else
    851 		usage();
    852 
    853 	/*
    854 	 * Look for the pg & exit if appropriate.  Also, if we're starting,
    855 	 * add the pg now so we can exit before launching the script if we
    856 	 * have insufficient repository privilege.
    857 	 *
    858 	 * If any other problem occurs, we carry on anyway.
    859 	 */
    860 	pg = get_script_pg(script, start_flag, &pg_ok);
    861 
    862 	/* Clean the environment.  Now so we can fail early. */
    863 	if (!source)
    864 		newenv = approved_env(envp);
    865 	else
    866 		newenv = env_without_smf(envp);
    867 	if (newenv == NULL)
    868 		uu_die(gettext(
    869 		    "Could not create new environment: out of memory.\n"));
    870 
    871 	if (prepare_contract(script, action) == -1) {
    872 		if (start_flag && pg != NULL)
    873 			cleanup_pg(pg);
    874 
    875 		exit(UU_EXIT_FATAL);
    876 	}
    877 
    878 	/* pipe to communicate exec success or failure */
    879 	if (pipe(pipefds) != 0) {
    880 		uu_warn(gettext("Could not create pipe"));
    881 
    882 		if (start_flag && pg != NULL)
    883 			cleanup_pg(pg);
    884 
    885 		exit(UU_EXIT_FATAL);
    886 	}
    887 
    888 	if (!pg_ok)
    889 		(void) printf(gettext("Executing legacy init script \"%s\" "
    890 		    "despite previous errors.\n"), script);
    891 	else
    892 		(void) printf(gettext("Executing legacy init script \"%s\".\n"),
    893 		    script);
    894 	(void) fflush(stdout);
    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);
    979 		else
    980 			cleanup_pg(pg);
    981 		scf_pg_destroy(pg);
    982 	}
    983 
    984 	return (UU_EXIT_OK);
    985 }
    986