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