xref: /titanic_52/usr/src/cmd/svc/lsvcrun/lsvcrun.c (revision ba4e3c84e6b9390bbf7df80b5f1d11dec34cc525)
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