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
usage(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
sopen(const char * format,const char * error,const char * permerror,...)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
hdr_event(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
get_event(int fd,int type,int verbose)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
get_type(const char * typestr)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
contract_type(ctid_t id)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
ctid_compar(const void * a1,const void * a2)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
main(int argc,char ** argv)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