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