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