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