4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/types.h>
31 #include <sys/contract.h>
32 #include <sys/contract/process.h>
42 #include <libcontract.h>
43 #include <libcontract_priv.h>
48 static int opt_verbose
;
49 static int opt_Verbose
;
51 #define OPT_NORMAL 0x1
54 typedef struct optvect
{
60 static optvect_t option_params
[] = {
61 { "noorphan", CT_PR_NOORPHAN
},
62 { "pgrponly", CT_PR_PGRPONLY
},
63 { "regent", CT_PR_REGENT
},
64 { "inherit", CT_PR_INHERIT
},
68 static optvect_t option_events
[] = {
69 { "core", CT_PR_EV_CORE
, OPT_NORMAL
| OPT_FATAL
},
70 { "signal", CT_PR_EV_SIGNAL
, OPT_NORMAL
| OPT_FATAL
},
71 { "hwerr", CT_PR_EV_HWERR
, OPT_NORMAL
| OPT_FATAL
},
72 { "empty", CT_PR_EV_EMPTY
, OPT_NORMAL
},
73 { "fork", CT_PR_EV_FORK
, OPT_NORMAL
},
74 { "exit", CT_PR_EV_EXIT
, OPT_NORMAL
},
78 typedef enum lifetime
{
85 * Exit code to use when the child exited abnormally (i.e. exited with
86 * a status we are unable to emulate).
88 #define EXIT_BADCHILD 123
91 "Usage: %s [-i eventlist] [-f eventlist] [-l lifetime] \n" \
92 "\t[-o optionlist] [-r count [-t]] [-v]\n" \
93 "\t[-F fmri] [-A aux] command\n"
103 (void) fprintf(stderr
, gettext(USAGESTR
), uu_getpname());
110 * Convert a bit into its string representation.
113 bit2str(optvect_t
*options
, uint_t bit
)
115 for (; options
->opt_name
; options
++)
116 if (options
->opt_value
== bit
)
117 return (options
->opt_name
);
124 * Convert a string into its bit representation. If match is set, only
125 * look at those options with the match bit set in its opt_flags
129 str2bit(optvect_t
*options
, int match
, const char *str
, int len
)
131 for (; options
->opt_name
; options
++) {
132 if (match
&& (options
->opt_flags
& match
) == 0)
134 if (strncmp(str
, options
->opt_name
, len
) == 0)
135 return (options
->opt_value
);
143 * Given a set of textual options separated by commas or spaces,
144 * convert them to a set of bits. Errors are fatal, except for empty
145 * options (which are ignored) and duplicate options (which are
149 opt2bits(optvect_t
*options
, int match
, const char *str
, uint_t
*bits
, char c
)
151 const char *ptr
, *next
= str
;
159 ptr
= strpbrk(str
, ", ");
168 uu_warn(gettext("empty option\n"));
171 bit
= str2bit(options
, match
, str
, len
);
172 if (bit
== 0 && strncmp(str
, "none", len
) == 0) {
176 } else if (bit
== 0) {
177 uu_warn(gettext("unrecognized option '%.*s'\n"),
179 uu_warn(gettext("error parsing '-%c' option\n"),
186 uu_warn(gettext("option '%.*s' "
187 "specified twice\n"), len
, str
);
197 uu_warn(gettext("option is incompatible with others: '%s'\n"), "none");
204 * Given a fd, marks it close-on-exec.
207 close_on_exec(int fd
)
209 int flags
= fcntl(fd
, F_GETFD
, 0);
210 if ((flags
!= -1) && (fcntl(fd
, F_SETFD
, flags
| FD_CLOEXEC
) != -1))
218 * Output routine for messages printed only when -v is specified.
222 v_printf(const char *format
, ...)
227 (void) printf("%s(%ld): ", uu_getpname(), getpid());
228 va_start(va
, format
);
229 (void) vprintf(format
, va
);
237 * Reads and acknowledges an event. Returns the event type.
240 get_event(int fd
, int ctfd
, ctid_t ctid
)
250 * Normally we only need to look at critical messages.
251 * If we are displaying contract events, however, we
252 * have to read them all.
254 errno
= opt_verbose
? ct_event_read(fd
, &ev
) :
255 ct_event_read_critical(fd
, &ev
);
257 uu_die(gettext("failed to listen to contract events"));
260 * If requested, display the event.
263 v_printf(gettext("event from contract %ld: "),
264 ct_event_get_ctid(ev
));
265 contract_event_dump(stdout
, ev
, opt_Verbose
);
266 if ((ct_event_get_flags(ev
) & CTE_INFO
) != 0) {
273 * We're done if this event is one of ours.
275 evid
= ct_event_get_evid(ev
);
276 if (ct_event_get_ctid(ev
) == ctid
)
280 * ACK events from other contracts.
281 * This shouldn't happen, but it could.
283 efd
= contract_open(ct_event_get_ctid(ev
), "process", "ctl",
286 (void) ct_ctl_ack(efd
, evid
);
293 * Note that if we want to use ctrun as a simple restarter, we
294 * need persistently keep track of fatal events so we can
295 * properly handle the death of the contract. Rather than keep
296 * a file or somesuch lying around, it might make more sense to
297 * leave the significant fatal event sitting in the queue so
298 * that a restarted instance of ctrun can pick it up. For now
299 * we'll just ACK all events.
301 (void) ct_ctl_ack(ctfd
, evid
);
303 result
= ct_event_get_type(ev
);
312 * Given an fd for a contract's ctl file, abandon the contract and
318 if (ct_ctl_abandon(ctfd
) == -1)
319 uu_die(gettext("failed to abandon contract %d"), ctfd
);
325 static int chldexited
;
330 * Our SIGCHLD handler. Sets chldstat and chldexited so the
331 * interrupted code knows what happened.
335 sigchld(int sig
, struct siginfo
*si
, void *ucp
)
339 if (si
->si_code
== CLD_EXITED
)
340 chldstat
= si
->si_status
;
342 chldstat
= EXIT_BADCHILD
;
344 while (waitpid(si
->si_pid
, NULL
, 0) == -1 && errno
== EINTR
)
352 * Waits for the specified child to exit. Returns the exit code ctrun
362 wpid
= waitpid(pid
, &wstatus
, 0);
363 while (wpid
== -1 && errno
== EINTR
);
366 uu_die(gettext("wait failed"));
368 if (WIFEXITED(wstatus
))
369 return (WEXITSTATUS(wstatus
));
371 return (EXIT_BADCHILD
);
375 main(int argc
, char **argv
)
382 struct sigaction osact
;
385 ctid_t opt_adopt
= 0;
386 int opt_transfer
= 0;
388 uint_t opt_info
= CT_PR_EV_CORE
;
390 uint_t eff_fatal
, opt_fatal
= CT_PR_EV_HWERR
;
391 uint_t eff_param
, opt_param
= 0;
392 lifetime_t opt_life
= LT_CONTRACT
;
394 char *svc_fmri
= NULL
;
395 char *svc_aux
= NULL
;
397 (void) setlocale(LC_ALL
, "");
398 (void) textdomain(TEXT_DOMAIN
);
399 uu_alt_exit(UU_PROFILE_LAUNCHER
);
401 (void) uu_setpname(argv
[0]);
403 while ((s
= getopt(argc
, argv
, "a:A:l:o:i:c:f:F:r:tvV")) != EOF
) {
406 if (uu_strtoint(optarg
, &opt_adopt
, sizeof (opt_adopt
),
407 0, 0, INT32_MAX
) == -1) {
408 uu_warn(gettext("invalid contract ID '%s'\n"),
424 if (uu_strtoint(optarg
, &opt_count
, sizeof (opt_adopt
),
425 0, 0, INT32_MAX
) == -1) {
426 uu_warn(gettext("invalid count '%s'\n"),
432 if (strcmp(optarg
, "none") == 0) {
434 } else if (strcmp(optarg
, "child") == 0) {
436 } else if (strcmp(optarg
, "contract") == 0) {
437 opt_life
= LT_CONTRACT
;
439 uu_warn(gettext("invalid lifetime '%s'\n"),
446 opt2bits(option_params
, 0, optarg
, &opt_param
,
450 opt2bits(option_events
, OPT_NORMAL
, optarg
, &opt_info
,
454 opt2bits(option_events
, OPT_NORMAL
, optarg
, &opt_crit
,
458 opt2bits(option_events
, OPT_FATAL
, optarg
, &opt_fatal
,
475 * Basic argument sanity checks.
477 if ((opt_life
== LT_NONE
) && (opt_param
& CT_PR_NOORPHAN
)) {
478 uu_warn(gettext("cannot use option '%s' with lifetime '%s'\n"),
479 bit2str(option_params
, CT_PR_NOORPHAN
), "none");
483 if ((opt_life
!= LT_CONTRACT
) && (opt_count
>= 0)) {
484 uu_warn(gettext("cannot restart with lifetime '%s'\n"),
485 opt_life
== LT_NONE
? "none" : "child");
489 if ((opt_param
& CT_PR_PGRPONLY
) && (opt_count
>= 0)) {
490 uu_warn(gettext("cannot restart with option '%s'\n"),
491 bit2str(option_params
, CT_PR_PGRPONLY
));
495 if (opt_transfer
&& (opt_count
== -1)) {
496 uu_warn(gettext("cannot transfer when not restarting\n"));
504 * Create a process contract template and our process's process
505 * contract bundle endpoint. Mark them close-on-exec so we
506 * don't have to worry about closing them in our child.
508 fd
= open64(CTFS_ROOT
"/process/template", O_RDWR
);
510 uu_die(gettext("template open failed"));
512 efd
= open64(CTFS_ROOT
"/process/pbundle", O_RDONLY
);
514 uu_die(gettext("process bundle open failed"));
516 if (close_on_exec(fd
) || close_on_exec(efd
))
517 uu_die(gettext("could not set FD_CLOEXEC"));
520 * Set the process contract's terms based on our arguments.
522 if (errno
= ct_pr_tmpl_set_param(fd
, opt_param
))
523 uu_die(gettext("set param failed"));
525 if (errno
= ct_tmpl_set_informative(fd
, opt_info
))
526 uu_die(gettext("set notify failed"));
528 if (errno
= ct_pr_tmpl_set_fatal(fd
, opt_fatal
))
529 uu_die(gettext("set fatal failed"));
531 if (opt_param
& CT_PR_PGRPONLY
)
532 opt_crit
= CT_PR_EV_EMPTY
;
534 opt_crit
|= opt_fatal
| CT_PR_EV_EMPTY
;
535 if (errno
= ct_tmpl_set_critical(fd
, opt_crit
))
536 uu_die(gettext("set critical failed"));
537 if (svc_fmri
&& (errno
= ct_pr_tmpl_set_svc_fmri(fd
, svc_fmri
)))
538 uu_die(gettext("set fmri failed: "
539 "insufficient privileges\n"));
540 if (svc_aux
&& (errno
= ct_pr_tmpl_set_svc_aux(fd
, svc_aux
)))
541 uu_die(gettext("set aux failed"));
544 * Activate the template.
546 if (errno
= ct_tmpl_activate(fd
))
547 uu_die(gettext("template activate failed"));
552 * Adopt a specific contract.
557 if ((ctfd
= contract_open(opt_adopt
, "process", "ctl",
559 uu_die(gettext("could not open contract %ld"),
563 * Read the contract's terms so that we interpret its
566 if (((stfd
= contract_open(opt_adopt
, "process", "status",
568 (errno
= ct_status_read(stfd
, CTD_FIXED
, &st
)) ||
569 (errno
= ct_pr_status_get_fatal(st
, &eff_fatal
)) ||
570 (errno
= ct_pr_status_get_param(st
, &eff_param
)))
571 uu_die(gettext("could not stat contract %ld"),
576 if (errno
= ct_ctl_adopt(ctfd
))
577 uu_die(gettext("could not adopt contract %ld"),
582 v_printf(gettext("adopted contract id %ld\n"), ctid
);
585 * Create a new process.
587 if (opt_life
== LT_CONTRACT
) {
588 struct sigaction sact
;
591 * Since we are going to be waiting for and
592 * reacting to contract events, install a
593 * signal handler so we capture the exit status
596 chldstat
= UU_EXIT_OK
;
598 sact
.sa_sigaction
= sigchld
;
599 sact
.sa_flags
= SA_SIGINFO
| SA_RESTART
|
601 (void) sigemptyset(&sact
.sa_mask
);
602 if (sigaction(SIGCHLD
, &sact
, &osact
) == -1)
603 uu_die(gettext("failed to install "
605 } else if (opt_life
== LT_NONE
) {
607 * Though we aren't waiting for our child to
608 * exit, as a well-behaved command launcher we
609 * must wait for it to exec. On success the
610 * pipe will simply close, and on failure the
611 * proper exit status will be sent.
613 if (pipe(pipefds
) == -1 ||
614 close_on_exec(pipefds
[0]) == -1 ||
615 close_on_exec(pipefds
[1]) == -1)
616 uu_die(gettext("failed to create pipe"));
619 if ((pid
= fork()) == -1) {
620 uu_die(gettext("fork failed"));
621 } else if (pid
== 0) {
622 int result
= execvp(argv
[0], argv
);
623 if (opt_life
== LT_NONE
) {
627 (void) write(pipefds
[1], &a
, sizeof (a
));
631 uu_xdie(errno
== ENOENT
? 127 : 126,
632 gettext("exec failed"));
633 uu_die(gettext("exec returned!\n"));
637 * Get the newly-created contract's id and ctl fd.
639 if (errno
= contract_latest(&ctid
))
640 uu_die(gettext("could not get new contract's id"));
641 if ((ctfd
= contract_open(ctid
, "process", "ctl",
643 uu_die(gettext("could not open contract"));
646 * Clear the transfer parameter so that the contract
647 * will be freed sooner and admins won't get nervous.
650 (void) ct_pr_tmpl_set_transfer(fd
, 0);
651 (void) ct_tmpl_activate(fd
);
654 v_printf(gettext("created contract id %ld\n"), ctid
);
655 eff_param
= opt_param
;
656 eff_fatal
= opt_fatal
;
659 if (opt_life
== LT_CONTRACT
) {
660 uint_t event
, errevent
= 0;
663 * Wait until the contract empties out.
666 event
= get_event(efd
, ctfd
, ctid
);
667 if (event
& eff_fatal
) {
668 if ((eff_param
& CT_PR_PGRPONLY
) == 0)
671 "fatal \"%s\" event from contract %ld\n"),
672 bit2str(option_events
, event
), ctid
);
674 } while ((event
& CT_PR_EV_EMPTY
) == 0);
677 * If we encountered a fatal error event, and we
678 * haven't expended our maximum loop count, restart.
680 if ((errevent
!= 0) &&
681 ((opt_count
== 0) || (opt_count
-- > 1))) {
682 v_printf(gettext("failure in contract %ld, "
683 "restarting command\n"), ctid
);
686 * Add the failed contract to the new
687 * contract's terms so that its
688 * inherited subcontracts can be
689 * adopted by the new process.
691 if (errno
= ct_pr_tmpl_set_transfer(fd
, ctid
))
692 uu_die(gettext("set transfer failed"));
693 if (errno
= ct_tmpl_activate(fd
))
695 "template activate failed"));
704 * At this point we are done with the contract; we
705 * don't want it to be inherited when we exit.
710 * In case there was a race between SIGCHLD delivery
711 * and contract event delivery, disable the signal
712 * handler and look for the child.
714 (void) sigaction(SIGCHLD
, &osact
, NULL
);
716 chldstat
= dowait(pid
);
717 } else if (opt_life
== LT_NONE
) {
721 chldstat
= UU_EXIT_OK
;
722 (void) close(pipefds
[1]);
724 result
= read(pipefds
[0], &a
, sizeof (a
));
725 if (result
== -1 && errno
!= EINTR
)
726 uu_die(gettext("read failed"));
728 chldstat
= dowait(pid
);
729 } while (result
== -1);
731 chldstat
= dowait(pid
);