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 }