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