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