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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * poolstat - report active pool statistics
28 */
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <locale.h>
34#include <string.h>
35#include <ctype.h>
36#include <limits.h>
37#include <errno.h>
38#include <stddef.h>
39
40#include <pool.h>
41#include "utils.h"
42#include "poolstat.h"
43#include "poolstat_utils.h"
44#include "statcommon.h"
45
46#ifndef	TEXT_DOMAIN
47#define	TEXT_DOMAIN	"SYS_TEST"
48#endif
49
50#define	addrof(s)  ((char **)&(s))
51
52/* verify if a field is printable in respect of the current option flags */
53#define	PRINTABLE(i)	((lf->plf_ffs[(i)].pff_prt & D_FIELD) || \
54	(lf->plf_ffs[(i)].pff_prt & X_FIELD))
55
56typedef int (* formatter) (char *, int, int, poolstat_field_format_t *, char *);
57
58static uint_t timestamp_fmt = NODATE;
59
60/* available field formatters	*/
61static int default_f(char *, int, int, poolstat_field_format_t *, char *);
62static int bigno_f(char *, int, int, poolstat_field_format_t *, char *);
63static int used_stat_f(char *, int, int, poolstat_field_format_t *, char *);
64static int header_f(char *, int, int, poolstat_field_format_t *, char *);
65
66/* statistics bags used to collect data from various provider	*/
67static statistic_bag_t 	pool_sbag_s;
68static statistic_bag_t 	pset_sbag_s;
69static statistic_bag_t 	*pool_sbag = &pool_sbag_s;
70static statistic_bag_t 	*pset_sbag = &pset_sbag_s;
71
72/* formatter objects for pset, defined in a default printing sequence	*/
73static poolstat_field_format_t pset_ffs[] = {
74	/* prt flags,name,header,type,width,minwidth,offset,formatter	*/
75	{ DX_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag),
76		offsetof(statistic_bag_t, sb_sysid),
77		(formatter)default_f },
78	{ DX_FIELD, "pool", "pool", STR, 20, 14, addrof(pool_sbag),
79		offsetof(statistic_bag_t, sb_name),
80		(formatter)default_f },
81	{ DX_FIELD, "type", "type", STR, 4, 5, addrof(pset_sbag),
82		offsetof(statistic_bag_t, sb_type),
83		(formatter)default_f },
84	{ D_FIELD, "rid", "rid", LL, 3, 1, addrof(pset_sbag_s.bag),
85		offsetof(pset_statistic_bag_t, pset_sb_sysid),
86		(formatter)default_f },
87	{ DX_FIELD, "rset", "rset", STR, 20, 14, addrof(pset_sbag),
88		offsetof(statistic_bag_t, sb_name),
89		(formatter)default_f },
90	{ DX_FIELD, "min", "min", ULL, 4, 1, addrof(pset_sbag_s.bag),
91		offsetof(pset_statistic_bag_t, pset_sb_min),
92		(formatter)bigno_f },
93	{ DX_FIELD, "max", "max", ULL, 4, 1, addrof(pset_sbag_s.bag),
94		offsetof(pset_statistic_bag_t, pset_sb_max),
95		(formatter)bigno_f },
96	{ DX_FIELD, "size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag),
97		offsetof(pset_statistic_bag_t, pset_sb_size),
98		(formatter)default_f },
99	{ DX_FIELD, "used", "used", FL, 4, -1, addrof(pset_sbag_s.bag),
100		offsetof(pset_statistic_bag_t, pset_sb_used),
101		(formatter)used_stat_f },
102	{ DX_FIELD, "load", "load", FL, 4, -1, addrof(pset_sbag_s.bag),
103		offsetof(pset_statistic_bag_t, pset_sb_load),
104		(formatter)default_f }
105};
106
107/* formatter objects for pool, defined in a default printing sequence	*/
108static poolstat_field_format_t pool_ffs[] = {
109	/* prt flags,name,header,type,width,minwidth,offset,formatter	*/
110	{ D_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag),
111		offsetof(statistic_bag_t, sb_sysid),
112		(formatter)default_f },
113	{ D_FIELD, "pool", "pool", STR, 20, 13, addrof(pool_sbag),
114		offsetof(statistic_bag_t, sb_name),
115		(formatter)default_f },
116	{ D_FIELD, "p_size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag),
117		offsetof(pset_statistic_bag_t, pset_sb_size),
118		(formatter)default_f },
119	{ D_FIELD, "p_used", "used", FL, 4, -1, addrof(pset_sbag_s.bag),
120		offsetof(pset_statistic_bag_t, pset_sb_used),
121		(formatter)default_f },
122	{ D_FIELD, "p_load", "load", FL, 4, -1, addrof(pset_sbag_s.bag),
123		offsetof(pset_statistic_bag_t, pset_sb_load),
124		(formatter)default_f },
125};
126
127/* lists with formatter objects, one for each statistics field */
128static poolstat_line_format_t   pool_lf; /* formatting list in default mode */
129static poolstat_line_format_t   pset_lf; /* formatting list for psets    */
130
131/* name of pools to be shown */
132static poolstat_list_element_t	*pnames;
133/*
134 * type of resources to be shown, currently we only have one type 'pset'
135 * but, poolstat can be extended to handle new upcoming resource types.
136 */
137static poolstat_list_element_t   *rtypes;
138
139/* a handle to the pool configuration	*/
140static pool_conf_t *conf;
141
142/* option flags		*/
143static int 	rflag;
144static int 	pflag;
145static int 	oflag;
146
147/* operands	*/
148static int 	interval = 0;	/* update interval	*/
149static long 	count    = 1; 	/* one run		*/
150
151/* data structure handlers	*/
152static poolstat_list_element_t *
153	create_prt_sequence_list(char *, poolstat_line_format_t *);
154static poolstat_list_element_t *
155	create_args_list(char *, poolstat_list_element_t *, const char *);
156
157/* statistics update function	*/
158static void sa_update(statistic_bag_t *, int);
159
160/* statistics printing function	*/
161static void prt_pool_stats(poolstat_list_element_t *);
162
163static void usage(void) __NORETURN;
164
165static void
166usage(void)
167{
168	(void) fprintf(stderr, gettext(
169"Usage:\n"
170"poolstat [-p pool-list] [-r rset-list] [-T d|u] [interval [count]]\n"
171"poolstat [-p pool-list] [-o format -r rset-list] [-T d|u] [interval [count]]\n"
172"  \'pool-list\' is a space-separated list of pool IDs or names\n"
173"  \'rset-list\' is \'all\' or \'pset\'\n"
174"  \'format\' for all resource types is one or more of:\n"
175"\tid pool type rid rset min max size used load\n"));
176	(void) exit(E_USAGE);
177}
178
179static int
180Atoi(char *p, int *errp)
181{
182	int i;
183	char *q;
184	errno = 0;
185	i = strtol(p, &q, 10);
186	if (errno != 0 || q == p || *q != '\0')
187		*errp = -1;
188	else
189		*errp = 0;
190	return (i);
191}
192
193int
194main(int argc, char *argv[])
195{
196	char		c;
197	int 		error = 0;
198
199	(void) getpname(argv[0]);
200	(void) setlocale(LC_ALL, "");
201	(void) textdomain(TEXT_DOMAIN);
202
203	/* pset_sbag_s is used to collect pset statistics   */
204	pset_sbag_s.sb_type = PSET_TYPE_NAME;
205	pset_sbag_s.bag	= ZALLOC(sizeof (pset_statistic_bag_t));
206	pool_sbag_s.sb_type = POOL_TYPE_NAME;
207
208	pset_lf.plf_ffs = pset_ffs;
209	pset_lf.plf_ff_len = sizeof (pset_ffs) /
210	    sizeof (poolstat_field_format_t);
211	pool_lf.plf_ffs = pool_ffs;
212	pool_lf.plf_ff_len = sizeof (pool_ffs) /
213	    sizeof (poolstat_field_format_t);
214
215	/* Don't let buffering interfere with piped output. */
216	(void) setvbuf(stdout, NULL, _IOLBF, 0);
217
218	while ((c = getopt(argc, argv, ":p:r:o:T:")) != EOF) {
219		switch (c) {
220		case 'p':	/* pool name specification	*/
221			pflag++;
222			pnames = create_args_list(optarg, pnames,
223			    " \t");
224			break;
225		case 'r': {	/* resource type 		*/
226			rflag++;
227			rtypes = create_args_list(optarg, rtypes,
228			    " \t,");
229			break;
230			}
231		case 'o': { 	/* format specification		*/
232			oflag++;
233			if (create_prt_sequence_list(optarg, &pset_lf) == NULL)
234				usage();
235			break;
236			}
237		case 'T':
238			if (optarg) {
239				if (*optarg == 'u')
240					timestamp_fmt = UDATE;
241				else if (*optarg == 'd')
242					timestamp_fmt = DDATE;
243				else
244					usage();
245			} else {
246					usage();
247			}
248			break;
249		case ':': {
250			(void) fprintf(stderr,
251			    gettext(ERR_OPTION_ARGS), optopt);
252			usage();
253			/*NOTREACHED*/
254			}
255		default:
256			(void) fprintf(stderr, gettext(ERR_OPTION), optopt);
257			usage();
258			/*NOTREACHED*/
259		}
260	}
261
262	/* get operands	*/
263	if (argc > optind) {
264		if ((interval = Atoi(argv[optind++], &error)) < 1 || error != 0)
265			usage();
266		count = -1;
267	}
268	if (argc > optind) {
269		if ((count = Atoi(argv[optind++], &error)) < 1 || error != 0)
270			usage();
271	}
272	/* check for extra options/operands	*/
273	if (argc > optind)
274		usage();
275
276	/* check options	*/
277	if (oflag && !rflag)
278		usage();
279
280	/* global initializations	*/
281	if (!oflag) {
282		/* create the default print sequences	*/
283		(void) create_prt_sequence_list(NULL, &pool_lf);
284		(void) create_prt_sequence_list(NULL, &pset_lf);
285	}
286
287	if (rtypes == NULL || strcmp(rtypes->ple_obj, "all") == 0) {
288		/* crate a default resource list	*/
289		FREE(rtypes);
290		rtypes = create_args_list("pset", NULL, " \t,");
291	}
292
293	if ((conf = pool_conf_alloc()) == NULL)
294		die(gettext(ERR_NOMEM));
295	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY)
296	    != PO_SUCCESS)
297		die(gettext(ERR_OPEN_DYNAMIC), get_errstr());
298
299	/* initialize statistic adapters	*/
300	sa_libpool_init(conf);
301	sa_kstat_init(NULL);
302
303	/* collect and print out statistics	*/
304	while (count-- != 0) {
305		sa_update(pool_sbag, SA_REFRESH);
306		if (timestamp_fmt != NODATE)
307			print_timestamp(timestamp_fmt);
308		if (pool_sbag->sb_changed & POU_POOL)
309				(void) printf(
310				"<<State change>>\n");
311		prt_pool_stats(pnames);
312		if (count != 0) {
313			(void) sleep(interval);
314			if (rflag)
315				(void) printf("\n");
316		}
317	}
318
319	return (E_PO_SUCCESS);
320}
321
322/*
323 * Take the arguments and create/append a string list to the 'le' list.
324 */
325static poolstat_list_element_t  *
326create_args_list(char *arg, poolstat_list_element_t  *le, const char *delim)
327{
328	poolstat_list_element_t *head = le;
329
330	while (arg != NULL && *arg != '\0') {
331		char *name = arg;
332		arg = strpbrk(arg, delim);
333		if (arg != NULL) {
334			*arg++ = '\0';
335		}
336		if (le == NULL) {
337			/* create first element */
338			NEW0(le);
339			head = le;
340		} else {
341			/* find last and append	*/
342			while (le->ple_next != NULL)
343				le = le->ple_next;
344			NEW0(le->ple_next);
345			le = le->ple_next;
346		}
347		le->ple_obj = (void *)name;
348	}
349
350	return (head);
351}
352
353/*
354 * Take the arguments to the -o option, and create a format field list in order
355 * specified by 'arg'.
356 * If 'arg' is NULL a list in a default printing order is created.
357 */
358static poolstat_list_element_t *
359create_prt_sequence_list(char *arg, poolstat_line_format_t *lf)
360{
361	/*
362	 * Create a default print sequence. It is the sequence defined
363	 * statically in the format list. At the same time mark the fields
364	 * printable according to the current option settings.
365	 */
366	if (arg == NULL) {
367		int	i;
368		NEW0(lf->plf_prt_seq);
369		lf->plf_ffs[0].pff_prt |= PRINTABLE(0) ? PABLE_FIELD : 0;
370		lf->plf_last = lf->plf_prt_seq;
371		lf->plf_last->ple_obj = &(lf->plf_ffs[0]);
372		for (i = 1; i < lf->plf_ff_len; i++) {
373			lf->plf_ffs[i].pff_prt |=
374			    PRINTABLE(i) ? PABLE_FIELD : 0;
375			NEW0(lf->plf_last->ple_next);
376			lf->plf_last = lf->plf_last->ple_next;
377			lf->plf_last->ple_obj = &(lf->plf_ffs[i]);
378		}
379		return (lf->plf_prt_seq);
380	}
381
382	while (arg != NULL && *arg != '\0') {
383		poolstat_field_format_t *ff;	/* current format field */
384		int 	ffIdx;	/* format field index	    */
385		char 	*name;	/* name of field	    */
386		int	n; 	/* no. of chars to strip    */
387
388		n = strspn(arg, " ,\t\r\v\f\n");
389		arg += n;	/* strip multiples separator	*/
390		name = arg;
391
392		if (strlen(name) < 1)
393			break;
394
395		if ((arg = strpbrk(arg, " ,\t\r\v\f\n")) != NULL)
396			*arg++ = '\0';
397
398		/* search for a named format field */
399		for (ffIdx = 0; ffIdx < lf->plf_ff_len; ffIdx++) {
400			ff = lf->plf_ffs + ffIdx;
401			if (strcmp(ff->pff_name, name) == 0) {
402				ff->pff_prt |= PABLE_FIELD;
403				break;
404			}
405		}
406		/* if the name wasn't found	*/
407		if (ffIdx == lf->plf_ff_len) {
408			(void) fprintf(stderr, gettext(ERR_UNSUPP_STAT_FIELD),
409			    name);
410			usage();
411		}
412		if (lf->plf_last == NULL) {
413			/* create first print handle */
414			NEW0(lf->plf_prt_seq);
415			lf->plf_last = lf->plf_prt_seq;
416		} else {
417			NEW0(lf->plf_last->ple_next);
418			lf->plf_last = lf->plf_last->ple_next;
419		}
420		lf->plf_last->ple_obj = ff; 	/* refer to the format field */
421	}
422
423	return (lf->plf_prt_seq);
424}
425
426/* update the statistic data by adapters	*/
427static void
428sa_update(statistic_bag_t *sbag, int flags)
429{
430	sa_libpool_update(sbag, flags);
431	sa_kstat_update(sbag, flags);
432}
433
434/*
435 * Format one statistic field and put it into the 'str' buffer. 'ff' contains
436 * the field formatting parameters. Return the number of used bytes.
437 */
438static int
439default_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data)
440{
441	int  used;
442
443	switch (ff->pff_type) {
444	case LL: {
445			int64_t v;
446			v = *((int64_t *)(void *)(data + ff->pff_offset));
447			used = snprintf(str + pos, left, "%*.*lld",
448			    ff->pff_width, ff->pff_minwidth, v);
449		}
450		break;
451	case ULL: {
452			uint64_t v;
453			v = *((uint64_t *)(void *)(data + ff->pff_offset));
454			used = snprintf(str + pos, left, "%*.*llu",
455			    ff->pff_width, ff->pff_minwidth, v);
456		};
457		break;
458	case FL: {
459			int	pw = 0;
460			double v = *((double *)(void *)(data + ff->pff_offset));
461			if (v < 10) {
462				pw = ff->pff_width - 2;
463			} else if (v < 100) {
464				pw = ff->pff_width - 3;
465			} else if (v < 1000) {
466				pw = ff->pff_width - 4;
467			}
468			if (pw < 0)
469				pw = 0;
470			used = snprintf(str + pos, left, "%*.*f",
471			    ff->pff_width, pw, v);
472		};
473		break;
474	case STR: {
475			char 	*v;
476			int 	sl;
477			v = *((char **)(void *)(data + ff->pff_offset));
478			sl = strlen(v);
479			/* truncate if it doesn't fit	*/
480			if (sl > ff->pff_width) {
481				char *cp = v +  ff->pff_width - 1;
482				if (ff->pff_width < 4)
483					die(gettext(ERR_STATS_FORMAT),
484					    ff->pff_header);
485				*cp-- = 0;
486				*cp-- = '.';
487				*cp-- = '.';
488				*cp-- = '.';
489			}
490			used = snprintf(str + pos, left, "%-*s", ff->pff_width,
491			    v);
492		}
493		break;
494	}
495
496	return (used);
497}
498
499/* format big numbers */
500static int
501bigno_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data)
502{
503	uint64_t v;
504	char	tag;
505	int	pw = ff->pff_width - 4;
506	double 	pv;
507	int  	used;
508
509	v = *((uint64_t *)(void *)(data + ff->pff_offset));
510	/*
511	 * the max value can be ULONG_MAX, which is formatted as:
512	 * E  P   T   G   M   K
513	 * 18 446 744 073 709 551 615
514	 * As a result ULONG_MAX is displayed as 18E
515	 */
516	pv = v;
517	if (v < 1000) {
518		pw = 0;
519	} else if (v < KILO * 10) {
520		pv = (double)v / KILO;
521		tag = 'K';
522	} else if (v < KILO * 100) {
523		pv = (double)v / KILO;
524		tag = 'K'; pw -= 1;
525	} else if (v < KILO * 1000) {
526		pv = (double)v / KILO;
527		tag = 'K'; pw -= 2;
528	} else if (v < MEGA * 10) {
529		pv = (double)v / MEGA;
530		tag = 'M';
531	} else if (v < MEGA * 100) {
532		pv = (double)v / MEGA;
533		tag = 'M'; pw -= 1;
534	} else if (v < MEGA * 1000) {
535		pv = (double)v / MEGA;
536		tag = 'M'; pw -= 2;
537	} else if (v < GIGA * 10) {
538		pv = (double)v / GIGA;
539		tag = 'G';
540	} else if (v < GIGA * 100) {
541		pv = (double)v / GIGA;
542		tag = 'G'; pw -= 1;
543	} else if (v < GIGA * 1000) {
544		pv = (double)v / GIGA;
545		tag = 'G'; pw -= 2;
546	} else if (v < TERA * 10) {
547		pv = (double)v / TERA;
548		tag = 'T';
549	} else if (v < TERA * 100) {
550		pv = (double)v / TERA;
551		tag = 'T'; pw -= 1;
552	} else if (v < TERA * 1000) {
553		pv = (double)v / TERA;
554		tag = 'T'; pw -= 2;
555	} else if (v < PETA * 10) {
556		pv = (double)v / PETA;
557		tag = 'P';
558	} else if (v < PETA * 100) {
559		pv = (double)v / PETA;
560		tag = 'P'; pw -= 1;
561	} else if (v < PETA * 1000) {
562		pv = (double)v / PETA;
563		tag = 'P'; pw -= 2;
564	} else if (v < EXA * 10) {
565		pv = (double)v / EXA;
566		tag = 'E';
567	} else if (v < EXA * 100) {
568		pv = (double)v / EXA;
569		tag = 'E'; pw -= 1;
570	} else {
571		pv = (double)v / EXA;
572		tag = 'E'; pw -= 2;
573	}
574	if (pw < 0)
575		pw = 0;
576	if (v < 1000)
577		used = snprintf(str + pos, left, "%*.*f",
578		    ff->pff_width, pw, pv);
579	else
580		used = snprintf(str + pos, left, "%*.*f%c",
581		    ff->pff_width - 1, pw, pv, tag);
582
583	return (used);
584}
585
586/* format usage statistic, if configuration has changed print '-'. */
587static int
588used_stat_f(char *str, int pos, int left, poolstat_field_format_t *ff,
589	char *data)
590{
591	int	pw = 0;
592	double v = *((double *)(void *)(data + ff->pff_offset));
593	int  	used;
594
595	if (pool_sbag->sb_changed & POU_POOL) {
596		used = snprintf(str + pos, left, "%*c", ff->pff_width, '-');
597	} else {
598		if (v < 10) {
599			pw = ff->pff_width - 2;
600		} else if (v < 100) {
601			pw = ff->pff_width - 3;
602		} else if (v < 1000) {
603			pw = ff->pff_width - 4;
604		}
605		if (pw < 0)
606			pw = 0;
607		used = snprintf(str + pos, left, "%*.*f",
608		    ff->pff_width, pw, v);
609	}
610	return (used);
611}
612
613/*
614 * Format one header field and put it into the 'str' buffer.
615 */
616/*ARGSUSED*/
617static int
618header_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data)
619{
620	int  used = 0;
621
622	if (ff->pff_type == STR)
623		/* strings are left justified	*/
624		used = snprintf(str + pos, left, "%-*s",
625		    ff->pff_width, ff->pff_header);
626	else
627		used = snprintf(str + pos, left, "%*s",
628		    ff->pff_width, ff->pff_header);
629	return (used);
630}
631
632/*
633 * Print one statistic line according to the definitions in 'lf'.
634 */
635static void
636prt_stat_line(poolstat_line_format_t *lf)
637{
638	poolstat_list_element_t *le; 	/* list element in the print sequence */
639	char 	*line;
640	int 	pos	= 0;		/* position in the printed line	*/
641	int 	len 	= MAXLINE;	/* the length of the line	*/
642	int	left 	= len;		/* chars left to use in the line */
643
644	line = ZALLOC(len);
645	for (le = lf->plf_prt_seq; le; le = le->ple_next) {
646		int used;
647		poolstat_field_format_t *ff =
648		    (poolstat_field_format_t *)le->ple_obj;
649		/* if the filed is marked to be printed	*/
650		if (ff->pff_prt & PABLE_FIELD) {
651			if (((used = ff->pff_format(line, pos, left, ff,
652			    *ff->pff_data_ptr)) + 1) >= left) {
653				/* if field doesn't fit allocate new space */
654				len += used + MAXLINE;
655				left += used + MAXLINE;
656				line = REALLOC(line, len);
657				if (((used = ff->pff_format(line, pos, left, ff,
658				    *ff->pff_data_ptr)) + 1) >= left)
659					die(gettext(ERR_STATS_FORMAT), line);
660			}
661			left -= used;
662			pos += used;
663			if (le->ple_next != NULL) {
664				/* separate columns with a space */
665				line[pos++] = ' ';
666				left--;
667			}
668		}
669	}
670
671	(void) printf("%s\n", line);
672	FREE(line);
673}
674
675/*
676 * Print a statistics header line for a given resource type.
677 */
678static void
679prt_stat_hd(const char *type)
680{
681	poolstat_line_format_t	*lf;	/* line format	*/
682	poolstat_list_element_t *le; 	/* list element in the print sequence */
683	char 	*line;
684	int 	pos	= 0;		/* position in the printed line	 */
685	int 	len 	= MAXLINE;	/* the length of the line	 */
686	int	left 	= len;		/* chars left to use in the line */
687
688	if (strcmp(type, POOL_TYPE_NAME) == 0) {
689		/* pool format needs an extra header	*/
690		(void) printf("%*s\n", 19 + 15, "pset");
691		lf = &pool_lf;
692	} else if (strcmp(type, PSET_TYPE_NAME) == 0) {
693		lf = &pset_lf;
694	} else {
695		die(gettext(ERR_UNSUPP_RTYPE), type);
696	}
697	line = ZALLOC(len);
698	for (le = lf->plf_prt_seq; le; le = le->ple_next) {
699		int used;	/* used chars in line	*/
700		poolstat_field_format_t *ff =
701		    (poolstat_field_format_t *)le->ple_obj;
702		/* if the filed is marked to be printed	*/
703		if (ff->pff_prt& PABLE_FIELD) {
704			if (((used = header_f(line, pos, left, ff, NULL)) + 1)
705			    >= left) {
706				/* if field doesn't fit allocate new space */
707				len += used + MAXLINE;
708				left += used + MAXLINE;
709				line = REALLOC(line, len);
710				if (((used = header_f(line, pos, left, ff,
711				    NULL)) + 1) >= left)
712					die(gettext(ERR_STATS_FORMAT), line);
713			}
714			left -= used;
715			pos += used;
716			if (le->ple_next != NULL) {
717				/* separate columns with a space */
718				line[pos++] = ' ';
719				left--;
720			}
721		}
722	}
723
724	/* only header line with non space characters should be printed */
725	pos = 0;
726	while (*(line + pos) != '\n') {
727		if (!isspace(*(line + pos))) {
728			(void) printf("%s\n", line);
729
730			break;
731		}
732		pos++;
733	}
734	FREE(line);
735}
736
737/*
738 * Create a pool value instance and set its name to 'name'.
739 */
740static pool_value_t *
741create_pool_value(const char *name)
742{
743	pool_value_t *pval;
744
745	if ((pval = pool_value_alloc()) == NULL) {
746		return (NULL);
747	}
748	if (pool_value_set_name(pval, name) != PO_SUCCESS) {
749		pool_value_free(pval);
750		return (NULL);
751	}
752
753	return (pval);
754}
755
756/*
757 * Find all resources of type 'rtype'.
758 * If 'pool_name' is defined find all resources bound to this pool.
759 */
760static pool_resource_t **
761get_resources(const char *pool_name, const char *rtype, uint_t *nelem)
762{
763	pool_resource_t **resources = NULL;
764	pool_value_t 	*pvals[] = { NULL, NULL, NULL};
765	pool_value_t 	*pv_sys_id;
766	pool_value_t 	*pv_name;
767	char		*name_prop; /* set name property	*/
768
769	if (strcmp(rtype, PSET_TYPE_NAME) == 0) {
770		if ((pv_sys_id = create_pool_value(PSET_SYSID)) == NULL)
771			goto on_error;
772		name_prop = PSET_NAME;
773	} else {
774		die(gettext(ERR_UNSUPP_RTYPE), rtype);
775	}
776
777	if ((pvals[0] = create_pool_value("type")) == NULL)
778		goto on_error;
779	if ((pool_value_set_string(pvals[0], rtype)) == -1)
780		goto on_error;
781
782	if ((pv_name = create_pool_value(name_prop)) == NULL)
783		goto on_error;
784
785	if (pool_name != NULL) {
786		/* collect resources associated to 'pool_name'	*/
787		pool_t 	*pool;
788		if ((pool = pool_get_pool(conf, pool_name)) == NULL)
789			die(gettext(ERR_STATS_POOL_N), pool_name);
790		if ((resources = pool_query_pool_resources(
791		    conf, pool, nelem, pvals)) == NULL)
792			goto on_error;
793	} else {
794		/* collect all resources  */
795		if ((resources =
796		    pool_query_resources(conf, nelem, pvals)) == NULL)
797			goto on_error;
798	}
799
800	if (pv_name != NULL)
801		pool_value_free(pv_name);
802	if (pv_sys_id != NULL)
803		pool_value_free(pv_sys_id);
804	if (pvals[0] != NULL)
805		pool_value_free(pvals[0]);
806
807	return (resources);
808on_error:
809	die(gettext(ERR_STATS_RES), get_errstr());
810	/*NOTREACHED*/
811}
812
813/*
814 * Print statistics for all resources of type 'rtype' passed in 'resources'.
815 */
816static void
817prt_resource_stats_by_type(pool_resource_t **resources, const char *rtype)
818{
819	int		i;
820	pool_elem_t	*elem;
821	pool_value_t 	*pv_name;
822	char		*name_prop;
823
824	poolstat_line_format_t	*lf;
825	statistic_bag_t		*sbag;
826
827	if (strcmp(rtype, PSET_TYPE_NAME) == 0) {
828		name_prop = PSET_NAME;
829		lf = &pset_lf;
830		sbag = pset_sbag;
831	} else {
832		die(gettext(ERR_UNSUPP_RTYPE), rtype);
833	}
834
835	if ((pv_name = create_pool_value(name_prop)) == NULL)
836		goto on_error;
837
838	/* collect and print statistics for the given resources	*/
839	for (i = 0; resources[i] != NULL; i++) {
840		if ((elem = pool_resource_to_elem(conf, resources[i])) == NULL)
841			goto on_error;
842		if (pool_get_property(conf, elem, name_prop, pv_name) == -1)
843			goto on_error;
844		if (pool_value_get_string(pv_name, &sbag->sb_name) == -1)
845			goto on_error;
846		sa_update(sbag, 0);
847
848		prt_stat_line(lf);
849	}
850
851	if (pv_name != NULL)
852		pool_value_free(pv_name);
853	return;
854on_error:
855	die(gettext(ERR_STATS_RES), get_errstr());
856}
857
858/*
859 * Update statistics for all resources of type 'rtype' pased in 'resources'.
860 */
861static void
862update_resource_stats(pool_resource_t *resource, const char *rtype)
863{
864	pool_elem_t	*elem;
865	pool_value_t 	*pv_name;
866	char		*name_prop; 		/* set name property	*/
867
868	statistic_bag_t	*sbag;
869
870	if (strcmp(rtype, PSET_TYPE_NAME) == 0) {
871		name_prop = PSET_NAME;
872		sbag 	= pset_sbag;
873	} else {
874		die(gettext(ERR_UNSUPP_RTYPE), rtype);
875	}
876
877	if ((pv_name = create_pool_value(name_prop)) == NULL)
878		goto on_error;
879
880	if ((elem = pool_resource_to_elem(conf, resource)) == NULL)
881		goto on_error;
882	if (pool_get_property(conf, elem, name_prop, pv_name) == -1)
883		goto on_error;
884	if (pool_value_get_string(pv_name, &sbag->sb_name) == -1)
885		goto on_error;
886	sa_update(sbag, 0);
887
888	if (pv_name != NULL)
889		pool_value_free(pv_name);
890	return;
891
892on_error:
893	die(gettext(ERR_STATS_RES), get_errstr());
894}
895
896/*
897 * For each pool in the configuration print statistics of associated resources.
898 * If the pool name list 'pn' is defined, only print resources of pools
899 * specified in the list. The list can specify the pool name or its system id.
900 */
901static void
902prt_pool_stats(poolstat_list_element_t *pn)
903{
904	uint_t 		nelem;
905	pool_elem_t	*elem;
906	int		i;
907	int 		error;
908	pool_t 		**pools = NULL;
909	pool_value_t 	*pvals[] = { NULL, NULL };
910	pool_value_t 	*pv_name = NULL;
911	pool_value_t 	*pv_sys_id = NULL;
912	statistic_bag_t	*sbag = pool_sbag;
913	poolstat_list_element_t 	*rtype;
914	pool_resource_t **resources;
915
916	if ((pv_sys_id = create_pool_value(POOL_SYSID)) == NULL)
917		goto on_error;
918	if ((pv_name = create_pool_value(POOL_NAME)) == NULL)
919		goto on_error;
920
921	if (pn == NULL) {
922		/* collect all pools	*/
923		if ((pools = pool_query_pools(conf, &nelem, NULL)) == NULL)
924			goto on_error;
925	} else {
926		/*
927		 * collect pools specified in the 'pn' list.
928		 * 'poolid' the pool identifier can be a pool name or sys_id.
929		 */
930		poolstat_list_element_t	*poolid;
931		for (poolid = pn, i = 1; poolid; poolid = poolid->ple_next)
932			i++;
933		pools = ZALLOC(sizeof (pool_t *) * (i + 1));
934		for (poolid = pn, i = 0; poolid;
935		    poolid = poolid->ple_next, i++) {
936			pool_t **pool;
937			int64_t sysid = Atoi(poolid->ple_obj, &error);
938			if (error == 0) {
939				/* the pool is identified by sys_id	*/
940				pool_value_set_int64(pv_sys_id, sysid);
941				pvals[0] = pv_sys_id;
942				pool = pool_query_pools(conf, &nelem, pvals);
943			} else {
944				if (pool_value_set_string(pv_name,
945				    poolid->ple_obj) == -1)
946					die(gettext(ERR_NOMEM));
947				pvals[0] = pv_name;
948				pool = pool_query_pools(conf, &nelem, pvals);
949			}
950			if (pool == NULL)
951				die(gettext(ERR_STATS_POOL_N), poolid->ple_obj);
952			pools[i] = pool[0];
953			FREE(pool);
954		}
955	}
956
957	/* print statistic for all pools found		*/
958	if (!rflag) {
959		/* print the common resource header 	*/
960		prt_stat_hd(POOL_TYPE_NAME);
961
962		/* print statistics for the resources bound to the pools */
963		for (i = 0; pools[i] != NULL; i++) {
964			elem = pool_to_elem(conf, pools[i]);
965			if (pool_get_property(conf, elem, POOL_NAME, pv_name)
966			    == -1)
967				goto on_error;
968			if (pool_value_get_string(pv_name, &sbag->sb_name) != 0)
969				goto on_error;
970			if (pool_get_property(
971			    conf, elem, "pool.sys_id", pv_sys_id) == -1)
972				goto on_error;
973			if (pool_value_get_int64(
974			    pv_sys_id, &sbag->sb_sysid) != 0)
975				goto on_error;
976
977			for (rtype = rtypes; rtype; rtype = rtype->ple_next) {
978				resources = get_resources(
979				    sbag->sb_name, rtype->ple_obj, &nelem);
980				update_resource_stats(*resources,
981				    rtype->ple_obj);
982				FREE(resources);
983			}
984			prt_stat_line(&pool_lf);
985		}
986	} else {
987		/* print statistic for all resource types defined in rtypes */
988		for (rtype = rtypes; rtype; rtype = rtype->ple_next) {
989			prt_stat_hd(rtype->ple_obj);
990			for (i = 0; pools[i] != NULL; i++) {
991				elem = pool_to_elem(conf, pools[i]);
992				if (pool_get_property(
993				    conf, elem, POOL_NAME, pv_name) == -1)
994					goto on_error;
995				if (pool_value_get_string(
996				    pv_name, &sbag->sb_name) != 0)
997					goto on_error;
998				if (pool_get_property(
999				    conf, elem, POOL_SYSID, pv_sys_id) == -1)
1000					goto on_error;
1001				if (pool_value_get_int64(
1002				    pv_sys_id, &sbag->sb_sysid) != 0)
1003					goto on_error;
1004				resources = get_resources(
1005				    sbag->sb_name, rtype->ple_obj, &nelem);
1006				if (resources == NULL)
1007					continue;
1008				update_resource_stats(
1009				    *resources, rtype->ple_obj);
1010				prt_resource_stats_by_type(resources,
1011				    rtype->ple_obj);
1012				FREE(resources);
1013			}
1014		}
1015	}
1016
1017	FREE(pools);
1018	if (pv_name != NULL)
1019		pool_value_free(pv_name);
1020	if (pv_sys_id != NULL)
1021		pool_value_free(pv_sys_id);
1022
1023	return;
1024on_error:
1025	die(gettext(ERR_STATS_POOL), get_errstr());
1026}
1027