xref: /illumos-gate/usr/src/cmd/ctstat/ctstat.c (revision 40cb5e5daa7b80bb70fcf8dadfb20f9281566331)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/ctfs.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <libuutil.h>
38 #include <sys/contract/process.h>
39 #include <limits.h>
40 #include <libcontract.h>
41 #include <libcontract_priv.h>
42 #include <dirent.h>
43 
44 #include <locale.h>
45 #include <langinfo.h>
46 
47 static int opt_verbose = 0;
48 static int opt_showall = 0;
49 
50 /*
51  * usage
52  *
53  * Educate the user.
54  */
55 static void
56 usage(void)
57 {
58 	(void) fprintf(stderr, gettext("Usage: %s [-a] [-i ctidlist] "
59 	    "[-t typelist] [-v] [interval [count]]\n"), uu_getpname());
60 	exit(UU_EXIT_USAGE);
61 }
62 
63 /*
64  * mystrtoul
65  *
66  * Convert a string into an int in [0, INT_MAX].  Exit if the argument
67  * doen't fit this description.
68  */
69 static int
70 mystrtoul(const char *arg)
71 {
72 	unsigned int result;
73 
74 	if (uu_strtoint(arg, &result, sizeof (result), 10, 0, INT_MAX) == -1) {
75 		uu_warn(gettext("invalid numerical argument \"%s\"\n"), arg);
76 		usage();
77 	}
78 
79 	return (result);
80 }
81 
82 /*
83  * int_compar
84  *
85  * A simple integer comparator.  Also used for id_ts, since they're the
86  * same thing.
87  */
88 static int
89 int_compar(const void *a1, const void *a2)
90 {
91 	int id1 = *(int *)a1;
92 	int id2 = *(int *)a2;
93 
94 	if (id1 > id2)
95 		return (1);
96 	if (id2 > id1)
97 		return (-1);
98 	return (0);
99 }
100 
101 typedef struct optvect {
102 	const char	*option;
103 	uint_t		bit;
104 } optvect_t;
105 
106 static optvect_t option_params[] = {
107 	{ "inherit", CT_PR_INHERIT },
108 	{ "noorphan", CT_PR_NOORPHAN },
109 	{ "pgrponly", CT_PR_PGRPONLY },
110 	{ "regent", CT_PR_REGENT },
111 	{ NULL }
112 };
113 
114 static optvect_t option_events[] = {
115 	{ "core", CT_PR_EV_CORE },
116 	{ "signal", CT_PR_EV_SIGNAL },
117 	{ "hwerr", CT_PR_EV_HWERR },
118 	{ "empty", CT_PR_EV_EMPTY },
119 	{ "fork", CT_PR_EV_FORK },
120 	{ "exit", CT_PR_EV_EXIT },
121 	{ NULL }
122 };
123 
124 /*
125  * print_bits
126  *
127  * Display a set whose membership is identified by a bitfield.
128  */
129 static void
130 print_bits(uint_t bits, optvect_t *desc)
131 {
132 	int i, printed = 0;
133 
134 	for (i = 0; desc[i].option; i++)
135 		if (desc[i].bit & bits) {
136 			if (printed)
137 				(void) putchar(' ');
138 			printed = 1;
139 			(void) fputs(desc[i].option, stdout);
140 		}
141 	if (printed)
142 		(void) putchar('\n');
143 	else
144 		(void) puts("none");
145 }
146 
147 /*
148  * print_ids
149  *
150  * Display a list of ids, sorted.
151  */
152 static void
153 print_ids(id_t *ids, uint_t nids)
154 {
155 	int i;
156 	int first = 1;
157 
158 	qsort(ids, nids, sizeof (int), int_compar);
159 
160 	for (i = 0; i < nids; i++) {
161 		/*LINTED*/
162 		(void) printf(" %d" + first, ids[i]);
163 		first = 0;
164 	}
165 	if (first)
166 		(void) puts("none");
167 	else
168 		(void) putchar('\n');
169 }
170 
171 typedef void printfunc_t(ct_stathdl_t);
172 
173 /*
174  * A structure defining a displayed field.  Includes a label to be
175  * printed along side the field value, and a function which extracts
176  * the data from a status structure, formats it, and displays it on
177  * stdout.
178  */
179 typedef struct verbout {
180 	const char	*label;	/* field label */
181 	printfunc_t	*func;	/* field display function */
182 } verbout_t;
183 
184 /*
185  * verb_cookie
186  *
187  * Used to display an error encountered when reading a contract status
188  * field.
189  */
190 static void
191 verb_error(int err)
192 {
193 	(void) printf("(error: %s)\n", strerror(err));
194 }
195 
196 /*
197  * verb_cookie
198  *
199  * Display the contract's cookie.
200  */
201 static void
202 verb_cookie(ct_stathdl_t hdl)
203 {
204 	(void) printf("%#llx\n", ct_status_get_cookie(hdl));
205 }
206 
207 /*
208  * verb_info
209  *
210  * Display the parameters in the parameter set.
211  */
212 static void
213 verb_param(ct_stathdl_t hdl)
214 {
215 	uint_t param;
216 	int err;
217 
218 	if (err = ct_pr_status_get_param(hdl, &param))
219 		verb_error(err);
220 	else
221 		print_bits(param, option_params);
222 }
223 
224 /*
225  * verb_info
226  *
227  * Display the events in the informative event set.
228  */
229 static void
230 verb_info(ct_stathdl_t hdl)
231 {
232 	print_bits(ct_status_get_informative(hdl), option_events);
233 }
234 
235 /*
236  * verb_crit
237  *
238  * Display the events in the critical event set.
239  */
240 static void
241 verb_crit(ct_stathdl_t hdl)
242 {
243 	print_bits(ct_status_get_critical(hdl), option_events);
244 }
245 
246 /*
247  * verb_fatal
248  *
249  * Display the events in the fatal event set.
250  */
251 static void
252 verb_fatal(ct_stathdl_t hdl)
253 {
254 	uint_t event;
255 	int err;
256 
257 	if (err = ct_pr_status_get_fatal(hdl, &event))
258 		verb_error(err);
259 	else
260 		print_bits(event, option_events);
261 }
262 
263 /*
264  * verb_members
265  *
266  * Display the list of member contracts.
267  */
268 static void
269 verb_members(ct_stathdl_t hdl)
270 {
271 	pid_t *pids;
272 	uint_t npids;
273 	int err;
274 
275 	if (err = ct_pr_status_get_members(hdl, &pids, &npids)) {
276 		verb_error(err);
277 		return;
278 	}
279 
280 	print_ids(pids, npids);
281 }
282 
283 /*
284  * verb_inherit
285  *
286  * Display the list of inherited contracts.
287  */
288 static void
289 verb_inherit(ct_stathdl_t hdl)
290 {
291 	ctid_t *ctids;
292 	uint_t nctids;
293 	int err;
294 
295 	if (err = ct_pr_status_get_contracts(hdl, &ctids, &nctids))
296 		verb_error(err);
297 	else
298 		print_ids(ctids, nctids);
299 }
300 
301 /*
302  * Common contract status fields.
303  */
304 static verbout_t vcommon[] = {
305 	"cookie", verb_cookie,
306 	NULL,
307 };
308 
309 /*
310  * Process contract-specific status fields.
311  * The critical and informative event sets are here because the event
312  * names are contract-specific.  They are listed first, however, so
313  * they are displayed adjacent to the "normal" common output.
314  */
315 static verbout_t vprocess[] = {
316 	"informative event set", verb_info,
317 	"critical event set", verb_crit,
318 	"fatal event set", verb_fatal,
319 	"parameter set", verb_param,
320 	"member processes", verb_members,
321 	"inherited contracts", verb_inherit,
322 	NULL
323 };
324 
325 /*
326  * print_verbose
327  *
328  * Displays a contract's verbose status, common fields first.
329  */
330 static void
331 print_verbose(ct_stathdl_t hdl, verbout_t *spec, verbout_t *common)
332 {
333 	int i;
334 	int tmp, maxwidth = 0;
335 
336 	/*
337 	 * Compute the width of all the fields.
338 	 */
339 	for (i = 0; common[i].label; i++)
340 		if ((tmp = strlen(common[i].label)) > maxwidth)
341 			maxwidth = tmp;
342 	if (spec)
343 		for (i = 0; spec[i].label; i++)
344 			if ((tmp = strlen(spec[i].label)) > maxwidth)
345 				maxwidth = tmp;
346 	maxwidth += 2;
347 
348 	/*
349 	 * Display the data.
350 	 */
351 	for (i = 0; common[i].label; i++) {
352 		tmp = printf("\t%s", common[i].label);
353 		if (tmp < 0)
354 			tmp = 0;
355 		(void) printf("%-*s", maxwidth - tmp + 1, ":");
356 		common[i].func(hdl);
357 	}
358 	if (spec)
359 		for (i = 0; spec[i].label; i++) {
360 			(void) printf("\t%s%n", spec[i].label, &tmp);
361 			(void) printf("%-*s", maxwidth - tmp + 1, ":");
362 			spec[i].func(hdl);
363 		}
364 }
365 
366 struct {
367 	const char *name;
368 	verbout_t *verbout;
369 } cttypes[] = {
370 	{ "process", vprocess },
371 	{ NULL }
372 };
373 
374 /*
375  * get_type
376  *
377  * Given a type name, return an index into the above array of types.
378  */
379 static int
380 get_type(const char *typestr)
381 {
382 	int i;
383 	for (i = 0; cttypes[i].name; i++)
384 		if (strcmp(cttypes[i].name, typestr) == 0)
385 			return (i);
386 	uu_die(gettext("invalid contract type: %s\n"), typestr);
387 	/* NOTREACHED */
388 }
389 
390 /*
391  * print_header
392  *
393  * Display the status header.
394  */
395 static void
396 print_header(void)
397 {
398 	(void) printf("%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s\n", "CTID", "ZONEID",
399 	    "TYPE", "STATE", "HOLDER", "EVENTS", "QTIME", "NTIME");
400 }
401 
402 /*
403  * print_contract
404  *
405  * Display status for contract ID 'id' from type directory 'dir'.  If
406  * only contracts of a specific set of types should be displayed,
407  * 'types' will be a sorted list of type indices of length 'ntypes'.
408  */
409 static void
410 print_contract(const char *dir, ctid_t id, verbout_t *spec,
411     int *types, int ntypes)
412 {
413 	ct_stathdl_t status;
414 	char hstr[100], qstr[20], nstr[20];
415 	ctstate_t state;
416 	int fd = 0;
417 	int t;
418 
419 	/*
420 	 * Open and obtain status.
421 	 */
422 	if ((fd = contract_open(id, dir, "status", O_RDONLY)) == -1) {
423 		if (errno == ENOENT)
424 			return;
425 		uu_die(gettext("could not open contract status file"));
426 	}
427 
428 	if (errno = ct_status_read(fd, opt_verbose ? CTD_ALL : CTD_COMMON,
429 	    &status))
430 		uu_die(gettext("failed to get contract status for %d"), id);
431 	(void) close(fd);
432 
433 	/*
434 	 * Unless otherwise directed, don't display dead contracts.
435 	 */
436 	state = ct_status_get_state(status);
437 	if (!opt_showall && state == CTS_DEAD) {
438 		ct_status_free(status);
439 		return;
440 	}
441 
442 	/*
443 	 * If we are only allowed to display certain contract types,
444 	 * perform that filtering here.  We stash a copy of spec so we
445 	 * don't have to recompute it later.
446 	 */
447 	if (types) {
448 		int key = get_type(ct_status_get_type(status));
449 		spec = cttypes[key].verbout;
450 		if (bsearch(&key, types, ntypes, sizeof (int), int_compar) ==
451 		    NULL) {
452 			ct_status_free(status);
453 			return;
454 		}
455 	}
456 
457 	/*
458 	 * Precompute those fields which have both textual and
459 	 * numerical values.
460 	 */
461 	if ((state == CTS_OWNED) || (state == CTS_INHERITED))
462 		(void) snprintf(hstr, sizeof (hstr), "%ld",
463 		    ct_status_get_holder(status));
464 	else
465 		(void) snprintf(hstr, sizeof (hstr), "%s", "-");
466 
467 	if ((t = ct_status_get_qtime(status)) == -1) {
468 		qstr[0] = nstr[0] = '-';
469 		qstr[1] = nstr[1] = '\0';
470 	} else {
471 		(void) snprintf(qstr, sizeof (qstr), "%d", t);
472 		(void) snprintf(nstr, sizeof (nstr), "%d",
473 		    ct_status_get_ntime(status));
474 	}
475 
476 	/*
477 	 * Emit the contract's status.
478 	 */
479 	(void) printf("%-7ld %-7ld %-7s %-7s %-7s %-7d %-7s %-8s\n",
480 	    ct_status_get_id(status),
481 	    ct_status_get_zoneid(status),
482 	    ct_status_get_type(status),
483 	    (state == CTS_OWNED) ? "owned" :
484 	    (state == CTS_INHERITED) ? "inherit" :
485 	    (state == CTS_ORPHAN) ? "orphan" : "dead", hstr,
486 	    ct_status_get_nevents(status), qstr, nstr);
487 
488 	/*
489 	 * Emit verbose status information, if requested.  If we
490 	 * weren't provided a verbose output spec or didn't compute it
491 	 * earlier, do it now.
492 	 */
493 	if (opt_verbose) {
494 		if (spec == NULL)
495 			spec = cttypes[get_type(ct_status_get_type(status))].
496 			    verbout;
497 		print_verbose(status, spec, vcommon);
498 	}
499 
500 	ct_status_free(status);
501 }
502 
503 /*
504  * scan_type
505  *
506  * Display all contracts of the requested type.
507  */
508 static void
509 scan_type(int typeno)
510 {
511 	DIR *dir;
512 	struct dirent64 *de;
513 	char path[PATH_MAX];
514 
515 	verbout_t *vo = cttypes[typeno].verbout;
516 	const char *type = cttypes[typeno].name;
517 
518 	if (snprintf(path, PATH_MAX, CTFS_ROOT "/%s", type) >= PATH_MAX ||
519 	    (dir = opendir(path)) == NULL)
520 		uu_die(gettext("bad contract type: %s\n"), type);
521 	while ((de = readdir64(dir)) != NULL) {
522 		/*
523 		 * Eliminate special files (e.g. '.', '..').
524 		 */
525 		if (de->d_name[0] < '0' || de->d_name[0] > '9')
526 			continue;
527 		print_contract(type, mystrtoul(de->d_name), vo, NULL, 0);
528 	}
529 	(void) closedir(dir);
530 }
531 
532 /*
533  * scan_ids
534  *
535  * Display all contracts with the requested IDs.
536  */
537 static void
538 scan_ids(ctid_t *ids, int nids)
539 {
540 	int i;
541 	for (i = 0; i < nids; i++)
542 		print_contract("all", ids[i], NULL, NULL, 0);
543 }
544 
545 /*
546  * scan_all
547  *
548  * Display the union of the requested IDs and types.  So that the
549  * output is sorted by contract ID, it takes the slow road by testing
550  * each entry in /system/contract/all against its criteria.  Used when
551  * the number of types is greater than 1, when we have a mixture of
552  * types and ids, or no lists were provided at all.
553  */
554 static void
555 scan_all(int *types, int ntypes, ctid_t *ids, int nids)
556 {
557 	DIR *dir;
558 	struct dirent64 *de;
559 	const char *path = CTFS_ROOT "/all";
560 	int key, test;
561 
562 	if ((dir = opendir(path)) == NULL)
563 		uu_die(gettext("could not open %s"), path);
564 	while ((de = readdir64(dir)) != NULL) {
565 		/*
566 		 * Eliminate special files (e.g. '.', '..').
567 		 */
568 		if (de->d_name[0] < '0' || de->d_name[0] > '9')
569 			continue;
570 		key = mystrtoul(de->d_name);
571 
572 		/*
573 		 * If we are given IDs to look at and this contract
574 		 * isn't in the ID list, or if we weren't given a list
575 		 * if IDs but were given a list of types, provide the
576 		 * list of acceptable types to print_contract.
577 		 */
578 		test = nids ? (bsearch(&key, ids, nids, sizeof (int),
579 			    int_compar) == NULL) : (ntypes != 0);
580 		print_contract("all", key, NULL, (test ? types : NULL), ntypes);
581 	}
582 	(void) closedir(dir);
583 }
584 
585 /*
586  * walk_args
587  *
588  * Apply fp to each token in the comma- or space- separated argument
589  * string str and store the results in the array starting at results.
590  */
591 static int
592 walk_args(const char *str, int (*fp)(const char *), int *results)
593 {
594 	char *copy, *token;
595 	int count = 0;
596 
597 	if ((copy = strdup(str)) == NULL)
598 		uu_die(gettext("strdup() failed"));
599 
600 	token = strtok(copy, ", ");
601 	if (token == NULL) {
602 		free(copy);
603 		return (0);
604 	}
605 
606 	do {
607 		if (fp)
608 			*(results++) = fp(token);
609 		count++;
610 	} while (token = strtok(NULL, ", "));
611 	free(copy);
612 
613 	return (count);
614 }
615 
616 /*
617  * parse
618  *
619  * Parse the comma- or space- separated string str, using fp to covert
620  * the tokens to integers.  Append the list of integers to the array
621  * pointed to by *idps, growing the array if necessary.
622  */
623 static int
624 parse(const char *str, int **idsp, int nids, int (*fp)(const char *fp))
625 {
626 	int count;
627 	int *array;
628 
629 	count = walk_args(str, NULL, NULL);
630 	if (count == 0)
631 		return (0);
632 
633 	if ((array = calloc(nids + count, sizeof (int))) == NULL)
634 		uu_die(gettext("calloc() failed"));
635 
636 	if (*idsp) {
637 		(void) memcpy(array, *idsp, nids * sizeof (int));
638 		free(*idsp);
639 	}
640 
641 	(void) walk_args(str, fp, array + nids);
642 
643 	*idsp = array;
644 	return (count + nids);
645 }
646 
647 /*
648  * parse_ids
649  *
650  * Extract a list of ids from the comma- or space- separated string str
651  * and append them to the array *idsp, growing it if necessary.
652  */
653 static int
654 parse_ids(const char *arg, int **idsp, int nids)
655 {
656 	return (parse(arg, idsp, nids, mystrtoul));
657 }
658 
659 /*
660  * parse_types
661  *
662  * Extract a list of types from the comma- or space- separated string
663  * str and append them to the array *idsp, growing it if necessary.
664  */
665 static int
666 parse_types(const char *arg, int **typesp, int ntypes)
667 {
668 	return (parse(arg, typesp, ntypes, get_type));
669 }
670 
671 /*
672  * compact
673  *
674  * Sorts and removes duplicates from array.  Initial size of array is
675  * in *size; final size is stored in *size.
676  */
677 static void
678 compact(int *array, int *size)
679 {
680 	int i, j, last = -1;
681 
682 	qsort(array, *size, sizeof (int), int_compar);
683 	for (i = j = 0; i < *size; i++) {
684 		if (array[i] != last) {
685 			last = array[i];
686 			array[j++] = array[i];
687 		}
688 	}
689 	*size = j;
690 }
691 
692 int
693 main(int argc, char **argv)
694 {
695 	unsigned int interval = 0, count = 1;
696 	ctid_t	*ids = NULL;
697 	int	*types = NULL;
698 	int	nids = 0, ntypes = 0;
699 	int	i, s;
700 
701 	(void) setlocale(LC_ALL, "");
702 	(void) textdomain(TEXT_DOMAIN);
703 
704 	(void) uu_setpname(argv[0]);
705 
706 	while ((s = getopt(argc, argv, "ai:t:v")) != EOF) {
707 		switch (s) {
708 		case 'a':
709 			opt_showall = 1;
710 			break;
711 		case 'i':
712 			nids = parse_ids(optarg, (int **)&ids, nids);
713 			break;
714 		case 't':
715 			ntypes = parse_types(optarg, &types, ntypes);
716 			break;
717 		case 'v':
718 			opt_verbose = 1;
719 			break;
720 		default:
721 			usage();
722 		}
723 	}
724 
725 	argc -= optind;
726 	argv += optind;
727 
728 	if (argc > 2 || argc < 0)
729 		usage();
730 
731 	if (argc > 0) {
732 		interval = mystrtoul(argv[0]);
733 		count = 0;
734 	}
735 
736 	if (argc > 1) {
737 		count = mystrtoul(argv[1]);
738 		if (count == 0)
739 			return (0);
740 	}
741 
742 	if (nids)
743 		compact((int *)ids, &nids);
744 	if (ntypes)
745 		compact(types, &ntypes);
746 
747 	for (i = 0; count == 0 || i < count; i++) {
748 		if (i)
749 			(void) sleep(interval);
750 		print_header();
751 		if (nids && ntypes)
752 			scan_all(types, ntypes, ids, nids);
753 		else if (ntypes == 1)
754 			scan_type(*types);
755 		else if (nids)
756 			scan_ids(ids, nids);
757 		else
758 			scan_all(types, ntypes, ids, nids);
759 	}
760 
761 	return (0);
762 }
763