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