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 }