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