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