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 = "usage: flowstat [-r | -t] [-i interval] " 193 "[-l link] [flow]\n" 194 " flowstat [-p | -u R|K|M|G|T|P] [ -o field[,...]] " 195 "[-l link] [flow]\n" 196 " flowstat [-A] [-l link] [flow]\n" 197 " flowstat [-S] [-i interval] [-l link] [flow]\n" 198 " flowstat -h [-a] [-d] [-F format] " 199 "[-s <DD/MM/YYYY,HH:MM:SS>]\n" 200 " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<flow>]"; 201 202 static void 203 usage(void) 204 { 205 (void) fprintf(stderr, "%s\n", gettext(usage_ermsg)); 206 207 /* close dladm handle if it was opened */ 208 if (handle != NULL) 209 dladm_close(handle); 210 211 exit(1); 212 } 213 214 boolean_t 215 flowstat_unit(char *oarg, char *unit) 216 { 217 if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) || 218 (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) || 219 (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) { 220 *unit = oarg[0]; 221 return (B_TRUE); 222 } 223 224 return (B_FALSE); 225 } 226 227 void 228 map_to_units(char *buf, uint_t bufsize, double num, char unit, 229 boolean_t parsable) 230 { 231 if (parsable) { 232 (void) snprintf(buf, bufsize, "%.0lf", num); 233 return; 234 } 235 236 if (unit == '\0') { 237 int index; 238 239 for (index = 0; (int)(num/1000) != 0; index++, num /= 1000) 240 ; 241 242 switch (index) { 243 case 0: 244 unit = '\0'; 245 break; 246 case 1: 247 unit = 'K'; 248 break; 249 case 2: 250 unit = 'M'; 251 break; 252 case 3: 253 unit = 'G'; 254 break; 255 case 4: 256 unit = 'T'; 257 break; 258 case 5: 259 /* Largest unit supported */ 260 default: 261 unit = 'P'; 262 break; 263 } 264 } else { 265 switch (unit) { 266 case 'R': 267 /* Already raw numbers */ 268 unit = '\0'; 269 break; 270 case 'K': 271 num /= 1000; 272 break; 273 case 'M': 274 num /= (1000*1000); 275 break; 276 case 'G': 277 num /= (1000*1000*1000); 278 break; 279 case 'T': 280 num /= (1000.0*1000.0*1000.0*1000.0); 281 break; 282 case 'P': 283 /* Largest unit supported */ 284 default: 285 num /= (1000.0*1000.0*1000.0*1000.0*1000.0); 286 break; 287 } 288 } 289 290 if (unit == '\0') 291 (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit); 292 else 293 (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit); 294 } 295 296 flow_chain_t * 297 get_flow_prev_stat(const char *flowname, void *arg) 298 { 299 show_flow_state_t *state = arg; 300 flow_chain_t *flow_curr = NULL; 301 302 /* Scan prev flowname list and look for entry matching this entry */ 303 for (flow_curr = state->fs_flowchain; flow_curr; 304 flow_curr = flow_curr->fc_next) { 305 if (strcmp(flow_curr->fc_flowname, flowname) == 0) 306 break; 307 } 308 309 /* New flow, add it */ 310 if (flow_curr == NULL) { 311 flow_curr = (flow_chain_t *)malloc(sizeof (flow_chain_t)); 312 if (flow_curr == NULL) 313 goto done; 314 (void) strncpy(flow_curr->fc_flowname, flowname, 315 MAXFLOWNAMELEN); 316 flow_curr->fc_stat = NULL; 317 flow_curr->fc_next = state->fs_flowchain; 318 state->fs_flowchain = flow_curr; 319 } 320 done: 321 return (flow_curr); 322 } 323 324 /* 325 * Number of flows may change while flowstat -i is executing. 326 * Free memory allocated for flows that are no longer there. 327 * Prepare for next iteration by marking visited = false for 328 * existing stat entries. 329 */ 330 static void 331 cleanup_removed_flows(show_flow_state_t *state) 332 { 333 flow_chain_t *fcurr; 334 flow_chain_t *fprev; 335 flow_chain_t *tofree; 336 337 /* Delete all nodes from the list that have fc_visited marked false */ 338 fcurr = state->fs_flowchain; 339 while (fcurr != NULL) { 340 if (fcurr->fc_visited) { 341 fcurr->fc_visited = B_FALSE; 342 fprev = fcurr; 343 fcurr = fcurr->fc_next; 344 continue; 345 } 346 347 /* Is it head of the list? */ 348 if (fcurr == state->fs_flowchain) 349 state->fs_flowchain = fcurr->fc_next; 350 else 351 fprev->fc_next = fcurr->fc_next; 352 353 /* fprev remains the same */ 354 tofree = fcurr; 355 fcurr = fcurr->fc_next; 356 357 /* Free stats memory for the removed flow */ 358 dladm_flow_stat_free(tofree->fc_stat); 359 free(tofree); 360 } 361 } 362 363 static boolean_t 364 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 365 { 366 flow_args_t *fargs = of_arg->ofmt_cbarg; 367 flow_stat_t *diff_stats = fargs->flow_s_stat; 368 char unit = fargs->flow_s_unit; 369 boolean_t parsable = fargs->flow_s_parsable; 370 371 switch (of_arg->ofmt_id) { 372 case FLOW_S_FLOW: 373 (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow); 374 break; 375 case FLOW_S_IPKTS: 376 map_to_units(buf, bufsize, diff_stats->fl_ipackets, unit, 377 parsable); 378 break; 379 case FLOW_S_RBYTES: 380 map_to_units(buf, bufsize, diff_stats->fl_rbytes, unit, 381 parsable); 382 break; 383 case FLOW_S_IERRORS: 384 map_to_units(buf, bufsize, diff_stats->fl_ierrors, unit, 385 parsable); 386 break; 387 case FLOW_S_OPKTS: 388 map_to_units(buf, bufsize, diff_stats->fl_opackets, unit, 389 parsable); 390 break; 391 case FLOW_S_OBYTES: 392 map_to_units(buf, bufsize, diff_stats->fl_obytes, unit, 393 parsable); 394 break; 395 case FLOW_S_OERRORS: 396 map_to_units(buf, bufsize, diff_stats->fl_oerrors, unit, 397 parsable); 398 break; 399 default: 400 die("invalid input"); 401 break; 402 } 403 return (B_TRUE); 404 } 405 406 /* ARGSUSED */ 407 static int 408 query_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 409 { 410 show_flow_state_t *state = arg; 411 flow_chain_t *flow_node; 412 flow_stat_t *curr_stat; 413 flow_stat_t *prev_stat; 414 flow_stat_t *diff_stat; 415 char *flowname = attr->fa_flowname; 416 flow_args_t fargs; 417 418 /* Get previous stats for the flow */ 419 flow_node = get_flow_prev_stat(flowname, arg); 420 if (flow_node == NULL) 421 goto done; 422 423 flow_node->fc_visited = B_TRUE; 424 prev_stat = flow_node->fc_stat; 425 426 /* Query library for current stats */ 427 curr_stat = dladm_flow_stat_query(flowname); 428 if (curr_stat == NULL) 429 goto done; 430 431 /* current stats - prev iteration stats */ 432 diff_stat = dladm_flow_stat_diff(curr_stat, prev_stat); 433 434 /* Free prev stats */ 435 dladm_flow_stat_free(prev_stat); 436 437 /* Prev <- curr stats */ 438 flow_node->fc_stat = curr_stat; 439 440 if (diff_stat == NULL) 441 goto done; 442 443 /* Print stats */ 444 fargs.flow_s_flow = flowname; 445 fargs.flow_s_stat = diff_stat; 446 fargs.flow_s_unit = state->fs_unit; 447 fargs.flow_s_parsable = state->fs_parsable; 448 ofmt_print(state->fs_ofmt, &fargs); 449 450 /* Free diff stats */ 451 dladm_flow_stat_free(diff_stat); 452 done: 453 return (DLADM_WALK_CONTINUE); 454 } 455 456 /* 457 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for 458 * dladm_walk_datalink_id(). Used for showing flow stats for 459 * all flows on all links. 460 */ 461 static int 462 query_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 463 { 464 if (dladm_walk_flow(query_flow_stats, dh, linkid, arg, B_FALSE) 465 == DLADM_STATUS_OK) 466 return (DLADM_WALK_CONTINUE); 467 else 468 return (DLADM_WALK_TERMINATE); 469 } 470 471 void 472 print_all_stats(name_value_stat_entry_t *stat_entry) 473 { 474 name_value_stat_t *curr_stat; 475 476 printf("%s\n", stat_entry->nve_header); 477 478 for (curr_stat = stat_entry->nve_stats; curr_stat != NULL; 479 curr_stat = curr_stat->nv_nextstat) { 480 printf("\t%15s", curr_stat->nv_statname); 481 printf("\t%15llu\n", curr_stat->nv_statval); 482 } 483 } 484 485 /* ARGSUSED */ 486 static int 487 dump_one_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 488 { 489 char *flowname = attr->fa_flowname; 490 void *stat; 491 492 stat = dladm_flow_stat_query_all(flowname); 493 if (stat == NULL) 494 goto done; 495 print_all_stats(stat); 496 dladm_flow_stat_query_all_free(stat); 497 498 done: 499 return (DLADM_WALK_CONTINUE); 500 } 501 502 /* 503 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for 504 * dladm_walk_datalink_id(). Used for showing flow stats for 505 * all flows on all links. 506 */ 507 static int 508 dump_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 509 { 510 if (dladm_walk_flow(dump_one_flow_stats, dh, linkid, arg, B_FALSE) 511 == DLADM_STATUS_OK) 512 return (DLADM_WALK_CONTINUE); 513 else 514 return (DLADM_WALK_TERMINATE); 515 } 516 517 static void 518 dump_all_flow_stats(dladm_flow_attr_t *attrp, void *arg, datalink_id_t linkid, 519 boolean_t flow_arg) 520 { 521 /* Show stats for named flow */ 522 if (flow_arg) { 523 (void) dump_one_flow_stats(handle, attrp, arg); 524 525 /* Show stats for flows on one link */ 526 } else if (linkid != DATALINK_INVALID_LINKID) { 527 (void) dladm_walk_flow(dump_one_flow_stats, handle, linkid, 528 arg, B_FALSE); 529 530 /* Show stats for all flows on all links */ 531 } else { 532 (void) dladm_walk_datalink_id(dump_link_flow_stats, 533 handle, arg, DATALINK_CLASS_ALL, 534 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 535 } 536 } 537 538 int 539 main(int argc, char *argv[]) 540 { 541 dladm_status_t status; 542 int option; 543 boolean_t r_arg = B_FALSE; 544 boolean_t t_arg = B_FALSE; 545 boolean_t p_arg = B_FALSE; 546 boolean_t i_arg = B_FALSE; 547 boolean_t o_arg = B_FALSE; 548 boolean_t u_arg = B_FALSE; 549 boolean_t A_arg = B_FALSE; 550 boolean_t S_arg = B_FALSE; 551 boolean_t flow_arg = B_FALSE; 552 datalink_id_t linkid = DATALINK_ALL_LINKID; 553 char linkname[MAXLINKNAMELEN]; 554 char flowname[MAXFLOWNAMELEN]; 555 uint32_t interval = 0; 556 char unit = '\0'; 557 show_flow_state_t state; 558 char *fields_str = NULL; 559 char *o_fields_str = NULL; 560 561 char *total_stat_fields = 562 "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs"; 563 char *rx_stat_fields = 564 "flow,ipkts,rbytes,ierrs"; 565 char *tx_stat_fields = 566 "flow,opkts,obytes,oerrs"; 567 568 ofmt_handle_t ofmt; 569 ofmt_status_t oferr; 570 uint_t ofmtflags = OFMT_RIGHTJUST; 571 572 dladm_flow_attr_t attr; 573 574 (void) setlocale(LC_ALL, ""); 575 #if !defined(TEXT_DOMAIN) 576 #define TEXT_DOMAIN "SYS_TEST" 577 #endif 578 (void) textdomain(TEXT_DOMAIN); 579 580 progname = argv[0]; 581 582 /* Open the libdladm handle */ 583 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) 584 die_dlerr(status, "could not open /dev/dld"); 585 586 bzero(&state, sizeof (state)); 587 588 opterr = 0; 589 while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:h", 590 NULL, NULL)) != -1) { 591 switch (option) { 592 case 'r': 593 if (r_arg) 594 die_optdup(option); 595 596 r_arg = B_TRUE; 597 break; 598 case 't': 599 if (t_arg) 600 die_optdup(option); 601 602 t_arg = B_TRUE; 603 break; 604 case 'A': 605 if (A_arg) 606 die_optdup(option); 607 608 A_arg = B_TRUE; 609 break; 610 case 'p': 611 if (p_arg) 612 die_optdup(option); 613 614 p_arg = B_TRUE; 615 break; 616 case 'S': 617 if (S_arg) 618 die_optdup(option); 619 S_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 || S_arg || A_arg) { 653 die("the option -h is not compatible with " 654 "-r, -t, -p, -o, -u, -i, -S, -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 (S_arg && 678 (r_arg || t_arg || p_arg || o_arg || u_arg)) 679 die("the option -S is not compatible with " 680 "-r, -t, -p, -o, -u"); 681 682 if (A_arg && 683 (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) 684 die("the option -A is not compatible with " 685 "-r, -t, -p, -o, -u, -i"); 686 687 /* get flow name (optional last argument) */ 688 if (optind == (argc-1)) { 689 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 690 >= MAXFLOWNAMELEN) 691 die("flow name too long"); 692 flow_arg = B_TRUE; 693 } else if (optind != argc) { 694 usage(); 695 } 696 697 if (S_arg) { 698 dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL), 699 interval, FLOW_REPORT); 700 return (0); 701 } 702 703 if (flow_arg && 704 dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK) 705 die("invalid flow %s", flowname); 706 707 if (A_arg) { 708 dump_all_flow_stats(&attr, &state, linkid, flow_arg); 709 return (0); 710 } 711 712 state.fs_unit = unit; 713 state.fs_parsable = p_arg; 714 715 if (state.fs_parsable) 716 ofmtflags |= OFMT_PARSABLE; 717 718 if (r_arg) 719 fields_str = rx_stat_fields; 720 else if (t_arg) 721 fields_str = tx_stat_fields; 722 else 723 fields_str = total_stat_fields; 724 725 if (o_arg) { 726 fields_str = (strcasecmp(o_fields_str, "all") == 0) ? 727 fields_str : o_fields_str; 728 } 729 730 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt); 731 flowstat_ofmt_check(oferr, state.fs_parsable, ofmt); 732 state.fs_ofmt = ofmt; 733 734 for (;;) { 735 /* Show stats for named flow */ 736 if (flow_arg) { 737 (void) query_flow_stats(handle, &attr, &state); 738 739 /* Show stats for flows on one link */ 740 } else if (linkid != DATALINK_INVALID_LINKID) { 741 (void) dladm_walk_flow(query_flow_stats, handle, linkid, 742 &state, B_FALSE); 743 744 /* Show stats for all flows on all links */ 745 } else { 746 (void) dladm_walk_datalink_id(query_link_flow_stats, 747 handle, &state, DATALINK_CLASS_ALL, 748 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 749 } 750 751 if (interval == 0) 752 break; 753 754 (void) fflush(stdout); 755 cleanup_removed_flows(&state); 756 (void) sleep(interval); 757 } 758 ofmt_close(ofmt); 759 760 dladm_close(handle); 761 return (0); 762 } 763 764 /* ARGSUSED */ 765 static int 766 show_history_date(dladm_usage_t *history, void *arg) 767 { 768 show_history_state_t *state = (show_history_state_t *)arg; 769 time_t stime; 770 char timebuf[20]; 771 dladm_flow_attr_t attr; 772 dladm_status_t status; 773 774 /* 775 * Only show historical information for existing flows unless '-a' 776 * is specified. 777 */ 778 if (!state->us_showall && ((status = dladm_flow_info(handle, 779 history->du_name, &attr)) != DLADM_STATUS_OK)) { 780 return (status); 781 } 782 783 stime = history->du_stime; 784 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", 785 localtime(&stime)); 786 (void) printf("%s\n", timebuf); 787 788 return (DLADM_STATUS_OK); 789 } 790 791 static int 792 show_history_time(dladm_usage_t *history, void *arg) 793 { 794 show_history_state_t *state = (show_history_state_t *)arg; 795 char buf[DLADM_STRSIZE]; 796 history_l_fields_buf_t ubuf; 797 time_t time; 798 double bw; 799 dladm_flow_attr_t attr; 800 dladm_status_t status; 801 802 /* 803 * Only show historical information for existing flows unless '-a' 804 * is specified. 805 */ 806 if (!state->us_showall && ((status = dladm_flow_info(handle, 807 history->du_name, &attr)) != DLADM_STATUS_OK)) { 808 return (status); 809 } 810 811 if (state->us_plot) { 812 if (!state->us_printheader) { 813 if (state->us_first) { 814 (void) printf("# Time"); 815 state->us_first = B_FALSE; 816 } 817 (void) printf(" %s", history->du_name); 818 if (history->du_last) { 819 (void) printf("\n"); 820 state->us_first = B_TRUE; 821 state->us_printheader = B_TRUE; 822 } 823 } else { 824 if (state->us_first) { 825 time = history->du_etime; 826 (void) strftime(buf, sizeof (buf), "%T", 827 localtime(&time)); 828 state->us_first = B_FALSE; 829 (void) printf("%s", buf); 830 } 831 bw = (double)history->du_bandwidth/1000; 832 (void) printf(" %.2f", bw); 833 if (history->du_last) { 834 (void) printf("\n"); 835 state->us_first = B_TRUE; 836 } 837 } 838 return (DLADM_STATUS_OK); 839 } 840 841 bzero(&ubuf, sizeof (ubuf)); 842 843 (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s", 844 history->du_name); 845 time = history->du_stime; 846 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 847 (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime), 848 "%s", buf); 849 time = history->du_etime; 850 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 851 (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime), 852 "%s", buf); 853 (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes), 854 "%llu", history->du_rbytes); 855 (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes), 856 "%llu", history->du_obytes); 857 (void) snprintf(ubuf.history_l_bandwidth, 858 sizeof (ubuf.history_l_bandwidth), "%s Mbps", 859 dladm_bw2str(history->du_bandwidth, buf)); 860 861 ofmt_print(state->us_ofmt, (void *)&ubuf); 862 return (DLADM_STATUS_OK); 863 } 864 865 static int 866 show_history_res(dladm_usage_t *history, void *arg) 867 { 868 show_history_state_t *state = (show_history_state_t *)arg; 869 char buf[DLADM_STRSIZE]; 870 history_fields_buf_t ubuf; 871 dladm_flow_attr_t attr; 872 dladm_status_t status; 873 874 /* 875 * Only show historical information for existing flows unless '-a' 876 * is specified. 877 */ 878 if (!state->us_showall && ((status = dladm_flow_info(handle, 879 history->du_name, &attr)) != DLADM_STATUS_OK)) { 880 return (status); 881 } 882 883 bzero(&ubuf, sizeof (ubuf)); 884 885 (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s", 886 history->du_name); 887 (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration), 888 "%llu", history->du_duration); 889 (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets), 890 "%llu", history->du_ipackets); 891 (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes), 892 "%llu", history->du_rbytes); 893 (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets), 894 "%llu", history->du_opackets); 895 (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes), 896 "%llu", history->du_obytes); 897 (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth), 898 "%s Mbps", dladm_bw2str(history->du_bandwidth, buf)); 899 900 ofmt_print(state->us_ofmt, (void *)&ubuf); 901 902 return (DLADM_STATUS_OK); 903 } 904 905 static boolean_t 906 valid_formatspec(char *formatspec_str) 907 { 908 return (strcmp(formatspec_str, "gnuplot") == 0); 909 } 910 911 /* ARGSUSED */ 912 static void 913 do_show_history(int argc, char *argv[]) 914 { 915 char *file = NULL; 916 int opt; 917 dladm_status_t status; 918 boolean_t d_arg = B_FALSE; 919 char *stime = NULL; 920 char *etime = NULL; 921 char *resource = NULL; 922 show_history_state_t state; 923 boolean_t o_arg = B_FALSE; 924 boolean_t F_arg = B_FALSE; 925 char *fields_str = NULL; 926 char *formatspec_str = NULL; 927 char *all_fields = 928 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; 929 char *all_l_fields = 930 "flow,start,end,rbytes,obytes,bandwidth"; 931 ofmt_handle_t ofmt; 932 ofmt_status_t oferr; 933 uint_t ofmtflags = 0; 934 935 bzero(&state, sizeof (show_history_state_t)); 936 state.us_parsable = B_FALSE; 937 state.us_printheader = B_FALSE; 938 state.us_plot = B_FALSE; 939 state.us_first = B_TRUE; 940 941 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) { 942 switch (opt) { 943 case 'd': 944 d_arg = B_TRUE; 945 break; 946 case 'a': 947 state.us_showall = B_TRUE; 948 break; 949 case 'f': 950 file = optarg; 951 break; 952 case 's': 953 stime = optarg; 954 break; 955 case 'e': 956 etime = optarg; 957 break; 958 case 'o': 959 o_arg = B_TRUE; 960 fields_str = optarg; 961 break; 962 case 'F': 963 state.us_plot = F_arg = B_TRUE; 964 formatspec_str = optarg; 965 break; 966 default: 967 die_opterr(optopt, opt, usage_ermsg); 968 } 969 } 970 971 if (file == NULL) 972 die("-h requires a file"); 973 974 if (optind == (argc-1)) { 975 dladm_flow_attr_t attr; 976 977 resource = argv[optind]; 978 if (!state.us_showall && 979 dladm_flow_info(handle, resource, &attr) != 980 DLADM_STATUS_OK) { 981 die("invalid flow: '%s'", resource); 982 } 983 } 984 985 if (state.us_parsable) 986 ofmtflags |= OFMT_PARSABLE; 987 if (resource == NULL && stime == NULL && etime == NULL) { 988 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 989 fields_str = all_fields; 990 oferr = ofmt_open(fields_str, history_fields, ofmtflags, 991 0, &ofmt); 992 } else { 993 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 994 fields_str = all_l_fields; 995 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 996 0, &ofmt); 997 } 998 999 flowstat_ofmt_check(oferr, state.us_parsable, ofmt); 1000 state.us_ofmt = ofmt; 1001 1002 if (F_arg && d_arg) 1003 die("incompatible -d and -F options"); 1004 1005 if (F_arg && !valid_formatspec(formatspec_str)) 1006 die("Format specifier %s not supported", formatspec_str); 1007 1008 if (d_arg) { 1009 /* Print log dates */ 1010 status = dladm_usage_dates(show_history_date, 1011 DLADM_LOGTYPE_FLOW, file, resource, &state); 1012 } else if (resource == NULL && stime == NULL && etime == NULL && 1013 !F_arg) { 1014 /* Print summary */ 1015 status = dladm_usage_summary(show_history_res, 1016 DLADM_LOGTYPE_FLOW, file, &state); 1017 } else if (resource != NULL) { 1018 /* Print log entries for named resource */ 1019 status = dladm_walk_usage_res(show_history_time, 1020 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); 1021 } else { 1022 /* Print time and information for each flow */ 1023 status = dladm_walk_usage_time(show_history_time, 1024 DLADM_LOGTYPE_FLOW, file, stime, etime, &state); 1025 } 1026 1027 ofmt_close(ofmt); 1028 if (status != DLADM_STATUS_OK) 1029 die_dlerr(status, "-h"); 1030 dladm_close(handle); 1031 } 1032 1033 static void 1034 warn(const char *format, ...) 1035 { 1036 va_list alist; 1037 1038 format = gettext(format); 1039 (void) fprintf(stderr, "%s: warning: ", progname); 1040 1041 va_start(alist, format); 1042 (void) vfprintf(stderr, format, alist); 1043 va_end(alist); 1044 1045 (void) putc('\n', stderr); 1046 } 1047 1048 /* PRINTFLIKE1 */ 1049 static void 1050 die(const char *format, ...) 1051 { 1052 va_list alist; 1053 1054 format = gettext(format); 1055 (void) fprintf(stderr, "%s: ", progname); 1056 1057 va_start(alist, format); 1058 (void) vfprintf(stderr, format, alist); 1059 va_end(alist); 1060 1061 (void) putc('\n', stderr); 1062 1063 /* close dladm handle if it was opened */ 1064 if (handle != NULL) 1065 dladm_close(handle); 1066 1067 exit(EXIT_FAILURE); 1068 } 1069 1070 static void 1071 die_optdup(int opt) 1072 { 1073 die("the option -%c cannot be specified more than once", opt); 1074 } 1075 1076 static void 1077 die_opterr(int opt, int opterr, const char *usage) 1078 { 1079 switch (opterr) { 1080 case ':': 1081 die("option '-%c' requires a value\n%s", opt, 1082 gettext(usage)); 1083 break; 1084 case '?': 1085 default: 1086 die("unrecognized option '-%c'\n%s", opt, 1087 gettext(usage)); 1088 break; 1089 } 1090 } 1091 1092 /* PRINTFLIKE2 */ 1093 static void 1094 die_dlerr(dladm_status_t err, const char *format, ...) 1095 { 1096 va_list alist; 1097 char errmsg[DLADM_STRSIZE]; 1098 1099 format = gettext(format); 1100 (void) fprintf(stderr, "%s: ", progname); 1101 1102 va_start(alist, format); 1103 (void) vfprintf(stderr, format, alist); 1104 va_end(alist); 1105 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1106 1107 /* close dladm handle if it was opened */ 1108 if (handle != NULL) 1109 dladm_close(handle); 1110 1111 exit(EXIT_FAILURE); 1112 } 1113 1114 1115 /* 1116 * default output callback function that, when invoked from dladm_print_output, 1117 * prints string which is offset by of_arg->ofmt_id within buf. 1118 */ 1119 static boolean_t 1120 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 1121 { 1122 char *value; 1123 1124 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id; 1125 (void) strlcpy(buf, value, bufsize); 1126 return (B_TRUE); 1127 } 1128 1129 static void 1130 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable, 1131 ofmt_handle_t ofmt) 1132 { 1133 char buf[OFMT_BUFSIZE]; 1134 1135 if (oferr == OFMT_SUCCESS) 1136 return; 1137 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); 1138 /* 1139 * All errors are considered fatal in parsable mode. 1140 * NOMEM errors are always fatal, regardless of mode. 1141 * For other errors, we print diagnostics in human-readable 1142 * mode and processs what we can. 1143 */ 1144 if (parsable || oferr == OFMT_ENOFIELDS) { 1145 ofmt_close(ofmt); 1146 die(buf); 1147 } else { 1148 warn(buf); 1149 } 1150 }