Merge commit 'b1e7e97d3b60469b243b3b2e22c7d8cbd11c7c90'
[unleashed.git] / usr / src / cmd / ctwatch / ctwatch.c
bloba42d109983b27407e1ae15ed37c4cba44a69ac24
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2007 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>
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>
48 #include <locale.h>
49 #include <langinfo.h>
51 struct {
52 const char *name;
53 int found;
54 } types[] = {
55 { "process", 0 },
56 { "device", 0 },
57 { NULL }
60 typedef struct watched_fd {
61 int wf_fd;
62 int wf_type;
63 } watched_fd_t;
66 * usage
68 * Educate the user.
70 static void
71 usage(void)
73 (void) fprintf(stderr, gettext(
74 "Usage: %s [-f] [-r] [-v] contract-id | contract-type ...\n"),
75 uu_getpname());
76 exit(UU_EXIT_USAGE);
80 * sopen
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.
87 static int
88 sopen(const char *format, const char *error, const char *permerror, ...)
90 char path[PATH_MAX];
91 int fd;
92 va_list varg;
94 va_start(varg, permerror);
95 if (vsnprintf(path, PATH_MAX, format, varg) >= PATH_MAX) {
96 errno = ENAMETOOLONG;
97 uu_vdie(error, varg);
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);
106 va_end(varg);
108 return (fd);
112 * hdr_event
114 * Display the output header.
116 static void
117 hdr_event(void)
119 (void) printf("%-8s%-8s%-5s%-4s%-9s%s\n",
120 "CTID", "EVID", "CRIT", "ACK", "CTTYPE", "SUMMARY");
124 * get_event
126 * Read and display a contract event.
128 static int
129 get_event(int fd, int type, int verbose)
131 ct_evthdl_t ev;
132 uint_t flags;
135 * Read a contract event.
137 if (errno = ct_event_read(fd, &ev)) {
138 if (errno == EAGAIN)
139 return (0);
140 uu_die(gettext("could not receive contract event"));
144 * Emit a one-line event summary.
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);
155 * Display event details, if requested.
156 * (Since this is also needed by ctrun, the common
157 * contract_event_dump is found in libcontract.)
159 contract_event_dump(stdout, ev, verbose);
161 ct_event_free(ev);
162 return (1);
166 * get_type
168 * Given a contract type name, return an index into the 'types' array.
169 * Exits on failure.
171 static int
172 get_type(const char *typestr)
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 */
183 * contract_type
185 * Given a contract id, return an index into the 'types' array.
186 * Returns -1 on failure.
188 static int
189 contract_type(ctid_t id)
191 ct_stathdl_t hdl;
192 int type, fd;
195 * This could be faster (e.g. by reading the link itself), but
196 * this is the most straightforward implementation.
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);
204 type = get_type(ct_status_get_type(hdl));
205 ct_status_free(hdl);
206 (void) close(fd);
207 return (type);
211 * ctid_compar
213 * A simple contract ID comparator.
215 static int
216 ctid_compar(const void *a1, const void *a2)
218 ctid_t id1 = *(ctid_t *)a1;
219 ctid_t id2 = *(ctid_t *)a2;
221 if (id1 > id2)
222 return (1);
223 if (id2 > id1)
224 return (-1);
225 return (0);
229 main(int argc, char **argv)
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;
239 (void) setlocale(LC_ALL, "");
240 (void) textdomain(TEXT_DOMAIN);
242 (void) uu_setpname(argv[0]);
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();
260 argc -= optind;
261 argv += optind;
263 if (argc <= 0)
264 usage();
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");
274 * Scan our operands for contract ids and types.
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]);
284 * If argument isn't a number between 0 and INT_MAX,
285 * treat it as a contract type.
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;
298 types[type].found = 1;
299 nfds++;
300 } else {
301 ids[nids++] = id;
306 * Eliminate those contract ids which are represented by
307 * contract types, so we don't get duplicate event reports from
308 * them.
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].
314 qsort(ids, nids, sizeof (ctid_t), ctid_compar);
315 last = -1;
316 for (i = 0; i < nids; i++) {
317 int type, fd;
319 if (ids[i] == last)
320 continue;
321 last = ids[i];
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;
334 if (types[type].found) {
335 (void) close(fd);
336 continue;
338 wfd[nfds].wf_fd = fd;
339 wfd[nfds].wf_type = type;
340 nfds++;
342 free(ids);
344 if (nfds == 0)
345 uu_die(gettext("no contracts to watch\n"));
348 * Handle options.
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;
357 if (opt_reset)
358 for (i = 0; i < nfds; i++)
359 (void) ioctl(wfd[i].wf_fd, CT_ERESET, NULL);
363 * Allocate an event point, and associate all our endpoint file
364 * descriptors with it.
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;
374 * Loop waiting for and displaying events.
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;
385 w = pe.portev_user;
386 while (get_event(pe.portev_object, w->wf_type, opt_verbose))
388 if (port_associate(port_fd, PORT_SOURCE_FD, pe.portev_object,
389 POLLIN, pe.portev_user) == -1)
390 goto port_error;
393 port_error:
394 uu_die(gettext("error waiting for contract events"));
396 return (1); /* placate cc */