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