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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/sysmacros.h>
34 #include <sys/note.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <libgen.h>
39 #include <kstat.h>
40 #include <ofmt.h>
41 #include <libilb.h>
42 #include "ilbadm.h"
43
44 #define ILBST_TIMESTAMP_HEADER 0x01 /* a timestamp w. every header */
45 #define ILBST_DELTA_INTERVAL 0x02 /* delta over specified interval */
46 #define ILBST_ABS_NUMBERS 0x04 /* print absolute numbers, no d's */
47 #define ILBST_ITEMIZE 0x08 /* itemize */
48 #define ILBST_VERBOSE 0x10 /* verbose error info */
49
50 #define ILBST_OLD_VALUES 0x20 /* for internal processing */
51 #define ILBST_RULES_CHANGED 0x40
52
53 typedef struct {
54 char is_name[KSTAT_STRLEN];
55 uint64_t is_value;
56 } ilbst_stat_t;
57
58 static ilbst_stat_t rulestats[] = {
59 {"num_servers", 0},
60 {"bytes_not_processed", 0},
61 {"pkt_not_processed", 0},
62 {"bytes_dropped", 0},
63 {"pkt_dropped", 0},
64 {"nomem_bytes_dropped", 0},
65 {"nomem_pkt_dropped", 0},
66 {"noport_bytes_dropped", 0},
67 {"noport_pkt_dropped", 0},
68 {"icmp_echo_processed", 0},
69 {"icmp_dropped", 0},
70 {"icmp_too_big_processed", 0},
71 {"icmp_too_big_dropped", 0}
72 };
73
74 /* indices into array above, to avoid searching */
75 #define RLSTA_NUM_SRV 0
76 #define RLSTA_BYTES_U 1
77 #define RLSTA_PKT_U 2
78 #define RLSTA_BYTES_D 3
79 #define RLSTA_PKT_D 4
80 #define RLSTA_NOMEMBYTES_D 5
81 #define RLSTA_NOMEMPKT_D 6
82 #define RLSTA_NOPORTBYTES_D 7
83 #define RLSTA_NOPORTPKT_D 8
84 #define RLSTA_ICMP_P 9
85 #define RLSTA_ICMP_D 10
86 #define RLSTA_ICMP2BIG_P 11
87 #define RLSTA_ICMP2BIG_D 12
88
89 static ilbst_stat_t servstats[] = {
90 {"bytes_processed", 0},
91 {"pkt_processed", 0}
92 };
93 /* indices into array above, to avoid searching */
94 #define SRVST_BYTES_P 0
95 #define SRVST_PKT_P 1
96
97 /* values used for of_* commands as id */
98 #define ILBST_PKT_P 0
99 #define ILBST_BYTES_P 1
100 #define ILBST_PKT_U 2
101 #define ILBST_BYTES_U 3
102 #define ILBST_PKT_D 4
103 #define ILBST_BYTES_D 5
104 #define ILBST_ICMP_P 6
105 #define ILBST_ICMP_D 7
106 #define ILBST_ICMP2BIG_P 8
107 #define ILBST_ICMP2BIG_D 9
108 #define ILBST_NOMEMP_D 10
109 #define ILBST_NOPORTP_D 11
110 #define ILBST_NOMEMB_D 12
111 #define ILBST_NOPORTB_D 13
112
113 #define ILBST_ITEMIZE_SNAME 97
114 #define ILBST_ITEMIZE_RNAME 98
115 #define ILBST_TIMESTAMP 99
116
117 /* approx field widths */
118 #define ILBST_PKTCTR_W 8
119 #define ILBST_BYTECTR_W 10
120 #define ILBST_TIME_W 15
121
122 static boolean_t of_rule_stats(ofmt_arg_t *, char *, uint_t);
123 static boolean_t of_server_stats(ofmt_arg_t *, char *, uint_t);
124 static boolean_t of_itemize_stats(ofmt_arg_t *, char *, uint_t);
125 static boolean_t of_timestamp(ofmt_arg_t *, char *, uint_t);
126
127 static ofmt_field_t stat_itemize_fields[] = {
128 {"RULENAME", ILB_NAMESZ, ILBST_ITEMIZE_RNAME, of_itemize_stats},
129 {"SERVERNAME", ILB_NAMESZ, ILBST_ITEMIZE_SNAME, of_itemize_stats},
130 {"PKT_P", ILBST_PKTCTR_W, ILBST_PKT_P, of_itemize_stats},
131 {"BYTES_P", ILBST_BYTECTR_W, ILBST_BYTES_P, of_itemize_stats},
132 {"TIME", ILBST_TIME_W, ILBST_TIMESTAMP, of_timestamp},
133 {NULL, 0, 0, NULL}
134 };
135 static ofmt_field_t stat_stdfields[] = {
136 {"PKT_P", ILBST_PKTCTR_W, ILBST_PKT_P, of_server_stats},
137 {"BYTES_P", ILBST_BYTECTR_W, ILBST_BYTES_P, of_server_stats},
138 {"PKT_U", ILBST_PKTCTR_W, ILBST_PKT_U, of_rule_stats},
139 {"BYTES_U", ILBST_BYTECTR_W, ILBST_BYTES_U, of_rule_stats},
140 {"PKT_D", ILBST_PKTCTR_W, ILBST_PKT_D, of_rule_stats},
141 {"BYTES_D", ILBST_BYTECTR_W, ILBST_BYTES_D, of_rule_stats},
142 {"ICMP_P", ILBST_PKTCTR_W, ILBST_ICMP_P, of_rule_stats},
143 {"ICMP_D", ILBST_PKTCTR_W, ILBST_ICMP_D, of_rule_stats},
144 {"ICMP2BIG_P", 11, ILBST_ICMP2BIG_P, of_rule_stats},
145 {"ICMP2BIG_D", 11, ILBST_ICMP2BIG_D, of_rule_stats},
146 {"NOMEMP_D", ILBST_PKTCTR_W, ILBST_NOMEMP_D, of_rule_stats},
147 {"NOPORTP_D", ILBST_PKTCTR_W, ILBST_NOPORTP_D, of_rule_stats},
148 {"NOMEMB_D", ILBST_PKTCTR_W, ILBST_NOMEMB_D, of_rule_stats},
149 {"NOPORTB_D", ILBST_PKTCTR_W, ILBST_NOPORTB_D, of_rule_stats},
150 {"TIME", ILBST_TIME_W, ILBST_TIMESTAMP, of_timestamp},
151 {NULL, 0, 0, NULL}
152 };
153
154 static char stat_stdhdrs[] = "PKT_P,BYTES_P,PKT_U,BYTES_U,PKT_D,BYTES_D";
155 static char stat_stdv_hdrs[] = "PKT_P,BYTES_P,PKT_U,BYTES_U,PKT_D,BYTES_D,"
156 "ICMP_P,ICMP_D,ICMP2BIG_P,ICMP2BIG_D,NOMEMP_D,NOPORTP_D";
157 static char stat_itemize_rule_hdrs[] = "SERVERNAME,PKT_P,BYTES_P";
158 static char stat_itemize_server_hdrs[] = "RULENAME,PKT_P,BYTES_P";
159
160 #define RSTAT_SZ (sizeof (rulestats)/sizeof (rulestats[0]))
161 #define SSTAT_SZ (sizeof (servstats)/sizeof (servstats[0]))
162
163 typedef struct {
164 char isd_servername[KSTAT_STRLEN]; /* serverID */
165 ilbst_stat_t isd_serverstats[SSTAT_SZ];
166 hrtime_t isd_crtime; /* save for comparison purpose */
167 } ilbst_srv_desc_t;
168
169 /*
170 * this data structure stores statistics for a rule - both an old set
171 * and a current/new set. we use pointers to the actual stores and switch
172 * the pointers for every round. old_is_old in ilbst_arg_t indicates
173 * which pointer points to the "old" data struct (ie, if true, _o pointer
174 * points to old)
175 */
176 typedef struct {
177 char ird_rulename[KSTAT_STRLEN];
178 int ird_num_servers;
179 int ird_num_servers_o;
180 int ird_srv_ind;
181 hrtime_t ird_crtime; /* save for comparison */
182 hrtime_t ird_crtime_o; /* save for comparison */
183 ilbst_srv_desc_t *ird_srvlist;
184 ilbst_srv_desc_t *ird_srvlist_o;
185 ilbst_stat_t ird_rstats[RSTAT_SZ];
186 ilbst_stat_t ird_rstats_o[RSTAT_SZ];
187 ilbst_stat_t *ird_rulestats;
188 ilbst_stat_t *ird_rulestats_o;
189 } ilbst_rule_desc_t;
190
191 /*
192 * overall "container" for information pertaining to statistics, and
193 * how to display them.
194 */
195 typedef struct {
196 int ilbst_flags;
197 /* fields representing user input */
198 char *ilbst_rulename; /* optional */
199 char *ilbst_server; /* optional */
200 int ilbst_interval;
201 int ilbst_count;
202 /* "internal" fields for data and data presentation */
203 ofmt_handle_t ilbst_oh;
204 boolean_t ilbst_old_is_old;
205 ilbst_rule_desc_t *ilbst_rlist;
206 int ilbst_rcount; /* current list count */
207 int ilbst_rcount_prev; /* prev (different) count */
208 int ilbst_rlist_sz; /* number of alloc'ed rules */
209 int ilbst_rule_index; /* for itemizes display */
210 } ilbst_arg_t;
211
212 /* ARGSUSED */
213 static boolean_t
of_timestamp(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)214 of_timestamp(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
215 {
216 time_t now;
217 struct tm *now_tm;
218
219 now = time(NULL);
220 now_tm = localtime(&now);
221
222 (void) strftime(buf, bufsize, "%F:%H.%M.%S", now_tm);
223 return (B_TRUE);
224 }
225
226 static boolean_t
i_sum_per_rule_processed(ilbst_rule_desc_t * rp,uint64_t * resp,int index,int flags)227 i_sum_per_rule_processed(ilbst_rule_desc_t *rp, uint64_t *resp, int index,
228 int flags)
229 {
230 int i, num_servers;
231 ilbst_srv_desc_t *srv, *o_srv, *n_srv;
232 uint64_t res = 0;
233 boolean_t valid = B_TRUE;
234 boolean_t old = flags & ILBST_OLD_VALUES;
235 boolean_t check_valid;
236
237 /* if we do abs. numbers, we never look at the _o fields */
238 assert((old && (flags & ILBST_ABS_NUMBERS)) == B_FALSE);
239
240 /* we only check for validity under certain conditions */
241 check_valid = !(old || (flags & ILBST_ABS_NUMBERS));
242
243 if (check_valid && rp->ird_num_servers != rp->ird_num_servers_o)
244 valid = B_FALSE;
245
246 num_servers = old ? rp->ird_num_servers_o : rp->ird_num_servers;
247
248 for (i = 0; i < num_servers; i++) {
249 n_srv = &rp->ird_srvlist[i];
250 o_srv = &rp->ird_srvlist_o[i];
251
252 if (old)
253 srv = o_srv;
254 else
255 srv = n_srv;
256
257 res += srv->isd_serverstats[index].is_value;
258 /*
259 * if creation times don't match, comparison is wrong; if
260 * if we already know something is invalid, we don't
261 * need to compare again.
262 */
263 if (check_valid && valid == B_TRUE &&
264 o_srv->isd_crtime != n_srv->isd_crtime) {
265 valid = B_FALSE;
266 break;
267 }
268 }
269 /*
270 * save the result even though it may be imprecise - let the
271 * caller decide what to do
272 */
273 *resp = res;
274
275 return (valid);
276 }
277
278 typedef boolean_t (*sumfunc_t)(ilbst_rule_desc_t *, uint64_t *, int);
279
280 static boolean_t
i_sum_per_rule_pkt_p(ilbst_rule_desc_t * rp,uint64_t * resp,int flags)281 i_sum_per_rule_pkt_p(ilbst_rule_desc_t *rp, uint64_t *resp, int flags)
282 {
283 return (i_sum_per_rule_processed(rp, resp, SRVST_PKT_P, flags));
284 }
285
286 static boolean_t
i_sum_per_rule_bytes_p(ilbst_rule_desc_t * rp,uint64_t * resp,int flags)287 i_sum_per_rule_bytes_p(ilbst_rule_desc_t *rp, uint64_t *resp, int flags)
288 {
289 return (i_sum_per_rule_processed(rp, resp, SRVST_BYTES_P, flags));
290 }
291
292 static boolean_t
of_server_stats(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)293 of_server_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
294 {
295 ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg;
296 uint64_t count = 0, val;
297 int i;
298 boolean_t valid = B_TRUE;
299 sumfunc_t sumfunc;
300
301 switch (of_arg->ofmt_id) {
302 case ILBST_PKT_P: sumfunc = i_sum_per_rule_pkt_p;
303 break;
304 case ILBST_BYTES_P: sumfunc = i_sum_per_rule_bytes_p;
305 break;
306 }
307
308 for (i = 0; i < sta->ilbst_rcount; i++) {
309 valid = sumfunc(&sta->ilbst_rlist[i], &val, sta->ilbst_flags);
310 if (!valid)
311 return (valid);
312 count += val;
313 }
314
315 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0)
316 goto out;
317
318 for (i = 0; i < sta->ilbst_rcount; i++) {
319 (void) sumfunc(&sta->ilbst_rlist[i], &val,
320 sta->ilbst_flags | ILBST_OLD_VALUES);
321 count -= val;
322 }
323
324 out:
325 /*
326 * normally, we print "change per second", which we calculate
327 * here. otherwise, we print "change over interval"
328 */
329 if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0)
330 count /= sta->ilbst_interval;
331
332 (void) snprintf(buf, bufsize, "%llu", count);
333 return (B_TRUE);
334 }
335
336 /*
337 * this function is called when user wants itemized stats of every
338 * server for a named rule, or vice vera.
339 * i_do_print sets sta->rule_index and the proper ird_srv_ind so
340 * we don't have to differentiate between these two cases here.
341 */
342 static boolean_t
of_itemize_stats(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)343 of_itemize_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
344 {
345 ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg;
346 int stat_ind;
347 uint64_t count;
348 int rule_index = sta->ilbst_rule_index;
349 int srv_ind = sta->ilbst_rlist[rule_index].ird_srv_ind;
350 boolean_t ret = B_TRUE;
351 ilbst_srv_desc_t *srv, *osrv;
352
353 srv = &sta->ilbst_rlist[rule_index].ird_srvlist[srv_ind];
354
355 switch (of_arg->ofmt_id) {
356 case ILBST_PKT_P: stat_ind = SRVST_PKT_P;
357 break;
358 case ILBST_BYTES_P: stat_ind = SRVST_BYTES_P;
359 break;
360 case ILBST_ITEMIZE_RNAME:
361 (void) snprintf(buf, bufsize, "%s",
362 sta->ilbst_rlist[rule_index].ird_rulename);
363 return (B_TRUE);
364 /* not reached */
365 break;
366 case ILBST_ITEMIZE_SNAME:
367 (void) snprintf(buf, bufsize, "%s", srv->isd_servername);
368 return (B_TRUE);
369 /* not reached */
370 break;
371 }
372
373 count = srv->isd_serverstats[stat_ind].is_value;
374
375 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0)
376 goto out;
377
378 osrv = &sta->ilbst_rlist[rule_index].ird_srvlist_o[srv_ind];
379 if (srv->isd_crtime != osrv->isd_crtime)
380 ret = B_FALSE;
381
382 count -= osrv->isd_serverstats[stat_ind].is_value;
383 out:
384 /*
385 * normally, we print "change per second", which we calculate
386 * here. otherwise, we print "change over interval" or absolute
387 * values.
388 */
389 if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0)
390 count /= sta->ilbst_interval;
391
392 (void) snprintf(buf, bufsize, "%llu", count);
393 return (ret);
394
395 }
396
397 static boolean_t
of_rule_stats(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)398 of_rule_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
399 {
400 ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg;
401 int i, ind;
402 uint64_t count = 0;
403
404 switch (of_arg->ofmt_id) {
405 case ILBST_PKT_U: ind = RLSTA_PKT_U;
406 break;
407 case ILBST_BYTES_U: ind = RLSTA_BYTES_U;
408 break;
409 case ILBST_PKT_D: ind = RLSTA_PKT_D;
410 break;
411 case ILBST_BYTES_D: ind = RLSTA_BYTES_D;
412 break;
413 case ILBST_ICMP_P: ind = RLSTA_ICMP_P;
414 break;
415 case ILBST_ICMP_D: ind = RLSTA_ICMP_D;
416 break;
417 case ILBST_ICMP2BIG_P: ind = RLSTA_ICMP2BIG_P;
418 break;
419 case ILBST_ICMP2BIG_D: ind = RLSTA_ICMP2BIG_D;
420 break;
421 case ILBST_NOMEMP_D: ind = RLSTA_NOMEMPKT_D;
422 break;
423 case ILBST_NOPORTP_D: ind = RLSTA_NOPORTPKT_D;
424 break;
425 case ILBST_NOMEMB_D: ind = RLSTA_NOMEMBYTES_D;
426 break;
427 case ILBST_NOPORTB_D: ind = RLSTA_NOPORTBYTES_D;
428 break;
429 }
430
431 for (i = 0; i < sta->ilbst_rcount; i++)
432 count += sta->ilbst_rlist[i].ird_rulestats[ind].is_value;
433
434 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0)
435 goto out;
436
437 /*
438 * the purist approach: if we can't say 100% that what we
439 * calculate is correct, don't.
440 */
441 if (sta->ilbst_flags & ILBST_RULES_CHANGED)
442 return (B_FALSE);
443
444 for (i = 0; i < sta->ilbst_rcount; i++) {
445 if (sta->ilbst_rlist[i].ird_crtime_o != 0 &&
446 sta->ilbst_rlist[i].ird_crtime !=
447 sta->ilbst_rlist[i].ird_crtime_o)
448 return (B_FALSE);
449
450 count -= sta->ilbst_rlist[i].ird_rulestats_o[ind].is_value;
451 }
452 out:
453 /*
454 * normally, we print "change per second", which we calculate
455 * here. otherwise, we print "change over interval"
456 */
457 if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0)
458 count /= sta->ilbst_interval;
459
460 (void) snprintf(buf, bufsize, "%llu", count);
461 return (B_TRUE);
462 }
463
464 /*
465 * Get the number of kstat instances. Note that when rules are being
466 * drained the number of kstats instances may be different than the
467 * kstat counter num_rules (ilb:0:global:num_rules").
468 *
469 * Also there can be multiple instances of a rule in the following
470 * scenario:
471 *
472 * A rule named rule A has been deleted but remains in kstats because
473 * its undergoing connection draining. During this time, the user adds
474 * a new rule with the same name(rule A). In this case, there would
475 * be two kstats instances for rule A. Currently ilbadm's aggregate
476 * results will include data from both instances of rule A. In,
477 * future we should have ilbadm stats only consider the latest instance
478 * of the rule (ie only consider the the instance that corresponds
479 * to the rule that was just added).
480 *
481 */
482 static int
i_get_num_kinstances(kstat_ctl_t * kctl)483 i_get_num_kinstances(kstat_ctl_t *kctl)
484 {
485 kstat_t *kp;
486 int num_instances = 0; /* nothing found, 0 rules */
487
488 for (kp = kctl->kc_chain; kp != NULL; kp = kp->ks_next) {
489 if (strncmp("rulestat", kp->ks_class, 8) == 0 &&
490 strncmp("ilb", kp->ks_module, 3) == 0) {
491 num_instances++;
492 }
493 }
494
495 return (num_instances);
496 }
497
498
499 /*
500 * since server stat's classname is made up of <rulename>-sstat,
501 * we walk the rule list to construct the comparison
502 * Return: pointer to rule whose name matches the class
503 * NULL if no match
504 */
505 static ilbst_rule_desc_t *
match_2_rnames(char * class,ilbst_rule_desc_t * rlist,int rcount)506 match_2_rnames(char *class, ilbst_rule_desc_t *rlist, int rcount)
507 {
508 int i;
509 char classname[KSTAT_STRLEN];
510
511 for (i = 0; i < rcount; i++) {
512 (void) snprintf(classname, sizeof (classname), "%s-sstat",
513 rlist[i].ird_rulename);
514 if (strncmp(classname, class, sizeof (classname)) == 0)
515 return (&rlist[i]);
516 }
517 return (NULL);
518 }
519
520 static int
i_stat_index(kstat_named_t * knp,ilbst_stat_t * stats,int count)521 i_stat_index(kstat_named_t *knp, ilbst_stat_t *stats, int count)
522 {
523 int i;
524
525 for (i = 0; i < count; i++) {
526 if (strcasecmp(stats[i].is_name, knp->name) == 0)
527 return (i);
528 }
529
530 return (-1);
531 }
532
533 static void
i_copy_sstats(ilbst_srv_desc_t * sp,kstat_t * kp)534 i_copy_sstats(ilbst_srv_desc_t *sp, kstat_t *kp)
535 {
536 kstat_named_t *knp;
537 int i, ind;
538
539 knp = KSTAT_NAMED_PTR(kp);
540 for (i = 0; i < kp->ks_ndata; i++, knp++) {
541 ind = i_stat_index(knp, servstats, SSTAT_SZ);
542 if (ind == -1)
543 continue;
544 (void) strlcpy(sp->isd_serverstats[ind].is_name, knp->name,
545 sizeof (sp->isd_serverstats[ind].is_name));
546 sp->isd_serverstats[ind].is_value = knp->value.ui64;
547 sp->isd_crtime = kp->ks_crtime;
548 }
549 }
550
551
552 static ilbadm_status_t
i_get_server_descs(ilbst_arg_t * sta,kstat_ctl_t * kctl)553 i_get_server_descs(ilbst_arg_t *sta, kstat_ctl_t *kctl)
554 {
555 ilbadm_status_t rc = ILBADM_OK;
556 kstat_t *kp;
557 int i = -1;
558 ilbst_rule_desc_t *rp;
559 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
560 int rcount = sta->ilbst_rcount;
561
562 /*
563 * find all "server" kstats, or the one specified in
564 * sta->server
565 */
566 for (kp = kctl->kc_chain; kp != NULL; kp = kp->ks_next) {
567 if (strncmp("ilb", kp->ks_module, 3) != 0)
568 continue;
569 if (sta->ilbst_server != NULL &&
570 strcasecmp(sta->ilbst_server, kp->ks_name) != 0)
571 continue;
572 rp = match_2_rnames(kp->ks_class, rlist, rcount);
573 if (rp == NULL)
574 continue;
575
576 (void) kstat_read(kctl, kp, NULL);
577 i = rp->ird_srv_ind++;
578
579 rc = ILBADM_OK;
580 /*
581 * This means that a server is added after we check last
582 * time... Just make the array bigger.
583 */
584 if (i+1 > rp->ird_num_servers) {
585 ilbst_srv_desc_t *srvlist;
586
587 if ((srvlist = realloc(rp->ird_srvlist, (i+1) *
588 sizeof (*srvlist))) == NULL) {
589 rc = ILBADM_ENOMEM;
590 break;
591 }
592 rp->ird_srvlist = srvlist;
593 rp->ird_num_servers = i;
594 }
595
596 (void) strlcpy(rp->ird_srvlist[i].isd_servername, kp->ks_name,
597 sizeof (rp->ird_srvlist[i].isd_servername));
598 i_copy_sstats(&rp->ird_srvlist[i], kp);
599 }
600
601 for (i = 0; i < rcount; i++)
602 rlist[i].ird_srv_ind = 0;
603
604 if (sta->ilbst_server != NULL && i == -1)
605 rc = ILBADM_ENOSERVER;
606 return (rc);
607 }
608
609 static void
i_copy_rstats(ilbst_rule_desc_t * rp,kstat_t * kp)610 i_copy_rstats(ilbst_rule_desc_t *rp, kstat_t *kp)
611 {
612 kstat_named_t *knp;
613 int i, ind;
614
615 knp = KSTAT_NAMED_PTR(kp);
616 for (i = 0; i < kp->ks_ndata; i++, knp++) {
617 ind = i_stat_index(knp, rulestats, RSTAT_SZ);
618 if (ind == -1)
619 continue;
620
621 (void) strlcpy(rp->ird_rulestats[ind].is_name, knp->name,
622 sizeof (rp->ird_rulestats[ind].is_name));
623 rp->ird_rulestats[ind].is_value = knp->value.ui64;
624 }
625 }
626
627 static void
i_set_rlstats_ptr(ilbst_rule_desc_t * rp,boolean_t old_is_old)628 i_set_rlstats_ptr(ilbst_rule_desc_t *rp, boolean_t old_is_old)
629 {
630 if (old_is_old) {
631 rp->ird_rulestats = rp->ird_rstats;
632 rp->ird_rulestats_o = rp->ird_rstats_o;
633 } else {
634 rp->ird_rulestats = rp->ird_rstats_o;
635 rp->ird_rulestats_o = rp->ird_rstats;
636 }
637 }
638 /*
639 * this function walks the array of rules and switches pointer to old
640 * and new stats as well as serverlists.
641 */
642 static void
i_swap_rl_pointers(ilbst_arg_t * sta,int rcount)643 i_swap_rl_pointers(ilbst_arg_t *sta, int rcount)
644 {
645 int i, tmp_num;
646 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
647 ilbst_srv_desc_t *tmp_srv;
648
649 for (i = 0; i < rcount; i++) {
650 /* swap srvlist pointers */
651 tmp_srv = rlist[i].ird_srvlist;
652 rlist[i].ird_srvlist = rlist[i].ird_srvlist_o;
653 rlist[i].ird_srvlist_o = tmp_srv;
654
655 /*
656 * swap server counts - we need the old one to
657 * save reallocation calls
658 */
659 tmp_num = rlist[i].ird_num_servers_o;
660 rlist[i].ird_num_servers_o = rlist[i].ird_num_servers;
661 rlist[i].ird_num_servers = tmp_num;
662
663 /* preserve creation time */
664 rlist[i].ird_crtime_o = rlist[i].ird_crtime;
665
666 i_set_rlstats_ptr(&rlist[i], sta->ilbst_old_is_old);
667 rlist[i].ird_srv_ind = 0;
668 }
669 }
670
671 static void
i_init_rulelist(ilbst_arg_t * sta,int rcount)672 i_init_rulelist(ilbst_arg_t *sta, int rcount)
673 {
674 int i;
675 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
676
677 for (i = 0; i < rcount; i++) {
678 rlist[i].ird_rulestats = rlist[i].ird_rstats;
679 rlist[i].ird_rulestats_o = rlist[i].ird_rstats_o;
680 rlist[i].ird_srv_ind = 0;
681 }
682 }
683
684
685 /*
686 * this function searches for kstats describing individual rules and
687 * saves name, # of servers, and the kstat_t * describing them (this is
688 * for sta->rulename == NULL);
689 * if sta->rulename != NULL, it names the rule we're looking for
690 * and this function will fill in the other data (like the all_rules case)
691 * Returns: ILBADM_ENORULE named rule not found
692 * ILBADM_ENOMEM no mem. available
693 */
694 static ilbadm_status_t
i_get_rule_descs(ilbst_arg_t * sta,kstat_ctl_t * kctl)695 i_get_rule_descs(ilbst_arg_t *sta, kstat_ctl_t *kctl)
696 {
697 ilbadm_status_t rc = ILBADM_OK;
698 kstat_t *kp;
699 kstat_named_t *knp;
700 int i;
701 int num_servers;
702 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
703 int rcount = sta->ilbst_rcount;
704
705 /*
706 * find all "rule" kstats, or the one specified in
707 * sta->ilbst_rulename.
708 */
709 for (i = 0, kp = kctl->kc_chain; i < rcount && kp != NULL;
710 kp = kp->ks_next) {
711 if (strncmp("rulestat", kp->ks_class, 8) != 0 ||
712 strncmp("ilb", kp->ks_module, 3) != 0)
713 continue;
714
715 (void) kstat_read(kctl, kp, NULL);
716
717 knp = kstat_data_lookup(kp, "num_servers");
718 if (knp == NULL) {
719 ilbadm_err(gettext("kstat_data_lookup() failed: %s"),
720 strerror(errno));
721 rc = ILBADM_LIBERR;
722 break;
723 }
724 if (sta->ilbst_rulename != NULL) {
725 if (strcasecmp(kp->ks_name, sta->ilbst_rulename)
726 != 0)
727 continue;
728 }
729 (void) strlcpy(rlist[i].ird_rulename, kp->ks_name,
730 sizeof (rlist[i].ird_rulename));
731
732 /* only alloc the space we need, set counter here ... */
733 if (sta->ilbst_server != NULL)
734 num_servers = 1;
735 else
736 num_servers = (int)knp->value.ui64;
737
738 /* ... furthermore, only reallocate if necessary */
739 if (num_servers != rlist[i].ird_num_servers) {
740 ilbst_srv_desc_t *srvlist;
741
742 rlist[i].ird_num_servers = num_servers;
743
744 if (rlist[i].ird_srvlist == NULL)
745 srvlist = calloc(num_servers,
746 sizeof (*srvlist));
747 else
748 srvlist = realloc(rlist[i].ird_srvlist,
749 sizeof (*srvlist) * num_servers);
750 if (srvlist == NULL) {
751 rc = ILBADM_ENOMEM;
752 break;
753 }
754 rlist[i].ird_srvlist = srvlist;
755 }
756 rlist[i].ird_srv_ind = 0;
757 rlist[i].ird_crtime = kp->ks_crtime;
758
759 i_copy_rstats(&rlist[i], kp);
760 i++;
761
762 /* if we know we're done, return */
763 if (sta->ilbst_rulename != NULL || i == rcount) {
764 rc = ILBADM_OK;
765 break;
766 }
767 }
768
769 if (sta->ilbst_rulename != NULL && i == 0)
770 rc = ILBADM_ENORULE;
771 return (rc);
772 }
773
774 static void
i_do_print(ilbst_arg_t * sta)775 i_do_print(ilbst_arg_t *sta)
776 {
777 int i;
778
779 /* non-itemized display can go right ahead */
780 if ((sta->ilbst_flags & ILBST_ITEMIZE) == 0) {
781 ofmt_print(sta->ilbst_oh, sta);
782 return;
783 }
784
785 /*
786 * rulename is given, list a line per server
787 * here's how we do it:
788 * the _ITEMIZE flag indicates to the print function (called
789 * from ofmt_print()) to look at server [ird_srv_ind] only.
790 */
791 if (sta->ilbst_rulename != NULL) {
792 sta->ilbst_rule_index = 0;
793 for (i = 0; i < sta->ilbst_rlist->ird_num_servers; i++) {
794 sta->ilbst_rlist->ird_srv_ind = i;
795 ofmt_print(sta->ilbst_oh, sta);
796 }
797 sta->ilbst_rlist->ird_srv_ind = 0;
798 return;
799 }
800
801 /* list one line for every rule for a given server */
802 for (i = 0; i < sta->ilbst_rcount; i++) {
803 /*
804 * if a rule doesn't contain a given server, there's no
805 * need to print it. Luckily, we can check that
806 * fairly easily
807 */
808 if (sta->ilbst_rlist[i].ird_srvlist[0].isd_servername[0] ==
809 '\0')
810 continue;
811
812 sta->ilbst_rule_index = i;
813 sta->ilbst_rlist[i].ird_srv_ind = 0;
814 ofmt_print(sta->ilbst_oh, sta);
815 }
816 sta->ilbst_rule_index = 0;
817 }
818
819 static ilbadm_status_t
i_do_show_stats(ilbst_arg_t * sta)820 i_do_show_stats(ilbst_arg_t *sta)
821 {
822 kstat_ctl_t *kctl;
823 kid_t nkid;
824 int rcount = 1, i;
825 ilbadm_status_t rc = ILBADM_OK;
826 ilbst_rule_desc_t *rlist, *rp;
827 boolean_t pseudo_abs = B_FALSE; /* for first pass */
828
829 if ((kctl = kstat_open()) == NULL) {
830 ilbadm_err(gettext("kstat_open() failed: %s"), strerror(errno));
831 return (ILBADM_LIBERR);
832 }
833
834
835 if (sta->ilbst_rulename == NULL)
836 rcount = i_get_num_kinstances(kctl);
837
838 rlist = calloc(sizeof (*rlist), rcount);
839 if (rlist == NULL) {
840 rc = ILBADM_ENOMEM;
841 goto out;
842 }
843
844 sta->ilbst_old_is_old = B_TRUE;
845 sta->ilbst_rlist = rlist;
846 sta->ilbst_rcount = sta->ilbst_rcount_prev = rcount;
847 sta->ilbst_rlist_sz = rcount;
848
849 /*
850 * in the first pass, we always print absolute numbers. We
851 * need to remember whether we wanted abs. numbers for
852 * other samples as well
853 */
854 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) == 0) {
855 sta->ilbst_flags |= ILBST_ABS_NUMBERS;
856 pseudo_abs = B_TRUE;
857 }
858
859 i_init_rulelist(sta, rcount);
860 do {
861 rc = i_get_rule_descs(sta, kctl);
862 if (rc != ILBADM_OK)
863 goto out;
864
865 rc = i_get_server_descs(sta, kctl);
866 if (rc != ILBADM_OK)
867 goto out;
868
869 i_do_print(sta);
870
871 if (sta->ilbst_count == -1 || --(sta->ilbst_count) > 0)
872 (void) sleep(sta->ilbst_interval);
873 else
874 break;
875
876 nkid = kstat_chain_update(kctl);
877 sta->ilbst_flags &= ~ILBST_RULES_CHANGED;
878 /*
879 * we only need to continue with most of the rest of this if
880 * the kstat chain id has changed
881 */
882 if (nkid == 0)
883 goto swap_old_new;
884 if (nkid == -1) {
885 ilbadm_err(gettext("kstat_chain_update() failed: %s"),
886 strerror(errno));
887 rc = ILBADM_LIBERR;
888 break;
889 }
890
891 /*
892 * find out whether the number of rules has changed.
893 * if so, adjust rcount and _o; if number has increased,
894 * expand array to hold all rules.
895 * we only shrink if rlist_sz is larger than both rcount and
896 * rcount_prev;
897 */
898 if (sta->ilbst_rulename == NULL)
899 rcount = i_get_num_kinstances(kctl);
900 if (rcount != sta->ilbst_rcount) {
901 sta->ilbst_flags |= ILBST_RULES_CHANGED;
902 sta->ilbst_rcount_prev = sta->ilbst_rcount;
903 sta->ilbst_rcount = rcount;
904
905 if (rcount > sta->ilbst_rcount_prev) {
906 rlist = realloc(sta->ilbst_rlist,
907 sizeof (*sta->ilbst_rlist) * rcount);
908 if (rlist == NULL) {
909 rc = ILBADM_ENOMEM;
910 break;
911 }
912 sta->ilbst_rlist = rlist;
913 /* realloc doesn't zero out memory */
914 for (i = sta->ilbst_rcount_prev;
915 i < rcount; i++) {
916 rp = &sta->ilbst_rlist[i];
917 bzero(rp, sizeof (*rp));
918 i_set_rlstats_ptr(rp,
919 sta->ilbst_old_is_old);
920 }
921 /*
922 * even if rlist_sz was > rcount, it's now
923 * shrunk to rcount
924 */
925 sta->ilbst_rlist_sz = sta->ilbst_rcount;
926 }
927 }
928
929 /*
930 * we may need to shrink the allocated slots down to the
931 * actually required number - we need to make sure we
932 * don't delete old or new stats.
933 */
934 if (sta->ilbst_rlist_sz > MAX(sta->ilbst_rcount,
935 sta->ilbst_rcount_prev)) {
936 sta->ilbst_rlist_sz =
937 MAX(sta->ilbst_rcount, sta->ilbst_rcount_prev);
938 rlist = realloc(sta->ilbst_rlist,
939 sizeof (*sta->ilbst_rlist) * sta->ilbst_rlist_sz);
940 if (rlist == NULL) {
941 rc = ILBADM_ENOMEM;
942 break;
943 }
944 sta->ilbst_rlist = rlist;
945 }
946
947 /*
948 * move pointers around so what used to point to "old"
949 * stats now points to new, and vice versa
950 * if we're printing absolute numbers, this rigmarole is
951 * not necessary.
952 */
953 swap_old_new:
954 if (pseudo_abs)
955 sta->ilbst_flags &= ~ILBST_ABS_NUMBERS;
956
957 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) == 0) {
958 sta->ilbst_old_is_old = !sta->ilbst_old_is_old;
959 i_swap_rl_pointers(sta, rcount);
960 }
961 _NOTE(CONSTCOND)
962 } while (B_TRUE);
963
964 out:
965 (void) kstat_close(kctl);
966 if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
967 ilbadm_err(ilbadm_errstr(rc));
968
969 if (sta->ilbst_rlist != NULL)
970 free(sta->ilbst_rlist);
971
972 return (rc);
973 }
974
975 /*
976 * read ilb's kernel statistics and (periodically) display
977 * them.
978 */
979 /* ARGSUSED */
980 ilbadm_status_t
ilbadm_show_stats(int argc,char * argv[])981 ilbadm_show_stats(int argc, char *argv[])
982 {
983 ilbadm_status_t rc;
984 int c;
985 ilbst_arg_t sta;
986 int oflags = 0;
987 char *fieldnames = stat_stdhdrs;
988 ofmt_field_t *fields = stat_stdfields;
989 boolean_t r_opt = B_FALSE, s_opt = B_FALSE, i_opt = B_FALSE;
990 boolean_t o_opt = B_FALSE, p_opt = B_FALSE, t_opt = B_FALSE;
991 boolean_t v_opt = B_FALSE, A_opt = B_FALSE, d_opt = B_FALSE;
992 ofmt_status_t oerr;
993 ofmt_handle_t oh = NULL;
994
995 bzero(&sta, sizeof (sta));
996 sta.ilbst_interval = 1;
997 sta.ilbst_count = 1;
998
999 while ((c = getopt(argc, argv, ":tdAr:s:ivo:p")) != -1) {
1000 switch ((char)c) {
1001 case 't': sta.ilbst_flags |= ILBST_TIMESTAMP_HEADER;
1002 t_opt = B_TRUE;
1003 break;
1004 case 'd': sta.ilbst_flags |= ILBST_DELTA_INTERVAL;
1005 d_opt = B_TRUE;
1006 break;
1007 case 'A': sta.ilbst_flags |= ILBST_ABS_NUMBERS;
1008 A_opt = B_TRUE;
1009 break;
1010 case 'r': sta.ilbst_rulename = optarg;
1011 r_opt = B_TRUE;
1012 break;
1013 case 's': sta.ilbst_server = optarg;
1014 s_opt = B_TRUE;
1015 break;
1016 case 'i': sta.ilbst_flags |= ILBST_ITEMIZE;
1017 i_opt = B_TRUE;
1018 break;
1019 case 'o': fieldnames = optarg;
1020 o_opt = B_TRUE;
1021 break;
1022 case 'p': oflags |= OFMT_PARSABLE;
1023 p_opt = B_TRUE;
1024 break;
1025 case 'v': sta.ilbst_flags |= ILBST_VERBOSE;
1026 v_opt = B_TRUE;
1027 fieldnames = stat_stdv_hdrs;
1028 break;
1029 case ':': ilbadm_err(gettext("missing option-argument"
1030 " detected for %c"), (char)optopt);
1031 exit(1);
1032 /* not reached */
1033 break;
1034 case '?': /* fallthrough */
1035 default:
1036 unknown_opt(argv, optind-1);
1037 /* not reached */
1038 break;
1039 }
1040 }
1041
1042 if (s_opt && r_opt) {
1043 ilbadm_err(gettext("options -s and -r are mutually exclusive"));
1044 exit(1);
1045 }
1046
1047 if (i_opt) {
1048 if (!(s_opt || r_opt)) {
1049 ilbadm_err(gettext("option -i requires"
1050 " either -r or -s"));
1051 exit(1);
1052 }
1053 if (v_opt) {
1054 ilbadm_err(gettext("option -i and -v are mutually"
1055 " exclusive"));
1056 exit(1);
1057 }
1058 /* only use "std" headers if none are specified */
1059 if (!o_opt)
1060 if (r_opt)
1061 fieldnames = stat_itemize_rule_hdrs;
1062 else /* must be s_opt */
1063 fieldnames = stat_itemize_server_hdrs;
1064 fields = stat_itemize_fields;
1065 }
1066
1067 if (p_opt) {
1068 if (!o_opt) {
1069 ilbadm_err(gettext("option -p requires -o"));
1070 exit(1);
1071 }
1072 if (v_opt) {
1073 ilbadm_err(gettext("option -o and -v are mutually"
1074 " exclusive"));
1075 exit(1);
1076 }
1077 if (strcasecmp(fieldnames, "all") == 0) {
1078 ilbadm_err(gettext("option -p requires"
1079 " explicit field names"));
1080 exit(1);
1081 }
1082 }
1083
1084 if (t_opt) {
1085 if (v_opt) {
1086 fieldnames = "all";
1087 } else {
1088 int len = strlen(fieldnames) + 6;
1089 char *fnames;
1090
1091 fnames = malloc(len);
1092 if (fnames == NULL) {
1093 rc = ILBADM_ENOMEM;
1094 return (rc);
1095 }
1096 (void) snprintf(fnames, len, "%s,TIME", fieldnames);
1097 fieldnames = fnames;
1098 }
1099 }
1100
1101 if (A_opt && d_opt) {
1102 ilbadm_err(gettext("options -d and -A are mutually exclusive"));
1103 exit(1);
1104 }
1105
1106 /* find and parse interval and count arguments if present */
1107 if (optind < argc) {
1108 sta.ilbst_interval = atoi(argv[optind]);
1109 if (sta.ilbst_interval < 1) {
1110 ilbadm_err(gettext("illegal interval spec %s"),
1111 argv[optind]);
1112 exit(1);
1113 }
1114 sta.ilbst_count = -1;
1115 if (++optind < argc) {
1116 sta.ilbst_count = atoi(argv[optind]);
1117 if (sta.ilbst_count < 1) {
1118 ilbadm_err(gettext("illegal count spec %s"),
1119 argv[optind]);
1120 exit(1);
1121 }
1122 }
1123 }
1124
1125 oerr = ofmt_open(fieldnames, fields, oflags, 80, &oh);
1126 if (oerr != OFMT_SUCCESS) {
1127 char e[80];
1128
1129 ilbadm_err(gettext("ofmt_open failed: %s"),
1130 ofmt_strerror(oh, oerr, e, sizeof (e)));
1131 return (ILBADM_LIBERR);
1132 }
1133
1134 sta.ilbst_oh = oh;
1135
1136 rc = i_do_show_stats(&sta);
1137
1138 ofmt_close(oh);
1139 return (rc);
1140 }
1141