xref: /onnv-gate/usr/src/cmd/ctrun/ctrun.c (revision 6073:47f6aa7a8077)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*6073Sacruz  * Common Development and Distribution License (the "License").
6*6073Sacruz  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*6073Sacruz  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/wait.h>
300Sstevel@tonic-gate #include <sys/ctfs.h>
310Sstevel@tonic-gate #include <sys/contract.h>
320Sstevel@tonic-gate #include <sys/contract/process.h>
330Sstevel@tonic-gate #include <stdio.h>
340Sstevel@tonic-gate #include <stdlib.h>
350Sstevel@tonic-gate #include <unistd.h>
360Sstevel@tonic-gate #include <fcntl.h>
370Sstevel@tonic-gate #include <string.h>
380Sstevel@tonic-gate #include <errno.h>
390Sstevel@tonic-gate #include <signal.h>
400Sstevel@tonic-gate #include <limits.h>
410Sstevel@tonic-gate #include <libuutil.h>
420Sstevel@tonic-gate #include <libcontract.h>
430Sstevel@tonic-gate #include <libcontract_priv.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include <locale.h>
460Sstevel@tonic-gate #include <langinfo.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate static int opt_verbose;
490Sstevel@tonic-gate static int opt_Verbose;
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #define	OPT_NORMAL	0x1
520Sstevel@tonic-gate #define	OPT_FATAL	0x2
530Sstevel@tonic-gate 
540Sstevel@tonic-gate typedef struct optvect {
550Sstevel@tonic-gate 	const char	*opt_name;
560Sstevel@tonic-gate 	uint_t		opt_value;
570Sstevel@tonic-gate 	uint_t		opt_flags;
580Sstevel@tonic-gate } optvect_t;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate static optvect_t option_params[] = {
610Sstevel@tonic-gate 	{ "noorphan", CT_PR_NOORPHAN },
620Sstevel@tonic-gate 	{ "pgrponly", CT_PR_PGRPONLY },
630Sstevel@tonic-gate 	{ "regent", CT_PR_REGENT },
640Sstevel@tonic-gate 	{ "inherit", CT_PR_INHERIT },
650Sstevel@tonic-gate 	{ NULL }
660Sstevel@tonic-gate };
670Sstevel@tonic-gate 
680Sstevel@tonic-gate static optvect_t option_events[] = {
690Sstevel@tonic-gate 	{ "core", CT_PR_EV_CORE, OPT_NORMAL | OPT_FATAL },
700Sstevel@tonic-gate 	{ "signal", CT_PR_EV_SIGNAL, OPT_NORMAL | OPT_FATAL },
710Sstevel@tonic-gate 	{ "hwerr", CT_PR_EV_HWERR, OPT_NORMAL | OPT_FATAL },
720Sstevel@tonic-gate 	{ "empty", CT_PR_EV_EMPTY, OPT_NORMAL },
730Sstevel@tonic-gate 	{ "fork", CT_PR_EV_FORK, OPT_NORMAL },
740Sstevel@tonic-gate 	{ "exit", CT_PR_EV_EXIT, OPT_NORMAL },
750Sstevel@tonic-gate 	{ NULL }
760Sstevel@tonic-gate };
770Sstevel@tonic-gate 
780Sstevel@tonic-gate typedef enum lifetime {
790Sstevel@tonic-gate 	LT_NONE,
800Sstevel@tonic-gate 	LT_CHILD,
810Sstevel@tonic-gate 	LT_CONTRACT
820Sstevel@tonic-gate } lifetime_t;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /*
850Sstevel@tonic-gate  * Exit code to use when the child exited abnormally (i.e. exited with
860Sstevel@tonic-gate  * a status we are unable to emulate).
870Sstevel@tonic-gate  */
880Sstevel@tonic-gate #define	EXIT_BADCHILD	123
890Sstevel@tonic-gate 
900Sstevel@tonic-gate #define	USAGESTR	\
910Sstevel@tonic-gate 	"Usage: %s [-i eventlist] [-f eventlist] [-l lifetime] \n" \
92*6073Sacruz 	"\t[-o optionlist] [-r count [-t]] [-v]\n" \
93*6073Sacruz 	"\t[-F fmri] [-A aux] command\n"
940Sstevel@tonic-gate 
950Sstevel@tonic-gate /*
960Sstevel@tonic-gate  * usage
970Sstevel@tonic-gate  *
980Sstevel@tonic-gate  * Educate the user.
990Sstevel@tonic-gate  */
1000Sstevel@tonic-gate static void
usage(void)1010Sstevel@tonic-gate usage(void)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(USAGESTR), uu_getpname());
1040Sstevel@tonic-gate 	exit(UU_EXIT_USAGE);
1050Sstevel@tonic-gate }
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate /*
1080Sstevel@tonic-gate  * bit2str
1090Sstevel@tonic-gate  *
1100Sstevel@tonic-gate  * Convert a bit into its string representation.
1110Sstevel@tonic-gate  */
1120Sstevel@tonic-gate static const char *
bit2str(optvect_t * options,uint_t bit)1130Sstevel@tonic-gate bit2str(optvect_t *options, uint_t bit)
1140Sstevel@tonic-gate {
1150Sstevel@tonic-gate 	for (; options->opt_name; options++)
1160Sstevel@tonic-gate 		if (options->opt_value == bit)
1170Sstevel@tonic-gate 			return (options->opt_name);
1180Sstevel@tonic-gate 	return (NULL);
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * str2bit
1230Sstevel@tonic-gate  *
1240Sstevel@tonic-gate  * Convert a string into its bit representation.  If match is set, only
1250Sstevel@tonic-gate  * look at those options with the match bit set in its opt_flags
1260Sstevel@tonic-gate  * field.
1270Sstevel@tonic-gate  */
1280Sstevel@tonic-gate static uint_t
str2bit(optvect_t * options,int match,const char * str,int len)1290Sstevel@tonic-gate str2bit(optvect_t *options, int match, const char *str, int len)
1300Sstevel@tonic-gate {
1310Sstevel@tonic-gate 	for (; options->opt_name; options++) {
1320Sstevel@tonic-gate 		if (match && (options->opt_flags & match) == 0)
1330Sstevel@tonic-gate 			continue;
1340Sstevel@tonic-gate 		if (strncmp(str, options->opt_name, len) == 0)
1350Sstevel@tonic-gate 			return (options->opt_value);
1360Sstevel@tonic-gate 	}
1370Sstevel@tonic-gate 	return (0);
1380Sstevel@tonic-gate }
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate /*
1410Sstevel@tonic-gate  * opt2bits
1420Sstevel@tonic-gate  *
1430Sstevel@tonic-gate  * Given a set of textual options separated by commas or spaces,
1440Sstevel@tonic-gate  * convert them to a set of bits.  Errors are fatal, except for empty
1450Sstevel@tonic-gate  * options (which are ignored) and duplicate options (which are
1460Sstevel@tonic-gate  * idempotent).
1470Sstevel@tonic-gate  */
1480Sstevel@tonic-gate static void
opt2bits(optvect_t * options,int match,const char * str,uint_t * bits,char c)1490Sstevel@tonic-gate opt2bits(optvect_t *options, int match, const char *str, uint_t *bits, char c)
1500Sstevel@tonic-gate {
1510Sstevel@tonic-gate 	const char *ptr, *next = str;
1520Sstevel@tonic-gate 	uint_t result = 0;
1530Sstevel@tonic-gate 	uint_t bit;
1540Sstevel@tonic-gate 	int none = 0;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	while (*str) {
1570Sstevel@tonic-gate 		int len;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 		ptr = strpbrk(str, ", ");
1600Sstevel@tonic-gate 		if (ptr != NULL) {
1610Sstevel@tonic-gate 			len = ptr - str;
1620Sstevel@tonic-gate 			next = ptr + 1;
1630Sstevel@tonic-gate 		} else {
1640Sstevel@tonic-gate 			len = strlen(str);
1650Sstevel@tonic-gate 			next = str + len;
1660Sstevel@tonic-gate 		}
1670Sstevel@tonic-gate 		if (len == 0) {
1680Sstevel@tonic-gate 			uu_warn(gettext("empty option\n"));
1690Sstevel@tonic-gate 			bit = 0;
1700Sstevel@tonic-gate 		} else {
1710Sstevel@tonic-gate 			bit = str2bit(options, match, str, len);
1720Sstevel@tonic-gate 			if (bit == 0 && strncmp(str, "none", len) == 0) {
1730Sstevel@tonic-gate 				none = 1;
1740Sstevel@tonic-gate 				if (result)
1750Sstevel@tonic-gate 					goto noneerr;
1760Sstevel@tonic-gate 			} else if (bit == 0) {
1770Sstevel@tonic-gate 				uu_warn(gettext("unrecognized option '%.*s'\n"),
1780Sstevel@tonic-gate 				    len, str);
1790Sstevel@tonic-gate 				uu_warn(gettext("error parsing '-%c' option\n"),
1800Sstevel@tonic-gate 				    c);
1810Sstevel@tonic-gate 				usage();
1820Sstevel@tonic-gate 			} else if (none) {
1830Sstevel@tonic-gate 				goto noneerr;
1840Sstevel@tonic-gate 			}
1850Sstevel@tonic-gate 			if (result & bit)
1860Sstevel@tonic-gate 				uu_warn(gettext("option '%.*s' "
1870Sstevel@tonic-gate 				    "specified twice\n"), len, str);
1880Sstevel@tonic-gate 		}
1890Sstevel@tonic-gate 		result |= bit;
1900Sstevel@tonic-gate 		str = next;
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	*bits = result;
1940Sstevel@tonic-gate 	return;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate noneerr:
1970Sstevel@tonic-gate 	uu_warn(gettext("option is incompatible with others: '%s'\n"), "none");
1980Sstevel@tonic-gate 	usage();
1990Sstevel@tonic-gate }
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate /*
2020Sstevel@tonic-gate  * close_on_exec
2030Sstevel@tonic-gate  *
2040Sstevel@tonic-gate  * Given a fd, marks it close-on-exec.
2050Sstevel@tonic-gate  */
2060Sstevel@tonic-gate static int
close_on_exec(int fd)2070Sstevel@tonic-gate close_on_exec(int fd)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate 	int flags = fcntl(fd, F_GETFD, 0);
2100Sstevel@tonic-gate 	if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1))
2110Sstevel@tonic-gate 		return (0);
2120Sstevel@tonic-gate 	return (-1);
2130Sstevel@tonic-gate }
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate /*
2160Sstevel@tonic-gate  * v_printf
2170Sstevel@tonic-gate  *
2180Sstevel@tonic-gate  * Output routine for messages printed only when -v is specified.
2190Sstevel@tonic-gate  */
2200Sstevel@tonic-gate /* PRINTFLIKE1 */
2210Sstevel@tonic-gate static void
v_printf(const char * format,...)2220Sstevel@tonic-gate v_printf(const char *format, ...)
2230Sstevel@tonic-gate {
2240Sstevel@tonic-gate 	va_list va;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	if (opt_verbose) {
2270Sstevel@tonic-gate 		(void) printf("%s(%ld): ", uu_getpname(), getpid());
2280Sstevel@tonic-gate 		va_start(va, format);
2290Sstevel@tonic-gate 		(void) vprintf(format, va);
2300Sstevel@tonic-gate 		va_end(va);
2310Sstevel@tonic-gate 	}
2320Sstevel@tonic-gate }
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate /*
2350Sstevel@tonic-gate  * get_event
2360Sstevel@tonic-gate  *
2370Sstevel@tonic-gate  * Reads and acknowledges an event.  Returns the event type.
2380Sstevel@tonic-gate  */
2390Sstevel@tonic-gate static uint_t
get_event(int fd,int ctfd,ctid_t ctid)2400Sstevel@tonic-gate get_event(int fd, int ctfd, ctid_t ctid)
2410Sstevel@tonic-gate {
2420Sstevel@tonic-gate 	ct_evthdl_t ev;
2430Sstevel@tonic-gate 	uint_t result;
2440Sstevel@tonic-gate 	ctevid_t evid;
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	for (;;) {
2470Sstevel@tonic-gate 		int efd;
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 		/*
2500Sstevel@tonic-gate 		 * Normally we only need to look at critical messages.
2510Sstevel@tonic-gate 		 * If we are displaying contract events, however, we
2520Sstevel@tonic-gate 		 * have to read them all.
2530Sstevel@tonic-gate 		 */
2540Sstevel@tonic-gate 		errno = opt_verbose ? ct_event_read(fd, &ev) :
2550Sstevel@tonic-gate 		    ct_event_read_critical(fd, &ev);
2560Sstevel@tonic-gate 		if (errno != 0)
2570Sstevel@tonic-gate 			uu_die(gettext("failed to listen to contract events"));
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 		/*
2600Sstevel@tonic-gate 		 * If requested, display the event.
2610Sstevel@tonic-gate 		 */
2620Sstevel@tonic-gate 		if (opt_verbose) {
2630Sstevel@tonic-gate 			v_printf(gettext("event from contract %ld: "),
2640Sstevel@tonic-gate 			    ct_event_get_ctid(ev));
2650Sstevel@tonic-gate 			contract_event_dump(stdout, ev, opt_Verbose);
2660Sstevel@tonic-gate 			if ((ct_event_get_flags(ev) & CTE_INFO) != 0) {
2670Sstevel@tonic-gate 				ct_event_free(ev);
2680Sstevel@tonic-gate 				continue;
2690Sstevel@tonic-gate 			}
2700Sstevel@tonic-gate 		}
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 		/*
2730Sstevel@tonic-gate 		 * We're done if this event is one of ours.
2740Sstevel@tonic-gate 		 */
2750Sstevel@tonic-gate 		evid = ct_event_get_evid(ev);
2760Sstevel@tonic-gate 		if (ct_event_get_ctid(ev) == ctid)
2770Sstevel@tonic-gate 			break;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 		/*
2800Sstevel@tonic-gate 		 * ACK events from other contracts.
2810Sstevel@tonic-gate 		 * This shouldn't happen, but it could.
2820Sstevel@tonic-gate 		 */
2830Sstevel@tonic-gate 		efd = contract_open(ct_event_get_ctid(ev), "process", "ctl",
2840Sstevel@tonic-gate 		    O_WRONLY);
2850Sstevel@tonic-gate 		if (efd != -1) {
2860Sstevel@tonic-gate 			(void) ct_ctl_ack(efd, evid);
2870Sstevel@tonic-gate 			(void) close(efd);
2880Sstevel@tonic-gate 		}
2890Sstevel@tonic-gate 		ct_event_free(ev);
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	/*
2930Sstevel@tonic-gate 	 * Note that if we want to use ctrun as a simple restarter, we
2940Sstevel@tonic-gate 	 * need persistently keep track of fatal events so we can
2950Sstevel@tonic-gate 	 * properly handle the death of the contract.  Rather than keep
2960Sstevel@tonic-gate 	 * a file or somesuch lying around, it might make more sense to
2970Sstevel@tonic-gate 	 * leave the significant fatal event sitting in the queue so
2980Sstevel@tonic-gate 	 * that a restarted instance of ctrun can pick it up.  For now
2990Sstevel@tonic-gate 	 * we'll just ACK all events.
3000Sstevel@tonic-gate 	 */
3010Sstevel@tonic-gate 	(void) ct_ctl_ack(ctfd, evid);
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	result = ct_event_get_type(ev);
3040Sstevel@tonic-gate 	ct_event_free(ev);
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	return (result);
3070Sstevel@tonic-gate }
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate /*
3100Sstevel@tonic-gate  * abandon
3110Sstevel@tonic-gate  *
3120Sstevel@tonic-gate  * Given an fd for a contract's ctl file, abandon the contract and
3130Sstevel@tonic-gate  * close the file.
3140Sstevel@tonic-gate  */
3150Sstevel@tonic-gate static void
abandon(int ctfd)3160Sstevel@tonic-gate abandon(int ctfd)
3170Sstevel@tonic-gate {
3180Sstevel@tonic-gate 	if (ct_ctl_abandon(ctfd) == -1)
3190Sstevel@tonic-gate 		uu_die(gettext("failed to abandon contract %d"), ctfd);
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	(void) close(ctfd);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate static int chldstat;
3250Sstevel@tonic-gate static int chldexited;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate /*
3280Sstevel@tonic-gate  * sigchld
3290Sstevel@tonic-gate  *
3300Sstevel@tonic-gate  * Our SIGCHLD handler.  Sets chldstat and chldexited so the
3310Sstevel@tonic-gate  * interrupted code knows what happened.
3320Sstevel@tonic-gate  */
3330Sstevel@tonic-gate /*ARGSUSED*/
3340Sstevel@tonic-gate static void
sigchld(int sig,struct siginfo * si,void * ucp)3350Sstevel@tonic-gate sigchld(int sig, struct siginfo *si, void *ucp)
3360Sstevel@tonic-gate {
3370Sstevel@tonic-gate 	int err = errno;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	if (si->si_code == CLD_EXITED)
3400Sstevel@tonic-gate 		chldstat = si->si_status;
3410Sstevel@tonic-gate 	else
3420Sstevel@tonic-gate 		chldstat = EXIT_BADCHILD;
3430Sstevel@tonic-gate 	chldexited = 1;
3440Sstevel@tonic-gate 	while (waitpid(si->si_pid, NULL, 0) == -1 && errno == EINTR)
3450Sstevel@tonic-gate 		;
3460Sstevel@tonic-gate 	errno = err;
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate /*
3500Sstevel@tonic-gate  * dowait
3510Sstevel@tonic-gate  *
3520Sstevel@tonic-gate  * Waits for the specified child to exit.  Returns the exit code ctrun
3530Sstevel@tonic-gate  * should return.
3540Sstevel@tonic-gate  */
3550Sstevel@tonic-gate static int
dowait(int pid)3560Sstevel@tonic-gate dowait(int pid)
3570Sstevel@tonic-gate {
3580Sstevel@tonic-gate 	pid_t wpid;
3590Sstevel@tonic-gate 	int wstatus;
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	do
3620Sstevel@tonic-gate 		wpid = waitpid(pid, &wstatus, 0);
3630Sstevel@tonic-gate 	while (wpid == -1 && errno == EINTR);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	if (wpid == -1)
3660Sstevel@tonic-gate 		uu_die(gettext("wait failed"));
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	if (WIFEXITED(wstatus))
3690Sstevel@tonic-gate 		return (WEXITSTATUS(wstatus));
3700Sstevel@tonic-gate 	else
3710Sstevel@tonic-gate 		return (EXIT_BADCHILD);
3720Sstevel@tonic-gate }
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate int
main(int argc,char ** argv)3750Sstevel@tonic-gate main(int argc, char **argv)
3760Sstevel@tonic-gate {
3770Sstevel@tonic-gate 	int	fd, efd;
3780Sstevel@tonic-gate 	pid_t	pid;
3790Sstevel@tonic-gate 	ctid_t	ctid = 0;
3800Sstevel@tonic-gate 	int	ctfd;
3810Sstevel@tonic-gate 	int	pipefds[2];
3820Sstevel@tonic-gate 	struct sigaction osact;
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	int	s;
3850Sstevel@tonic-gate 	ctid_t	opt_adopt = 0;
3860Sstevel@tonic-gate 	int	opt_transfer = 0;
3870Sstevel@tonic-gate 	int	opt_count = -1;
3880Sstevel@tonic-gate 	uint_t	opt_info = CT_PR_EV_CORE;
3890Sstevel@tonic-gate 	uint_t	opt_crit = 0;
3900Sstevel@tonic-gate 	uint_t	eff_fatal, opt_fatal = CT_PR_EV_HWERR;
3910Sstevel@tonic-gate 	uint_t	eff_param, opt_param = 0;
3920Sstevel@tonic-gate 	lifetime_t opt_life = LT_CONTRACT;
3930Sstevel@tonic-gate 
394*6073Sacruz 	char *svc_fmri = NULL;
395*6073Sacruz 	char *svc_aux = NULL;
396*6073Sacruz 
3970Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
3980Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
3990Sstevel@tonic-gate 	uu_alt_exit(UU_PROFILE_LAUNCHER);
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	(void) uu_setpname(argv[0]);
4020Sstevel@tonic-gate 
403*6073Sacruz 	while ((s = getopt(argc, argv, "a:A:l:o:i:c:f:F:r:tvV")) != EOF) {
4040Sstevel@tonic-gate 		switch (s) {
4050Sstevel@tonic-gate 		case 'a':
4060Sstevel@tonic-gate 			if (uu_strtoint(optarg, &opt_adopt, sizeof (opt_adopt),
4070Sstevel@tonic-gate 			    0, 0, INT32_MAX) == -1) {
4080Sstevel@tonic-gate 				uu_warn(gettext("invalid contract ID '%s'\n"),
4090Sstevel@tonic-gate 				    optarg);
4100Sstevel@tonic-gate 				usage();
4110Sstevel@tonic-gate 			}
4120Sstevel@tonic-gate 			break;
4130Sstevel@tonic-gate 		case 'v':
4140Sstevel@tonic-gate 			opt_verbose = 1;
4150Sstevel@tonic-gate 			break;
4160Sstevel@tonic-gate 		case 'V':
4170Sstevel@tonic-gate 			opt_Verbose = 1;
4180Sstevel@tonic-gate 			opt_verbose = 1;
4190Sstevel@tonic-gate 			break;
4200Sstevel@tonic-gate 		case 't':
4210Sstevel@tonic-gate 			opt_transfer = 1;
4220Sstevel@tonic-gate 			break;
4230Sstevel@tonic-gate 		case 'r':
4240Sstevel@tonic-gate 			if (uu_strtoint(optarg, &opt_count, sizeof (opt_adopt),
4250Sstevel@tonic-gate 			    0, 0, INT32_MAX) == -1) {
4260Sstevel@tonic-gate 				uu_warn(gettext("invalid count '%s'\n"),
4270Sstevel@tonic-gate 				    optarg);
4280Sstevel@tonic-gate 				usage();
4290Sstevel@tonic-gate 			}
4300Sstevel@tonic-gate 			break;
4310Sstevel@tonic-gate 		case 'l':
4320Sstevel@tonic-gate 			if (strcmp(optarg, "none") == 0) {
4330Sstevel@tonic-gate 				opt_life = LT_NONE;
4340Sstevel@tonic-gate 			} else if (strcmp(optarg, "child") == 0) {
4350Sstevel@tonic-gate 				opt_life = LT_CHILD;
4360Sstevel@tonic-gate 			} else if (strcmp(optarg, "contract") == 0) {
4370Sstevel@tonic-gate 				opt_life = LT_CONTRACT;
4380Sstevel@tonic-gate 			} else {
4390Sstevel@tonic-gate 				uu_warn(gettext("invalid lifetime '%s'\n"),
4400Sstevel@tonic-gate 				    optarg);
4410Sstevel@tonic-gate 				usage();
4420Sstevel@tonic-gate 			}
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 			break;
4450Sstevel@tonic-gate 		case 'o':
4460Sstevel@tonic-gate 			opt2bits(option_params, 0, optarg, &opt_param,
4470Sstevel@tonic-gate 			    optopt);
4480Sstevel@tonic-gate 			break;
4490Sstevel@tonic-gate 		case 'i':
4500Sstevel@tonic-gate 			opt2bits(option_events, OPT_NORMAL, optarg, &opt_info,
4510Sstevel@tonic-gate 			    optopt);
4520Sstevel@tonic-gate 			break;
4530Sstevel@tonic-gate 		case 'c':
4540Sstevel@tonic-gate 			opt2bits(option_events, OPT_NORMAL, optarg, &opt_crit,
4550Sstevel@tonic-gate 			    optopt);
4560Sstevel@tonic-gate 			break;
4570Sstevel@tonic-gate 		case 'f':
4580Sstevel@tonic-gate 			opt2bits(option_events, OPT_FATAL, optarg, &opt_fatal,
4590Sstevel@tonic-gate 			    optopt);
4600Sstevel@tonic-gate 			break;
461*6073Sacruz 		case 'F':
462*6073Sacruz 			svc_fmri = optarg;
463*6073Sacruz 			break;
464*6073Sacruz 		case 'A':
465*6073Sacruz 			svc_aux = optarg;
466*6073Sacruz 			break;
4670Sstevel@tonic-gate 		default:
4680Sstevel@tonic-gate 			usage();
4690Sstevel@tonic-gate 		}
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 	argc -= optind;
4720Sstevel@tonic-gate 	argv += optind;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	/*
4750Sstevel@tonic-gate 	 * Basic argument sanity checks.
4760Sstevel@tonic-gate 	 */
4770Sstevel@tonic-gate 	if ((opt_life == LT_NONE) && (opt_param & CT_PR_NOORPHAN)) {
4780Sstevel@tonic-gate 		uu_warn(gettext("cannot use option '%s' with lifetime '%s'\n"),
4790Sstevel@tonic-gate 		    bit2str(option_params, CT_PR_NOORPHAN), "none");
4800Sstevel@tonic-gate 		usage();
4810Sstevel@tonic-gate 	}
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	if ((opt_life != LT_CONTRACT) && (opt_count >= 0)) {
4840Sstevel@tonic-gate 		uu_warn(gettext("cannot restart with lifetime '%s'\n"),
4850Sstevel@tonic-gate 		    opt_life == LT_NONE ? "none" : "child");
4860Sstevel@tonic-gate 		usage();
4870Sstevel@tonic-gate 	}
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	if ((opt_param & CT_PR_PGRPONLY) && (opt_count >= 0)) {
4900Sstevel@tonic-gate 		uu_warn(gettext("cannot restart with option '%s'\n"),
4910Sstevel@tonic-gate 		    bit2str(option_params, CT_PR_PGRPONLY));
4920Sstevel@tonic-gate 		usage();
4930Sstevel@tonic-gate 	}
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	if (opt_transfer && (opt_count == -1)) {
4960Sstevel@tonic-gate 		uu_warn(gettext("cannot transfer when not restarting\n"));
4970Sstevel@tonic-gate 		usage();
4980Sstevel@tonic-gate 	}
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 	if (argc <= 0)
5010Sstevel@tonic-gate 		usage();
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	/*
5040Sstevel@tonic-gate 	 * Create a process contract template and our process's process
5050Sstevel@tonic-gate 	 * contract bundle endpoint.  Mark them close-on-exec so we
5060Sstevel@tonic-gate 	 * don't have to worry about closing them in our child.
5070Sstevel@tonic-gate 	 */
5080Sstevel@tonic-gate 	fd = open64(CTFS_ROOT "/process/template", O_RDWR);
5090Sstevel@tonic-gate 	if (fd == -1)
5100Sstevel@tonic-gate 		uu_die(gettext("template open failed"));
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	efd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY);
5130Sstevel@tonic-gate 	if (efd == -1)
5140Sstevel@tonic-gate 		uu_die(gettext("process bundle open failed"));
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	if (close_on_exec(fd) || close_on_exec(efd))
5170Sstevel@tonic-gate 		uu_die(gettext("could not set FD_CLOEXEC"));
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	/*
5200Sstevel@tonic-gate 	 * Set the process contract's terms based on our arguments.
5210Sstevel@tonic-gate 	 */
5220Sstevel@tonic-gate 	if (errno = ct_pr_tmpl_set_param(fd, opt_param))
5230Sstevel@tonic-gate 		uu_die(gettext("set param failed"));
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	if (errno = ct_tmpl_set_informative(fd, opt_info))
5260Sstevel@tonic-gate 		uu_die(gettext("set notify failed"));
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	if (errno = ct_pr_tmpl_set_fatal(fd, opt_fatal))
5290Sstevel@tonic-gate 		uu_die(gettext("set fatal failed"));
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	if (opt_param & CT_PR_PGRPONLY)
5320Sstevel@tonic-gate 		opt_crit = CT_PR_EV_EMPTY;
5330Sstevel@tonic-gate 	else
5340Sstevel@tonic-gate 		opt_crit |= opt_fatal | CT_PR_EV_EMPTY;
5350Sstevel@tonic-gate 	if (errno = ct_tmpl_set_critical(fd, opt_crit))
5360Sstevel@tonic-gate 		uu_die(gettext("set critical failed"));
537*6073Sacruz 	if (svc_fmri && (errno = ct_pr_tmpl_set_svc_fmri(fd, svc_fmri)))
538*6073Sacruz 		uu_die(gettext("set fmri failed: "
539*6073Sacruz 		    "insufficient privileges\n"));
540*6073Sacruz 	if (svc_aux && (errno = ct_pr_tmpl_set_svc_aux(fd, svc_aux)))
541*6073Sacruz 		uu_die(gettext("set aux failed"));
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	/*
5440Sstevel@tonic-gate 	 * Activate the template.
5450Sstevel@tonic-gate 	 */
5460Sstevel@tonic-gate 	if (errno = ct_tmpl_activate(fd))
5470Sstevel@tonic-gate 		uu_die(gettext("template activate failed"));
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate restart:
5500Sstevel@tonic-gate 	if (opt_adopt) {
5510Sstevel@tonic-gate 		/*
5520Sstevel@tonic-gate 		 * Adopt a specific contract.
5530Sstevel@tonic-gate 		 */
5540Sstevel@tonic-gate 		ct_stathdl_t st;
5550Sstevel@tonic-gate 		int stfd;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 		if ((ctfd = contract_open(opt_adopt, "process", "ctl",
5580Sstevel@tonic-gate 		    O_WRONLY)) == -1)
5590Sstevel@tonic-gate 			uu_die(gettext("could not open contract %ld"),
5600Sstevel@tonic-gate 			    opt_adopt);
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 		/*
5630Sstevel@tonic-gate 		 * Read the contract's terms so that we interpret its
5640Sstevel@tonic-gate 		 * events properly.
5650Sstevel@tonic-gate 		 */
5660Sstevel@tonic-gate 		if (((stfd = contract_open(opt_adopt, "process", "status",
5670Sstevel@tonic-gate 		    O_RDONLY)) == -1) ||
5680Sstevel@tonic-gate 		    (errno = ct_status_read(stfd, CTD_FIXED, &st)) ||
5690Sstevel@tonic-gate 		    (errno = ct_pr_status_get_fatal(st, &eff_fatal)) ||
5700Sstevel@tonic-gate 		    (errno = ct_pr_status_get_param(st, &eff_param)))
5710Sstevel@tonic-gate 			uu_die(gettext("could not stat contract %ld"),
5720Sstevel@tonic-gate 			    opt_adopt);
5730Sstevel@tonic-gate 		ct_status_free(st);
5740Sstevel@tonic-gate 		(void) close(stfd);
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 		if (errno = ct_ctl_adopt(ctfd))
5770Sstevel@tonic-gate 			uu_die(gettext("could not adopt contract %ld"),
5780Sstevel@tonic-gate 			    opt_adopt);
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 		ctid = opt_adopt;
5810Sstevel@tonic-gate 		opt_adopt = 0;
5820Sstevel@tonic-gate 		v_printf(gettext("adopted contract id %ld\n"), ctid);
5830Sstevel@tonic-gate 	} else {
5840Sstevel@tonic-gate 		/*
5850Sstevel@tonic-gate 		 * Create a new process.
5860Sstevel@tonic-gate 		 */
5870Sstevel@tonic-gate 		if (opt_life == LT_CONTRACT) {
5880Sstevel@tonic-gate 			struct sigaction sact;
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 			/*
5910Sstevel@tonic-gate 			 * Since we are going to be waiting for and
5920Sstevel@tonic-gate 			 * reacting to contract events, install a
5930Sstevel@tonic-gate 			 * signal handler so we capture the exit status
5940Sstevel@tonic-gate 			 * of our child.
5950Sstevel@tonic-gate 			 */
5960Sstevel@tonic-gate 			chldstat = UU_EXIT_OK;
5970Sstevel@tonic-gate 			chldexited = 0;
5980Sstevel@tonic-gate 			sact.sa_sigaction = sigchld;
5990Sstevel@tonic-gate 			sact.sa_flags = SA_SIGINFO | SA_RESTART |
6000Sstevel@tonic-gate 			    SA_NOCLDSTOP;
6010Sstevel@tonic-gate 			(void) sigemptyset(&sact.sa_mask);
6020Sstevel@tonic-gate 			if (sigaction(SIGCHLD, &sact, &osact) == -1)
6030Sstevel@tonic-gate 				uu_die(gettext("failed to install "
6040Sstevel@tonic-gate 				    "sigchld handler"));
6050Sstevel@tonic-gate 		} else if (opt_life == LT_NONE) {
6060Sstevel@tonic-gate 			/*
6070Sstevel@tonic-gate 			 * Though we aren't waiting for our child to
6080Sstevel@tonic-gate 			 * exit, as a well-behaved command launcher we
6090Sstevel@tonic-gate 			 * must wait for it to exec.  On success the
6100Sstevel@tonic-gate 			 * pipe will simply close, and on failure the
6110Sstevel@tonic-gate 			 * proper exit status will be sent.
6120Sstevel@tonic-gate 			 */
6130Sstevel@tonic-gate 			if (pipe(pipefds) == -1 ||
6140Sstevel@tonic-gate 			    close_on_exec(pipefds[0]) == -1 ||
6150Sstevel@tonic-gate 			    close_on_exec(pipefds[1]) == -1)
6160Sstevel@tonic-gate 				uu_die(gettext("failed to create pipe"));
6170Sstevel@tonic-gate 		}
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 		if ((pid = fork()) == -1) {
6200Sstevel@tonic-gate 			uu_die(gettext("fork failed"));
6210Sstevel@tonic-gate 		} else if (pid == 0) {
6220Sstevel@tonic-gate 			int result = execvp(argv[0], argv);
6230Sstevel@tonic-gate 			if (opt_life == LT_NONE) {
6240Sstevel@tonic-gate 				char a = 1;
6250Sstevel@tonic-gate 				int err = errno;
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 				(void) write(pipefds[1], &a, sizeof (a));
6280Sstevel@tonic-gate 				errno = err;
6290Sstevel@tonic-gate 			}
6300Sstevel@tonic-gate 			if (result == -1)
6310Sstevel@tonic-gate 				uu_xdie(errno == ENOENT ? 127 : 126,
6320Sstevel@tonic-gate 				    gettext("exec failed"));
6330Sstevel@tonic-gate 			uu_die(gettext("exec returned!\n"));
6340Sstevel@tonic-gate 		}
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		/*
6370Sstevel@tonic-gate 		 * Get the newly-created contract's id and ctl fd.
6380Sstevel@tonic-gate 		 */
6390Sstevel@tonic-gate 		if (errno = contract_latest(&ctid))
6400Sstevel@tonic-gate 			uu_die(gettext("could not get new contract's id"));
6410Sstevel@tonic-gate 		if ((ctfd = contract_open(ctid, "process", "ctl",
6420Sstevel@tonic-gate 		    O_WRONLY)) == -1)
6430Sstevel@tonic-gate 			uu_die(gettext("could not open contract"));
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 		/*
6460Sstevel@tonic-gate 		 * Clear the transfer parameter so that the contract
6470Sstevel@tonic-gate 		 * will be freed sooner and admins won't get nervous.
6480Sstevel@tonic-gate 		 */
6490Sstevel@tonic-gate 		if (opt_transfer) {
6500Sstevel@tonic-gate 			(void) ct_pr_tmpl_set_transfer(fd, 0);
6510Sstevel@tonic-gate 			(void) ct_tmpl_activate(fd);
6520Sstevel@tonic-gate 		}
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 		v_printf(gettext("created contract id %ld\n"), ctid);
6550Sstevel@tonic-gate 		eff_param = opt_param;
6560Sstevel@tonic-gate 		eff_fatal = opt_fatal;
6570Sstevel@tonic-gate 	}
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	if (opt_life == LT_CONTRACT) {
6600Sstevel@tonic-gate 		uint_t event, errevent = 0;
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		/*
6630Sstevel@tonic-gate 		 * Wait until the contract empties out.
6640Sstevel@tonic-gate 		 */
6650Sstevel@tonic-gate 		do {
6660Sstevel@tonic-gate 			event = get_event(efd, ctfd, ctid);
6670Sstevel@tonic-gate 			if (event & eff_fatal) {
6680Sstevel@tonic-gate 				if ((eff_param & CT_PR_PGRPONLY) == 0)
6690Sstevel@tonic-gate 					errevent = event;
6700Sstevel@tonic-gate 				v_printf(gettext(
6710Sstevel@tonic-gate 				    "fatal \"%s\" event from contract %ld\n"),
6720Sstevel@tonic-gate 				    bit2str(option_events, event), ctid);
6730Sstevel@tonic-gate 			}
6740Sstevel@tonic-gate 		} while ((event & CT_PR_EV_EMPTY) == 0);
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 		/*
6770Sstevel@tonic-gate 		 * If we encountered a fatal error event, and we
6780Sstevel@tonic-gate 		 * haven't expended our maximum loop count, restart.
6790Sstevel@tonic-gate 		 */
6800Sstevel@tonic-gate 		if ((errevent != 0) &&
6810Sstevel@tonic-gate 		    ((opt_count == 0) || (opt_count-- > 1))) {
6820Sstevel@tonic-gate 			v_printf(gettext("failure in contract %ld, "
6830Sstevel@tonic-gate 			    "restarting command\n"), ctid);
6840Sstevel@tonic-gate 			if (opt_transfer) {
6850Sstevel@tonic-gate 				/*
6860Sstevel@tonic-gate 				 * Add the failed contract to the new
6870Sstevel@tonic-gate 				 * contract's terms so that its
6880Sstevel@tonic-gate 				 * inherited subcontracts can be
6890Sstevel@tonic-gate 				 * adopted by the new process.
6900Sstevel@tonic-gate 				 */
6910Sstevel@tonic-gate 				if (errno = ct_pr_tmpl_set_transfer(fd, ctid))
6920Sstevel@tonic-gate 					uu_die(gettext("set transfer failed"));
6930Sstevel@tonic-gate 				if (errno = ct_tmpl_activate(fd))
6940Sstevel@tonic-gate 					uu_die(gettext(
6950Sstevel@tonic-gate 					    "template activate failed"));
6960Sstevel@tonic-gate 				(void) close(ctfd);
6970Sstevel@tonic-gate 			} else {
6980Sstevel@tonic-gate 				abandon(ctfd);
6990Sstevel@tonic-gate 			}
7000Sstevel@tonic-gate 			goto restart;
7010Sstevel@tonic-gate 		}
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 		/*
7040Sstevel@tonic-gate 		 * At this point we are done with the contract; we
7050Sstevel@tonic-gate 		 * don't want it to be inherited when we exit.
7060Sstevel@tonic-gate 		 */
7070Sstevel@tonic-gate 		abandon(ctfd);
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 		/*
7100Sstevel@tonic-gate 		 * In case there was a race between SIGCHLD delivery
7110Sstevel@tonic-gate 		 * and contract event delivery, disable the signal
7120Sstevel@tonic-gate 		 * handler and look for the child.
7130Sstevel@tonic-gate 		 */
7140Sstevel@tonic-gate 		(void) sigaction(SIGCHLD, &osact, NULL);
7150Sstevel@tonic-gate 		if (chldexited == 0)
7160Sstevel@tonic-gate 			chldstat = dowait(pid);
7170Sstevel@tonic-gate 	} else if (opt_life == LT_NONE) {
7180Sstevel@tonic-gate 		char a;
7190Sstevel@tonic-gate 		int result;
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 		chldstat = UU_EXIT_OK;
7220Sstevel@tonic-gate 		(void) close(pipefds[1]);
7230Sstevel@tonic-gate 		do {
7240Sstevel@tonic-gate 			result = read(pipefds[0], &a, sizeof (a));
7250Sstevel@tonic-gate 			if (result == -1 && errno != EINTR)
7260Sstevel@tonic-gate 				uu_die(gettext("read failed"));
7270Sstevel@tonic-gate 			if (result == 1)
7280Sstevel@tonic-gate 				chldstat = dowait(pid);
7290Sstevel@tonic-gate 		} while (result == -1);
7300Sstevel@tonic-gate 	} else {
7310Sstevel@tonic-gate 		chldstat = dowait(pid);
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	return (chldstat);
7350Sstevel@tonic-gate }
736