ctstat.c revision 6073:47f6aa7a8077
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
46static int opt_verbose = 0;
47static int opt_showall = 0;
48
49/*
50 * usage
51 *
52 * Educate the user.
53 */
54static void
55usage(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 */
68static int
69mystrtoul(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 */
87static int
88int_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
100typedef struct optvect {
101	const char	*option;
102	uint_t		bit;
103} optvect_t;
104
105static 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
113static 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 */
128static void
129print_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 */
151static void
152print_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
170typedef 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 */
178typedef 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 */
189static void
190verb_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 */
200static void
201verb_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 */
211static void
212verb_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 */
228static void
229verb_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 */
239static void
240verb_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 */
250static void
251verb_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 */
267static void
268verb_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 */
287static void
288verb_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 */
305static void
306verb_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 */
321static void
322verb_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 */
337static void
338verb_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 */
353static void
354verb_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 */
367static 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 */
378static 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 */
397static void
398print_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
433struct {
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 */
446static int
447get_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 */
462static void
463print_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 */
476static void
477print_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 */
575static void
576scan_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 */
604static void
605scan_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 */
621static void
622scan_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 */
658static int
659walk_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 */
690static int
691parse(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 */
720static int
721parse_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 */
732static int
733parse_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 */
744static void
745compact(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
759int
760main(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