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 * Copyright 2012 Milan Jurik. All rights reserved.
26 */
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <strings.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/sysmacros.h>
35 #include <sys/note.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <libgen.h>
40 #include <kstat.h>
41 #include <ofmt.h>
42 #include <libilb.h>
43 #include "ilbadm.h"
44
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 */
50
51 #define ILBST_OLD_VALUES 0x20 /* for internal processing */
52 #define ILBST_RULES_CHANGED 0x40
53
54 typedef struct {
55 char is_name[KSTAT_STRLEN];
56 uint64_t is_value;
57 } ilbst_stat_t;
58
59 static ilbst_stat_t rulestats[] = {
60 {"num_servers", 0},
61 {"bytes_not_processed", 0},
62 {"pkt_not_processed", 0},
63 {"bytes_dropped", 0},
64 {"pkt_dropped", 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},
70 {"icmp_dropped", 0},
71 {"icmp_too_big_processed", 0},
72 {"icmp_too_big_dropped", 0}
73 };
74
75 /* indices into array above, to avoid searching */
76 #define RLSTA_NUM_SRV 0
77 #define RLSTA_BYTES_U 1
78 #define RLSTA_PKT_U 2
79 #define RLSTA_BYTES_D 3
80 #define RLSTA_PKT_D 4
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
89
90 static ilbst_stat_t servstats[] = {
91 {"bytes_processed", 0},
92 {"pkt_processed", 0}
93 };
94 /* indices into array above, to avoid searching */
95 #define SRVST_BYTES_P 0
96 #define SRVST_PKT_P 1
97
98 /* values used for of_* commands as id */
99 #define ILBST_PKT_P 0
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
113
114 #define ILBST_ITEMIZE_SNAME 97
115 #define ILBST_ITEMIZE_RNAME 98
116 #define ILBST_TIMESTAMP 99
117
118 /* approx field widths */
119 #define ILBST_PKTCTR_W 8
120 #define ILBST_BYTECTR_W 10
121 #define ILBST_TIME_W 15
122
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);
127
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},
134 {NULL, 0, 0, NULL}
135 };
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},
152 {NULL, 0, 0, NULL}
153 };
154
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";
160
161 #define RSTAT_SZ (sizeof (rulestats)/sizeof (rulestats[0]))
162 #define SSTAT_SZ (sizeof (servstats)/sizeof (servstats[0]))
163
164 typedef struct {
165 char isd_servername[KSTAT_STRLEN]; /* serverID */
166 ilbst_stat_t isd_serverstats[SSTAT_SZ];
167 hrtime_t isd_crtime; /* save for comparison purpose */
168 } ilbst_srv_desc_t;
169
170 /*
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
175 * points to old)
176 */
177 typedef struct {
178 char ird_rulename[KSTAT_STRLEN];
179 int ird_num_servers;
180 int ird_num_servers_o;
181 int ird_srv_ind;
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;
190 } ilbst_rule_desc_t;
191
192 /*
193 * overall "container" for information pertaining to statistics, and
194 * how to display them.
195 */
196 typedef struct {
197 int ilbst_flags;
198 /* fields representing user input */
199 char *ilbst_rulename; /* optional */
200 char *ilbst_server; /* optional */
201 int ilbst_interval;
202 int ilbst_count;
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 */
211 } ilbst_arg_t;
212
213 /* ARGSUSED */
214 static boolean_t
of_timestamp(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)215 of_timestamp(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
216 {
217 time_t now;
218 struct tm *now_tm;
219
220 now = time(NULL);
221 now_tm = localtime(&now);
222
223 (void) strftime(buf, bufsize, "%F:%H.%M.%S", now_tm);
224 return (B_TRUE);
225 }
226
227 static boolean_t
i_sum_per_rule_processed(ilbst_rule_desc_t * rp,uint64_t * resp,int index,int flags)228 i_sum_per_rule_processed(ilbst_rule_desc_t *rp, uint64_t *resp, int index,
229 int flags)
230 {
231 int i, num_servers;
232 ilbst_srv_desc_t *srv, *o_srv, *n_srv;
233 uint64_t res = 0;
234 boolean_t valid = B_TRUE;
235 boolean_t old = flags & ILBST_OLD_VALUES;
236 boolean_t check_valid;
237
238 /* if we do abs. numbers, we never look at the _o fields */
239 assert((old && (flags & ILBST_ABS_NUMBERS)) == B_FALSE);
240
241 /* we only check for validity under certain conditions */
242 check_valid = !(old || (flags & ILBST_ABS_NUMBERS));
243
244 if (check_valid && rp->ird_num_servers != rp->ird_num_servers_o)
245 valid = B_FALSE;
246
247 num_servers = old ? rp->ird_num_servers_o : rp->ird_num_servers;
248
249 for (i = 0; i < num_servers; i++) {
250 n_srv = &rp->ird_srvlist[i];
251 o_srv = &rp->ird_srvlist_o[i];
252
253 if (old)
254 srv = o_srv;
255 else
256 srv = n_srv;
257
258 res += srv->isd_serverstats[index].is_value;
259 /*
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.
263 */
264 if (check_valid && valid == B_TRUE &&
265 o_srv->isd_crtime != n_srv->isd_crtime) {
266 valid = B_FALSE;
267 break;
268 }
269 }
270 /*
271 * save the result even though it may be imprecise - let the
272 * caller decide what to do
273 */
274 *resp = res;
275
276 return (valid);
277 }
278
279 typedef boolean_t (*sumfunc_t)(ilbst_rule_desc_t *, uint64_t *, int);
280
281 static boolean_t
i_sum_per_rule_pkt_p(ilbst_rule_desc_t * rp,uint64_t * resp,int flags)282 i_sum_per_rule_pkt_p(ilbst_rule_desc_t *rp, uint64_t *resp, int flags)
283 {
284 return (i_sum_per_rule_processed(rp, resp, SRVST_PKT_P, flags));
285 }
286
287 static boolean_t
i_sum_per_rule_bytes_p(ilbst_rule_desc_t * rp,uint64_t * resp,int flags)288 i_sum_per_rule_bytes_p(ilbst_rule_desc_t *rp, uint64_t *resp, int flags)
289 {
290 return (i_sum_per_rule_processed(rp, resp, SRVST_BYTES_P, flags));
291 }
292
293 static boolean_t
of_server_stats(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)294 of_server_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
295 {
296 ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg;
297 uint64_t count = 0, val;
298 int i;
299 boolean_t valid = B_TRUE;
300 sumfunc_t sumfunc;
301
302 switch (of_arg->ofmt_id) {
303 case ILBST_PKT_P: sumfunc = i_sum_per_rule_pkt_p;
304 break;
305 case ILBST_BYTES_P: sumfunc = i_sum_per_rule_bytes_p;
306 break;
307 }
308
309 for (i = 0; i < sta->ilbst_rcount; i++) {
310 valid = sumfunc(&sta->ilbst_rlist[i], &val, sta->ilbst_flags);
311 if (!valid)
312 return (valid);
313 count += val;
314 }
315
316 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0)
317 goto out;
318
319 for (i = 0; i < sta->ilbst_rcount; i++) {
320 (void) sumfunc(&sta->ilbst_rlist[i], &val,
321 sta->ilbst_flags | ILBST_OLD_VALUES);
322 count -= val;
323 }
324
325 out:
326 /*
327 * normally, we print "change per second", which we calculate
328 * here. otherwise, we print "change over interval"
329 */
330 if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0)
331 count /= sta->ilbst_interval;
332
333 (void) snprintf(buf, bufsize, "%llu", count);
334 return (B_TRUE);
335 }
336
337 /*
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.
342 */
343 static boolean_t
of_itemize_stats(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)344 of_itemize_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
345 {
346 ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg;
347 int stat_ind;
348 uint64_t count;
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;
353
354 srv = &sta->ilbst_rlist[rule_index].ird_srvlist[srv_ind];
355
356 switch (of_arg->ofmt_id) {
357 case ILBST_PKT_P: stat_ind = SRVST_PKT_P;
358 break;
359 case ILBST_BYTES_P: stat_ind = SRVST_BYTES_P;
360 break;
361 case ILBST_ITEMIZE_RNAME:
362 (void) snprintf(buf, bufsize, "%s",
363 sta->ilbst_rlist[rule_index].ird_rulename);
364 return (B_TRUE);
365 case ILBST_ITEMIZE_SNAME:
366 (void) snprintf(buf, bufsize, "%s", srv->isd_servername);
367 return (B_TRUE);
368 }
369
370 count = srv->isd_serverstats[stat_ind].is_value;
371
372 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0)
373 goto out;
374
375 osrv = &sta->ilbst_rlist[rule_index].ird_srvlist_o[srv_ind];
376 if (srv->isd_crtime != osrv->isd_crtime)
377 ret = B_FALSE;
378
379 count -= osrv->isd_serverstats[stat_ind].is_value;
380 out:
381 /*
382 * normally, we print "change per second", which we calculate
383 * here. otherwise, we print "change over interval" or absolute
384 * values.
385 */
386 if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0)
387 count /= sta->ilbst_interval;
388
389 (void) snprintf(buf, bufsize, "%llu", count);
390 return (ret);
391
392 }
393
394 static boolean_t
of_rule_stats(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)395 of_rule_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
396 {
397 ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg;
398 int i, ind;
399 uint64_t count = 0;
400
401 switch (of_arg->ofmt_id) {
402 case ILBST_PKT_U: ind = RLSTA_PKT_U;
403 break;
404 case ILBST_BYTES_U: ind = RLSTA_BYTES_U;
405 break;
406 case ILBST_PKT_D: ind = RLSTA_PKT_D;
407 break;
408 case ILBST_BYTES_D: ind = RLSTA_BYTES_D;
409 break;
410 case ILBST_ICMP_P: ind = RLSTA_ICMP_P;
411 break;
412 case ILBST_ICMP_D: ind = RLSTA_ICMP_D;
413 break;
414 case ILBST_ICMP2BIG_P: ind = RLSTA_ICMP2BIG_P;
415 break;
416 case ILBST_ICMP2BIG_D: ind = RLSTA_ICMP2BIG_D;
417 break;
418 case ILBST_NOMEMP_D: ind = RLSTA_NOMEMPKT_D;
419 break;
420 case ILBST_NOPORTP_D: ind = RLSTA_NOPORTPKT_D;
421 break;
422 case ILBST_NOMEMB_D: ind = RLSTA_NOMEMBYTES_D;
423 break;
424 case ILBST_NOPORTB_D: ind = RLSTA_NOPORTBYTES_D;
425 break;
426 }
427
428 for (i = 0; i < sta->ilbst_rcount; i++)
429 count += sta->ilbst_rlist[i].ird_rulestats[ind].is_value;
430
431 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0)
432 goto out;
433
434 /*
435 * the purist approach: if we can't say 100% that what we
436 * calculate is correct, don't.
437 */
438 if (sta->ilbst_flags & ILBST_RULES_CHANGED)
439 return (B_FALSE);
440
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)
445 return (B_FALSE);
446
447 count -= sta->ilbst_rlist[i].ird_rulestats_o[ind].is_value;
448 }
449 out:
450 /*
451 * normally, we print "change per second", which we calculate
452 * here. otherwise, we print "change over interval"
453 */
454 if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0)
455 count /= sta->ilbst_interval;
456
457 (void) snprintf(buf, bufsize, "%llu", count);
458 return (B_TRUE);
459 }
460
461 /*
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").
465 *
466 * Also there can be multiple instances of a rule in the following
467 * scenario:
468 *
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).
477 *
478 */
479 static int
i_get_num_kinstances(kstat_ctl_t * kctl)480 i_get_num_kinstances(kstat_ctl_t *kctl)
481 {
482 kstat_t *kp;
483 int num_instances = 0; /* nothing found, 0 rules */
484
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) {
488 num_instances++;
489 }
490 }
491
492 return (num_instances);
493 }
494
495
496 /*
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
500 * NULL if no match
501 */
502 static ilbst_rule_desc_t *
match_2_rnames(char * class,ilbst_rule_desc_t * rlist,int rcount)503 match_2_rnames(char *class, ilbst_rule_desc_t *rlist, int rcount)
504 {
505 int i;
506 char classname[KSTAT_STRLEN];
507
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)
512 return (&rlist[i]);
513 }
514 return (NULL);
515 }
516
517 static int
i_stat_index(kstat_named_t * knp,ilbst_stat_t * stats,int count)518 i_stat_index(kstat_named_t *knp, ilbst_stat_t *stats, int count)
519 {
520 int i;
521
522 for (i = 0; i < count; i++) {
523 if (strcasecmp(stats[i].is_name, knp->name) == 0)
524 return (i);
525 }
526
527 return (-1);
528 }
529
530 static void
i_copy_sstats(ilbst_srv_desc_t * sp,kstat_t * kp)531 i_copy_sstats(ilbst_srv_desc_t *sp, kstat_t *kp)
532 {
533 kstat_named_t *knp;
534 int i, ind;
535
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);
539 if (ind == -1)
540 continue;
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;
545 }
546 }
547
548
549 static ilbadm_status_t
i_get_server_descs(ilbst_arg_t * sta,kstat_ctl_t * kctl)550 i_get_server_descs(ilbst_arg_t *sta, kstat_ctl_t *kctl)
551 {
552 ilbadm_status_t rc = ILBADM_OK;
553 kstat_t *kp;
554 int i = -1;
555 ilbst_rule_desc_t *rp;
556 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
557 int rcount = sta->ilbst_rcount;
558
559 /*
560 * find all "server" kstats, or the one specified in
561 * sta->server
562 */
563 for (kp = kctl->kc_chain; kp != NULL; kp = kp->ks_next) {
564 if (strncmp("ilb", kp->ks_module, 3) != 0)
565 continue;
566 if (sta->ilbst_server != NULL &&
567 strcasecmp(sta->ilbst_server, kp->ks_name) != 0)
568 continue;
569 rp = match_2_rnames(kp->ks_class, rlist, rcount);
570 if (rp == NULL)
571 continue;
572
573 (void) kstat_read(kctl, kp, NULL);
574 i = rp->ird_srv_ind++;
575
576 rc = ILBADM_OK;
577 /*
578 * This means that a server is added after we check last
579 * time... Just make the array bigger.
580 */
581 if (i+1 > rp->ird_num_servers) {
582 ilbst_srv_desc_t *srvlist;
583
584 if ((srvlist = realloc(rp->ird_srvlist, (i+1) *
585 sizeof (*srvlist))) == NULL) {
586 rc = ILBADM_ENOMEM;
587 break;
588 }
589 rp->ird_srvlist = srvlist;
590 rp->ird_num_servers = i;
591 }
592
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);
596 }
597
598 for (i = 0; i < rcount; i++)
599 rlist[i].ird_srv_ind = 0;
600
601 if (sta->ilbst_server != NULL && i == -1)
602 rc = ILBADM_ENOSERVER;
603 return (rc);
604 }
605
606 static void
i_copy_rstats(ilbst_rule_desc_t * rp,kstat_t * kp)607 i_copy_rstats(ilbst_rule_desc_t *rp, kstat_t *kp)
608 {
609 kstat_named_t *knp;
610 int i, ind;
611
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);
615 if (ind == -1)
616 continue;
617
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;
621 }
622 }
623
624 static void
i_set_rlstats_ptr(ilbst_rule_desc_t * rp,boolean_t old_is_old)625 i_set_rlstats_ptr(ilbst_rule_desc_t *rp, boolean_t old_is_old)
626 {
627 if (old_is_old) {
628 rp->ird_rulestats = rp->ird_rstats;
629 rp->ird_rulestats_o = rp->ird_rstats_o;
630 } else {
631 rp->ird_rulestats = rp->ird_rstats_o;
632 rp->ird_rulestats_o = rp->ird_rstats;
633 }
634 }
635 /*
636 * this function walks the array of rules and switches pointer to old
637 * and new stats as well as serverlists.
638 */
639 static void
i_swap_rl_pointers(ilbst_arg_t * sta,int rcount)640 i_swap_rl_pointers(ilbst_arg_t *sta, int rcount)
641 {
642 int i, tmp_num;
643 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
644 ilbst_srv_desc_t *tmp_srv;
645
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;
651
652 /*
653 * swap server counts - we need the old one to
654 * save reallocation calls
655 */
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;
659
660 /* preserve creation time */
661 rlist[i].ird_crtime_o = rlist[i].ird_crtime;
662
663 i_set_rlstats_ptr(&rlist[i], sta->ilbst_old_is_old);
664 rlist[i].ird_srv_ind = 0;
665 }
666 }
667
668 static void
i_init_rulelist(ilbst_arg_t * sta,int rcount)669 i_init_rulelist(ilbst_arg_t *sta, int rcount)
670 {
671 int i;
672 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
673
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;
678 }
679 }
680
681
682 /*
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
690 */
691 static ilbadm_status_t
i_get_rule_descs(ilbst_arg_t * sta,kstat_ctl_t * kctl)692 i_get_rule_descs(ilbst_arg_t *sta, kstat_ctl_t *kctl)
693 {
694 ilbadm_status_t rc = ILBADM_OK;
695 kstat_t *kp;
696 kstat_named_t *knp;
697 int i;
698 int num_servers;
699 ilbst_rule_desc_t *rlist = sta->ilbst_rlist;
700 int rcount = sta->ilbst_rcount;
701
702 /*
703 * find all "rule" kstats, or the one specified in
704 * sta->ilbst_rulename.
705 */
706 for (i = 0, kp = kctl->kc_chain; i < rcount && kp != NULL;
707 kp = kp->ks_next) {
708 if (strncmp("rulestat", kp->ks_class, 8) != 0 ||
709 strncmp("ilb", kp->ks_module, 3) != 0)
710 continue;
711
712 (void) kstat_read(kctl, kp, NULL);
713
714 knp = kstat_data_lookup(kp, "num_servers");
715 if (knp == NULL) {
716 ilbadm_err(gettext("kstat_data_lookup() failed: %s"),
717 strerror(errno));
718 rc = ILBADM_LIBERR;
719 break;
720 }
721 if (sta->ilbst_rulename != NULL) {
722 if (strcasecmp(kp->ks_name, sta->ilbst_rulename)
723 != 0)
724 continue;
725 }
726 (void) strlcpy(rlist[i].ird_rulename, kp->ks_name,
727 sizeof (rlist[i].ird_rulename));
728
729 /* only alloc the space we need, set counter here ... */
730 if (sta->ilbst_server != NULL)
731 num_servers = 1;
732 else
733 num_servers = (int)knp->value.ui64;
734
735 /* ... furthermore, only reallocate if necessary */
736 if (num_servers != rlist[i].ird_num_servers) {
737 ilbst_srv_desc_t *srvlist;
738
739 rlist[i].ird_num_servers = num_servers;
740
741 if (rlist[i].ird_srvlist == NULL)
742 srvlist = calloc(num_servers,
743 sizeof (*srvlist));
744 else
745 srvlist = realloc(rlist[i].ird_srvlist,
746 sizeof (*srvlist) * num_servers);
747 if (srvlist == NULL) {
748 rc = ILBADM_ENOMEM;
749 break;
750 }
751 rlist[i].ird_srvlist = srvlist;
752 }
753 rlist[i].ird_srv_ind = 0;
754 rlist[i].ird_crtime = kp->ks_crtime;
755
756 i_copy_rstats(&rlist[i], kp);
757 i++;
758
759 /* if we know we're done, return */
760 if (sta->ilbst_rulename != NULL || i == rcount) {
761 rc = ILBADM_OK;
762 break;
763 }
764 }
765
766 if (sta->ilbst_rulename != NULL && i == 0)
767 rc = ILBADM_ENORULE;
768 return (rc);
769 }
770
771 static void
i_do_print(ilbst_arg_t * sta)772 i_do_print(ilbst_arg_t *sta)
773 {
774 int i;
775
776 /* non-itemized display can go right ahead */
777 if ((sta->ilbst_flags & ILBST_ITEMIZE) == 0) {
778 ofmt_print(sta->ilbst_oh, sta);
779 return;
780 }
781
782 /*
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.
787 */
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);
793 }
794 sta->ilbst_rlist->ird_srv_ind = 0;
795 return;
796 }
797
798 /* list one line for every rule for a given server */
799 for (i = 0; i < sta->ilbst_rcount; i++) {
800 /*
801 * if a rule doesn't contain a given server, there's no
802 * need to print it. Luckily, we can check that
803 * fairly easily
804 */
805 if (sta->ilbst_rlist[i].ird_srvlist[0].isd_servername[0] ==
806 '\0')
807 continue;
808
809 sta->ilbst_rule_index = i;
810 sta->ilbst_rlist[i].ird_srv_ind = 0;
811 ofmt_print(sta->ilbst_oh, sta);
812 }
813 sta->ilbst_rule_index = 0;
814 }
815
816 static ilbadm_status_t
i_do_show_stats(ilbst_arg_t * sta)817 i_do_show_stats(ilbst_arg_t *sta)
818 {
819 kstat_ctl_t *kctl;
820 kid_t nkid;
821 int rcount = 1, i;
822 ilbadm_status_t rc = ILBADM_OK;
823 ilbst_rule_desc_t *rlist, *rp;
824 boolean_t pseudo_abs = B_FALSE; /* for first pass */
825
826 if ((kctl = kstat_open()) == NULL) {
827 ilbadm_err(gettext("kstat_open() failed: %s"), strerror(errno));
828 return (ILBADM_LIBERR);
829 }
830
831
832 if (sta->ilbst_rulename == NULL)
833 rcount = i_get_num_kinstances(kctl);
834
835 rlist = calloc(sizeof (*rlist), rcount);
836 if (rlist == NULL) {
837 rc = ILBADM_ENOMEM;
838 goto out;
839 }
840
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;
845
846 /*
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
850 */
851 if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) == 0) {
852 sta->ilbst_flags |= ILBST_ABS_NUMBERS;
853 pseudo_abs = B_TRUE;
854 }
855
856 i_init_rulelist(sta, rcount);
857 do {
858 rc = i_get_rule_descs(sta, kctl);
859 if (rc != ILBADM_OK)
860 goto out;
861
862 rc = i_get_server_descs(sta, kctl);
863 if (rc != ILBADM_OK)
864 goto out;
865
866 i_do_print(sta);
867
868 if (sta->ilbst_count == -1 || --(sta->ilbst_count) > 0)
869 (void) sleep(sta->ilbst_interval);
870 else
871 break;
872
873 nkid = kstat_chain_update(kctl);
874 sta->ilbst_flags &= ~ILBST_RULES_CHANGED;
875 /*
876 * we only need to continue with most of the rest of this if
877 * the kstat chain id has changed
878 */
879 if (nkid == 0)
880 goto swap_old_new;
881 if (nkid == -1) {
882 ilbadm_err(gettext("kstat_chain_update() failed: %s"),
883 strerror(errno));
884 rc = ILBADM_LIBERR;
885 break;
886 }
887
888 /*
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
893 * rcount_prev;
894 */
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;
901
902 if (rcount > sta->ilbst_rcount_prev) {
903 rlist = realloc(sta->ilbst_rlist,
904 sizeof (*sta->ilbst_rlist) * rcount);
905 if (rlist == NULL) {
906 rc = ILBADM_ENOMEM;
907 break;
908 }
909 sta->ilbst_rlist = rlist;
910 /* realloc doesn't zero out memory */
911 for (i = sta->ilbst_rcount_prev;
912 i < rcount; i++) {
913 rp = &sta->ilbst_rlist[i];
914 bzero(rp, sizeof (*rp));
915 i_set_rlstats_ptr(rp,
916 sta->ilbst_old_is_old);
917 }
918 /*
919 * even if rlist_sz was > rcount, it's now
920 * shrunk to rcount
921 */
922 sta->ilbst_rlist_sz = sta->ilbst_rcount;
923 }
924 }
925
926 /*
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.
930 */
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 = realloc(sta->ilbst_rlist,
936 sizeof (*sta->ilbst_rlist) * sta->ilbst_rlist_sz);
937 if (rlist == NULL) {
938 rc = ILBADM_ENOMEM;
939 break;
940 }
941 sta->ilbst_rlist = rlist;
942 }
943
944 /*
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
948 * not necessary.
949 */
950 swap_old_new:
951 if (pseudo_abs)
952 sta->ilbst_flags &= ~ILBST_ABS_NUMBERS;
953
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);
957 }
958 _NOTE(CONSTCOND)
959 } while (B_TRUE);
960
961 out:
962 (void) kstat_close(kctl);
963 if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
964 ilbadm_err(ilbadm_errstr(rc));
965
966 if (sta->ilbst_rlist != NULL)
967 free(sta->ilbst_rlist);
968
969 return (rc);
970 }
971
972 /*
973 * read ilb's kernel statistics and (periodically) display
974 * them.
975 */
976 /* ARGSUSED */
977 ilbadm_status_t
ilbadm_show_stats(int argc,char * argv[])978 ilbadm_show_stats(int argc, char *argv[])
979 {
980 ilbadm_status_t rc;
981 int c;
982 ilbst_arg_t sta;
983 int oflags = 0;
984 char *fieldnames = stat_stdhdrs;
985 ofmt_field_t *fields = stat_stdfields;
986 boolean_t r_opt = B_FALSE, s_opt = B_FALSE, i_opt = B_FALSE;
987 boolean_t o_opt = B_FALSE, p_opt = B_FALSE, t_opt = B_FALSE;
988 boolean_t v_opt = B_FALSE, A_opt = B_FALSE, d_opt = B_FALSE;
989 ofmt_status_t oerr;
990 ofmt_handle_t oh = NULL;
991
992 bzero(&sta, sizeof (sta));
993 sta.ilbst_interval = 1;
994 sta.ilbst_count = 1;
995
996 while ((c = getopt(argc, argv, ":tdAr:s:ivo:p")) != -1) {
997 switch ((char)c) {
998 case 't': sta.ilbst_flags |= ILBST_TIMESTAMP_HEADER;
999 t_opt = B_TRUE;
1000 break;
1001 case 'd': sta.ilbst_flags |= ILBST_DELTA_INTERVAL;
1002 d_opt = B_TRUE;
1003 break;
1004 case 'A': sta.ilbst_flags |= ILBST_ABS_NUMBERS;
1005 A_opt = B_TRUE;
1006 break;
1007 case 'r': sta.ilbst_rulename = optarg;
1008 r_opt = B_TRUE;
1009 break;
1010 case 's': sta.ilbst_server = optarg;
1011 s_opt = B_TRUE;
1012 break;
1013 case 'i': sta.ilbst_flags |= ILBST_ITEMIZE;
1014 i_opt = B_TRUE;
1015 break;
1016 case 'o': fieldnames = optarg;
1017 o_opt = B_TRUE;
1018 break;
1019 case 'p': oflags |= OFMT_PARSABLE;
1020 p_opt = B_TRUE;
1021 break;
1022 case 'v': sta.ilbst_flags |= ILBST_VERBOSE;
1023 v_opt = B_TRUE;
1024 fieldnames = stat_stdv_hdrs;
1025 break;
1026 case ':': ilbadm_err(gettext("missing option-argument"
1027 " detected for %c"), (char)optopt);
1028 exit(1);
1029 /* not reached */
1030 break;
1031 case '?': /* fallthrough */
1032 default:
1033 unknown_opt(argv, optind-1);
1034 /* not reached */
1035 break;
1036 }
1037 }
1038
1039 if (s_opt && r_opt) {
1040 ilbadm_err(gettext("options -s and -r are mutually exclusive"));
1041 exit(1);
1042 }
1043
1044 if (i_opt) {
1045 if (!(s_opt || r_opt)) {
1046 ilbadm_err(gettext("option -i requires"
1047 " either -r or -s"));
1048 exit(1);
1049 }
1050 if (v_opt) {
1051 ilbadm_err(gettext("option -i and -v are mutually"
1052 " exclusive"));
1053 exit(1);
1054 }
1055 /* only use "std" headers if none are specified */
1056 if (!o_opt)
1057 if (r_opt)
1058 fieldnames = stat_itemize_rule_hdrs;
1059 else /* must be s_opt */
1060 fieldnames = stat_itemize_server_hdrs;
1061 fields = stat_itemize_fields;
1062 }
1063
1064 if (p_opt) {
1065 if (!o_opt) {
1066 ilbadm_err(gettext("option -p requires -o"));
1067 exit(1);
1068 }
1069 if (v_opt) {
1070 ilbadm_err(gettext("option -o and -v are mutually"
1071 " exclusive"));
1072 exit(1);
1073 }
1074 if (strcasecmp(fieldnames, "all") == 0) {
1075 ilbadm_err(gettext("option -p requires"
1076 " explicit field names"));
1077 exit(1);
1078 }
1079 }
1080
1081 if (t_opt) {
1082 if (v_opt) {
1083 fieldnames = "all";
1084 } else {
1085 int len = strlen(fieldnames) + 6;
1086 char *fnames;
1087
1088 fnames = malloc(len);
1089 if (fnames == NULL) {
1090 rc = ILBADM_ENOMEM;
1091 return (rc);
1092 }
1093 (void) snprintf(fnames, len, "%s,TIME", fieldnames);
1094 fieldnames = fnames;
1095 }
1096 }
1097
1098 if (A_opt && d_opt) {
1099 ilbadm_err(gettext("options -d and -A are mutually exclusive"));
1100 exit(1);
1101 }
1102
1103 /* find and parse interval and count arguments if present */
1104 if (optind < argc) {
1105 sta.ilbst_interval = atoi(argv[optind]);
1106 if (sta.ilbst_interval < 1) {
1107 ilbadm_err(gettext("illegal interval spec %s"),
1108 argv[optind]);
1109 exit(1);
1110 }
1111 sta.ilbst_count = -1;
1112 if (++optind < argc) {
1113 sta.ilbst_count = atoi(argv[optind]);
1114 if (sta.ilbst_count < 1) {
1115 ilbadm_err(gettext("illegal count spec %s"),
1116 argv[optind]);
1117 exit(1);
1118 }
1119 }
1120 }
1121
1122 oerr = ofmt_open(fieldnames, fields, oflags, 80, &oh);
1123 if (oerr != OFMT_SUCCESS) {
1124 char e[80];
1125
1126 ilbadm_err(gettext("ofmt_open failed: %s"),
1127 ofmt_strerror(oh, oerr, e, sizeof (e)));
1128 return (ILBADM_LIBERR);
1129 }
1130
1131 sta.ilbst_oh = oh;
1132
1133 rc = i_do_show_stats(&sta);
1134
1135 ofmt_close(oh);
1136 return (rc);
1137 }
1138