xref: /illumos-gate/usr/src/cmd/ctwatch/ctwatch.c (revision 2a8bcb4e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*25e8c5aaSvikram  * Common Development and Distribution License (the "License").
6*25e8c5aaSvikram  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*25e8c5aaSvikram  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/wait.h>
287c478bd9Sstevel@tonic-gate #include <sys/ctfs.h>
297c478bd9Sstevel@tonic-gate #include <sys/contract/process.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <unistd.h>
337c478bd9Sstevel@tonic-gate #include <fcntl.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <limits.h>
377c478bd9Sstevel@tonic-gate #include <libcontract.h>
387c478bd9Sstevel@tonic-gate #include <libcontract_priv.h>
397c478bd9Sstevel@tonic-gate #include <libuutil.h>
407c478bd9Sstevel@tonic-gate #include <poll.h>
417c478bd9Sstevel@tonic-gate #include <port.h>
427c478bd9Sstevel@tonic-gate #include <signal.h>
437c478bd9Sstevel@tonic-gate #include <sys/wait.h>
447c478bd9Sstevel@tonic-gate #include <stdarg.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <locale.h>
477c478bd9Sstevel@tonic-gate #include <langinfo.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate struct {
507c478bd9Sstevel@tonic-gate 	const char *name;
517c478bd9Sstevel@tonic-gate 	int found;
527c478bd9Sstevel@tonic-gate } types[] = {
537c478bd9Sstevel@tonic-gate 	{ "process", 0 },
54*25e8c5aaSvikram 	{ "device", 0 },
557c478bd9Sstevel@tonic-gate 	{ NULL }
567c478bd9Sstevel@tonic-gate };
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate typedef struct watched_fd {
597c478bd9Sstevel@tonic-gate 	int wf_fd;
607c478bd9Sstevel@tonic-gate 	int wf_type;
617c478bd9Sstevel@tonic-gate } watched_fd_t;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate /*
647c478bd9Sstevel@tonic-gate  * usage
657c478bd9Sstevel@tonic-gate  *
667c478bd9Sstevel@tonic-gate  * Educate the user.
677c478bd9Sstevel@tonic-gate  */
687c478bd9Sstevel@tonic-gate static void
usage(void)697c478bd9Sstevel@tonic-gate usage(void)
707c478bd9Sstevel@tonic-gate {
717c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
727c478bd9Sstevel@tonic-gate 	    "Usage: %s [-f] [-r] [-v] contract-id | contract-type ...\n"),
737c478bd9Sstevel@tonic-gate 	    uu_getpname());
747c478bd9Sstevel@tonic-gate 	exit(UU_EXIT_USAGE);
757c478bd9Sstevel@tonic-gate }
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /*
787c478bd9Sstevel@tonic-gate  * sopen
797c478bd9Sstevel@tonic-gate  *
807c478bd9Sstevel@tonic-gate  * Given a format string and a variable number of arguments, create a
817c478bd9Sstevel@tonic-gate  * file name and open it.  Warn with 'permerror' and return -1 if
827c478bd9Sstevel@tonic-gate  * opening the file returned EPERM or EACCES, die with 'error' on all
837c478bd9Sstevel@tonic-gate  * other error conditions.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate static int
sopen(const char * format,const char * error,const char * permerror,...)867c478bd9Sstevel@tonic-gate sopen(const char *format, const char *error, const char *permerror, ...)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
897c478bd9Sstevel@tonic-gate 	int fd;
907c478bd9Sstevel@tonic-gate 	va_list varg;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	va_start(varg, permerror);
937c478bd9Sstevel@tonic-gate 	if (vsnprintf(path, PATH_MAX, format, varg) >= PATH_MAX) {
947c478bd9Sstevel@tonic-gate 		errno = ENAMETOOLONG;
957c478bd9Sstevel@tonic-gate 		uu_vdie(error, varg);
967c478bd9Sstevel@tonic-gate 	}
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	if ((fd = open64(path, O_RDONLY | O_NONBLOCK)) == -1) {
997c478bd9Sstevel@tonic-gate 		if (permerror && (errno == EPERM || errno == EACCES))
1007c478bd9Sstevel@tonic-gate 			uu_vwarn(permerror, varg);
1017c478bd9Sstevel@tonic-gate 		else
1027c478bd9Sstevel@tonic-gate 			uu_vdie(error, varg);
1037c478bd9Sstevel@tonic-gate 	}
1047c478bd9Sstevel@tonic-gate 	va_end(varg);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	return (fd);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate  * hdr_event
1117c478bd9Sstevel@tonic-gate  *
1127c478bd9Sstevel@tonic-gate  * Display the output header.
1137c478bd9Sstevel@tonic-gate  */
1147c478bd9Sstevel@tonic-gate static void
hdr_event(void)1157c478bd9Sstevel@tonic-gate hdr_event(void)
1167c478bd9Sstevel@tonic-gate {
1177c478bd9Sstevel@tonic-gate 	(void) printf("%-8s%-8s%-5s%-4s%-9s%s\n",
1187c478bd9Sstevel@tonic-gate 	    "CTID", "EVID", "CRIT", "ACK", "CTTYPE", "SUMMARY");
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate  * get_event
1237c478bd9Sstevel@tonic-gate  *
1247c478bd9Sstevel@tonic-gate  * Read and display a contract event.
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate static int
get_event(int fd,int type,int verbose)1277c478bd9Sstevel@tonic-gate get_event(int fd, int type, int verbose)
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate 	ct_evthdl_t ev;
1307c478bd9Sstevel@tonic-gate 	uint_t flags;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	/*
1337c478bd9Sstevel@tonic-gate 	 * Read a contract event.
1347c478bd9Sstevel@tonic-gate 	 */
1357c478bd9Sstevel@tonic-gate 	if (errno = ct_event_read(fd, &ev)) {
1367c478bd9Sstevel@tonic-gate 		if (errno == EAGAIN)
1377c478bd9Sstevel@tonic-gate 			return (0);
1387c478bd9Sstevel@tonic-gate 		uu_die(gettext("could not receive contract event"));
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	/*
1427c478bd9Sstevel@tonic-gate 	 * Emit a one-line event summary.
1437c478bd9Sstevel@tonic-gate 	 */
1447c478bd9Sstevel@tonic-gate 	flags = ct_event_get_flags(ev);
1457c478bd9Sstevel@tonic-gate 	(void) printf("%-8ld%-8lld%-5s%-4s%-9s",
1467c478bd9Sstevel@tonic-gate 	    ct_event_get_ctid(ev),
1477c478bd9Sstevel@tonic-gate 	    ct_event_get_evid(ev),
1487c478bd9Sstevel@tonic-gate 	    (flags & CTE_INFO) ? "info" : (flags & CTE_NEG) ? "neg" : "crit",
1497c478bd9Sstevel@tonic-gate 	    flags & CTE_ACK ? "yes" : "no",
1507c478bd9Sstevel@tonic-gate 	    types[type].name);
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	/*
1537c478bd9Sstevel@tonic-gate 	 * Display event details, if requested.
1547c478bd9Sstevel@tonic-gate 	 * (Since this is also needed by ctrun, the common
1557c478bd9Sstevel@tonic-gate 	 * contract_event_dump is found in libcontract.)
1567c478bd9Sstevel@tonic-gate 	 */
1577c478bd9Sstevel@tonic-gate 	contract_event_dump(stdout, ev, verbose);
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	ct_event_free(ev);
1607c478bd9Sstevel@tonic-gate 	return (1);
1617c478bd9Sstevel@tonic-gate }
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate /*
1647c478bd9Sstevel@tonic-gate  * get_type
1657c478bd9Sstevel@tonic-gate  *
1667c478bd9Sstevel@tonic-gate  * Given a contract type name, return an index into the 'types' array.
1677c478bd9Sstevel@tonic-gate  * Exits on failure.
1687c478bd9Sstevel@tonic-gate  */
1697c478bd9Sstevel@tonic-gate static int
get_type(const char * typestr)1707c478bd9Sstevel@tonic-gate get_type(const char *typestr)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	int i;
1737c478bd9Sstevel@tonic-gate 	for (i = 0; types[i].name; i++)
1747c478bd9Sstevel@tonic-gate 		if (strcmp(types[i].name, typestr) == 0)
1757c478bd9Sstevel@tonic-gate 			return (i);
1767c478bd9Sstevel@tonic-gate 	uu_die(gettext("invalid contract type: %s\n"), typestr);
1777c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate /*
1817c478bd9Sstevel@tonic-gate  * contract_type
1827c478bd9Sstevel@tonic-gate  *
1837c478bd9Sstevel@tonic-gate  * Given a contract id, return an index into the 'types' array.
1847c478bd9Sstevel@tonic-gate  * Returns -1 on failure.
1857c478bd9Sstevel@tonic-gate  */
1867c478bd9Sstevel@tonic-gate static int
contract_type(ctid_t id)1877c478bd9Sstevel@tonic-gate contract_type(ctid_t id)
1887c478bd9Sstevel@tonic-gate {
1897c478bd9Sstevel@tonic-gate 	ct_stathdl_t hdl;
1907c478bd9Sstevel@tonic-gate 	int type, fd;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/*
1937c478bd9Sstevel@tonic-gate 	 * This could be faster (e.g. by reading the link itself), but
1947c478bd9Sstevel@tonic-gate 	 * this is the most straightforward implementation.
1957c478bd9Sstevel@tonic-gate 	 */
1967c478bd9Sstevel@tonic-gate 	if ((fd = contract_open(id, NULL, "status", O_RDONLY)) == -1)
1977c478bd9Sstevel@tonic-gate 		return (-1);
1987c478bd9Sstevel@tonic-gate 	if (errno = ct_status_read(fd, CTD_COMMON, &hdl)) {
1997c478bd9Sstevel@tonic-gate 		(void) close(fd);
2007c478bd9Sstevel@tonic-gate 		return (-1);
2017c478bd9Sstevel@tonic-gate 	}
2027c478bd9Sstevel@tonic-gate 	type = get_type(ct_status_get_type(hdl));
2037c478bd9Sstevel@tonic-gate 	ct_status_free(hdl);
2047c478bd9Sstevel@tonic-gate 	(void) close(fd);
2057c478bd9Sstevel@tonic-gate 	return (type);
2067c478bd9Sstevel@tonic-gate }
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate /*
2097c478bd9Sstevel@tonic-gate  * ctid_compar
2107c478bd9Sstevel@tonic-gate  *
2117c478bd9Sstevel@tonic-gate  * A simple contract ID comparator.
2127c478bd9Sstevel@tonic-gate  */
2137c478bd9Sstevel@tonic-gate static int
ctid_compar(const void * a1,const void * a2)2147c478bd9Sstevel@tonic-gate ctid_compar(const void *a1, const void *a2)
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate 	ctid_t id1 = *(ctid_t *)a1;
2177c478bd9Sstevel@tonic-gate 	ctid_t id2 = *(ctid_t *)a2;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	if (id1 > id2)
2207c478bd9Sstevel@tonic-gate 		return (1);
2217c478bd9Sstevel@tonic-gate 	if (id2 > id1)
2227c478bd9Sstevel@tonic-gate 		return (-1);
2237c478bd9Sstevel@tonic-gate 	return (0);
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)2277c478bd9Sstevel@tonic-gate main(int argc, char **argv)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	int	opt_reliable = 0;
2307c478bd9Sstevel@tonic-gate 	int	opt_reset = 0;
2317c478bd9Sstevel@tonic-gate 	int	opt_verbose = 0;
2327c478bd9Sstevel@tonic-gate 	int	port_fd;
2337c478bd9Sstevel@tonic-gate 	watched_fd_t *wfd;
2347c478bd9Sstevel@tonic-gate 	int	i, nfds, nids;
2357c478bd9Sstevel@tonic-gate 	ctid_t	*ids, last;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2387c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	(void) uu_setpname(argv[0]);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	while ((i = getopt(argc, argv, "rfv")) !=  EOF) {
2437c478bd9Sstevel@tonic-gate 		switch (i) {
2447c478bd9Sstevel@tonic-gate 		case 'r':
2457c478bd9Sstevel@tonic-gate 			opt_reliable = 1;
2467c478bd9Sstevel@tonic-gate 			break;
2477c478bd9Sstevel@tonic-gate 		case 'f':
2487c478bd9Sstevel@tonic-gate 			opt_reset = 1;
2497c478bd9Sstevel@tonic-gate 			break;
2507c478bd9Sstevel@tonic-gate 		case 'v':
2517c478bd9Sstevel@tonic-gate 			opt_verbose = 1;
2527c478bd9Sstevel@tonic-gate 			break;
2537c478bd9Sstevel@tonic-gate 		default:
2547c478bd9Sstevel@tonic-gate 			usage();
2557c478bd9Sstevel@tonic-gate 		}
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	argc -= optind;
2597c478bd9Sstevel@tonic-gate 	argv += optind;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if (argc <= 0)
2627c478bd9Sstevel@tonic-gate 		usage();
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	wfd = calloc(argc, sizeof (struct pollfd));
2657c478bd9Sstevel@tonic-gate 	if (wfd == NULL)
2667c478bd9Sstevel@tonic-gate 		uu_die("calloc");
2677c478bd9Sstevel@tonic-gate 	ids = calloc(argc, sizeof (ctid_t));
2687c478bd9Sstevel@tonic-gate 	if (ids == NULL)
2697c478bd9Sstevel@tonic-gate 		uu_die("calloc");
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	/*
2727c478bd9Sstevel@tonic-gate 	 * Scan our operands for contract ids and types.
2737c478bd9Sstevel@tonic-gate 	 */
2747c478bd9Sstevel@tonic-gate 	nfds = 0;
2757c478bd9Sstevel@tonic-gate 	nids = 0;
2767c478bd9Sstevel@tonic-gate 	for (i = 0; i < argc; i++) {
2777c478bd9Sstevel@tonic-gate 		int id;
2787c478bd9Sstevel@tonic-gate 		if (strchr(argv[i], '/') != NULL)
2797c478bd9Sstevel@tonic-gate 			uu_die(gettext("invalid contract type: %s\n"), argv[i]);
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 		/*
2827c478bd9Sstevel@tonic-gate 		 * If argument isn't a number between 0 and INT_MAX,
2837c478bd9Sstevel@tonic-gate 		 * treat it as a contract type.
2847c478bd9Sstevel@tonic-gate 		 */
2857c478bd9Sstevel@tonic-gate 		if (uu_strtoint(argv[i], &id, sizeof (id), 10, 1, INT_MAX)) {
2867c478bd9Sstevel@tonic-gate 			int type;
2877c478bd9Sstevel@tonic-gate 			wfd[nfds].wf_fd =
2887c478bd9Sstevel@tonic-gate 			    sopen(CTFS_ROOT "/%s/bundle",
2897c478bd9Sstevel@tonic-gate 			    gettext("invalid contract type: %s\n"), NULL,
2907c478bd9Sstevel@tonic-gate 			    argv[i]);
2917c478bd9Sstevel@tonic-gate 			wfd[nfds].wf_type = type = get_type(argv[i]);
2927c478bd9Sstevel@tonic-gate 			if (types[type].found) {
2937c478bd9Sstevel@tonic-gate 				(void) close(wfd[nfds].wf_fd);
2947c478bd9Sstevel@tonic-gate 				continue;
2957c478bd9Sstevel@tonic-gate 			}
2967c478bd9Sstevel@tonic-gate 			types[type].found = 1;
2977c478bd9Sstevel@tonic-gate 			nfds++;
2987c478bd9Sstevel@tonic-gate 		} else {
2997c478bd9Sstevel@tonic-gate 			ids[nids++] = id;
3007c478bd9Sstevel@tonic-gate 		}
3017c478bd9Sstevel@tonic-gate 	}
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * Eliminate those contract ids which are represented by
3057c478bd9Sstevel@tonic-gate 	 * contract types, so we don't get duplicate event reports from
3067c478bd9Sstevel@tonic-gate 	 * them.
3077c478bd9Sstevel@tonic-gate 	 *
3087c478bd9Sstevel@tonic-gate 	 * Sorting the array first allows us to efficiently skip
3097c478bd9Sstevel@tonic-gate 	 * duplicate ids.  We know that the array only contains
3107c478bd9Sstevel@tonic-gate 	 * integers [0, INT_MAX].
3117c478bd9Sstevel@tonic-gate 	 */
3127c478bd9Sstevel@tonic-gate 	qsort(ids, nids, sizeof (ctid_t), ctid_compar);
3137c478bd9Sstevel@tonic-gate 	last = -1;
3147c478bd9Sstevel@tonic-gate 	for (i = 0; i < nids; i++) {
3157c478bd9Sstevel@tonic-gate 		int type, fd;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 		if (ids[i] == last)
3187c478bd9Sstevel@tonic-gate 			continue;
3197c478bd9Sstevel@tonic-gate 		last = ids[i];
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 		fd = sopen(CTFS_ROOT "/all/%d/events",
3227c478bd9Sstevel@tonic-gate 		    gettext("invalid contract id: %d\n"),
3237c478bd9Sstevel@tonic-gate 		    gettext("could not access contract id %d\n"), ids[i]);
3247c478bd9Sstevel@tonic-gate 		if (fd == -1)
3257c478bd9Sstevel@tonic-gate 			continue;
3267c478bd9Sstevel@tonic-gate 		if ((type = contract_type(ids[i])) == -1) {
3277c478bd9Sstevel@tonic-gate 			(void) close(fd);
3287c478bd9Sstevel@tonic-gate 			uu_warn(gettext("could not access contract id %d\n"),
3297c478bd9Sstevel@tonic-gate 			    ids[i]);
3307c478bd9Sstevel@tonic-gate 			continue;
3317c478bd9Sstevel@tonic-gate 		}
3327c478bd9Sstevel@tonic-gate 		if (types[type].found) {
3337c478bd9Sstevel@tonic-gate 			(void) close(fd);
3347c478bd9Sstevel@tonic-gate 			continue;
3357c478bd9Sstevel@tonic-gate 		}
3367c478bd9Sstevel@tonic-gate 		wfd[nfds].wf_fd = fd;
3377c478bd9Sstevel@tonic-gate 		wfd[nfds].wf_type = type;
3387c478bd9Sstevel@tonic-gate 		nfds++;
3397c478bd9Sstevel@tonic-gate 	}
3407c478bd9Sstevel@tonic-gate 	free(ids);
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	if (nfds == 0)
3437c478bd9Sstevel@tonic-gate 		uu_die(gettext("no contracts to watch\n"));
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	/*
3467c478bd9Sstevel@tonic-gate 	 * Handle options.
3477c478bd9Sstevel@tonic-gate 	 */
3487c478bd9Sstevel@tonic-gate 	if (opt_reliable)
3497c478bd9Sstevel@tonic-gate 		for (i = 0; i < nfds; i++)
3507c478bd9Sstevel@tonic-gate 			if (ioctl(wfd[i].wf_fd, CT_ERELIABLE, NULL) == -1) {
3517c478bd9Sstevel@tonic-gate 				uu_warn("could not request reliable events");
3527c478bd9Sstevel@tonic-gate 				break;
3537c478bd9Sstevel@tonic-gate 			}
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	if (opt_reset)
3567c478bd9Sstevel@tonic-gate 		for (i = 0; i < nfds; i++)
3577c478bd9Sstevel@tonic-gate 			(void) ioctl(wfd[i].wf_fd, CT_ERESET, NULL);
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	/*
3617c478bd9Sstevel@tonic-gate 	 * Allocate an event point, and associate all our endpoint file
3627c478bd9Sstevel@tonic-gate 	 * descriptors with it.
3637c478bd9Sstevel@tonic-gate 	 */
3647c478bd9Sstevel@tonic-gate 	if ((port_fd = port_create()) == -1)
3657c478bd9Sstevel@tonic-gate 		goto port_error;
3667c478bd9Sstevel@tonic-gate 	for (i = 0; i < nfds; i++)
3677c478bd9Sstevel@tonic-gate 		if (port_associate(port_fd, PORT_SOURCE_FD, wfd[i].wf_fd,
3687c478bd9Sstevel@tonic-gate 		    POLLIN, &wfd[i]) == -1)
3697c478bd9Sstevel@tonic-gate 			goto port_error;
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	/*
3727c478bd9Sstevel@tonic-gate 	 * Loop waiting for and displaying events.
3737c478bd9Sstevel@tonic-gate 	 */
3747c478bd9Sstevel@tonic-gate 	hdr_event();
3757c478bd9Sstevel@tonic-gate 	for (;;) {
3767c478bd9Sstevel@tonic-gate 		port_event_t pe;
3777c478bd9Sstevel@tonic-gate 		watched_fd_t *w;
3787c478bd9Sstevel@tonic-gate 		if (port_get(port_fd, &pe, NULL) == -1) {
3797c478bd9Sstevel@tonic-gate 			if (errno == EINTR)
3807c478bd9Sstevel@tonic-gate 				continue;
3817c478bd9Sstevel@tonic-gate 			goto port_error;
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 		w = pe.portev_user;
3847c478bd9Sstevel@tonic-gate 		while (get_event(pe.portev_object, w->wf_type, opt_verbose))
3857c478bd9Sstevel@tonic-gate 			;
3867c478bd9Sstevel@tonic-gate 		if (port_associate(port_fd, PORT_SOURCE_FD, pe.portev_object,
3877c478bd9Sstevel@tonic-gate 		    POLLIN, pe.portev_user) == -1)
3887c478bd9Sstevel@tonic-gate 			goto port_error;
3897c478bd9Sstevel@tonic-gate 	}
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate port_error:
3927c478bd9Sstevel@tonic-gate 	uu_die(gettext("error waiting for contract events"));
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	return (1);	/* placate cc */
3957c478bd9Sstevel@tonic-gate }
396