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