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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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