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