xref: /illumos-gate/usr/src/cmd/ctwatch/ctwatch.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
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