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