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