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