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 [-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 flow_arg = B_FALSE; 550 datalink_id_t linkid = DATALINK_ALL_LINKID; 551 char linkname[MAXLINKNAMELEN]; 552 char flowname[MAXFLOWNAMELEN]; 553 uint32_t interval = 0; 554 char unit = '\0'; 555 show_flow_state_t state; 556 char *fields_str = NULL; 557 char *o_fields_str = NULL; 558 559 char *total_stat_fields = 560 "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs"; 561 char *rx_stat_fields = 562 "flow,ipkts,rbytes,ierrs"; 563 char *tx_stat_fields = 564 "flow,opkts,obytes,oerrs"; 565 566 ofmt_handle_t ofmt; 567 ofmt_status_t oferr; 568 uint_t ofmtflags = OFMT_RIGHTJUST; 569 570 dladm_flow_attr_t attr; 571 572 (void) setlocale(LC_ALL, ""); 573 #if !defined(TEXT_DOMAIN) 574 #define TEXT_DOMAIN "SYS_TEST" 575 #endif 576 (void) textdomain(TEXT_DOMAIN); 577 578 progname = argv[0]; 579 580 /* Open the libdladm handle */ 581 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) 582 die_dlerr(status, "could not open /dev/dld"); 583 584 bzero(&state, sizeof (state)); 585 586 opterr = 0; 587 while ((option = getopt_long(argc, argv, ":rtApi:o:u:l:h", 588 NULL, NULL)) != -1) { 589 switch (option) { 590 case 'r': 591 if (r_arg) 592 die_optdup(option); 593 594 r_arg = B_TRUE; 595 break; 596 case 't': 597 if (t_arg) 598 die_optdup(option); 599 600 t_arg = B_TRUE; 601 break; 602 case 'A': 603 if (A_arg) 604 die_optdup(option); 605 606 A_arg = B_TRUE; 607 break; 608 case 'p': 609 if (p_arg) 610 die_optdup(option); 611 612 p_arg = B_TRUE; 613 break; 614 case 'i': 615 if (i_arg) 616 die_optdup(option); 617 618 i_arg = B_TRUE; 619 if (!dladm_str2interval(optarg, &interval)) 620 die("invalid interval value '%s'", optarg); 621 break; 622 case 'o': 623 o_arg = B_TRUE; 624 o_fields_str = optarg; 625 break; 626 case 'u': 627 if (u_arg) 628 die_optdup(option); 629 630 u_arg = B_TRUE; 631 if (!flowstat_unit(optarg, &unit)) 632 die("invalid unit value '%s'," 633 "unit must be R|K|M|G|T|P", optarg); 634 break; 635 case 'l': 636 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 637 >= MAXLINKNAMELEN) 638 die("link name too long\n"); 639 if (dladm_name2info(handle, linkname, &linkid, NULL, 640 NULL, NULL) != DLADM_STATUS_OK) 641 die("invalid link '%s'", linkname); 642 break; 643 case 'h': 644 if (r_arg || t_arg || p_arg || o_arg || u_arg || 645 i_arg || A_arg) { 646 die("the option -h is not compatible with " 647 "-r, -t, -p, -o, -u, -i, -A"); 648 } 649 do_show_history(argc, argv); 650 return (0); 651 break; 652 default: 653 die_opterr(optopt, option, usage_ermsg); 654 break; 655 } 656 } 657 658 if (r_arg && t_arg) 659 die("the option -t and -r are not compatible"); 660 661 if (u_arg && p_arg) 662 die("the option -u and -p are not compatible"); 663 664 if (p_arg && !o_arg) 665 die("-p requires -o"); 666 667 if (p_arg && strcasecmp(o_fields_str, "all") == 0) 668 die("\"-o all\" is invalid with -p"); 669 670 if (A_arg && 671 (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) 672 die("the option -A is not compatible with " 673 "-r, -t, -p, -o, -u, -i"); 674 675 /* get flow name (optional last argument) */ 676 if (optind == (argc-1)) { 677 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 678 >= MAXFLOWNAMELEN) 679 die("flow name too long"); 680 flow_arg = B_TRUE; 681 } else if (optind != argc) { 682 usage(); 683 } 684 685 if (flow_arg && 686 dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK) 687 die("invalid flow %s", flowname); 688 689 if (A_arg) { 690 dump_all_flow_stats(&attr, &state, linkid, flow_arg); 691 return (0); 692 } 693 694 state.fs_unit = unit; 695 state.fs_parsable = p_arg; 696 697 if (state.fs_parsable) 698 ofmtflags |= OFMT_PARSABLE; 699 700 if (r_arg) 701 fields_str = rx_stat_fields; 702 else if (t_arg) 703 fields_str = tx_stat_fields; 704 else 705 fields_str = total_stat_fields; 706 707 if (o_arg) { 708 fields_str = (strcasecmp(o_fields_str, "all") == 0) ? 709 fields_str : o_fields_str; 710 } 711 712 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt); 713 flowstat_ofmt_check(oferr, state.fs_parsable, ofmt); 714 state.fs_ofmt = ofmt; 715 716 for (;;) { 717 /* Show stats for named flow */ 718 if (flow_arg) { 719 (void) query_flow_stats(handle, &attr, &state); 720 721 /* Show stats for flows on one link */ 722 } else if (linkid != DATALINK_INVALID_LINKID) { 723 (void) dladm_walk_flow(query_flow_stats, handle, linkid, 724 &state, B_FALSE); 725 726 /* Show stats for all flows on all links */ 727 } else { 728 (void) dladm_walk_datalink_id(query_link_flow_stats, 729 handle, &state, DATALINK_CLASS_ALL, 730 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 731 } 732 733 if (interval == 0) 734 break; 735 736 (void) fflush(stdout); 737 cleanup_removed_flows(&state); 738 (void) sleep(interval); 739 } 740 ofmt_close(ofmt); 741 742 dladm_close(handle); 743 return (0); 744 } 745 746 /* ARGSUSED */ 747 static int 748 show_history_date(dladm_usage_t *history, void *arg) 749 { 750 show_history_state_t *state = (show_history_state_t *)arg; 751 time_t stime; 752 char timebuf[20]; 753 dladm_flow_attr_t attr; 754 dladm_status_t status; 755 756 /* 757 * Only show historical information for existing flows unless '-a' 758 * is specified. 759 */ 760 if (!state->us_showall && ((status = dladm_flow_info(handle, 761 history->du_name, &attr)) != DLADM_STATUS_OK)) { 762 return (status); 763 } 764 765 stime = history->du_stime; 766 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", 767 localtime(&stime)); 768 (void) printf("%s\n", timebuf); 769 770 return (DLADM_STATUS_OK); 771 } 772 773 static int 774 show_history_time(dladm_usage_t *history, void *arg) 775 { 776 show_history_state_t *state = (show_history_state_t *)arg; 777 char buf[DLADM_STRSIZE]; 778 history_l_fields_buf_t ubuf; 779 time_t time; 780 double bw; 781 dladm_flow_attr_t attr; 782 dladm_status_t status; 783 784 /* 785 * Only show historical information for existing flows unless '-a' 786 * is specified. 787 */ 788 if (!state->us_showall && ((status = dladm_flow_info(handle, 789 history->du_name, &attr)) != DLADM_STATUS_OK)) { 790 return (status); 791 } 792 793 if (state->us_plot) { 794 if (!state->us_printheader) { 795 if (state->us_first) { 796 (void) printf("# Time"); 797 state->us_first = B_FALSE; 798 } 799 (void) printf(" %s", history->du_name); 800 if (history->du_last) { 801 (void) printf("\n"); 802 state->us_first = B_TRUE; 803 state->us_printheader = B_TRUE; 804 } 805 } else { 806 if (state->us_first) { 807 time = history->du_etime; 808 (void) strftime(buf, sizeof (buf), "%T", 809 localtime(&time)); 810 state->us_first = B_FALSE; 811 (void) printf("%s", buf); 812 } 813 bw = (double)history->du_bandwidth/1000; 814 (void) printf(" %.2f", bw); 815 if (history->du_last) { 816 (void) printf("\n"); 817 state->us_first = B_TRUE; 818 } 819 } 820 return (DLADM_STATUS_OK); 821 } 822 823 bzero(&ubuf, sizeof (ubuf)); 824 825 (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s", 826 history->du_name); 827 time = history->du_stime; 828 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 829 (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime), 830 "%s", buf); 831 time = history->du_etime; 832 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 833 (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime), 834 "%s", buf); 835 (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes), 836 "%llu", history->du_rbytes); 837 (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes), 838 "%llu", history->du_obytes); 839 (void) snprintf(ubuf.history_l_bandwidth, 840 sizeof (ubuf.history_l_bandwidth), "%s Mbps", 841 dladm_bw2str(history->du_bandwidth, buf)); 842 843 ofmt_print(state->us_ofmt, (void *)&ubuf); 844 return (DLADM_STATUS_OK); 845 } 846 847 static int 848 show_history_res(dladm_usage_t *history, void *arg) 849 { 850 show_history_state_t *state = (show_history_state_t *)arg; 851 char buf[DLADM_STRSIZE]; 852 history_fields_buf_t ubuf; 853 dladm_flow_attr_t attr; 854 dladm_status_t status; 855 856 /* 857 * Only show historical information for existing flows unless '-a' 858 * is specified. 859 */ 860 if (!state->us_showall && ((status = dladm_flow_info(handle, 861 history->du_name, &attr)) != DLADM_STATUS_OK)) { 862 return (status); 863 } 864 865 bzero(&ubuf, sizeof (ubuf)); 866 867 (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s", 868 history->du_name); 869 (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration), 870 "%llu", history->du_duration); 871 (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets), 872 "%llu", history->du_ipackets); 873 (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes), 874 "%llu", history->du_rbytes); 875 (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets), 876 "%llu", history->du_opackets); 877 (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes), 878 "%llu", history->du_obytes); 879 (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth), 880 "%s Mbps", dladm_bw2str(history->du_bandwidth, buf)); 881 882 ofmt_print(state->us_ofmt, (void *)&ubuf); 883 884 return (DLADM_STATUS_OK); 885 } 886 887 static boolean_t 888 valid_formatspec(char *formatspec_str) 889 { 890 return (strcmp(formatspec_str, "gnuplot") == 0); 891 } 892 893 /* ARGSUSED */ 894 static void 895 do_show_history(int argc, char *argv[]) 896 { 897 char *file = NULL; 898 int opt; 899 dladm_status_t status; 900 boolean_t d_arg = B_FALSE; 901 char *stime = NULL; 902 char *etime = NULL; 903 char *resource = NULL; 904 show_history_state_t state; 905 boolean_t o_arg = B_FALSE; 906 boolean_t F_arg = B_FALSE; 907 char *fields_str = NULL; 908 char *formatspec_str = NULL; 909 char *all_fields = 910 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; 911 char *all_l_fields = 912 "flow,start,end,rbytes,obytes,bandwidth"; 913 ofmt_handle_t ofmt; 914 ofmt_status_t oferr; 915 uint_t ofmtflags = 0; 916 917 bzero(&state, sizeof (show_history_state_t)); 918 state.us_parsable = B_FALSE; 919 state.us_printheader = B_FALSE; 920 state.us_plot = B_FALSE; 921 state.us_first = B_TRUE; 922 923 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) { 924 switch (opt) { 925 case 'd': 926 d_arg = B_TRUE; 927 break; 928 case 'a': 929 state.us_showall = B_TRUE; 930 break; 931 case 'f': 932 file = optarg; 933 break; 934 case 's': 935 stime = optarg; 936 break; 937 case 'e': 938 etime = optarg; 939 break; 940 case 'o': 941 o_arg = B_TRUE; 942 fields_str = optarg; 943 break; 944 case 'F': 945 state.us_plot = F_arg = B_TRUE; 946 formatspec_str = optarg; 947 break; 948 default: 949 die_opterr(optopt, opt, usage_ermsg); 950 } 951 } 952 953 if (file == NULL) 954 die("-h requires a file"); 955 956 if (optind == (argc-1)) { 957 dladm_flow_attr_t attr; 958 959 resource = argv[optind]; 960 if (!state.us_showall && 961 dladm_flow_info(handle, resource, &attr) != 962 DLADM_STATUS_OK) { 963 die("invalid flow: '%s'", resource); 964 } 965 } 966 967 if (state.us_parsable) 968 ofmtflags |= OFMT_PARSABLE; 969 if (resource == NULL && stime == NULL && etime == NULL) { 970 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 971 fields_str = all_fields; 972 oferr = ofmt_open(fields_str, history_fields, ofmtflags, 973 0, &ofmt); 974 } else { 975 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 976 fields_str = all_l_fields; 977 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 978 0, &ofmt); 979 } 980 981 flowstat_ofmt_check(oferr, state.us_parsable, ofmt); 982 state.us_ofmt = ofmt; 983 984 if (F_arg && d_arg) 985 die("incompatible -d and -F options"); 986 987 if (F_arg && !valid_formatspec(formatspec_str)) 988 die("Format specifier %s not supported", formatspec_str); 989 990 if (d_arg) { 991 /* Print log dates */ 992 status = dladm_usage_dates(show_history_date, 993 DLADM_LOGTYPE_FLOW, file, resource, &state); 994 } else if (resource == NULL && stime == NULL && etime == NULL && 995 !F_arg) { 996 /* Print summary */ 997 status = dladm_usage_summary(show_history_res, 998 DLADM_LOGTYPE_FLOW, file, &state); 999 } else if (resource != NULL) { 1000 /* Print log entries for named resource */ 1001 status = dladm_walk_usage_res(show_history_time, 1002 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); 1003 } else { 1004 /* Print time and information for each flow */ 1005 status = dladm_walk_usage_time(show_history_time, 1006 DLADM_LOGTYPE_FLOW, file, stime, etime, &state); 1007 } 1008 1009 ofmt_close(ofmt); 1010 if (status != DLADM_STATUS_OK) 1011 die_dlerr(status, "-h"); 1012 dladm_close(handle); 1013 } 1014 1015 static void 1016 warn(const char *format, ...) 1017 { 1018 va_list alist; 1019 1020 format = gettext(format); 1021 (void) fprintf(stderr, "%s: warning: ", progname); 1022 1023 va_start(alist, format); 1024 (void) vfprintf(stderr, format, alist); 1025 va_end(alist); 1026 1027 (void) putc('\n', stderr); 1028 } 1029 1030 /* PRINTFLIKE1 */ 1031 static void 1032 die(const char *format, ...) 1033 { 1034 va_list alist; 1035 1036 format = gettext(format); 1037 (void) fprintf(stderr, "%s: ", progname); 1038 1039 va_start(alist, format); 1040 (void) vfprintf(stderr, format, alist); 1041 va_end(alist); 1042 1043 (void) putc('\n', stderr); 1044 1045 /* close dladm handle if it was opened */ 1046 if (handle != NULL) 1047 dladm_close(handle); 1048 1049 exit(EXIT_FAILURE); 1050 } 1051 1052 static void 1053 die_optdup(int opt) 1054 { 1055 die("the option -%c cannot be specified more than once", opt); 1056 } 1057 1058 static void 1059 die_opterr(int opt, int opterr, const char *usage) 1060 { 1061 switch (opterr) { 1062 case ':': 1063 die("option '-%c' requires a value\nusage: %s", opt, 1064 gettext(usage)); 1065 break; 1066 case '?': 1067 default: 1068 die("unrecognized option '-%c'\nusage: %s", opt, 1069 gettext(usage)); 1070 break; 1071 } 1072 } 1073 1074 /* PRINTFLIKE2 */ 1075 static void 1076 die_dlerr(dladm_status_t err, const char *format, ...) 1077 { 1078 va_list alist; 1079 char errmsg[DLADM_STRSIZE]; 1080 1081 format = gettext(format); 1082 (void) fprintf(stderr, "%s: ", progname); 1083 1084 va_start(alist, format); 1085 (void) vfprintf(stderr, format, alist); 1086 va_end(alist); 1087 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1088 1089 /* close dladm handle if it was opened */ 1090 if (handle != NULL) 1091 dladm_close(handle); 1092 1093 exit(EXIT_FAILURE); 1094 } 1095 1096 1097 /* 1098 * default output callback function that, when invoked from dladm_print_output, 1099 * prints string which is offset by of_arg->ofmt_id within buf. 1100 */ 1101 static boolean_t 1102 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 1103 { 1104 char *value; 1105 1106 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id; 1107 (void) strlcpy(buf, value, bufsize); 1108 return (B_TRUE); 1109 } 1110 1111 static void 1112 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable, 1113 ofmt_handle_t ofmt) 1114 { 1115 char buf[OFMT_BUFSIZE]; 1116 1117 if (oferr == OFMT_SUCCESS) 1118 return; 1119 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); 1120 /* 1121 * All errors are considered fatal in parsable mode. 1122 * NOMEM errors are always fatal, regardless of mode. 1123 * For other errors, we print diagnostics in human-readable 1124 * mode and processs what we can. 1125 */ 1126 if (parsable || oferr == OFMT_ENOFIELDS) { 1127 ofmt_close(ofmt); 1128 die(buf); 1129 } else { 1130 warn(buf); 1131 } 1132 } 1133