Home | History | Annotate | Download | only in ctwatch
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <sys/types.h>
     29 #include <sys/wait.h>
     30 #include <sys/ctfs.h>
     31 #include <sys/contract/process.h>
     32 #include <stdio.h>
     33 #include <stdlib.h>
     34 #include <unistd.h>
     35 #include <fcntl.h>
     36 #include <string.h>
     37 #include <errno.h>
     38 #include <limits.h>
     39 #include <libcontract.h>
     40 #include <libcontract_priv.h>
     41 #include <libuutil.h>
     42 #include <poll.h>
     43 #include <port.h>
     44 #include <signal.h>
     45 #include <sys/wait.h>
     46 #include <stdarg.h>
     47 
     48 #include <locale.h>
     49 #include <langinfo.h>
     50 
     51 struct {
     52 	const char *name;
     53 	int found;
     54 } types[] = {
     55 	{ "process", 0 },
     56 	{ "device", 0 },
     57 	{ NULL }
     58 };
     59 
     60 typedef struct watched_fd {
     61 	int wf_fd;
     62 	int wf_type;
     63 } watched_fd_t;
     64 
     65 /*
     66  * usage
     67  *
     68  * Educate the user.
     69  */
     70 static void
     71 usage(void)
     72 {
     73 	(void) fprintf(stderr, gettext(
     74 	    "Usage: %s [-f] [-r] [-v] contract-id | contract-type ...\n"),
     75 	    uu_getpname());
     76 	exit(UU_EXIT_USAGE);
     77 }
     78 
     79 /*
     80  * sopen
     81  *
     82  * Given a format string and a variable number of arguments, create a
     83  * file name and open it.  Warn with 'permerror' and return -1 if
     84  * opening the file returned EPERM or EACCES, die with 'error' on all
     85  * other error conditions.
     86  */
     87 static int
     88 sopen(const char *format, const char *error, const char *permerror, ...)
     89 {
     90 	char path[PATH_MAX];
     91 	int fd;
     92 	va_list varg;
     93 
     94 	va_start(varg, permerror);
     95 	if (vsnprintf(path, PATH_MAX, format, varg) >= PATH_MAX) {
     96 		errno = ENAMETOOLONG;
     97 		uu_vdie(error, varg);
     98 	}
     99 
    100 	if ((fd = open64(path, O_RDONLY | O_NONBLOCK)) == -1) {
    101 		if (permerror && (errno == EPERM || errno == EACCES))
    102 			uu_vwarn(permerror, varg);
    103 		else
    104 			uu_vdie(error, varg);
    105 	}
    106 	va_end(varg);
    107 
    108 	return (fd);
    109 }
    110 
    111 /*
    112  * hdr_event
    113  *
    114  * Display the output header.
    115  */
    116 static void
    117 hdr_event(void)
    118 {
    119 	(void) printf("%-8s%-8s%-5s%-4s%-9s%s\n",
    120 	    "CTID", "EVID", "CRIT", "ACK", "CTTYPE", "SUMMARY");
    121 }
    122 
    123 /*
    124  * get_event
    125  *
    126  * Read and display a contract event.
    127  */
    128 static int
    129 get_event(int fd, int type, int verbose)
    130 {
    131 	ct_evthdl_t ev;
    132 	uint_t flags;
    133 
    134 	/*
    135 	 * Read a contract event.
    136 	 */
    137 	if (errno = ct_event_read(fd, &ev)) {
    138 		if (errno == EAGAIN)
    139 			return (0);
    140 		uu_die(gettext("could not receive contract event"));
    141 	}
    142 
    143 	/*
    144 	 * Emit a one-line event summary.
    145 	 */
    146 	flags = ct_event_get_flags(ev);
    147 	(void) printf("%-8ld%-8lld%-5s%-4s%-9s",
    148 	    ct_event_get_ctid(ev),
    149 	    ct_event_get_evid(ev),
    150 	    (flags & CTE_INFO) ? "info" : (flags & CTE_NEG) ? "neg" : "crit",
    151 	    flags & CTE_ACK ? "yes" : "no",
    152 	    types[type].name);
    153 
    154 	/*
    155 	 * Display event details, if requested.
    156 	 * (Since this is also needed by ctrun, the common
    157 	 * contract_event_dump is found in libcontract.)
    158 	 */
    159 	contract_event_dump(stdout, ev, verbose);
    160 
    161 	ct_event_free(ev);
    162 	return (1);
    163 }
    164 
    165 /*
    166  * get_type
    167  *
    168  * Given a contract type name, return an index into the 'types' array.
    169  * Exits on failure.
    170  */
    171 static int
    172 get_type(const char *typestr)
    173 {
    174 	int i;
    175 	for (i = 0; types[i].name; i++)
    176 		if (strcmp(types[i].name, typestr) == 0)
    177 			return (i);
    178 	uu_die(gettext("invalid contract type: %s\n"), typestr);
    179 	/* NOTREACHED */
    180 }
    181 
    182 /*
    183  * contract_type
    184  *
    185  * Given a contract id, return an index into the 'types' array.
    186  * Returns -1 on failure.
    187  */
    188 static int
    189 contract_type(ctid_t id)
    190 {
    191 	ct_stathdl_t hdl;
    192 	int type, fd;
    193 
    194 	/*
    195 	 * This could be faster (e.g. by reading the link itself), but
    196 	 * this is the most straightforward implementation.
    197 	 */
    198 	if ((fd = contract_open(id, NULL, "status", O_RDONLY)) == -1)
    199 		return (-1);
    200 	if (errno = ct_status_read(fd, CTD_COMMON, &hdl)) {
    201 		(void) close(fd);
    202 		return (-1);
    203 	}
    204 	type = get_type(ct_status_get_type(hdl));
    205 	ct_status_free(hdl);
    206 	(void) close(fd);
    207 	return (type);
    208 }
    209 
    210 /*
    211  * ctid_compar
    212  *
    213  * A simple contract ID comparator.
    214  */
    215 static int
    216 ctid_compar(const void *a1, const void *a2)
    217 {
    218 	ctid_t id1 = *(ctid_t *)a1;
    219 	ctid_t id2 = *(ctid_t *)a2;
    220 
    221 	if (id1 > id2)
    222 		return (1);
    223 	if (id2 > id1)
    224 		return (-1);
    225 	return (0);
    226 }
    227 
    228 int
    229 main(int argc, char **argv)
    230 {
    231 	int	opt_reliable = 0;
    232 	int	opt_reset = 0;
    233 	int	opt_verbose = 0;
    234 	int	port_fd;
    235 	watched_fd_t *wfd;
    236 	int	i, nfds, nids;
    237 	ctid_t	*ids, last;
    238 
    239 	(void) setlocale(LC_ALL, "");
    240 	(void) textdomain(TEXT_DOMAIN);
    241 
    242 	(void) uu_setpname(argv[0]);
    243 
    244 	while ((i = getopt(argc, argv, "rfv")) !=  EOF) {
    245 		switch (i) {
    246 		case 'r':
    247 			opt_reliable = 1;
    248 			break;
    249 		case 'f':
    250 			opt_reset = 1;
    251 			break;
    252 		case 'v':
    253 			opt_verbose = 1;
    254 			break;
    255 		default:
    256 			usage();
    257 		}
    258 	}
    259 
    260 	argc -= optind;
    261 	argv += optind;
    262 
    263 	if (argc <= 0)
    264 		usage();
    265 
    266 	wfd = calloc(argc, sizeof (struct pollfd));
    267 	if (wfd == NULL)
    268 		uu_die("calloc");
    269 	ids = calloc(argc, sizeof (ctid_t));
    270 	if (ids == NULL)
    271 		uu_die("calloc");
    272 
    273 	/*
    274 	 * Scan our operands for contract ids and types.
    275 	 */
    276 	nfds = 0;
    277 	nids = 0;
    278 	for (i = 0; i < argc; i++) {
    279 		int id;
    280 		if (strchr(argv[i], '/') != NULL)
    281 			uu_die(gettext("invalid contract type: %s\n"), argv[i]);
    282 
    283 		/*
    284 		 * If argument isn't a number between 0 and INT_MAX,
    285 		 * treat it as a contract type.
    286 		 */
    287 		if (uu_strtoint(argv[i], &id, sizeof (id), 10, 1, INT_MAX)) {
    288 			int type;
    289 			wfd[nfds].wf_fd =
    290 			    sopen(CTFS_ROOT "/%s/bundle",
    291 			    gettext("invalid contract type: %s\n"), NULL,
    292 			    argv[i]);
    293 			wfd[nfds].wf_type = type = get_type(argv[i]);
    294 			if (types[type].found) {
    295 				(void) close(wfd[nfds].wf_fd);
    296 				continue;
    297 			}
    298 			types[type].found = 1;
    299 			nfds++;
    300 		} else {
    301 			ids[nids++] = id;
    302 		}
    303 	}
    304 
    305 	/*
    306 	 * Eliminate those contract ids which are represented by
    307 	 * contract types, so we don't get duplicate event reports from
    308 	 * them.
    309 	 *
    310 	 * Sorting the array first allows us to efficiently skip
    311 	 * duplicate ids.  We know that the array only contains
    312 	 * integers [0, INT_MAX].
    313 	 */
    314 	qsort(ids, nids, sizeof (ctid_t), ctid_compar);
    315 	last = -1;
    316 	for (i = 0; i < nids; i++) {
    317 		int type, fd;
    318 
    319 		if (ids[i] == last)
    320 			continue;
    321 		last = ids[i];
    322 
    323 		fd = sopen(CTFS_ROOT "/all/%d/events",
    324 		    gettext("invalid contract id: %d\n"),
    325 		    gettext("could not access contract id %d\n"), ids[i]);
    326 		if (fd == -1)
    327 			continue;
    328 		if ((type = contract_type(ids[i])) == -1) {
    329 			(void) close(fd);
    330 			uu_warn(gettext("could not access contract id %d\n"),
    331 			    ids[i]);
    332 			continue;
    333 		}
    334 		if (types[type].found) {
    335 			(void) close(fd);
    336 			continue;
    337 		}
    338 		wfd[nfds].wf_fd = fd;
    339 		wfd[nfds].wf_type = type;
    340 		nfds++;
    341 	}
    342 	free(ids);
    343 
    344 	if (nfds == 0)
    345 		uu_die(gettext("no contracts to watch\n"));
    346 
    347 	/*
    348 	 * Handle options.
    349 	 */
    350 	if (opt_reliable)
    351 		for (i = 0; i < nfds; i++)
    352 			if (ioctl(wfd[i].wf_fd, CT_ERELIABLE, NULL) == -1) {
    353 				uu_warn("could not request reliable events");
    354 				break;
    355 			}
    356 
    357 	if (opt_reset)
    358 		for (i = 0; i < nfds; i++)
    359 			(void) ioctl(wfd[i].wf_fd, CT_ERESET, NULL);
    360 
    361 
    362 	/*
    363 	 * Allocate an event point, and associate all our endpoint file
    364 	 * descriptors with it.
    365 	 */
    366 	if ((port_fd = port_create()) == -1)
    367 		goto port_error;
    368 	for (i = 0; i < nfds; i++)
    369 		if (port_associate(port_fd, PORT_SOURCE_FD, wfd[i].wf_fd,
    370 		    POLLIN, &wfd[i]) == -1)
    371 			goto port_error;
    372 
    373 	/*
    374 	 * Loop waiting for and displaying events.
    375 	 */
    376 	hdr_event();
    377 	for (;;) {
    378 		port_event_t pe;
    379 		watched_fd_t *w;
    380 		if (port_get(port_fd, &pe, NULL) == -1) {
    381 			if (errno == EINTR)
    382 				continue;
    383 			goto port_error;
    384 		}
    385 		w = pe.portev_user;
    386 		while (get_event(pe.portev_object, w->wf_type, opt_verbose))
    387 			;
    388 		if (port_associate(port_fd, PORT_SOURCE_FD, pe.portev_object,
    389 		    POLLIN, pe.portev_user) == -1)
    390 			goto port_error;
    391 	}
    392 
    393 port_error:
    394 	uu_die(gettext("error waiting for contract events"));
    395 
    396 	return (1);	/* placate cc */
    397 }
    398