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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2012 Milan Jurik. All rights reserved.
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/sysmacros.h>
45 #define ILBST_TIMESTAMP_HEADER 0x01 /* a timestamp w. every header */
46 #define ILBST_DELTA_INTERVAL 0x02 /* delta over specified interval */
47 #define ILBST_ABS_NUMBERS 0x04 /* print absolute numbers, no d's */
48 #define ILBST_ITEMIZE 0x08 /* itemize */
49 #define ILBST_VERBOSE 0x10 /* verbose error info */
51 #define ILBST_OLD_VALUES 0x20 /* for internal processing */
52 #define ILBST_RULES_CHANGED 0x40
55 char is_name
[KSTAT_STRLEN
];
59 static ilbst_stat_t rulestats
[] = {
61 {"bytes_not_processed", 0},
62 {"pkt_not_processed", 0},
65 {"nomem_bytes_dropped", 0},
66 {"nomem_pkt_dropped", 0},
67 {"noport_bytes_dropped", 0},
68 {"noport_pkt_dropped", 0},
69 {"icmp_echo_processed", 0},
71 {"icmp_too_big_processed", 0},
72 {"icmp_too_big_dropped", 0}
75 /* indices into array above, to avoid searching */
76 #define RLSTA_NUM_SRV 0
77 #define RLSTA_BYTES_U 1
79 #define RLSTA_BYTES_D 3
81 #define RLSTA_NOMEMBYTES_D 5
82 #define RLSTA_NOMEMPKT_D 6
83 #define RLSTA_NOPORTBYTES_D 7
84 #define RLSTA_NOPORTPKT_D 8
85 #define RLSTA_ICMP_P 9
86 #define RLSTA_ICMP_D 10
87 #define RLSTA_ICMP2BIG_P 11
88 #define RLSTA_ICMP2BIG_D 12
90 static ilbst_stat_t servstats
[] = {
91 {"bytes_processed", 0},
94 /* indices into array above, to avoid searching */
95 #define SRVST_BYTES_P 0
98 /* values used for of_* commands as id */
100 #define ILBST_BYTES_P 1
101 #define ILBST_PKT_U 2
102 #define ILBST_BYTES_U 3
103 #define ILBST_PKT_D 4
104 #define ILBST_BYTES_D 5
105 #define ILBST_ICMP_P 6
106 #define ILBST_ICMP_D 7
107 #define ILBST_ICMP2BIG_P 8
108 #define ILBST_ICMP2BIG_D 9
109 #define ILBST_NOMEMP_D 10
110 #define ILBST_NOPORTP_D 11
111 #define ILBST_NOMEMB_D 12
112 #define ILBST_NOPORTB_D 13
114 #define ILBST_ITEMIZE_SNAME 97
115 #define ILBST_ITEMIZE_RNAME 98
116 #define ILBST_TIMESTAMP 99
118 /* approx field widths */
119 #define ILBST_PKTCTR_W 8
120 #define ILBST_BYTECTR_W 10
121 #define ILBST_TIME_W 15
123 static boolean_t
of_rule_stats(ofmt_arg_t
*, char *, uint_t
);
124 static boolean_t
of_server_stats(ofmt_arg_t
*, char *, uint_t
);
125 static boolean_t
of_itemize_stats(ofmt_arg_t
*, char *, uint_t
);
126 static boolean_t
of_timestamp(ofmt_arg_t
*, char *, uint_t
);
128 static ofmt_field_t stat_itemize_fields
[] = {
129 {"RULENAME", ILB_NAMESZ
, ILBST_ITEMIZE_RNAME
, of_itemize_stats
},
130 {"SERVERNAME", ILB_NAMESZ
, ILBST_ITEMIZE_SNAME
, of_itemize_stats
},
131 {"PKT_P", ILBST_PKTCTR_W
, ILBST_PKT_P
, of_itemize_stats
},
132 {"BYTES_P", ILBST_BYTECTR_W
, ILBST_BYTES_P
, of_itemize_stats
},
133 {"TIME", ILBST_TIME_W
, ILBST_TIMESTAMP
, of_timestamp
},
136 static ofmt_field_t stat_stdfields
[] = {
137 {"PKT_P", ILBST_PKTCTR_W
, ILBST_PKT_P
, of_server_stats
},
138 {"BYTES_P", ILBST_BYTECTR_W
, ILBST_BYTES_P
, of_server_stats
},
139 {"PKT_U", ILBST_PKTCTR_W
, ILBST_PKT_U
, of_rule_stats
},
140 {"BYTES_U", ILBST_BYTECTR_W
, ILBST_BYTES_U
, of_rule_stats
},
141 {"PKT_D", ILBST_PKTCTR_W
, ILBST_PKT_D
, of_rule_stats
},
142 {"BYTES_D", ILBST_BYTECTR_W
, ILBST_BYTES_D
, of_rule_stats
},
143 {"ICMP_P", ILBST_PKTCTR_W
, ILBST_ICMP_P
, of_rule_stats
},
144 {"ICMP_D", ILBST_PKTCTR_W
, ILBST_ICMP_D
, of_rule_stats
},
145 {"ICMP2BIG_P", 11, ILBST_ICMP2BIG_P
, of_rule_stats
},
146 {"ICMP2BIG_D", 11, ILBST_ICMP2BIG_D
, of_rule_stats
},
147 {"NOMEMP_D", ILBST_PKTCTR_W
, ILBST_NOMEMP_D
, of_rule_stats
},
148 {"NOPORTP_D", ILBST_PKTCTR_W
, ILBST_NOPORTP_D
, of_rule_stats
},
149 {"NOMEMB_D", ILBST_PKTCTR_W
, ILBST_NOMEMB_D
, of_rule_stats
},
150 {"NOPORTB_D", ILBST_PKTCTR_W
, ILBST_NOPORTB_D
, of_rule_stats
},
151 {"TIME", ILBST_TIME_W
, ILBST_TIMESTAMP
, of_timestamp
},
155 static char stat_stdhdrs
[] = "PKT_P,BYTES_P,PKT_U,BYTES_U,PKT_D,BYTES_D";
156 static char stat_stdv_hdrs
[] = "PKT_P,BYTES_P,PKT_U,BYTES_U,PKT_D,BYTES_D,"
157 "ICMP_P,ICMP_D,ICMP2BIG_P,ICMP2BIG_D,NOMEMP_D,NOPORTP_D";
158 static char stat_itemize_rule_hdrs
[] = "SERVERNAME,PKT_P,BYTES_P";
159 static char stat_itemize_server_hdrs
[] = "RULENAME,PKT_P,BYTES_P";
161 #define RSTAT_SZ (sizeof (rulestats)/sizeof (rulestats[0]))
162 #define SSTAT_SZ (sizeof (servstats)/sizeof (servstats[0]))
165 char isd_servername
[KSTAT_STRLEN
]; /* serverID */
166 ilbst_stat_t isd_serverstats
[SSTAT_SZ
];
167 hrtime_t isd_crtime
; /* save for comparison purpose */
171 * this data structure stores statistics for a rule - both an old set
172 * and a current/new set. we use pointers to the actual stores and switch
173 * the pointers for every round. old_is_old in ilbst_arg_t indicates
174 * which pointer points to the "old" data struct (ie, if true, _o pointer
178 char ird_rulename
[KSTAT_STRLEN
];
180 int ird_num_servers_o
;
182 hrtime_t ird_crtime
; /* save for comparison */
183 hrtime_t ird_crtime_o
; /* save for comparison */
184 ilbst_srv_desc_t
*ird_srvlist
;
185 ilbst_srv_desc_t
*ird_srvlist_o
;
186 ilbst_stat_t ird_rstats
[RSTAT_SZ
];
187 ilbst_stat_t ird_rstats_o
[RSTAT_SZ
];
188 ilbst_stat_t
*ird_rulestats
;
189 ilbst_stat_t
*ird_rulestats_o
;
193 * overall "container" for information pertaining to statistics, and
194 * how to display them.
198 /* fields representing user input */
199 char *ilbst_rulename
; /* optional */
200 char *ilbst_server
; /* optional */
203 /* "internal" fields for data and data presentation */
204 ofmt_handle_t ilbst_oh
;
205 boolean_t ilbst_old_is_old
;
206 ilbst_rule_desc_t
*ilbst_rlist
;
207 int ilbst_rcount
; /* current list count */
208 int ilbst_rcount_prev
; /* prev (different) count */
209 int ilbst_rlist_sz
; /* number of alloc'ed rules */
210 int ilbst_rule_index
; /* for itemizes display */
215 of_timestamp(ofmt_arg_t
*of_arg
, char *buf
, uint_t bufsize
)
221 now_tm
= localtime(&now
);
223 (void) strftime(buf
, bufsize
, "%F:%H.%M.%S", now_tm
);
228 i_sum_per_rule_processed(ilbst_rule_desc_t
*rp
, uint64_t *resp
, int index
,
232 ilbst_srv_desc_t
*srv
, *o_srv
, *n_srv
;
234 boolean_t valid
= B_TRUE
;
235 boolean_t old
= flags
& ILBST_OLD_VALUES
;
236 boolean_t check_valid
;
238 /* if we do abs. numbers, we never look at the _o fields */
239 assert((old
&& (flags
& ILBST_ABS_NUMBERS
)) == B_FALSE
);
241 /* we only check for validity under certain conditions */
242 check_valid
= !(old
|| (flags
& ILBST_ABS_NUMBERS
));
244 if (check_valid
&& rp
->ird_num_servers
!= rp
->ird_num_servers_o
)
247 num_servers
= old
? rp
->ird_num_servers_o
: rp
->ird_num_servers
;
249 for (i
= 0; i
< num_servers
; i
++) {
250 n_srv
= &rp
->ird_srvlist
[i
];
251 o_srv
= &rp
->ird_srvlist_o
[i
];
258 res
+= srv
->isd_serverstats
[index
].is_value
;
260 * if creation times don't match, comparison is wrong; if
261 * if we already know something is invalid, we don't
262 * need to compare again.
264 if (check_valid
&& valid
== B_TRUE
&&
265 o_srv
->isd_crtime
!= n_srv
->isd_crtime
) {
271 * save the result even though it may be imprecise - let the
272 * caller decide what to do
279 typedef boolean_t (*sumfunc_t
)(ilbst_rule_desc_t
*, uint64_t *, int);
282 i_sum_per_rule_pkt_p(ilbst_rule_desc_t
*rp
, uint64_t *resp
, int flags
)
284 return (i_sum_per_rule_processed(rp
, resp
, SRVST_PKT_P
, flags
));
288 i_sum_per_rule_bytes_p(ilbst_rule_desc_t
*rp
, uint64_t *resp
, int flags
)
290 return (i_sum_per_rule_processed(rp
, resp
, SRVST_BYTES_P
, flags
));
294 of_server_stats(ofmt_arg_t
*of_arg
, char *buf
, uint_t bufsize
)
296 ilbst_arg_t
*sta
= (ilbst_arg_t
*)of_arg
->ofmt_cbarg
;
297 uint64_t count
= 0, val
;
299 boolean_t valid
= B_TRUE
;
302 switch (of_arg
->ofmt_id
) {
303 case ILBST_PKT_P
: sumfunc
= i_sum_per_rule_pkt_p
;
305 case ILBST_BYTES_P
: sumfunc
= i_sum_per_rule_bytes_p
;
309 for (i
= 0; i
< sta
->ilbst_rcount
; i
++) {
310 valid
= sumfunc(&sta
->ilbst_rlist
[i
], &val
, sta
->ilbst_flags
);
316 if ((sta
->ilbst_flags
& ILBST_ABS_NUMBERS
) != 0)
319 for (i
= 0; i
< sta
->ilbst_rcount
; i
++) {
320 (void) sumfunc(&sta
->ilbst_rlist
[i
], &val
,
321 sta
->ilbst_flags
| ILBST_OLD_VALUES
);
327 * normally, we print "change per second", which we calculate
328 * here. otherwise, we print "change over interval"
330 if ((sta
->ilbst_flags
& (ILBST_DELTA_INTERVAL
|ILBST_ABS_NUMBERS
)) == 0)
331 count
/= sta
->ilbst_interval
;
333 (void) snprintf(buf
, bufsize
, "%llu", count
);
338 * this function is called when user wants itemized stats of every
339 * server for a named rule, or vice vera.
340 * i_do_print sets sta->rule_index and the proper ird_srv_ind so
341 * we don't have to differentiate between these two cases here.
344 of_itemize_stats(ofmt_arg_t
*of_arg
, char *buf
, uint_t bufsize
)
346 ilbst_arg_t
*sta
= (ilbst_arg_t
*)of_arg
->ofmt_cbarg
;
349 int rule_index
= sta
->ilbst_rule_index
;
350 int srv_ind
= sta
->ilbst_rlist
[rule_index
].ird_srv_ind
;
351 boolean_t ret
= B_TRUE
;
352 ilbst_srv_desc_t
*srv
, *osrv
;
354 srv
= &sta
->ilbst_rlist
[rule_index
].ird_srvlist
[srv_ind
];
356 switch (of_arg
->ofmt_id
) {
357 case ILBST_PKT_P
: stat_ind
= SRVST_PKT_P
;
359 case ILBST_BYTES_P
: stat_ind
= SRVST_BYTES_P
;
361 case ILBST_ITEMIZE_RNAME
:
362 (void) snprintf(buf
, bufsize
, "%s",
363 sta
->ilbst_rlist
[rule_index
].ird_rulename
);
365 case ILBST_ITEMIZE_SNAME
:
366 (void) snprintf(buf
, bufsize
, "%s", srv
->isd_servername
);
370 count
= srv
->isd_serverstats
[stat_ind
].is_value
;
372 if ((sta
->ilbst_flags
& ILBST_ABS_NUMBERS
) != 0)
375 osrv
= &sta
->ilbst_rlist
[rule_index
].ird_srvlist_o
[srv_ind
];
376 if (srv
->isd_crtime
!= osrv
->isd_crtime
)
379 count
-= osrv
->isd_serverstats
[stat_ind
].is_value
;
382 * normally, we print "change per second", which we calculate
383 * here. otherwise, we print "change over interval" or absolute
386 if ((sta
->ilbst_flags
& (ILBST_DELTA_INTERVAL
|ILBST_ABS_NUMBERS
)) == 0)
387 count
/= sta
->ilbst_interval
;
389 (void) snprintf(buf
, bufsize
, "%llu", count
);
395 of_rule_stats(ofmt_arg_t
*of_arg
, char *buf
, uint_t bufsize
)
397 ilbst_arg_t
*sta
= (ilbst_arg_t
*)of_arg
->ofmt_cbarg
;
401 switch (of_arg
->ofmt_id
) {
402 case ILBST_PKT_U
: ind
= RLSTA_PKT_U
;
404 case ILBST_BYTES_U
: ind
= RLSTA_BYTES_U
;
406 case ILBST_PKT_D
: ind
= RLSTA_PKT_D
;
408 case ILBST_BYTES_D
: ind
= RLSTA_BYTES_D
;
410 case ILBST_ICMP_P
: ind
= RLSTA_ICMP_P
;
412 case ILBST_ICMP_D
: ind
= RLSTA_ICMP_D
;
414 case ILBST_ICMP2BIG_P
: ind
= RLSTA_ICMP2BIG_P
;
416 case ILBST_ICMP2BIG_D
: ind
= RLSTA_ICMP2BIG_D
;
418 case ILBST_NOMEMP_D
: ind
= RLSTA_NOMEMPKT_D
;
420 case ILBST_NOPORTP_D
: ind
= RLSTA_NOPORTPKT_D
;
422 case ILBST_NOMEMB_D
: ind
= RLSTA_NOMEMBYTES_D
;
424 case ILBST_NOPORTB_D
: ind
= RLSTA_NOPORTBYTES_D
;
428 for (i
= 0; i
< sta
->ilbst_rcount
; i
++)
429 count
+= sta
->ilbst_rlist
[i
].ird_rulestats
[ind
].is_value
;
431 if ((sta
->ilbst_flags
& ILBST_ABS_NUMBERS
) != 0)
435 * the purist approach: if we can't say 100% that what we
436 * calculate is correct, don't.
438 if (sta
->ilbst_flags
& ILBST_RULES_CHANGED
)
441 for (i
= 0; i
< sta
->ilbst_rcount
; i
++) {
442 if (sta
->ilbst_rlist
[i
].ird_crtime_o
!= 0 &&
443 sta
->ilbst_rlist
[i
].ird_crtime
!=
444 sta
->ilbst_rlist
[i
].ird_crtime_o
)
447 count
-= sta
->ilbst_rlist
[i
].ird_rulestats_o
[ind
].is_value
;
451 * normally, we print "change per second", which we calculate
452 * here. otherwise, we print "change over interval"
454 if ((sta
->ilbst_flags
& (ILBST_DELTA_INTERVAL
|ILBST_ABS_NUMBERS
)) == 0)
455 count
/= sta
->ilbst_interval
;
457 (void) snprintf(buf
, bufsize
, "%llu", count
);
462 * Get the number of kstat instances. Note that when rules are being
463 * drained the number of kstats instances may be different than the
464 * kstat counter num_rules (ilb:0:global:num_rules").
466 * Also there can be multiple instances of a rule in the following
469 * A rule named rule A has been deleted but remains in kstats because
470 * its undergoing connection draining. During this time, the user adds
471 * a new rule with the same name(rule A). In this case, there would
472 * be two kstats instances for rule A. Currently ilbadm's aggregate
473 * results will include data from both instances of rule A. In,
474 * future we should have ilbadm stats only consider the latest instance
475 * of the rule (ie only consider the the instance that corresponds
476 * to the rule that was just added).
480 i_get_num_kinstances(kstat_ctl_t
*kctl
)
483 int num_instances
= 0; /* nothing found, 0 rules */
485 for (kp
= kctl
->kc_chain
; kp
!= NULL
; kp
= kp
->ks_next
) {
486 if (strncmp("rulestat", kp
->ks_class
, 8) == 0 &&
487 strncmp("ilb", kp
->ks_module
, 3) == 0) {
492 return (num_instances
);
497 * since server stat's classname is made up of <rulename>-sstat,
498 * we walk the rule list to construct the comparison
499 * Return: pointer to rule whose name matches the class
502 static ilbst_rule_desc_t
*
503 match_2_rnames(char *class, ilbst_rule_desc_t
*rlist
, int rcount
)
506 char classname
[KSTAT_STRLEN
];
508 for (i
= 0; i
< rcount
; i
++) {
509 (void) snprintf(classname
, sizeof (classname
), "%s-sstat",
510 rlist
[i
].ird_rulename
);
511 if (strncmp(classname
, class, sizeof (classname
)) == 0)
518 i_stat_index(kstat_named_t
*knp
, ilbst_stat_t
*stats
, int count
)
522 for (i
= 0; i
< count
; i
++) {
523 if (strcasecmp(stats
[i
].is_name
, knp
->name
) == 0)
531 i_copy_sstats(ilbst_srv_desc_t
*sp
, kstat_t
*kp
)
536 knp
= KSTAT_NAMED_PTR(kp
);
537 for (i
= 0; i
< kp
->ks_ndata
; i
++, knp
++) {
538 ind
= i_stat_index(knp
, servstats
, SSTAT_SZ
);
541 (void) strlcpy(sp
->isd_serverstats
[ind
].is_name
, knp
->name
,
542 sizeof (sp
->isd_serverstats
[ind
].is_name
));
543 sp
->isd_serverstats
[ind
].is_value
= knp
->value
.ui64
;
544 sp
->isd_crtime
= kp
->ks_crtime
;
549 static ilbadm_status_t
550 i_get_server_descs(ilbst_arg_t
*sta
, kstat_ctl_t
*kctl
)
552 ilbadm_status_t rc
= ILBADM_OK
;
555 ilbst_rule_desc_t
*rp
;
556 ilbst_rule_desc_t
*rlist
= sta
->ilbst_rlist
;
557 int rcount
= sta
->ilbst_rcount
;
560 * find all "server" kstats, or the one specified in
563 for (kp
= kctl
->kc_chain
; kp
!= NULL
; kp
= kp
->ks_next
) {
564 if (strncmp("ilb", kp
->ks_module
, 3) != 0)
566 if (sta
->ilbst_server
!= NULL
&&
567 strcasecmp(sta
->ilbst_server
, kp
->ks_name
) != 0)
569 rp
= match_2_rnames(kp
->ks_class
, rlist
, rcount
);
573 (void) kstat_read(kctl
, kp
, NULL
);
574 i
= rp
->ird_srv_ind
++;
578 * This means that a server is added after we check last
579 * time... Just make the array bigger.
581 if (i
+1 > rp
->ird_num_servers
) {
582 ilbst_srv_desc_t
*srvlist
;
584 if ((srvlist
= reallocarray(rp
->ird_srvlist
, i
+ 1,
585 sizeof (*srvlist
))) == NULL
) {
589 rp
->ird_srvlist
= srvlist
;
590 rp
->ird_num_servers
= i
;
593 (void) strlcpy(rp
->ird_srvlist
[i
].isd_servername
, kp
->ks_name
,
594 sizeof (rp
->ird_srvlist
[i
].isd_servername
));
595 i_copy_sstats(&rp
->ird_srvlist
[i
], kp
);
598 for (i
= 0; i
< rcount
; i
++)
599 rlist
[i
].ird_srv_ind
= 0;
601 if (sta
->ilbst_server
!= NULL
&& i
== -1)
602 rc
= ILBADM_ENOSERVER
;
607 i_copy_rstats(ilbst_rule_desc_t
*rp
, kstat_t
*kp
)
612 knp
= KSTAT_NAMED_PTR(kp
);
613 for (i
= 0; i
< kp
->ks_ndata
; i
++, knp
++) {
614 ind
= i_stat_index(knp
, rulestats
, RSTAT_SZ
);
618 (void) strlcpy(rp
->ird_rulestats
[ind
].is_name
, knp
->name
,
619 sizeof (rp
->ird_rulestats
[ind
].is_name
));
620 rp
->ird_rulestats
[ind
].is_value
= knp
->value
.ui64
;
625 i_set_rlstats_ptr(ilbst_rule_desc_t
*rp
, boolean_t old_is_old
)
628 rp
->ird_rulestats
= rp
->ird_rstats
;
629 rp
->ird_rulestats_o
= rp
->ird_rstats_o
;
631 rp
->ird_rulestats
= rp
->ird_rstats_o
;
632 rp
->ird_rulestats_o
= rp
->ird_rstats
;
636 * this function walks the array of rules and switches pointer to old
637 * and new stats as well as serverlists.
640 i_swap_rl_pointers(ilbst_arg_t
*sta
, int rcount
)
643 ilbst_rule_desc_t
*rlist
= sta
->ilbst_rlist
;
644 ilbst_srv_desc_t
*tmp_srv
;
646 for (i
= 0; i
< rcount
; i
++) {
647 /* swap srvlist pointers */
648 tmp_srv
= rlist
[i
].ird_srvlist
;
649 rlist
[i
].ird_srvlist
= rlist
[i
].ird_srvlist_o
;
650 rlist
[i
].ird_srvlist_o
= tmp_srv
;
653 * swap server counts - we need the old one to
654 * save reallocation calls
656 tmp_num
= rlist
[i
].ird_num_servers_o
;
657 rlist
[i
].ird_num_servers_o
= rlist
[i
].ird_num_servers
;
658 rlist
[i
].ird_num_servers
= tmp_num
;
660 /* preserve creation time */
661 rlist
[i
].ird_crtime_o
= rlist
[i
].ird_crtime
;
663 i_set_rlstats_ptr(&rlist
[i
], sta
->ilbst_old_is_old
);
664 rlist
[i
].ird_srv_ind
= 0;
669 i_init_rulelist(ilbst_arg_t
*sta
, int rcount
)
672 ilbst_rule_desc_t
*rlist
= sta
->ilbst_rlist
;
674 for (i
= 0; i
< rcount
; i
++) {
675 rlist
[i
].ird_rulestats
= rlist
[i
].ird_rstats
;
676 rlist
[i
].ird_rulestats_o
= rlist
[i
].ird_rstats_o
;
677 rlist
[i
].ird_srv_ind
= 0;
683 * this function searches for kstats describing individual rules and
684 * saves name, # of servers, and the kstat_t * describing them (this is
685 * for sta->rulename == NULL);
686 * if sta->rulename != NULL, it names the rule we're looking for
687 * and this function will fill in the other data (like the all_rules case)
688 * Returns: ILBADM_ENORULE named rule not found
689 * ILBADM_ENOMEM no mem. available
691 static ilbadm_status_t
692 i_get_rule_descs(ilbst_arg_t
*sta
, kstat_ctl_t
*kctl
)
694 ilbadm_status_t rc
= ILBADM_OK
;
699 ilbst_rule_desc_t
*rlist
= sta
->ilbst_rlist
;
700 int rcount
= sta
->ilbst_rcount
;
703 * find all "rule" kstats, or the one specified in
704 * sta->ilbst_rulename.
706 for (i
= 0, kp
= kctl
->kc_chain
; i
< rcount
&& kp
!= NULL
;
708 if (strncmp("rulestat", kp
->ks_class
, 8) != 0 ||
709 strncmp("ilb", kp
->ks_module
, 3) != 0)
712 (void) kstat_read(kctl
, kp
, NULL
);
714 knp
= kstat_data_lookup(kp
, "num_servers");
716 ilbadm_err(gettext("kstat_data_lookup() failed: %s"),
721 if (sta
->ilbst_rulename
!= NULL
) {
722 if (strcasecmp(kp
->ks_name
, sta
->ilbst_rulename
)
726 (void) strlcpy(rlist
[i
].ird_rulename
, kp
->ks_name
,
727 sizeof (rlist
[i
].ird_rulename
));
729 /* only alloc the space we need, set counter here ... */
730 if (sta
->ilbst_server
!= NULL
)
733 num_servers
= (int)knp
->value
.ui64
;
735 /* ... furthermore, only reallocate if necessary */
736 if (num_servers
!= rlist
[i
].ird_num_servers
) {
737 ilbst_srv_desc_t
*srvlist
;
739 rlist
[i
].ird_num_servers
= num_servers
;
741 if (rlist
[i
].ird_srvlist
== NULL
)
742 srvlist
= calloc(num_servers
,
745 srvlist
= reallocarray(rlist
[i
].ird_srvlist
,
746 num_servers
, sizeof (*srvlist
));
747 if (srvlist
== NULL
) {
751 rlist
[i
].ird_srvlist
= srvlist
;
753 rlist
[i
].ird_srv_ind
= 0;
754 rlist
[i
].ird_crtime
= kp
->ks_crtime
;
756 i_copy_rstats(&rlist
[i
], kp
);
759 /* if we know we're done, return */
760 if (sta
->ilbst_rulename
!= NULL
|| i
== rcount
) {
766 if (sta
->ilbst_rulename
!= NULL
&& i
== 0)
772 i_do_print(ilbst_arg_t
*sta
)
776 /* non-itemized display can go right ahead */
777 if ((sta
->ilbst_flags
& ILBST_ITEMIZE
) == 0) {
778 ofmt_print(sta
->ilbst_oh
, sta
);
783 * rulename is given, list a line per server
784 * here's how we do it:
785 * the _ITEMIZE flag indicates to the print function (called
786 * from ofmt_print()) to look at server [ird_srv_ind] only.
788 if (sta
->ilbst_rulename
!= NULL
) {
789 sta
->ilbst_rule_index
= 0;
790 for (i
= 0; i
< sta
->ilbst_rlist
->ird_num_servers
; i
++) {
791 sta
->ilbst_rlist
->ird_srv_ind
= i
;
792 ofmt_print(sta
->ilbst_oh
, sta
);
794 sta
->ilbst_rlist
->ird_srv_ind
= 0;
798 /* list one line for every rule for a given server */
799 for (i
= 0; i
< sta
->ilbst_rcount
; i
++) {
801 * if a rule doesn't contain a given server, there's no
802 * need to print it. Luckily, we can check that
805 if (sta
->ilbst_rlist
[i
].ird_srvlist
[0].isd_servername
[0] ==
809 sta
->ilbst_rule_index
= i
;
810 sta
->ilbst_rlist
[i
].ird_srv_ind
= 0;
811 ofmt_print(sta
->ilbst_oh
, sta
);
813 sta
->ilbst_rule_index
= 0;
816 static ilbadm_status_t
817 i_do_show_stats(ilbst_arg_t
*sta
)
822 ilbadm_status_t rc
= ILBADM_OK
;
823 ilbst_rule_desc_t
*rlist
, *rp
;
824 boolean_t pseudo_abs
= B_FALSE
; /* for first pass */
826 if ((kctl
= kstat_open()) == NULL
) {
827 ilbadm_err(gettext("kstat_open() failed: %s"), strerror(errno
));
828 return (ILBADM_LIBERR
);
832 if (sta
->ilbst_rulename
== NULL
)
833 rcount
= i_get_num_kinstances(kctl
);
835 rlist
= calloc(sizeof (*rlist
), rcount
);
841 sta
->ilbst_old_is_old
= B_TRUE
;
842 sta
->ilbst_rlist
= rlist
;
843 sta
->ilbst_rcount
= sta
->ilbst_rcount_prev
= rcount
;
844 sta
->ilbst_rlist_sz
= rcount
;
847 * in the first pass, we always print absolute numbers. We
848 * need to remember whether we wanted abs. numbers for
849 * other samples as well
851 if ((sta
->ilbst_flags
& ILBST_ABS_NUMBERS
) == 0) {
852 sta
->ilbst_flags
|= ILBST_ABS_NUMBERS
;
856 i_init_rulelist(sta
, rcount
);
858 rc
= i_get_rule_descs(sta
, kctl
);
862 rc
= i_get_server_descs(sta
, kctl
);
868 if (sta
->ilbst_count
== -1 || --(sta
->ilbst_count
) > 0)
869 (void) sleep(sta
->ilbst_interval
);
873 nkid
= kstat_chain_update(kctl
);
874 sta
->ilbst_flags
&= ~ILBST_RULES_CHANGED
;
876 * we only need to continue with most of the rest of this if
877 * the kstat chain id has changed
882 ilbadm_err(gettext("kstat_chain_update() failed: %s"),
889 * find out whether the number of rules has changed.
890 * if so, adjust rcount and _o; if number has increased,
891 * expand array to hold all rules.
892 * we only shrink if rlist_sz is larger than both rcount and
895 if (sta
->ilbst_rulename
== NULL
)
896 rcount
= i_get_num_kinstances(kctl
);
897 if (rcount
!= sta
->ilbst_rcount
) {
898 sta
->ilbst_flags
|= ILBST_RULES_CHANGED
;
899 sta
->ilbst_rcount_prev
= sta
->ilbst_rcount
;
900 sta
->ilbst_rcount
= rcount
;
902 if (rcount
> sta
->ilbst_rcount_prev
) {
903 rlist
= reallocarray(sta
->ilbst_rlist
,
904 rcount
, sizeof (*sta
->ilbst_rlist
));
909 sta
->ilbst_rlist
= rlist
;
910 /* realloc doesn't zero out memory */
911 for (i
= sta
->ilbst_rcount_prev
;
913 rp
= &sta
->ilbst_rlist
[i
];
914 bzero(rp
, sizeof (*rp
));
915 i_set_rlstats_ptr(rp
,
916 sta
->ilbst_old_is_old
);
919 * even if rlist_sz was > rcount, it's now
922 sta
->ilbst_rlist_sz
= sta
->ilbst_rcount
;
927 * we may need to shrink the allocated slots down to the
928 * actually required number - we need to make sure we
929 * don't delete old or new stats.
931 if (sta
->ilbst_rlist_sz
> MAX(sta
->ilbst_rcount
,
932 sta
->ilbst_rcount_prev
)) {
933 sta
->ilbst_rlist_sz
=
934 MAX(sta
->ilbst_rcount
, sta
->ilbst_rcount_prev
);
935 rlist
= reallocarray(sta
->ilbst_rlist
,
936 sta
->ilbst_rlist_sz
, sizeof (*sta
->ilbst_rlist
));
941 sta
->ilbst_rlist
= rlist
;
945 * move pointers around so what used to point to "old"
946 * stats now points to new, and vice versa
947 * if we're printing absolute numbers, this rigmarole is
952 sta
->ilbst_flags
&= ~ILBST_ABS_NUMBERS
;
954 if ((sta
->ilbst_flags
& ILBST_ABS_NUMBERS
) == 0) {
955 sta
->ilbst_old_is_old
= !sta
->ilbst_old_is_old
;
956 i_swap_rl_pointers(sta
, rcount
);
962 (void) kstat_close(kctl
);
963 if ((rc
!= ILBADM_OK
) && (rc
!= ILBADM_LIBERR
))
964 ilbadm_err(ilbadm_errstr(rc
));
966 free(sta
->ilbst_rlist
);
972 * read ilb's kernel statistics and (periodically) display
977 ilbadm_show_stats(int argc
, char *argv
[])
983 char *fieldnames
= stat_stdhdrs
;
984 ofmt_field_t
*fields
= stat_stdfields
;
985 boolean_t r_opt
= B_FALSE
, s_opt
= B_FALSE
, i_opt
= B_FALSE
;
986 boolean_t o_opt
= B_FALSE
, p_opt
= B_FALSE
, t_opt
= B_FALSE
;
987 boolean_t v_opt
= B_FALSE
, A_opt
= B_FALSE
, d_opt
= B_FALSE
;
989 ofmt_handle_t oh
= NULL
;
991 bzero(&sta
, sizeof (sta
));
992 sta
.ilbst_interval
= 1;
995 while ((c
= getopt(argc
, argv
, ":tdAr:s:ivo:p")) != -1) {
997 case 't': sta
.ilbst_flags
|= ILBST_TIMESTAMP_HEADER
;
1000 case 'd': sta
.ilbst_flags
|= ILBST_DELTA_INTERVAL
;
1003 case 'A': sta
.ilbst_flags
|= ILBST_ABS_NUMBERS
;
1006 case 'r': sta
.ilbst_rulename
= optarg
;
1009 case 's': sta
.ilbst_server
= optarg
;
1012 case 'i': sta
.ilbst_flags
|= ILBST_ITEMIZE
;
1015 case 'o': fieldnames
= optarg
;
1018 case 'p': oflags
|= OFMT_PARSABLE
;
1021 case 'v': sta
.ilbst_flags
|= ILBST_VERBOSE
;
1023 fieldnames
= stat_stdv_hdrs
;
1025 case ':': ilbadm_err(gettext("missing option-argument"
1026 " detected for %c"), (char)optopt
);
1030 case '?': /* fallthrough */
1032 unknown_opt(argv
, optind
-1);
1038 if (s_opt
&& r_opt
) {
1039 ilbadm_err(gettext("options -s and -r are mutually exclusive"));
1044 if (!(s_opt
|| r_opt
)) {
1045 ilbadm_err(gettext("option -i requires"
1046 " either -r or -s"));
1050 ilbadm_err(gettext("option -i and -v are mutually"
1054 /* only use "std" headers if none are specified */
1057 fieldnames
= stat_itemize_rule_hdrs
;
1058 else /* must be s_opt */
1059 fieldnames
= stat_itemize_server_hdrs
;
1060 fields
= stat_itemize_fields
;
1065 ilbadm_err(gettext("option -p requires -o"));
1069 ilbadm_err(gettext("option -o and -v are mutually"
1073 if (strcasecmp(fieldnames
, "all") == 0) {
1074 ilbadm_err(gettext("option -p requires"
1075 " explicit field names"));
1084 int len
= strlen(fieldnames
) + 6;
1087 fnames
= malloc(len
);
1088 if (fnames
== NULL
) {
1092 (void) snprintf(fnames
, len
, "%s,TIME", fieldnames
);
1093 fieldnames
= fnames
;
1097 if (A_opt
&& d_opt
) {
1098 ilbadm_err(gettext("options -d and -A are mutually exclusive"));
1102 /* find and parse interval and count arguments if present */
1103 if (optind
< argc
) {
1104 sta
.ilbst_interval
= atoi(argv
[optind
]);
1105 if (sta
.ilbst_interval
< 1) {
1106 ilbadm_err(gettext("illegal interval spec %s"),
1110 sta
.ilbst_count
= -1;
1111 if (++optind
< argc
) {
1112 sta
.ilbst_count
= atoi(argv
[optind
]);
1113 if (sta
.ilbst_count
< 1) {
1114 ilbadm_err(gettext("illegal count spec %s"),
1121 oerr
= ofmt_open(fieldnames
, fields
, oflags
, 80, &oh
);
1122 if (oerr
!= OFMT_SUCCESS
) {
1125 ilbadm_err(gettext("ofmt_open failed: %s"),
1126 ofmt_strerror(oh
, oerr
, e
, sizeof (e
)));
1127 return (ILBADM_LIBERR
);
1132 rc
= i_do_show_stats(&sta
);