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