text: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the
   * Common Development and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   * or http://www.opensolaris.org/os/licensing.
   * See the License for the specific language governing permissions
   * and limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each
   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   * If applicable, add the following below this CDDL HEADER, with the
   * fields enclosed by brackets "[]" replaced with your own identifying
   * information: Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   */
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  /*
   * poolstat - report active pool statistics
   */
  #include <stdio.h>
  #include <unistd.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <locale.h>
  #include <string.h>
  #include <ctype.h>
  #include <limits.h>
  #include <errno.h>
  #include <stddef.h>
  
  #include <pool.h>
  #include "utils.h"
  #include "poolstat.h"
  #include "poolstat_utils.h"
  #include "statcommon.h"
  
  #ifndef	TEXT_DOMAIN
  #define	TEXT_DOMAIN	"SYS_TEST"
  #endif
  
  #define	addrof(s)  ((char **)&(s))
  
  /* verify if a field is printable in respect of the current option flags */
  #define	PRINTABLE(i)	((lf->plf_ffs[(i)].pff_prt & D_FIELD) || \
  	(lf->plf_ffs[(i)].pff_prt & X_FIELD))
  
  typedef int (* formatter) (char *, int, int, poolstat_field_format_t *, char *);
  
  static uint_t timestamp_fmt = NODATE;
  
  /* available field formatters	*/
  static int default_f(char *, int, int, poolstat_field_format_t *, char *);
  static int bigno_f(char *, int, int, poolstat_field_format_t *, char *);
  static int used_stat_f(char *, int, int, poolstat_field_format_t *, char *);
  static int header_f(char *, int, int, poolstat_field_format_t *, char *);
  
  /* statistics bags used to collect data from various provider	*/
  static statistic_bag_t 	pool_sbag_s;
  static statistic_bag_t 	pset_sbag_s;
  static statistic_bag_t 	*pool_sbag = &pool_sbag_s;
  static statistic_bag_t 	*pset_sbag = &pset_sbag_s;
  
  /* formatter objects for pset, defined in a default printing sequence	*/
  static poolstat_field_format_t pset_ffs[] = {
  	/* prt flags,name,header,type,width,minwidth,offset,formatter	*/
  	{ DX_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag),
  		offsetof(statistic_bag_t, sb_sysid),
  		(formatter)default_f },
  	{ DX_FIELD, "pool", "pool", STR, 20, 14, addrof(pool_sbag),
  		offsetof(statistic_bag_t, sb_name),
  		(formatter)default_f },
  	{ DX_FIELD, "type", "type", STR, 4, 5, addrof(pset_sbag),
  		offsetof(statistic_bag_t, sb_type),
  		(formatter)default_f },
  	{ D_FIELD, "rid", "rid", LL, 3, 1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_sysid),
  		(formatter)default_f },
  	{ DX_FIELD, "rset", "rset", STR, 20, 14, addrof(pset_sbag),
  		offsetof(statistic_bag_t, sb_name),
  		(formatter)default_f },
  	{ DX_FIELD, "min", "min", ULL, 4, 1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_min),
  		(formatter)bigno_f },
  	{ DX_FIELD, "max", "max", ULL, 4, 1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_max),
  		(formatter)bigno_f },
  	{ DX_FIELD, "size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_size),
  		(formatter)default_f },
  	{ DX_FIELD, "used", "used", FL, 4, -1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_used),
  		(formatter)used_stat_f },
  	{ DX_FIELD, "load", "load", FL, 4, -1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_load),
  		(formatter)default_f }
  };
  
  /* formatter objects for pool, defined in a default printing sequence	*/
  static poolstat_field_format_t pool_ffs[] = {
  	/* prt flags,name,header,type,width,minwidth,offset,formatter	*/
  	{ D_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag),
  		offsetof(statistic_bag_t, sb_sysid),
  		(formatter)default_f },
  	{ D_FIELD, "pool", "pool", STR, 20, 13, addrof(pool_sbag),
  		offsetof(statistic_bag_t, sb_name),
  		(formatter)default_f },
  	{ D_FIELD, "p_size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_size),
  		(formatter)default_f },
  	{ D_FIELD, "p_used", "used", FL, 4, -1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_used),
  		(formatter)default_f },
  	{ D_FIELD, "p_load", "load", FL, 4, -1, addrof(pset_sbag_s.bag),
  		offsetof(pset_statistic_bag_t, pset_sb_load),
  		(formatter)default_f },
  };
  
  /* lists with formatter objects, one for each statistics field */
  static poolstat_line_format_t   pool_lf; /* formatting list in default mode */
  static poolstat_line_format_t   pset_lf; /* formatting list for psets    */
  
  /* name of pools to be shown */
  static poolstat_list_element_t	*pnames;
  /*
   * type of resources to be shown, currently we only have one type 'pset'
   * but, poolstat can be extended to handle new upcoming resource types.
   */
  static poolstat_list_element_t   *rtypes;
  
  /* a handle to the pool configuration	*/
  static pool_conf_t *conf;
  
  /* option flags		*/
  static int 	rflag;
  static int 	pflag;
  static int 	oflag;
  
  /* operands	*/
  static int 	interval = 0;	/* update interval	*/
  static long 	count    = 1; 	/* one run		*/
  
  /* data structure handlers	*/
  static poolstat_list_element_t *
  	create_prt_sequence_list(char *, poolstat_line_format_t *);
  static poolstat_list_element_t *
  	create_args_list(char *, poolstat_list_element_t *, const char *);
  
  /* statistics update function	*/
  static void sa_update(statistic_bag_t *, int);
  
  /* statistics printing function	*/
  static void prt_pool_stats(poolstat_list_element_t *);
  
  static void usage(void) __NORETURN;
  
  static void
  usage(void)
  {
  	(void) fprintf(stderr, gettext(
  "Usage:\n"
  "poolstat [-p pool-list] [-r rset-list] [-T d|u] [interval [count]]\n"
  "poolstat [-p pool-list] [-o format -r rset-list] [-T d|u] [interval [count]]\n"
  "  \'pool-list\' is a space-separated list of pool IDs or names\n"
  "  \'rset-list\' is \'all\' or \'pset\'\n"
  "  \'format\' for all resource types is one or more of:\n"
  "\tid pool type rid rset min max size used load\n"));
  	(void) exit(E_USAGE);
  }
  
  static int
  Atoi(char *p, int *errp)
  {
  	int i;
  	char *q;
  	errno = 0;
  	i = strtol(p, &q, 10);
  	if (errno != 0 || q == p || *q != '\0')
  		*errp = -1;
  	else
  		*errp = 0;
  	return (i);
  }
  
  int
  main(int argc, char *argv[])
  {
  	char		c;
  	int 		error = 0;
  
  	(void) getpname(argv[0]);
  	(void) setlocale(LC_ALL, "");
  	(void) textdomain(TEXT_DOMAIN);
  
  	/* pset_sbag_s is used to collect pset statistics   */
  	pset_sbag_s.sb_type = PSET_TYPE_NAME;
  	pset_sbag_s.bag	= ZALLOC(sizeof (pset_statistic_bag_t));
  	pool_sbag_s.sb_type = POOL_TYPE_NAME;
  
  	pset_lf.plf_ffs = pset_ffs;
  	pset_lf.plf_ff_len = sizeof (pset_ffs) /
  	    sizeof (poolstat_field_format_t);
  	pool_lf.plf_ffs = pool_ffs;
  	pool_lf.plf_ff_len = sizeof (pool_ffs) /
  	    sizeof (poolstat_field_format_t);
  
  	/* Don't let buffering interfere with piped output. */
  	(void) setvbuf(stdout, NULL, _IOLBF, 0);
  
  	while ((c = getopt(argc, argv, ":p:r:o:T:")) != EOF) {
  		switch (c) {
  		case 'p':	/* pool name specification	*/
  			pflag++;
  			pnames = create_args_list(optarg, pnames,
  			    " \t");
  			break;
  		case 'r': {	/* resource type 		*/
  			rflag++;
  			rtypes = create_args_list(optarg, rtypes,
  			    " \t,");
  			break;
  			}
  		case 'o': { 	/* format specification		*/
  			oflag++;
  			if (create_prt_sequence_list(optarg, &pset_lf) == NULL)
  				usage();
  			break;
  			}
  		case 'T':
  			if (optarg) {
  				if (*optarg == 'u')
  					timestamp_fmt = UDATE;
  				else if (*optarg == 'd')
  					timestamp_fmt = DDATE;
  				else
  					usage();
  			} else {
  					usage();
  			}
  			break;
  		case ':': {
  			(void) fprintf(stderr,
  			    gettext(ERR_OPTION_ARGS), optopt);
  			usage();
  			/*NOTREACHED*/
  			}
  		default:
  			(void) fprintf(stderr, gettext(ERR_OPTION), optopt);
  			usage();
  			/*NOTREACHED*/
  		}
  	}
  
  	/* get operands	*/
  	if (argc > optind) {
  		if ((interval = Atoi(argv[optind++], &error)) < 1 || error != 0)
  			usage();
  		count = -1;
  	}
  	if (argc > optind) {
  		if ((count = Atoi(argv[optind++], &error)) < 1 || error != 0)
  			usage();
  	}
  	/* check for extra options/operands	*/
  	if (argc > optind)
  		usage();
  
  	/* check options	*/
  	if (oflag && !rflag)
  		usage();
  
  	/* global initializations	*/
  	if (!oflag) {
  		/* create the default print sequences	*/
  		(void) create_prt_sequence_list(NULL, &pool_lf);
  		(void) create_prt_sequence_list(NULL, &pset_lf);
  	}
  
  	if (rtypes == NULL || strcmp(rtypes->ple_obj, "all") == 0) {
  		/* crate a default resource list	*/
  		FREE(rtypes);
  		rtypes = create_args_list("pset", NULL, " \t,");
  	}
  
  	if ((conf = pool_conf_alloc()) == NULL)
  		die(gettext(ERR_NOMEM));
  	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY)
  	    != PO_SUCCESS)
  		die(gettext(ERR_OPEN_DYNAMIC), get_errstr());
  
  	/* initialize statistic adapters	*/
  	sa_libpool_init(conf);
  	sa_kstat_init(NULL);
  
  	/* collect and print out statistics	*/
  	while (count-- != 0) {
  		sa_update(pool_sbag, SA_REFRESH);
  		if (timestamp_fmt != NODATE)
  			print_timestamp(timestamp_fmt);
  		if (pool_sbag->sb_changed & POU_POOL)
  				(void) printf(
  				"<<State change>>\n");
  		prt_pool_stats(pnames);
  		if (count != 0) {
  			(void) sleep(interval);
  			if (rflag)
  				(void) printf("\n");
  		}
  	}
  
  	return (E_PO_SUCCESS);
  }
  
  /*
   * Take the arguments and create/append a string list to the 'le' list.
   */
  static poolstat_list_element_t  *
  create_args_list(char *arg, poolstat_list_element_t  *le, const char *delim)
  {
  	poolstat_list_element_t *head = le;
  
  	while (arg != NULL && *arg != '\0') {
  		char *name = arg;
  		arg = strpbrk(arg, delim);
  		if (arg != NULL) {
  			*arg++ = '\0';
  		}
  		if (le == NULL) {
  			/* create first element */
  			NEW0(le);
  			head = le;
  		} else {
  			/* find last and append	*/
  			while (le->ple_next != NULL)
  				le = le->ple_next;
  			NEW0(le->ple_next);
  			le = le->ple_next;
  		}
  		le->ple_obj = (void *)name;
  	}
  
  	return (head);
  }
  
  /*
   * Take the arguments to the -o option, and create a format field list in order
   * specified by 'arg'.
   * If 'arg' is NULL a list in a default printing order is created.
   */
  static poolstat_list_element_t *
  create_prt_sequence_list(char *arg, poolstat_line_format_t *lf)
  {
  	/*
  	 * Create a default print sequence. It is the sequence defined
  	 * statically in the format list. At the same time mark the fields
  	 * printable according to the current option settings.
  	 */
  	if (arg == NULL) {
  		int	i;
  		NEW0(lf->plf_prt_seq);
  		lf->plf_ffs[0].pff_prt |= PRINTABLE(0) ? PABLE_FIELD : 0;
  		lf->plf_last = lf->plf_prt_seq;
  		lf->plf_last->ple_obj = &(lf->plf_ffs[0]);
  		for (i = 1; i < lf->plf_ff_len; i++) {
  			lf->plf_ffs[i].pff_prt |=
  			    PRINTABLE(i) ? PABLE_FIELD : 0;
  			NEW0(lf->plf_last->ple_next);
  			lf->plf_last = lf->plf_last->ple_next;
  			lf->plf_last->ple_obj = &(lf->plf_ffs[i]);
  		}
  		return (lf->plf_prt_seq);
  	}
  
  	while (arg != NULL && *arg != '\0') {
  		poolstat_field_format_t *ff;	/* current format field */
  		int 	ffIdx;	/* format field index	    */
  		char 	*name;	/* name of field	    */
  		int	n; 	/* no. of chars to strip    */
  
  		n = strspn(arg, " ,\t\r\v\f\n");
  		arg += n;	/* strip multiples separator	*/
  		name = arg;
  
  		if (strlen(name) < 1)
  			break;
  
  		if ((arg = strpbrk(arg, " ,\t\r\v\f\n")) != NULL)
  			*arg++ = '\0';
  
  		/* search for a named format field */
  		for (ffIdx = 0; ffIdx < lf->plf_ff_len; ffIdx++) {
  			ff = lf->plf_ffs + ffIdx;
  			if (strcmp(ff->pff_name, name) == 0) {
  				ff->pff_prt |= PABLE_FIELD;
  				break;
  			}
  		}
  		/* if the name wasn't found	*/
  		if (ffIdx == lf->plf_ff_len) {
  			(void) fprintf(stderr, gettext(ERR_UNSUPP_STAT_FIELD),
  			    name);
  			usage();
  		}
  		if (lf->plf_last == NULL) {
  			/* create first print handle */
  			NEW0(lf->plf_prt_seq);
  			lf->plf_last = lf->plf_prt_seq;
  		} else {
  			NEW0(lf->plf_last->ple_next);
  			lf->plf_last = lf->plf_last->ple_next;
  		}
  		lf->plf_last->ple_obj = ff; 	/* refer to the format field */
  	}
  
  	return (lf->plf_prt_seq);
  }
  
  /* update the statistic data by adapters	*/
  static void
  sa_update(statistic_bag_t *sbag, int flags)
  {
  	sa_libpool_update(sbag, flags);
  	sa_kstat_update(sbag, flags);
  }
  
  /*
   * Format one statistic field and put it into the 'str' buffer. 'ff' contains
   * the field formatting parameters. Return the number of used bytes.
   */
  static int
  default_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data)
  {
  	int  used;
  
  	switch (ff->pff_type) {
  	case LL: {
  			int64_t v;
  			v = *((int64_t *)(void *)(data + ff->pff_offset));
  			used = snprintf(str + pos, left, "%*.*lld",
  			    ff->pff_width, ff->pff_minwidth, v);
  		}
  		break;
  	case ULL: {
  			uint64_t v;
  			v = *((uint64_t *)(void *)(data + ff->pff_offset));
  			used = snprintf(str + pos, left, "%*.*llu",
  			    ff->pff_width, ff->pff_minwidth, v);
  		};
  		break;
  	case FL: {
  			int	pw = 0;
  			double v = *((double *)(void *)(data + ff->pff_offset));
  			if (v < 10) {
  				pw = ff->pff_width - 2;
  			} else if (v < 100) {
  				pw = ff->pff_width - 3;
  			} else if (v < 1000) {
  				pw = ff->pff_width - 4;
  			}
  			if (pw < 0)
  				pw = 0;
  			used = snprintf(str + pos, left, "%*.*f",
  			    ff->pff_width, pw, v);
  		};
  		break;
  	case STR: {
  			char 	*v;
  			int 	sl;
  			v = *((char **)(void *)(data + ff->pff_offset));
  			sl = strlen(v);
  			/* truncate if it doesn't fit	*/
  			if (sl > ff->pff_width) {
  				char *cp = v +  ff->pff_width - 1;
  				if (ff->pff_width < 4)
  					die(gettext(ERR_STATS_FORMAT),
  					    ff->pff_header);
  				*cp-- = 0;
  				*cp-- = '.';
  				*cp-- = '.';
  				*cp-- = '.';
  			}
  			used = snprintf(str + pos, left, "%-*s", ff->pff_width,
  			    v);
  		}
  		break;
  	}
  
  	return (used);
  }
  
  /* format big numbers */
  static int
  bigno_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data)
  {
  	uint64_t v;
  	char	tag;
  	int	pw = ff->pff_width - 4;
  	double 	pv;
  	int  	used;
  
  	v = *((uint64_t *)(void *)(data + ff->pff_offset));
  	/*
  	 * the max value can be ULONG_MAX, which is formatted as:
  	 * E  P   T   G   M   K
  	 * 18 446 744 073 709 551 615
  	 * As a result ULONG_MAX is displayed as 18E
  	 */
  	pv = v;
  	if (v < 1000) {
  		pw = 0;
  	} else if (v < KILO * 10) {
  		pv = (double)v / KILO;
  		tag = 'K';
  	} else if (v < KILO * 100) {
  		pv = (double)v / KILO;
  		tag = 'K'; pw -= 1;
  	} else if (v < KILO * 1000) {
  		pv = (double)v / KILO;
  		tag = 'K'; pw -= 2;
  	} else if (v < MEGA * 10) {
  		pv = (double)v / MEGA;
  		tag = 'M';
  	} else if (v < MEGA * 100) {
  		pv = (double)v / MEGA;
  		tag = 'M'; pw -= 1;
  	} else if (v < MEGA * 1000) {
  		pv = (double)v / MEGA;
  		tag = 'M'; pw -= 2;
  	} else if (v < GIGA * 10) {
  		pv = (double)v / GIGA;
  		tag = 'G';
  	} else if (v < GIGA * 100) {
  		pv = (double)v / GIGA;
  		tag = 'G'; pw -= 1;
  	} else if (v < GIGA * 1000) {
  		pv = (double)v / GIGA;
  		tag = 'G'; pw -= 2;
  	} else if (v < TERA * 10) {
  		pv = (double)v / TERA;
  		tag = 'T';
  	} else if (v < TERA * 100) {
  		pv = (double)v / TERA;
  		tag = 'T'; pw -= 1;
  	} else if (v < TERA * 1000) {
  		pv = (double)v / TERA;
  		tag = 'T'; pw -= 2;
  	} else if (v < PETA * 10) {
  		pv = (double)v / PETA;
  		tag = 'P';
  	} else if (v < PETA * 100) {
  		pv = (double)v / PETA;
  		tag = 'P'; pw -= 1;
  	} else if (v < PETA * 1000) {
  		pv = (double)v / PETA;
  		tag = 'P'; pw -= 2;
  	} else if (v < EXA * 10) {
  		pv = (double)v / EXA;
  		tag = 'E';
  	} else if (v < EXA * 100) {
  		pv = (double)v / EXA;
  		tag = 'E'; pw -= 1;
  	} else {
  		pv = (double)v / EXA;
  		tag = 'E'; pw -= 2;
  	}
  	if (pw < 0)
  		pw = 0;
  	if (v < 1000)
  		used = snprintf(str + pos, left, "%*.*f",
  		    ff->pff_width, pw, pv);
  	else
  		used = snprintf(str + pos, left, "%*.*f%c",
  		    ff->pff_width - 1, pw, pv, tag);
  
  	return (used);
  }
  
  /* format usage statistic, if configuration has changed print '-'. */
  static int
  used_stat_f(char *str, int pos, int left, poolstat_field_format_t *ff,
  	char *data)
  {
  	int	pw = 0;
  	double v = *((double *)(void *)(data + ff->pff_offset));
  	int  	used;
  
  	if (pool_sbag->sb_changed & POU_POOL) {
  		used = snprintf(str + pos, left, "%*c", ff->pff_width, '-');
  	} else {
  		if (v < 10) {
  			pw = ff->pff_width - 2;
  		} else if (v < 100) {
  			pw = ff->pff_width - 3;
  		} else if (v < 1000) {
  			pw = ff->pff_width - 4;
  		}
  		if (pw < 0)
  			pw = 0;
  		used = snprintf(str + pos, left, "%*.*f",
  		    ff->pff_width, pw, v);
  	}
  	return (used);
  }
  
  /*
   * Format one header field and put it into the 'str' buffer.
   */
  /*ARGSUSED*/
  static int
  header_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data)
  {
  	int  used = 0;
  
  	if (ff->pff_type == STR)
  		/* strings are left justified	*/
  		used = snprintf(str + pos, left, "%-*s",
  		    ff->pff_width, ff->pff_header);
  	else
  		used = snprintf(str + pos, left, "%*s",
  		    ff->pff_width, ff->pff_header);
  	return (used);
  }
  
  /*
   * Print one statistic line according to the definitions in 'lf'.
   */
  static void
  prt_stat_line(poolstat_line_format_t *lf)
  {
  	poolstat_list_element_t *le; 	/* list element in the print sequence */
  	char 	*line;
  	int 	pos	= 0;		/* position in the printed line	*/
  	int 	len 	= MAXLINE;	/* the length of the line	*/
  	int	left 	= len;		/* chars left to use in the line */
  
  	line = ZALLOC(len);
  	for (le = lf->plf_prt_seq; le; le = le->ple_next) {
  		int used;
  		poolstat_field_format_t *ff =
  		    (poolstat_field_format_t *)le->ple_obj;
  		/* if the filed is marked to be printed	*/
  		if (ff->pff_prt & PABLE_FIELD) {
  			if (((used = ff->pff_format(line, pos, left, ff,
  			    *ff->pff_data_ptr)) + 1) >= left) {
  				/* if field doesn't fit allocate new space */
  				len += used + MAXLINE;
  				left += used + MAXLINE;
  				line = REALLOC(line, len);
  				if (((used = ff->pff_format(line, pos, left, ff,
  				    *ff->pff_data_ptr)) + 1) >= left)
  					die(gettext(ERR_STATS_FORMAT), line);
  			}
  			left -= used;
  			pos += used;
  			if (le->ple_next != NULL) {
  				/* separate columns with a space */
  				line[pos++] = ' ';
  				left--;
  			}
  		}
  	}
  
  	(void) printf("%s\n", line);
  	FREE(line);
  }
  
  /*
   * Print a statistics header line for a given resource type.
   */
  static void
  prt_stat_hd(const char *type)
  {
  	poolstat_line_format_t	*lf;	/* line format	*/
  	poolstat_list_element_t *le; 	/* list element in the print sequence */
  	char 	*line;
  	int 	pos	= 0;		/* position in the printed line	 */
  	int 	len 	= MAXLINE;	/* the length of the line	 */
  	int	left 	= len;		/* chars left to use in the line */
  
  	if (strcmp(type, POOL_TYPE_NAME) == 0) {
  		/* pool format needs an extra header	*/
  		(void) printf("%*s\n", 19 + 15, "pset");
  		lf = &pool_lf;
  	} else if (strcmp(type, PSET_TYPE_NAME) == 0) {
  		lf = &pset_lf;
  	} else {
  		die(gettext(ERR_UNSUPP_RTYPE), type);
  	}
  	line = ZALLOC(len);
  	for (le = lf->plf_prt_seq; le; le = le->ple_next) {
  		int used;	/* used chars in line	*/
  		poolstat_field_format_t *ff =
  		    (poolstat_field_format_t *)le->ple_obj;
  		/* if the filed is marked to be printed	*/
  		if (ff->pff_prt& PABLE_FIELD) {
  			if (((used = header_f(line, pos, left, ff, NULL)) + 1)
  			    >= left) {
  				/* if field doesn't fit allocate new space */
  				len += used + MAXLINE;
  				left += used + MAXLINE;
  				line = REALLOC(line, len);
  				if (((used = header_f(line, pos, left, ff,
  				    NULL)) + 1) >= left)
  					die(gettext(ERR_STATS_FORMAT), line);
  			}
  			left -= used;
  			pos += used;
  			if (le->ple_next != NULL) {
  				/* separate columns with a space */
  				line[pos++] = ' ';
  				left--;
  			}
  		}
  	}
  
  	/* only header line with non space characters should be printed */
  	pos = 0;
  	while (*(line + pos) != '\n') {
  		if (!isspace(*(line + pos))) {
  			(void) printf("%s\n", line);
  
  			break;
  		}
  		pos++;
  	}
  	FREE(line);
  }
  
  /*
   * Create a pool value instance and set its name to 'name'.
   */
  static pool_value_t *
  create_pool_value(const char *name)
  {
  	pool_value_t *pval;
  
  	if ((pval = pool_value_alloc()) == NULL) {
  		return (NULL);
  	}
  	if (pool_value_set_name(pval, name) != PO_SUCCESS) {
  		pool_value_free(pval);
  		return (NULL);
  	}
  
  	return (pval);
  }
  
  /*
   * Find all resources of type 'rtype'.
   * If 'pool_name' is defined find all resources bound to this pool.
   */
  static pool_resource_t **
  get_resources(const char *pool_name, const char *rtype, uint_t *nelem)
  {
  	pool_resource_t **resources = NULL;
  	pool_value_t 	*pvals[] = { NULL, NULL, NULL};
  	pool_value_t 	*pv_sys_id;
  	pool_value_t 	*pv_name;
  	char		*name_prop; /* set name property	*/
  
  	if (strcmp(rtype, PSET_TYPE_NAME) == 0) {
  		if ((pv_sys_id = create_pool_value(PSET_SYSID)) == NULL)
  			goto on_error;
  		name_prop = PSET_NAME;
  	} else {
  		die(gettext(ERR_UNSUPP_RTYPE), rtype);
  	}
  
  	if ((pvals[0] = create_pool_value("type")) == NULL)
  		goto on_error;
  	if ((pool_value_set_string(pvals[0], rtype)) == -1)
  		goto on_error;
  
  	if ((pv_name = create_pool_value(name_prop)) == NULL)
  		goto on_error;
  
  	if (pool_name != NULL) {
  		/* collect resources associated to 'pool_name'	*/
  		pool_t 	*pool;
  		if ((pool = pool_get_pool(conf, pool_name)) == NULL)
  			die(gettext(ERR_STATS_POOL_N), pool_name);
  		if ((resources = pool_query_pool_resources(
  		    conf, pool, nelem, pvals)) == NULL)
  			goto on_error;
  	} else {
  		/* collect all resources  */
  		if ((resources =
  		    pool_query_resources(conf, nelem, pvals)) == NULL)
  			goto on_error;
  	}
  
  	if (pv_name != NULL)
  		pool_value_free(pv_name);
  	if (pv_sys_id != NULL)
  		pool_value_free(pv_sys_id);
  	if (pvals[0] != NULL)
  		pool_value_free(pvals[0]);
  
  	return (resources);
  on_error:
  	die(gettext(ERR_STATS_RES), get_errstr());
  	/*NOTREACHED*/
  }
  
  /*
   * Print statistics for all resources of type 'rtype' passed in 'resources'.
   */
  static void
  prt_resource_stats_by_type(pool_resource_t **resources, const char *rtype)
  {
  	int		i;
  	pool_elem_t	*elem;
  	pool_value_t 	*pv_name;
  	char		*name_prop;
  
  	poolstat_line_format_t	*lf;
  	statistic_bag_t		*sbag;
  
  	if (strcmp(rtype, PSET_TYPE_NAME) == 0) {
  		name_prop = PSET_NAME;
  		lf = &pset_lf;
  		sbag = pset_sbag;
  	} else {
  		die(gettext(ERR_UNSUPP_RTYPE), rtype);
  	}
  
  	if ((pv_name = create_pool_value(name_prop)) == NULL)
  		goto on_error;
  
  	/* collect and print statistics for the given resources	*/
  	for (i = 0; resources[i] != NULL; i++) {
  		if ((elem = pool_resource_to_elem(conf, resources[i])) == NULL)
  			goto on_error;
  		if (pool_get_property(conf, elem, name_prop, pv_name) == -1)
  			goto on_error;
  		if (pool_value_get_string(pv_name, &sbag->sb_name) == -1)
  			goto on_error;
  		sa_update(sbag, 0);
  
  		prt_stat_line(lf);
  	}
  
  	if (pv_name != NULL)
  		pool_value_free(pv_name);
  	return;
  on_error:
  	die(gettext(ERR_STATS_RES), get_errstr());
  }
  
  /*
   * Update statistics for all resources of type 'rtype' pased in 'resources'.
   */
  static void
  update_resource_stats(pool_resource_t *resource, const char *rtype)
  {
  	pool_elem_t	*elem;
  	pool_value_t 	*pv_name;
  	char		*name_prop; 		/* set name property	*/
  
  	statistic_bag_t	*sbag;
  
  	if (strcmp(rtype, PSET_TYPE_NAME) == 0) {
  		name_prop = PSET_NAME;
  		sbag 	= pset_sbag;
  	} else {
  		die(gettext(ERR_UNSUPP_RTYPE), rtype);
  	}
  
  	if ((pv_name = create_pool_value(name_prop)) == NULL)
  		goto on_error;
  
  	if ((elem = pool_resource_to_elem(conf, resource)) == NULL)
  		goto on_error;
  	if (pool_get_property(conf, elem, name_prop, pv_name) == -1)
  		goto on_error;
  	if (pool_value_get_string(pv_name, &sbag->sb_name) == -1)
  		goto on_error;
  	sa_update(sbag, 0);
  
  	if (pv_name != NULL)
  		pool_value_free(pv_name);
  	return;
  
  on_error:
  	die(gettext(ERR_STATS_RES), get_errstr());
  }
  
  /*
   * For each pool in the configuration print statistics of associated resources.
   * If the pool name list 'pn' is defined, only print resources of pools
   * specified in the list. The list can specify the pool name or its system id.
   */
  static void
  prt_pool_stats(poolstat_list_element_t *pn)
  {
  	uint_t 		nelem;
  	pool_elem_t	*elem;
  	int		i;
  	int 		error;
  	pool_t 		**pools = NULL;
  	pool_value_t 	*pvals[] = { NULL, NULL };
  	pool_value_t 	*pv_name = NULL;
  	pool_value_t 	*pv_sys_id = NULL;
  	statistic_bag_t	*sbag = pool_sbag;
  	poolstat_list_element_t 	*rtype;
  	pool_resource_t **resources;
  
  	if ((pv_sys_id = create_pool_value(POOL_SYSID)) == NULL)
  		goto on_error;
  	if ((pv_name = create_pool_value(POOL_NAME)) == NULL)
  		goto on_error;
  
  	if (pn == NULL) {
  		/* collect all pools	*/
  		if ((pools = pool_query_pools(conf, &nelem, NULL)) == NULL)
  			goto on_error;
  	} else {
  		/*
  		 * collect pools specified in the 'pn' list.
  		 * 'poolid' the pool identifier can be a pool name or sys_id.
  		 */
  		poolstat_list_element_t	*poolid;
  		for (poolid = pn, i = 1; poolid; poolid = poolid->ple_next)
  			i++;
  		pools = ZALLOC(sizeof (pool_t *) * (i + 1));
  		for (poolid = pn, i = 0; poolid;
  		    poolid = poolid->ple_next, i++) {
  			pool_t **pool;
  			int64_t sysid = Atoi(poolid->ple_obj, &error);
  			if (error == 0) {
  				/* the pool is identified by sys_id	*/
  				pool_value_set_int64(pv_sys_id, sysid);
  				pvals[0] = pv_sys_id;
  				pool = pool_query_pools(conf, &nelem, pvals);
  			} else {
  				if (pool_value_set_string(pv_name,
  				    poolid->ple_obj) == -1)
  					die(gettext(ERR_NOMEM));
  				pvals[0] = pv_name;
  				pool = pool_query_pools(conf, &nelem, pvals);
  			}
  			if (pool == NULL)
  				die(gettext(ERR_STATS_POOL_N), poolid->ple_obj);
  			pools[i] = pool[0];
  			FREE(pool);
  		}
  	}
  
  	/* print statistic for all pools found		*/
  	if (!rflag) {
  		/* print the common resource header 	*/
  		prt_stat_hd(POOL_TYPE_NAME);
  
  		/* print statistics for the resources bound to the pools */
  		for (i = 0; pools[i] != NULL; i++) {
  			elem = pool_to_elem(conf, pools[i]);
  			if (pool_get_property(conf, elem, POOL_NAME, pv_name)
  			    == -1)
  				goto on_error;
  			if (pool_value_get_string(pv_name, &sbag->sb_name) != 0)
  				goto on_error;
  			if (pool_get_property(
  			    conf, elem, "pool.sys_id", pv_sys_id) == -1)
  				goto on_error;
  			if (pool_value_get_int64(
  			    pv_sys_id, &sbag->sb_sysid) != 0)
  				goto on_error;
  
  			for (rtype = rtypes; rtype; rtype = rtype->ple_next) {
  				resources = get_resources(
  				    sbag->sb_name, rtype->ple_obj, &nelem);
  				update_resource_stats(*resources,
  				    rtype->ple_obj);
  				FREE(resources);
  			}
  			prt_stat_line(&pool_lf);
  		}
  	} else {
  		/* print statistic for all resource types defined in rtypes */
  		for (rtype = rtypes; rtype; rtype = rtype->ple_next) {
  			prt_stat_hd(rtype->ple_obj);
  			for (i = 0; pools[i] != NULL; i++) {
  				elem = pool_to_elem(conf, pools[i]);
  				if (pool_get_property(
  				    conf, elem, POOL_NAME, pv_name) == -1)
  					goto on_error;
  				if (pool_value_get_string(
  				    pv_name, &sbag->sb_name) != 0)
  					goto on_error;
  				if (pool_get_property(
  				    conf, elem, POOL_SYSID, pv_sys_id) == -1)
  					goto on_error;
  				if (pool_value_get_int64(
  				    pv_sys_id, &sbag->sb_sysid) != 0)
  					goto on_error;
  				resources = get_resources(
  				    sbag->sb_name, rtype->ple_obj, &nelem);
  				if (resources == NULL)
  					continue;
  				update_resource_stats(
  				    *resources, rtype->ple_obj);
  				prt_resource_stats_by_type(resources,
  				    rtype->ple_obj);
  				FREE(resources);
  			}
  		}
  	}
  
  	FREE(pools);
  	if (pv_name != NULL)
  		pool_value_free(pv_name);
  	if (pv_sys_id != NULL)
  		pool_value_free(pv_sys_id);
  
  	return;
  on_error:
  	die(gettext(ERR_STATS_POOL), get_errstr());
  }
  