xref: /illumos-gate/usr/src/cmd/pools/poolstat/poolstat.c (revision b8767451d156f585534afac0bf22721810d0dc63)
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 
56 typedef int (* formatter) (char *, int, int, poolstat_field_format_t *, char *);
57 
58 static uint_t timestamp_fmt = NODATE;
59 
60 /* available field formatters	*/
61 static int default_f(char *, int, int, poolstat_field_format_t *, char *);
62 static int bigno_f(char *, int, int, poolstat_field_format_t *, char *);
63 static int used_stat_f(char *, int, int, poolstat_field_format_t *, char *);
64 static int header_f(char *, int, int, poolstat_field_format_t *, char *);
65 
66 /* statistics bags used to collect data from various provider	*/
67 static statistic_bag_t 	pool_sbag_s;
68 static statistic_bag_t 	pset_sbag_s;
69 static statistic_bag_t 	*pool_sbag = &pool_sbag_s;
70 static statistic_bag_t 	*pset_sbag = &pset_sbag_s;
71 
72 /* formatter objects for pset, defined in a default printing sequence	*/
73 static 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	*/
108 static 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 */
128 static poolstat_line_format_t   pool_lf; /* formatting list in default mode */
129 static poolstat_line_format_t   pset_lf; /* formatting list for psets    */
130 
131 /* name of pools to be shown */
132 static 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  */
137 static poolstat_list_element_t   *rtypes;
138 
139 /* a handle to the pool configuration	*/
140 static pool_conf_t *conf;
141 
142 /* option flags		*/
143 static int 	rflag;
144 static int 	pflag;
145 static int 	oflag;
146 
147 /* operands	*/
148 static int 	interval = 0;	/* update interval	*/
149 static long 	count    = 1; 	/* one run		*/
150 
151 /* data structure handlers	*/
152 static poolstat_list_element_t *
153 	create_prt_sequence_list(char *, poolstat_line_format_t *);
154 static poolstat_list_element_t *
155 	create_args_list(char *, poolstat_list_element_t *, const char *);
156 
157 /* statistics update function	*/
158 static void sa_update(statistic_bag_t *, int);
159 
160 /* statistics printing function	*/
161 static void prt_pool_stats(poolstat_list_element_t *);
162 
163 static void usage(void) __NORETURN;
164 
165 static void
166 usage(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 
179 static int
180 Atoi(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 
193 int
194 main(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  */
325 static poolstat_list_element_t  *
326 create_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  */
358 static poolstat_list_element_t *
359 create_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	*/
427 static void
428 sa_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  */
438 static int
439 default_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 */
500 static int
501 bigno_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 '-'. */
587 static int
588 used_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*/
617 static int
618 header_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  */
635 static void
636 prt_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  */
678 static void
679 prt_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  */
740 static pool_value_t *
741 create_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  */
760 static pool_resource_t **
761 get_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);
808 on_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  */
816 static void
817 prt_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;
854 on_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  */
861 static void
862 update_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 
892 on_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  */
901 static void
902 prt_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;
1024 on_error:
1025 	die(gettext(ERR_STATS_POOL), get_errstr());
1026 }
1027