lsvcrun.c revision 471:fb6202c3da23
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(). */
84static 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
92static void
93usage()
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 */
104static char *
105start_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
131static char *
132script_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 */
162static char *
163path_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
187static void
188scferr(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
194static scf_propertygroup_t *
195get_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
210add:
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
290out:
291	free(pg_name);
292	scf_property_destroy(prop);
293	return (pg);
294}
295
296static scf_propertygroup_t *
297pg_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
375err:
376	scf_pg_destroy(pg);
377	pg = NULL;
378
379out:
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 */
392static scf_propertygroup_t *
393get_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
426out:
427	*ok = 1;
428	free(suffix);
429	return (pg);
430}
431
432static scf_propertygroup_t *
433get_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
491out:
492	scf_service_destroy(svc);
493	scf_scope_destroy(scope);
494	return (pg);
495}
496
497static int
498prepare_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
524static void
525cleanup_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 */
546static char **
547approved_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 */
584static char **
585env_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
611static int
612add_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
663err:
664	uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error()));
665	return (-1);
666}
667
668static void
669set_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
765scferr:
766	uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error()));
767err:
768	uu_die(gettext("Could not commit property values to repository.\n"));
769}
770
771int
772main(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