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