1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <locale.h> 28 #include <stdarg.h> 29 #include <stdlib.h> 30 #include <fcntl.h> 31 #include <string.h> 32 #include <stropts.h> 33 #include <errno.h> 34 #include <strings.h> 35 #include <getopt.h> 36 #include <unistd.h> 37 #include <priv.h> 38 #include <netdb.h> 39 #include <libintl.h> 40 #include <libdlflow.h> 41 #include <libdllink.h> 42 #include <libdlstat.h> 43 #include <sys/types.h> 44 #include <sys/socket.h> 45 #include <netinet/in.h> 46 #include <arpa/inet.h> 47 #include <sys/ethernet.h> 48 #include <inet/ip.h> 49 #include <inet/ip6.h> 50 #include <stddef.h> 51 #include <ofmt.h> 52 53 typedef struct flow_chain_s { 54 char fc_flowname[MAXFLOWNAMELEN]; 55 boolean_t fc_visited; 56 flow_stat_t *fc_stat; 57 struct flow_chain_s *fc_next; 58 } flow_chain_t; 59 60 typedef struct show_flow_state { 61 flow_chain_t *fs_flowchain; 62 ofmt_handle_t fs_ofmt; 63 char fs_unit; 64 boolean_t fs_parsable; 65 } show_flow_state_t; 66 67 typedef struct show_history_state_s { 68 boolean_t us_plot; 69 boolean_t us_parsable; 70 boolean_t us_printheader; 71 boolean_t us_first; 72 boolean_t us_showall; 73 ofmt_handle_t us_ofmt; 74 } show_history_state_t; 75 76 static void do_show_history(int, char **); 77 78 static int query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *); 79 static int query_link_flow_stats(dladm_handle_t, datalink_id_t, void *); 80 81 static void die(const char *, ...); 82 static void die_optdup(int); 83 static void die_opterr(int, int, const char *); 84 static void die_dlerr(dladm_status_t, const char *, ...); 85 static void warn(const char *, ...); 86 87 /* callback functions for printing output */ 88 static ofmt_cb_t print_default_cb, print_flow_stats_cb; 89 static void flowstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t); 90 91 #define NULL_OFMT {NULL, 0, 0, NULL} 92 93 /* 94 * structures for flowstat (printing live statistics) 95 */ 96 typedef enum { 97 FLOW_S_FLOW, 98 FLOW_S_IPKTS, 99 FLOW_S_RBYTES, 100 FLOW_S_IERRORS, 101 FLOW_S_OPKTS, 102 FLOW_S_OBYTES, 103 FLOW_S_OERRORS 104 } flow_s_field_index_t; 105 106 static ofmt_field_t flow_s_fields[] = { 107 /* name, field width, index, callback */ 108 { "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb}, 109 { "IPKTS", 8, FLOW_S_IPKTS, print_flow_stats_cb}, 110 { "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb}, 111 { "IERRS", 8, FLOW_S_IERRORS, print_flow_stats_cb}, 112 { "OPKTS", 8, FLOW_S_OPKTS, print_flow_stats_cb}, 113 { "OBYTES", 8, FLOW_S_OBYTES, print_flow_stats_cb}, 114 { "OERRS", 8, FLOW_S_OERRORS, print_flow_stats_cb}, 115 NULL_OFMT} 116 ; 117 118 typedef struct flow_args_s { 119 char *flow_s_flow; 120 flow_stat_t *flow_s_stat; 121 char flow_s_unit; 122 boolean_t flow_s_parsable; 123 } flow_args_t; 124 125 /* 126 * structures for 'flowstat -h' 127 */ 128 typedef struct history_fields_buf_s { 129 char history_flow[12]; 130 char history_duration[10]; 131 char history_ipackets[9]; 132 char history_rbytes[10]; 133 char history_opackets[9]; 134 char history_obytes[10]; 135 char history_bandwidth[14]; 136 } history_fields_buf_t; 137 138 static ofmt_field_t history_fields[] = { 139 /* name, field width, offset */ 140 { "FLOW", 13, 141 offsetof(history_fields_buf_t, history_flow), print_default_cb}, 142 { "DURATION", 11, 143 offsetof(history_fields_buf_t, history_duration), print_default_cb}, 144 { "IPACKETS", 10, 145 offsetof(history_fields_buf_t, history_ipackets), print_default_cb}, 146 { "RBYTES", 11, 147 offsetof(history_fields_buf_t, history_rbytes), print_default_cb}, 148 { "OPACKETS", 10, 149 offsetof(history_fields_buf_t, history_opackets), print_default_cb}, 150 { "OBYTES", 11, 151 offsetof(history_fields_buf_t, history_obytes), print_default_cb}, 152 { "BANDWIDTH", 15, 153 offsetof(history_fields_buf_t, history_bandwidth), print_default_cb}, 154 NULL_OFMT} 155 ; 156 157 typedef struct history_l_fields_buf_s { 158 char history_l_flow[12]; 159 char history_l_stime[13]; 160 char history_l_etime[13]; 161 char history_l_rbytes[8]; 162 char history_l_obytes[8]; 163 char history_l_bandwidth[14]; 164 } history_l_fields_buf_t; 165 166 static ofmt_field_t history_l_fields[] = { 167 /* name, field width, offset */ 168 { "FLOW", 13, 169 offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb}, 170 { "START", 14, 171 offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb}, 172 { "END", 14, 173 offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb}, 174 { "RBYTES", 9, 175 offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb}, 176 { "OBYTES", 9, 177 offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb}, 178 { "BANDWIDTH", 15, 179 offsetof(history_l_fields_buf_t, history_l_bandwidth), 180 print_default_cb}, 181 NULL_OFMT} 182 ; 183 184 static char *progname; 185 186 /* 187 * Handle to libdladm. Opened in main() before the sub-command 188 * specific function is called. 189 */ 190 static dladm_handle_t handle = NULL; 191 192 const char *usage_ermsg = "flowstat [-r | -t] [-i interval] " 193 "[-l link] [flow]\n" 194 " flowstat [-S] [-A] [-i interval] [-p] [ -o field[,...]]\n" 195 " [-u R|K|M|G|T|P] [-l link] [flow]\n" 196 " flowstat -h [-a] [-d] [-F format]" 197 " [-s <DD/MM/YYYY,HH:MM:SS>]\n" 198 " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> " 199 "[<flow>]"; 200 201 static void 202 usage(void) 203 { 204 (void) fprintf(stderr, "%s\n", gettext(usage_ermsg)); 205 206 /* close dladm handle if it was opened */ 207 if (handle != NULL) 208 dladm_close(handle); 209 210 exit(1); 211 } 212 213 boolean_t 214 flowstat_unit(char *oarg, char *unit) 215 { 216 if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) || 217 (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) || 218 (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) { 219 *unit = oarg[0]; 220 return (B_TRUE); 221 } 222 223 return (B_FALSE); 224 } 225 226 void 227 map_to_units(char *buf, uint_t bufsize, double num, char unit, 228 boolean_t parsable) 229 { 230 if (parsable) { 231 (void) snprintf(buf, bufsize, "%.0lf", num); 232 return; 233 } 234 235 if (unit == '\0') { 236 int index; 237 238 for (index = 0; (int)(num/1000) != 0; index++, num /= 1000) 239 ; 240 241 switch (index) { 242 case 0: 243 unit = '\0'; 244 break; 245 case 1: 246 unit = 'K'; 247 break; 248 case 2: 249 unit = 'M'; 250 break; 251 case 3: 252 unit = 'G'; 253 break; 254 case 4: 255 unit = 'T'; 256 break; 257 case 5: 258 /* Largest unit supported */ 259 default: 260 unit = 'P'; 261 break; 262 } 263 } else { 264 switch (unit) { 265 case 'R': 266 /* Already raw numbers */ 267 unit = '\0'; 268 break; 269 case 'K': 270 num /= 1000; 271 break; 272 case 'M': 273 num /= (1000*1000); 274 break; 275 case 'G': 276 num /= (1000*1000*1000); 277 break; 278 case 'T': 279 num /= (1000.0*1000.0*1000.0*1000.0); 280 break; 281 case 'P': 282 /* Largest unit supported */ 283 default: 284 num /= (1000.0*1000.0*1000.0*1000.0*1000.0); 285 break; 286 } 287 } 288 289 if (unit == '\0') 290 (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit); 291 else 292 (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit); 293 } 294 295 flow_chain_t * 296 get_flow_prev_stat(const char *flowname, void *arg) 297 { 298 show_flow_state_t *state = arg; 299 flow_chain_t *flow_curr = NULL; 300 301 /* Scan prev flowname list and look for entry matching this entry */ 302 for (flow_curr = state->fs_flowchain; flow_curr; 303 flow_curr = flow_curr->fc_next) { 304 if (strcmp(flow_curr->fc_flowname, flowname) == 0) 305 break; 306 } 307 308 /* New flow, add it */ 309 if (flow_curr == NULL) { 310 flow_curr = (flow_chain_t *)malloc(sizeof (flow_chain_t)); 311 if (flow_curr == NULL) 312 goto done; 313 (void) strncpy(flow_curr->fc_flowname, flowname, 314 MAXFLOWNAMELEN); 315 flow_curr->fc_stat = NULL; 316 flow_curr->fc_next = state->fs_flowchain; 317 state->fs_flowchain = flow_curr; 318 } 319 done: 320 return (flow_curr); 321 } 322 323 /* 324 * Number of flows may change while flowstat -i is executing. 325 * Free memory allocated for flows that are no longer there. 326 * Prepare for next iteration by marking visited = false for 327 * existing stat entries. 328 */ 329 static void 330 cleanup_removed_flows(show_flow_state_t *state) 331 { 332 flow_chain_t *fcurr; 333 flow_chain_t *fprev; 334 flow_chain_t *tofree; 335 336 /* Delete all nodes from the list that have fc_visited marked false */ 337 fcurr = state->fs_flowchain; 338 while (fcurr != NULL) { 339 if (fcurr->fc_visited) { 340 fcurr->fc_visited = B_FALSE; 341 fprev = fcurr; 342 fcurr = fcurr->fc_next; 343 continue; 344 } 345 346 /* Is it head of the list? */ 347 if (fcurr == state->fs_flowchain) 348 state->fs_flowchain = fcurr->fc_next; 349 else 350 fprev->fc_next = fcurr->fc_next; 351 352 /* fprev remains the same */ 353 tofree = fcurr; 354 fcurr = fcurr->fc_next; 355 356 /* Free stats memory for the removed flow */ 357 dladm_flow_stat_free(tofree->fc_stat); 358 free(tofree); 359 } 360 } 361 362 static boolean_t 363 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 364 { 365 flow_args_t *fargs = of_arg->ofmt_cbarg; 366 flow_stat_t *diff_stats = fargs->flow_s_stat; 367 char unit = fargs->flow_s_unit; 368 boolean_t parsable = fargs->flow_s_parsable; 369 370 switch (of_arg->ofmt_id) { 371 case FLOW_S_FLOW: 372 (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow); 373 break; 374 case FLOW_S_IPKTS: 375 map_to_units(buf, bufsize, diff_stats->fl_ipackets, unit, 376 parsable); 377 break; 378 case FLOW_S_RBYTES: 379 map_to_units(buf, bufsize, diff_stats->fl_rbytes, unit, 380 parsable); 381 break; 382 case FLOW_S_IERRORS: 383 map_to_units(buf, bufsize, diff_stats->fl_ierrors, unit, 384 parsable); 385 break; 386 case FLOW_S_OPKTS: 387 map_to_units(buf, bufsize, diff_stats->fl_opackets, unit, 388 parsable); 389 break; 390 case FLOW_S_OBYTES: 391 map_to_units(buf, bufsize, diff_stats->fl_obytes, unit, 392 parsable); 393 break; 394 case FLOW_S_OERRORS: 395 map_to_units(buf, bufsize, diff_stats->fl_oerrors, unit, 396 parsable); 397 break; 398 default: 399 die("invalid input"); 400 break; 401 } 402 return (B_TRUE); 403 } 404 405 /* ARGSUSED */ 406 static int 407 query_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 408 { 409 show_flow_state_t *state = arg; 410 flow_chain_t *flow_node; 411 flow_stat_t *curr_stat; 412 flow_stat_t *prev_stat; 413 flow_stat_t *diff_stat; 414 char *flowname = attr->fa_flowname; 415 flow_args_t fargs; 416 417 /* Get previous stats for the flow */ 418 flow_node = get_flow_prev_stat(flowname, arg); 419 if (flow_node == NULL) 420 goto done; 421 422 flow_node->fc_visited = B_TRUE; 423 prev_stat = flow_node->fc_stat; 424 425 /* Query library for current stats */ 426 curr_stat = dladm_flow_stat_query(flowname); 427 if (curr_stat == NULL) 428 goto done; 429 430 /* current stats - prev iteration stats */ 431 diff_stat = dladm_flow_stat_diff(curr_stat, prev_stat); 432 433 /* Free prev stats */ 434 dladm_flow_stat_free(prev_stat); 435 436 /* Prev <- curr stats */ 437 flow_node->fc_stat = curr_stat; 438 439 if (diff_stat == NULL) 440 goto done; 441 442 /* Print stats */ 443 fargs.flow_s_flow = flowname; 444 fargs.flow_s_stat = diff_stat; 445 fargs.flow_s_unit = state->fs_unit; 446 fargs.flow_s_parsable = state->fs_parsable; 447 ofmt_print(state->fs_ofmt, &fargs); 448 449 /* Free diff stats */ 450 dladm_flow_stat_free(diff_stat); 451 done: 452 return (DLADM_WALK_CONTINUE); 453 } 454 455 /* 456 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for 457 * dladm_walk_datalink_id(). Used for showing flow stats for 458 * all flows on all links. 459 */ 460 static int 461 query_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 462 { 463 if (dladm_walk_flow(query_flow_stats, dh, linkid, arg, B_FALSE) 464 == DLADM_STATUS_OK) 465 return (DLADM_WALK_CONTINUE); 466 else 467 return (DLADM_WALK_TERMINATE); 468 } 469 470 void 471 print_all_stats(name_value_stat_entry_t *stat_entry) 472 { 473 name_value_stat_t *curr_stat; 474 475 printf("%s\n", stat_entry->nve_header); 476 477 for (curr_stat = stat_entry->nve_stats; curr_stat != NULL; 478 curr_stat = curr_stat->nv_nextstat) { 479 printf("\t%15s", curr_stat->nv_statname); 480 printf("\t%15llu\n", curr_stat->nv_statval); 481 } 482 } 483 484 /* ARGSUSED */ 485 static int 486 dump_one_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 487 { 488 char *flowname = attr->fa_flowname; 489 void *stat; 490 491 stat = dladm_flow_stat_query_all(flowname); 492 if (stat == NULL) 493 goto done; 494 print_all_stats(stat); 495 dladm_flow_stat_query_all_free(stat); 496 497 done: 498 return (DLADM_WALK_CONTINUE); 499 } 500 501 /* 502 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for 503 * dladm_walk_datalink_id(). Used for showing flow stats for 504 * all flows on all links. 505 */ 506 static int 507 dump_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 508 { 509 if (dladm_walk_flow(dump_one_flow_stats, dh, linkid, arg, B_FALSE) 510 == DLADM_STATUS_OK) 511 return (DLADM_WALK_CONTINUE); 512 else 513 return (DLADM_WALK_TERMINATE); 514 } 515 516 static void 517 dump_all_flow_stats(dladm_flow_attr_t *attrp, void *arg, datalink_id_t linkid, 518 boolean_t flow_arg) 519 { 520 /* Show stats for named flow */ 521 if (flow_arg) { 522 (void) dump_one_flow_stats(handle, attrp, arg); 523 524 /* Show stats for flows on one link */ 525 } else if (linkid != DATALINK_INVALID_LINKID) { 526 (void) dladm_walk_flow(dump_one_flow_stats, handle, linkid, 527 arg, B_FALSE); 528 529 /* Show stats for all flows on all links */ 530 } else { 531 (void) dladm_walk_datalink_id(dump_link_flow_stats, 532 handle, arg, DATALINK_CLASS_ALL, 533 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 534 } 535 } 536 537 int 538 main(int argc, char *argv[]) 539 { 540 dladm_status_t status; 541 int option; 542 boolean_t r_arg = B_FALSE; 543 boolean_t t_arg = B_FALSE; 544 boolean_t p_arg = B_FALSE; 545 boolean_t i_arg = B_FALSE; 546 boolean_t o_arg = B_FALSE; 547 boolean_t u_arg = B_FALSE; 548 boolean_t A_arg = B_FALSE; 549 boolean_t S_arg = B_FALSE; 550 boolean_t flow_arg = B_FALSE; 551 datalink_id_t linkid = DATALINK_ALL_LINKID; 552 char linkname[MAXLINKNAMELEN]; 553 char flowname[MAXFLOWNAMELEN]; 554 uint32_t interval = 0; 555 char unit = '\0'; 556 show_flow_state_t state; 557 char *fields_str = NULL; 558 char *o_fields_str = NULL; 559 560 char *total_stat_fields = 561 "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs"; 562 char *rx_stat_fields = 563 "flow,ipkts,rbytes,ierrs"; 564 char *tx_stat_fields = 565 "flow,opkts,obytes,oerrs"; 566 567 ofmt_handle_t ofmt; 568 ofmt_status_t oferr; 569 uint_t ofmtflags = OFMT_RIGHTJUST; 570 571 dladm_flow_attr_t attr; 572 573 (void) setlocale(LC_ALL, ""); 574 #if !defined(TEXT_DOMAIN) 575 #define TEXT_DOMAIN "SYS_TEST" 576 #endif 577 (void) textdomain(TEXT_DOMAIN); 578 579 progname = argv[0]; 580 581 /* Open the libdladm handle */ 582 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) 583 die_dlerr(status, "could not open /dev/dld"); 584 585 bzero(&state, sizeof (state)); 586 587 opterr = 0; 588 while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:h", 589 NULL, NULL)) != -1) { 590 switch (option) { 591 case 'r': 592 if (r_arg) 593 die_optdup(option); 594 595 r_arg = B_TRUE; 596 break; 597 case 't': 598 if (t_arg) 599 die_optdup(option); 600 601 t_arg = B_TRUE; 602 break; 603 case 'A': 604 if (A_arg) 605 die_optdup(option); 606 607 A_arg = B_TRUE; 608 break; 609 case 'p': 610 if (p_arg) 611 die_optdup(option); 612 613 p_arg = B_TRUE; 614 break; 615 case 'S': 616 if (S_arg) 617 die_optdup(option); 618 S_arg = B_TRUE; 619 break; 620 case 'i': 621 if (i_arg) 622 die_optdup(option); 623 624 i_arg = B_TRUE; 625 if (!dladm_str2interval(optarg, &interval)) 626 die("invalid interval value '%s'", optarg); 627 break; 628 case 'o': 629 o_arg = B_TRUE; 630 o_fields_str = optarg; 631 break; 632 case 'u': 633 if (u_arg) 634 die_optdup(option); 635 636 u_arg = B_TRUE; 637 if (!flowstat_unit(optarg, &unit)) 638 die("invalid unit value '%s'," 639 "unit must be R|K|M|G|T|P", optarg); 640 break; 641 case 'l': 642 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 643 >= MAXLINKNAMELEN) 644 die("link name too long\n"); 645 if (dladm_name2info(handle, linkname, &linkid, NULL, 646 NULL, NULL) != DLADM_STATUS_OK) 647 die("invalid link '%s'", linkname); 648 break; 649 case 'h': 650 if (r_arg || t_arg || p_arg || o_arg || u_arg || 651 i_arg || S_arg || A_arg) { 652 die("the option -h is not compatible with " 653 "-r, -t, -p, -o, -u, -i, -S, -A"); 654 } 655 do_show_history(argc, argv); 656 return (0); 657 break; 658 default: 659 die_opterr(optopt, option, usage_ermsg); 660 break; 661 } 662 } 663 664 if (r_arg && t_arg) 665 die("the option -t and -r are not compatible"); 666 667 if (u_arg && p_arg) 668 die("the option -u and -p are not compatible"); 669 670 if (p_arg && !o_arg) 671 die("-p requires -o"); 672 673 if (p_arg && strcasecmp(o_fields_str, "all") == 0) 674 die("\"-o all\" is invalid with -p"); 675 676 if (S_arg && 677 (r_arg || t_arg || p_arg || o_arg || u_arg)) 678 die("the option -S is not compatible with " 679 "-r, -t, -p, -o, -u"); 680 681 if (A_arg && 682 (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) 683 die("the option -A is not compatible with " 684 "-r, -t, -p, -o, -u, -i"); 685 686 /* get flow name (optional last argument) */ 687 if (optind == (argc-1)) { 688 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 689 >= MAXFLOWNAMELEN) 690 die("flow name too long"); 691 flow_arg = B_TRUE; 692 } else if (optind != argc) { 693 usage(); 694 } 695 696 if (S_arg) { 697 dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL), 698 interval, FLOW_REPORT); 699 return (0); 700 } 701 702 if (flow_arg && 703 dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK) 704 die("invalid flow %s", flowname); 705 706 if (A_arg) { 707 dump_all_flow_stats(&attr, &state, linkid, flow_arg); 708 return (0); 709 } 710 711 state.fs_unit = unit; 712 state.fs_parsable = p_arg; 713 714 if (state.fs_parsable) 715 ofmtflags |= OFMT_PARSABLE; 716 717 if (r_arg) 718 fields_str = rx_stat_fields; 719 else if (t_arg) 720 fields_str = tx_stat_fields; 721 else 722 fields_str = total_stat_fields; 723 724 if (o_arg) { 725 fields_str = (strcasecmp(o_fields_str, "all") == 0) ? 726 fields_str : o_fields_str; 727 } 728 729 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt); 730 flowstat_ofmt_check(oferr, state.fs_parsable, ofmt); 731 state.fs_ofmt = ofmt; 732 733 for (;;) { 734 /* Show stats for named flow */ 735 if (flow_arg) { 736 (void) query_flow_stats(handle, &attr, &state); 737 738 /* Show stats for flows on one link */ 739 } else if (linkid != DATALINK_INVALID_LINKID) { 740 (void) dladm_walk_flow(query_flow_stats, handle, linkid, 741 &state, B_FALSE); 742 743 /* Show stats for all flows on all links */ 744 } else { 745 (void) dladm_walk_datalink_id(query_link_flow_stats, 746 handle, &state, DATALINK_CLASS_ALL, 747 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 748 } 749 750 if (interval == 0) 751 break; 752 753 (void) fflush(stdout); 754 cleanup_removed_flows(&state); 755 (void) sleep(interval); 756 } 757 ofmt_close(ofmt); 758 759 dladm_close(handle); 760 return (0); 761 } 762 763 /* ARGSUSED */ 764 static int 765 show_history_date(dladm_usage_t *history, void *arg) 766 { 767 show_history_state_t *state = (show_history_state_t *)arg; 768 time_t stime; 769 char timebuf[20]; 770 dladm_flow_attr_t attr; 771 dladm_status_t status; 772 773 /* 774 * Only show historical information for existing flows unless '-a' 775 * is specified. 776 */ 777 if (!state->us_showall && ((status = dladm_flow_info(handle, 778 history->du_name, &attr)) != DLADM_STATUS_OK)) { 779 return (status); 780 } 781 782 stime = history->du_stime; 783 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", 784 localtime(&stime)); 785 (void) printf("%s\n", timebuf); 786 787 return (DLADM_STATUS_OK); 788 } 789 790 static int 791 show_history_time(dladm_usage_t *history, void *arg) 792 { 793 show_history_state_t *state = (show_history_state_t *)arg; 794 char buf[DLADM_STRSIZE]; 795 history_l_fields_buf_t ubuf; 796 time_t time; 797 double bw; 798 dladm_flow_attr_t attr; 799 dladm_status_t status; 800 801 /* 802 * Only show historical information for existing flows unless '-a' 803 * is specified. 804 */ 805 if (!state->us_showall && ((status = dladm_flow_info(handle, 806 history->du_name, &attr)) != DLADM_STATUS_OK)) { 807 return (status); 808 } 809 810 if (state->us_plot) { 811 if (!state->us_printheader) { 812 if (state->us_first) { 813 (void) printf("# Time"); 814 state->us_first = B_FALSE; 815 } 816 (void) printf(" %s", history->du_name); 817 if (history->du_last) { 818 (void) printf("\n"); 819 state->us_first = B_TRUE; 820 state->us_printheader = B_TRUE; 821 } 822 } else { 823 if (state->us_first) { 824 time = history->du_etime; 825 (void) strftime(buf, sizeof (buf), "%T", 826 localtime(&time)); 827 state->us_first = B_FALSE; 828 (void) printf("%s", buf); 829 } 830 bw = (double)history->du_bandwidth/1000; 831 (void) printf(" %.2f", bw); 832 if (history->du_last) { 833 (void) printf("\n"); 834 state->us_first = B_TRUE; 835 } 836 } 837 return (DLADM_STATUS_OK); 838 } 839 840 bzero(&ubuf, sizeof (ubuf)); 841 842 (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s", 843 history->du_name); 844 time = history->du_stime; 845 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 846 (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime), 847 "%s", buf); 848 time = history->du_etime; 849 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 850 (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime), 851 "%s", buf); 852 (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes), 853 "%llu", history->du_rbytes); 854 (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes), 855 "%llu", history->du_obytes); 856 (void) snprintf(ubuf.history_l_bandwidth, 857 sizeof (ubuf.history_l_bandwidth), "%s Mbps", 858 dladm_bw2str(history->du_bandwidth, buf)); 859 860 ofmt_print(state->us_ofmt, (void *)&ubuf); 861 return (DLADM_STATUS_OK); 862 } 863 864 static int 865 show_history_res(dladm_usage_t *history, void *arg) 866 { 867 show_history_state_t *state = (show_history_state_t *)arg; 868 char buf[DLADM_STRSIZE]; 869 history_fields_buf_t ubuf; 870 dladm_flow_attr_t attr; 871 dladm_status_t status; 872 873 /* 874 * Only show historical information for existing flows unless '-a' 875 * is specified. 876 */ 877 if (!state->us_showall && ((status = dladm_flow_info(handle, 878 history->du_name, &attr)) != DLADM_STATUS_OK)) { 879 return (status); 880 } 881 882 bzero(&ubuf, sizeof (ubuf)); 883 884 (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s", 885 history->du_name); 886 (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration), 887 "%llu", history->du_duration); 888 (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets), 889 "%llu", history->du_ipackets); 890 (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes), 891 "%llu", history->du_rbytes); 892 (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets), 893 "%llu", history->du_opackets); 894 (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes), 895 "%llu", history->du_obytes); 896 (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth), 897 "%s Mbps", dladm_bw2str(history->du_bandwidth, buf)); 898 899 ofmt_print(state->us_ofmt, (void *)&ubuf); 900 901 return (DLADM_STATUS_OK); 902 } 903 904 static boolean_t 905 valid_formatspec(char *formatspec_str) 906 { 907 return (strcmp(formatspec_str, "gnuplot") == 0); 908 } 909 910 /* ARGSUSED */ 911 static void 912 do_show_history(int argc, char *argv[]) 913 { 914 char *file = NULL; 915 int opt; 916 dladm_status_t status; 917 boolean_t d_arg = B_FALSE; 918 char *stime = NULL; 919 char *etime = NULL; 920 char *resource = NULL; 921 show_history_state_t state; 922 boolean_t o_arg = B_FALSE; 923 boolean_t F_arg = B_FALSE; 924 char *fields_str = NULL; 925 char *formatspec_str = NULL; 926 char *all_fields = 927 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; 928 char *all_l_fields = 929 "flow,start,end,rbytes,obytes,bandwidth"; 930 ofmt_handle_t ofmt; 931 ofmt_status_t oferr; 932 uint_t ofmtflags = 0; 933 934 bzero(&state, sizeof (show_history_state_t)); 935 state.us_parsable = B_FALSE; 936 state.us_printheader = B_FALSE; 937 state.us_plot = B_FALSE; 938 state.us_first = B_TRUE; 939 940 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) { 941 switch (opt) { 942 case 'd': 943 d_arg = B_TRUE; 944 break; 945 case 'a': 946 state.us_showall = B_TRUE; 947 break; 948 case 'f': 949 file = optarg; 950 break; 951 case 's': 952 stime = optarg; 953 break; 954 case 'e': 955 etime = optarg; 956 break; 957 case 'o': 958 o_arg = B_TRUE; 959 fields_str = optarg; 960 break; 961 case 'F': 962 state.us_plot = F_arg = B_TRUE; 963 formatspec_str = optarg; 964 break; 965 default: 966 die_opterr(optopt, opt, usage_ermsg); 967 } 968 } 969 970 if (file == NULL) 971 die("-h requires a file"); 972 973 if (optind == (argc-1)) { 974 dladm_flow_attr_t attr; 975 976 resource = argv[optind]; 977 if (!state.us_showall && 978 dladm_flow_info(handle, resource, &attr) != 979 DLADM_STATUS_OK) { 980 die("invalid flow: '%s'", resource); 981 } 982 } 983 984 if (state.us_parsable) 985 ofmtflags |= OFMT_PARSABLE; 986 if (resource == NULL && stime == NULL && etime == NULL) { 987 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 988 fields_str = all_fields; 989 oferr = ofmt_open(fields_str, history_fields, ofmtflags, 990 0, &ofmt); 991 } else { 992 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 993 fields_str = all_l_fields; 994 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 995 0, &ofmt); 996 } 997 998 flowstat_ofmt_check(oferr, state.us_parsable, ofmt); 999 state.us_ofmt = ofmt; 1000 1001 if (F_arg && d_arg) 1002 die("incompatible -d and -F options"); 1003 1004 if (F_arg && !valid_formatspec(formatspec_str)) 1005 die("Format specifier %s not supported", formatspec_str); 1006 1007 if (d_arg) { 1008 /* Print log dates */ 1009 status = dladm_usage_dates(show_history_date, 1010 DLADM_LOGTYPE_FLOW, file, resource, &state); 1011 } else if (resource == NULL && stime == NULL && etime == NULL && 1012 !F_arg) { 1013 /* Print summary */ 1014 status = dladm_usage_summary(show_history_res, 1015 DLADM_LOGTYPE_FLOW, file, &state); 1016 } else if (resource != NULL) { 1017 /* Print log entries for named resource */ 1018 status = dladm_walk_usage_res(show_history_time, 1019 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); 1020 } else { 1021 /* Print time and information for each flow */ 1022 status = dladm_walk_usage_time(show_history_time, 1023 DLADM_LOGTYPE_FLOW, file, stime, etime, &state); 1024 } 1025 1026 ofmt_close(ofmt); 1027 if (status != DLADM_STATUS_OK) 1028 die_dlerr(status, "-h"); 1029 dladm_close(handle); 1030 } 1031 1032 static void 1033 warn(const char *format, ...) 1034 { 1035 va_list alist; 1036 1037 format = gettext(format); 1038 (void) fprintf(stderr, "%s: warning: ", progname); 1039 1040 va_start(alist, format); 1041 (void) vfprintf(stderr, format, alist); 1042 va_end(alist); 1043 1044 (void) putc('\n', stderr); 1045 } 1046 1047 /* PRINTFLIKE1 */ 1048 static void 1049 die(const char *format, ...) 1050 { 1051 va_list alist; 1052 1053 format = gettext(format); 1054 (void) fprintf(stderr, "%s: ", progname); 1055 1056 va_start(alist, format); 1057 (void) vfprintf(stderr, format, alist); 1058 va_end(alist); 1059 1060 (void) putc('\n', stderr); 1061 1062 /* close dladm handle if it was opened */ 1063 if (handle != NULL) 1064 dladm_close(handle); 1065 1066 exit(EXIT_FAILURE); 1067 } 1068 1069 static void 1070 die_optdup(int opt) 1071 { 1072 die("the option -%c cannot be specified more than once", opt); 1073 } 1074 1075 static void 1076 die_opterr(int opt, int opterr, const char *usage) 1077 { 1078 switch (opterr) { 1079 case ':': 1080 die("option '-%c' requires a value\nusage: %s", opt, 1081 gettext(usage)); 1082 break; 1083 case '?': 1084 default: 1085 die("unrecognized option '-%c'\nusage: %s", opt, 1086 gettext(usage)); 1087 break; 1088 } 1089 } 1090 1091 /* PRINTFLIKE2 */ 1092 static void 1093 die_dlerr(dladm_status_t err, const char *format, ...) 1094 { 1095 va_list alist; 1096 char errmsg[DLADM_STRSIZE]; 1097 1098 format = gettext(format); 1099 (void) fprintf(stderr, "%s: ", progname); 1100 1101 va_start(alist, format); 1102 (void) vfprintf(stderr, format, alist); 1103 va_end(alist); 1104 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1105 1106 /* close dladm handle if it was opened */ 1107 if (handle != NULL) 1108 dladm_close(handle); 1109 1110 exit(EXIT_FAILURE); 1111 } 1112 1113 1114 /* 1115 * default output callback function that, when invoked from dladm_print_output, 1116 * prints string which is offset by of_arg->ofmt_id within buf. 1117 */ 1118 static boolean_t 1119 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 1120 { 1121 char *value; 1122 1123 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id; 1124 (void) strlcpy(buf, value, bufsize); 1125 return (B_TRUE); 1126 } 1127 1128 static void 1129 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable, 1130 ofmt_handle_t ofmt) 1131 { 1132 char buf[OFMT_BUFSIZE]; 1133 1134 if (oferr == OFMT_SUCCESS) 1135 return; 1136 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); 1137 /* 1138 * All errors are considered fatal in parsable mode. 1139 * NOMEM errors are always fatal, regardless of mode. 1140 * For other errors, we print diagnostics in human-readable 1141 * mode and processs what we can. 1142 */ 1143 if (parsable || oferr == OFMT_ENOFIELDS) { 1144 ofmt_close(ofmt); 1145 die(buf); 1146 } else { 1147 warn(buf); 1148 } 1149 } 1150