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 2009 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 <kstat.h> 35 #include <strings.h> 36 #include <getopt.h> 37 #include <unistd.h> 38 #include <priv.h> 39 #include <netdb.h> 40 #include <libintl.h> 41 #include <libdlflow.h> 42 #include <libdllink.h> 43 #include <libdlstat.h> 44 #include <sys/types.h> 45 #include <sys/socket.h> 46 #include <netinet/in.h> 47 #include <arpa/inet.h> 48 #include <sys/ethernet.h> 49 #include <inet/ip.h> 50 #include <inet/ip6.h> 51 #include <stddef.h> 52 53 #define CMD_TYPE_ANY 0xffffffff 54 #define STR_UNDEF_VAL "--" 55 56 57 /* 58 * data structures and routines for printing output. 59 */ 60 61 typedef struct print_field_s { 62 const char *pf_name; 63 const char *pf_header; 64 uint_t pf_width; 65 union { 66 uint_t _pf_index; 67 size_t _pf_offset; 68 }_pf_un; 69 #define pf_index _pf_un._pf_index 70 #define pf_offset _pf_un._pf_offset; 71 uint_t pf_cmdtype; 72 } print_field_t; 73 74 typedef struct print_state_s { 75 print_field_t **ps_fields; 76 uint_t ps_nfields; 77 boolean_t ps_lastfield; 78 uint_t ps_overflow; 79 } print_state_t; 80 81 typedef struct show_usage_state_s { 82 boolean_t us_plot; 83 boolean_t us_parseable; 84 boolean_t us_printheader; 85 boolean_t us_first; 86 boolean_t us_showall; 87 print_state_t us_print; 88 } show_usage_state_t; 89 90 typedef char *(*print_callback_t)(print_field_t *, void *); 91 static print_field_t **parse_output_fields(char *, print_field_t *, int, 92 uint_t, uint_t *); 93 94 static void print_header(print_state_t *); 95 static void print_field(print_state_t *, print_field_t *, const char *, 96 boolean_t); 97 98 static void flowadm_print_output(print_state_t *, boolean_t, 99 print_callback_t, void *); 100 101 /* 102 * helper function that, when invoked as flowadm(print_field(pf, buf) 103 * prints string which is offset by pf->pf_offset within buf. 104 */ 105 static char *flowadm_print_field(print_field_t *, void *); 106 107 #define MAX_FIELD_LEN 32 108 109 typedef struct show_flow_state { 110 boolean_t fs_firstonly; 111 boolean_t fs_donefirst; 112 pktsum_t fs_prevstats; 113 uint32_t fs_flags; 114 dladm_status_t fs_status; 115 print_state_t fs_print; 116 const char *fs_flow; 117 const char *fs_link; 118 boolean_t fs_parseable; 119 boolean_t fs_printheader; 120 boolean_t fs_persist; 121 boolean_t fs_stats; 122 uint64_t fs_mask; 123 } show_flow_state_t; 124 125 typedef void cmdfunc_t(int, char **); 126 127 static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow; 128 static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop; 129 static cmdfunc_t do_show_usage; 130 131 static int show_flow(dladm_flow_attr_t *, void *); 132 static int show_flows_onelink(dladm_handle_t, datalink_id_t, void *); 133 134 static void flow_stats(const char *, datalink_id_t, uint_t, char *, 135 show_flow_state_t *); 136 static void get_flow_stats(const char *, pktsum_t *); 137 static int show_flow_stats(dladm_flow_attr_t *, void *); 138 static int show_link_flow_stats(dladm_handle_t, datalink_id_t, void *); 139 140 static int remove_flow(dladm_flow_attr_t *, void *); 141 142 static int show_flowprop(dladm_flow_attr_t *, void *); 143 static void show_flowprop_one_flow(void *, const char *); 144 static int show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *); 145 146 static void die(const char *, ...); 147 static void die_optdup(int); 148 static void die_opterr(int, int); 149 static void die_dlerr(dladm_status_t, const char *, ...); 150 static void warn(const char *, ...); 151 static void warn_dlerr(dladm_status_t, const char *, ...); 152 153 typedef struct cmd { 154 char *c_name; 155 void (*c_fn)(int, char **); 156 } cmd_t; 157 158 static cmd_t cmds[] = { 159 { "add-flow", do_add_flow }, 160 { "remove-flow", do_remove_flow }, 161 { "show-flowprop", do_show_flowprop }, 162 { "set-flowprop", do_set_flowprop }, 163 { "reset-flowprop", do_reset_flowprop }, 164 { "show-flow", do_show_flow }, 165 { "init-flow", do_init_flow }, 166 { "show-usage", do_show_usage } 167 }; 168 169 static const struct option longopts[] = { 170 {"link", required_argument, 0, 'l'}, 171 {"parseable", no_argument, 0, 'p'}, 172 {"statistics", no_argument, 0, 's'}, 173 {"interval", required_argument, 0, 'i'}, 174 {"temporary", no_argument, 0, 't'}, 175 {"root-dir", required_argument, 0, 'R'}, 176 { 0, 0, 0, 0 } 177 }; 178 179 static const struct option prop_longopts[] = { 180 {"link", required_argument, 0, 'l'}, 181 {"temporary", no_argument, 0, 't'}, 182 {"root-dir", required_argument, 0, 'R'}, 183 {"prop", required_argument, 0, 'p'}, 184 {"attr", required_argument, 0, 'a'}, 185 { 0, 0, 0, 0 } 186 }; 187 188 /* 189 * structures for 'flowadm remove-flow' 190 */ 191 192 typedef struct remove_flow_state { 193 boolean_t fs_tempop; 194 const char *fs_altroot; 195 dladm_status_t fs_status; 196 } remove_flow_state_t; 197 198 #define PROTO_MAXSTR_LEN 7 199 #define PORT_MAXSTR_LEN 6 200 #define DSFIELD_MAXSTR_LEN 10 201 202 typedef struct flow_fields_buf_s 203 { 204 char flow_name[MAXFLOWNAMELEN]; 205 char flow_link[MAXLINKNAMELEN]; 206 char flow_ipaddr[INET6_ADDRSTRLEN+4]; 207 char flow_proto[PROTO_MAXSTR_LEN]; 208 char flow_port[PORT_MAXSTR_LEN]; 209 char flow_dsfield[DSFIELD_MAXSTR_LEN]; 210 } flow_fields_buf_t; 211 212 static print_field_t flow_fields[] = { 213 /* name, header, field width, index, cmdtype */ 214 { "flow", "FLOW", 11, 215 offsetof(flow_fields_buf_t, flow_name), CMD_TYPE_ANY}, 216 { "link", "LINK", 11, 217 offsetof(flow_fields_buf_t, flow_link), CMD_TYPE_ANY}, 218 { "ipaddr", "IPADDR", 30, 219 offsetof(flow_fields_buf_t, flow_ipaddr), CMD_TYPE_ANY}, 220 { "proto", "PROTO", 6, 221 offsetof(flow_fields_buf_t, flow_proto), CMD_TYPE_ANY}, 222 { "port", "PORT", 7, 223 offsetof(flow_fields_buf_t, flow_port), CMD_TYPE_ANY}, 224 { "dsfld", "DSFLD", 9, 225 offsetof(flow_fields_buf_t, flow_dsfield), CMD_TYPE_ANY}} 226 ; 227 228 #define FLOW_MAX_FIELDS (sizeof (flow_fields) / sizeof (print_field_t)) 229 230 /* 231 * structures for 'flowadm show-flowprop' 232 */ 233 typedef enum { 234 FLOWPROP_FLOW, 235 FLOWPROP_PROPERTY, 236 FLOWPROP_VALUE, 237 FLOWPROP_DEFAULT, 238 FLOWPROP_POSSIBLE 239 } flowprop_field_index_t; 240 241 static print_field_t flowprop_fields[] = { 242 /* name, header, fieldwidth, index, cmdtype */ 243 { "flow", "FLOW", 12, FLOWPROP_FLOW, CMD_TYPE_ANY}, 244 { "property", "PROPERTY", 15, FLOWPROP_PROPERTY, CMD_TYPE_ANY}, 245 { "value", "VALUE", 14, FLOWPROP_VALUE, CMD_TYPE_ANY}, 246 { "default", "DEFAULT", 14, FLOWPROP_DEFAULT, CMD_TYPE_ANY}, 247 { "possible", "POSSIBLE", 20, FLOWPROP_POSSIBLE, CMD_TYPE_ANY}} 248 ; 249 #define FLOWPROP_MAX_FIELDS \ 250 (sizeof (flowprop_fields) / sizeof (print_field_t)) 251 252 #define MAX_PROP_LINE 512 253 254 typedef struct show_flowprop_state { 255 const char *fs_flow; 256 datalink_id_t fs_linkid; 257 char *fs_line; 258 char **fs_propvals; 259 dladm_arg_list_t *fs_proplist; 260 boolean_t fs_parseable; 261 boolean_t fs_persist; 262 boolean_t fs_header; 263 dladm_status_t fs_status; 264 dladm_status_t fs_retstatus; 265 print_state_t fs_print; 266 } show_flowprop_state_t; 267 268 typedef struct set_flowprop_state { 269 const char *fs_name; 270 boolean_t fs_reset; 271 boolean_t fs_temp; 272 dladm_status_t fs_status; 273 } set_flowprop_state_t; 274 275 typedef struct flowprop_args_s { 276 show_flowprop_state_t *fs_state; 277 char *fs_propname; 278 char *fs_flowname; 279 } flowprop_args_t; 280 /* 281 * structures for 'flowadm show-flow -s' (print statistics) 282 */ 283 typedef enum { 284 FLOW_S_FLOW, 285 FLOW_S_IPKTS, 286 FLOW_S_RBYTES, 287 FLOW_S_IERRORS, 288 FLOW_S_OPKTS, 289 FLOW_S_OBYTES, 290 FLOW_S_OERRORS 291 } flow_s_field_index_t; 292 293 static print_field_t flow_s_fields[] = { 294 /* name, header, field width, index, cmdtype */ 295 { "flow", "FLOW", 15, FLOW_S_FLOW, CMD_TYPE_ANY}, 296 { "ipackets", "IPACKETS", 10, FLOW_S_IPKTS, CMD_TYPE_ANY}, 297 { "rbytes", "RBYTES", 8, FLOW_S_RBYTES, CMD_TYPE_ANY}, 298 { "ierrors", "IERRORS", 10, FLOW_S_IERRORS, CMD_TYPE_ANY}, 299 { "opackets", "OPACKETS", 12, FLOW_S_OPKTS, CMD_TYPE_ANY}, 300 { "obytes", "OBYTES", 12, FLOW_S_OBYTES, CMD_TYPE_ANY}, 301 { "oerrors", "OERRORS", 8, FLOW_S_OERRORS, CMD_TYPE_ANY}} 302 ; 303 #define FLOW_S_MAX_FIELDS \ 304 (sizeof (flow_s_fields) / sizeof (print_field_t)) 305 306 typedef struct flow_args_s { 307 char *flow_s_flow; 308 pktsum_t *flow_s_psum; 309 } flow_args_t; 310 static char *print_flow_stats(print_field_t *, void *); 311 312 /* 313 * structures for 'flowadm show-usage' 314 */ 315 316 typedef struct usage_fields_buf_s { 317 char usage_flow[12]; 318 char usage_duration[10]; 319 char usage_ipackets[9]; 320 char usage_rbytes[10]; 321 char usage_opackets[9]; 322 char usage_obytes[10]; 323 char usage_bandwidth[14]; 324 } usage_fields_buf_t; 325 326 static print_field_t usage_fields[] = { 327 /* name, header, field width, offset, cmdtype */ 328 { "flow", "FLOW", 12, 329 offsetof(usage_fields_buf_t, usage_flow), CMD_TYPE_ANY}, 330 { "duration", "DURATION", 10, 331 offsetof(usage_fields_buf_t, usage_duration), CMD_TYPE_ANY}, 332 { "ipackets", "IPACKETS", 9, 333 offsetof(usage_fields_buf_t, usage_ipackets), CMD_TYPE_ANY}, 334 { "rbytes", "RBYTES", 10, 335 offsetof(usage_fields_buf_t, usage_rbytes), CMD_TYPE_ANY}, 336 { "opackets", "OPACKETS", 9, 337 offsetof(usage_fields_buf_t, usage_opackets), CMD_TYPE_ANY}, 338 { "obytes", "OBYTES", 10, 339 offsetof(usage_fields_buf_t, usage_obytes), CMD_TYPE_ANY}, 340 { "bandwidth", "BANDWIDTH", 14, 341 offsetof(usage_fields_buf_t, usage_bandwidth), CMD_TYPE_ANY}} 342 ; 343 344 #define USAGE_MAX_FIELDS (sizeof (usage_fields) / sizeof (print_field_t)) 345 346 /* 347 * structures for 'dladm show-usage link' 348 */ 349 350 typedef struct usage_l_fields_buf_s { 351 char usage_l_flow[12]; 352 char usage_l_stime[13]; 353 char usage_l_etime[13]; 354 char usage_l_rbytes[8]; 355 char usage_l_obytes[8]; 356 char usage_l_bandwidth[14]; 357 } usage_l_fields_buf_t; 358 359 static print_field_t usage_l_fields[] = { 360 /* name, header, field width, offset, cmdtype */ 361 { "flow", "FLOW", 12, 362 offsetof(usage_l_fields_buf_t, usage_l_flow), CMD_TYPE_ANY}, 363 { "start", "START", 13, 364 offsetof(usage_l_fields_buf_t, usage_l_stime), CMD_TYPE_ANY}, 365 { "end", "END", 13, 366 offsetof(usage_l_fields_buf_t, usage_l_etime), CMD_TYPE_ANY}, 367 { "rbytes", "RBYTES", 8, 368 offsetof(usage_l_fields_buf_t, usage_l_rbytes), CMD_TYPE_ANY}, 369 { "obytes", "OBYTES", 8, 370 offsetof(usage_l_fields_buf_t, usage_l_obytes), CMD_TYPE_ANY}, 371 { "bandwidth", "BANDWIDTH", 14, 372 offsetof(usage_l_fields_buf_t, usage_l_bandwidth), CMD_TYPE_ANY}} 373 ; 374 375 #define USAGE_L_MAX_FIELDS \ 376 (sizeof (usage_l_fields) /sizeof (print_field_t)) 377 378 #define PRI_HI 100 379 #define PRI_LO 10 380 #define PRI_NORM 50 381 382 #define FLOWADM_CONF "/etc/dladm/flowadm.conf" 383 #define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n')) 384 385 static char *progname; 386 387 boolean_t t_arg = B_FALSE; /* changes are persistent */ 388 char *altroot = NULL; 389 390 /* 391 * Handle to libdladm. Opened in main() before the sub-command 392 * specific function is called. 393 */ 394 static dladm_handle_t handle = NULL; 395 396 static const char *attr_table[] = 397 {"local_ip", "remote_ip", "transport", "local_port", "dsfield"}; 398 399 #define NATTR (sizeof (attr_table)/sizeof (char *)) 400 401 static void 402 usage(void) 403 { 404 (void) fprintf(stderr, gettext("usage: flowadm <subcommand>" 405 " <args>...\n" 406 " add-flow [-t] -l <link> -a <attr>=<value>[,...]\n" 407 "\t\t [-p <prop>=<value>,...] <flow>\n" 408 " remove-flow [-t] {-l <link> | <flow>}\n" 409 " show-flow [-p] [-s [-i <interval>]] [-l <link>] " 410 "[<flow>]\n\n" 411 " set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n" 412 " reset-flowprop [-t] [-p <prop>,...] <flow>\n" 413 " show-flowprop [-cP] [-l <link>] [-p <prop>,...] " 414 "[<flow>]\n\n" 415 " show-usage [-a] [-d | -F <format>] " 416 "[-s <DD/MM/YYYY,HH:MM:SS>]\n" 417 "\t\t [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<flow>]\n")); 418 419 /* close dladm handle if it was opened */ 420 if (handle != NULL) 421 dladm_close(handle); 422 423 exit(1); 424 } 425 426 int 427 main(int argc, char *argv[]) 428 { 429 int i, arglen, cmdlen; 430 cmd_t *cmdp; 431 dladm_status_t status; 432 433 (void) setlocale(LC_ALL, ""); 434 #if !defined(TEXT_DOMAIN) 435 #define TEXT_DOMAIN "SYS_TEST" 436 #endif 437 (void) textdomain(TEXT_DOMAIN); 438 439 progname = argv[0]; 440 441 if (argc < 2) 442 usage(); 443 444 for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { 445 cmdp = &cmds[i]; 446 arglen = strlen(argv[1]); 447 cmdlen = strlen(cmdp->c_name); 448 if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name, 449 cmdlen) == 0)) { 450 /* Open the libdladm handle */ 451 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) { 452 die_dlerr(status, 453 "could not open /dev/dld"); 454 } 455 456 cmdp->c_fn(argc - 1, &argv[1]); 457 458 dladm_close(handle); 459 exit(EXIT_SUCCESS); 460 } 461 } 462 463 (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), 464 progname, argv[1]); 465 usage(); 466 467 return (0); 468 } 469 470 static const char * 471 match_attr(char *attr) 472 { 473 int i; 474 475 for (i = 0; i < NATTR; i++) { 476 if (strlen(attr) == strlen(attr_table[i]) && 477 strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) { 478 return (attr); 479 } 480 } 481 return (NULL); 482 } 483 484 /* ARGSUSED */ 485 static void 486 do_init_flow(int argc, char *argv[]) 487 { 488 dladm_status_t status; 489 490 status = dladm_flow_init(handle); 491 if (status != DLADM_STATUS_OK) 492 die_dlerr(status, "flows initialization failed"); 493 } 494 495 /* ARGSUSED */ 496 static int 497 show_usage_date(dladm_usage_t *usage, void *arg) 498 { 499 show_usage_state_t *state = (show_usage_state_t *)arg; 500 time_t stime; 501 char timebuf[20]; 502 dladm_flow_attr_t attr; 503 dladm_status_t status; 504 505 /* 506 * Only show usage information for existing flows unless '-a' 507 * is specified. 508 */ 509 if (!state->us_showall && ((status = dladm_flow_info(handle, 510 usage->du_name, &attr)) != DLADM_STATUS_OK)) { 511 return (status); 512 } 513 514 stime = usage->du_stime; 515 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", 516 localtime(&stime)); 517 (void) printf("%s\n", timebuf); 518 519 return (DLADM_STATUS_OK); 520 } 521 522 static int 523 show_usage_time(dladm_usage_t *usage, void *arg) 524 { 525 show_usage_state_t *state = (show_usage_state_t *)arg; 526 char buf[DLADM_STRSIZE]; 527 usage_l_fields_buf_t ubuf; 528 time_t time; 529 double bw; 530 dladm_flow_attr_t attr; 531 dladm_status_t status; 532 533 /* 534 * Only show usage information for existing flows unless '-a' 535 * is specified. 536 */ 537 if (!state->us_showall && ((status = dladm_flow_info(handle, 538 usage->du_name, &attr)) != DLADM_STATUS_OK)) { 539 return (status); 540 } 541 542 if (state->us_plot) { 543 if (!state->us_printheader) { 544 if (state->us_first) { 545 (void) printf("# Time"); 546 state->us_first = B_FALSE; 547 } 548 (void) printf(" %s", usage->du_name); 549 if (usage->du_last) { 550 (void) printf("\n"); 551 state->us_first = B_TRUE; 552 state->us_printheader = B_TRUE; 553 } 554 } else { 555 if (state->us_first) { 556 time = usage->du_etime; 557 (void) strftime(buf, sizeof (buf), "%T", 558 localtime(&time)); 559 state->us_first = B_FALSE; 560 (void) printf("%s", buf); 561 } 562 bw = (double)usage->du_bandwidth/1000; 563 (void) printf(" %.2f", bw); 564 if (usage->du_last) { 565 (void) printf("\n"); 566 state->us_first = B_TRUE; 567 } 568 } 569 return (DLADM_STATUS_OK); 570 } 571 572 bzero(&ubuf, sizeof (ubuf)); 573 574 (void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s", 575 usage->du_name); 576 time = usage->du_stime; 577 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 578 (void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s", 579 buf); 580 time = usage->du_etime; 581 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 582 (void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s", 583 buf); 584 (void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes), 585 "%llu", usage->du_rbytes); 586 (void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes), 587 "%llu", usage->du_obytes); 588 (void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth), 589 "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); 590 591 if (!state->us_parseable && !state->us_printheader) { 592 print_header(&state->us_print); 593 state->us_printheader = B_TRUE; 594 } 595 596 flowadm_print_output(&state->us_print, state->us_parseable, 597 flowadm_print_field, (void *)&ubuf); 598 599 return (DLADM_STATUS_OK); 600 } 601 602 static int 603 show_usage_res(dladm_usage_t *usage, void *arg) 604 { 605 show_usage_state_t *state = (show_usage_state_t *)arg; 606 char buf[DLADM_STRSIZE]; 607 usage_fields_buf_t ubuf; 608 dladm_flow_attr_t attr; 609 dladm_status_t status; 610 611 /* 612 * Only show usage information for existing flows unless '-a' 613 * is specified. 614 */ 615 if (!state->us_showall && ((status = dladm_flow_info(handle, 616 usage->du_name, &attr)) != DLADM_STATUS_OK)) { 617 return (status); 618 } 619 620 bzero(&ubuf, sizeof (ubuf)); 621 622 (void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s", 623 usage->du_name); 624 (void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration), 625 "%llu", usage->du_duration); 626 (void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets), 627 "%llu", usage->du_ipackets); 628 (void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes), 629 "%llu", usage->du_rbytes); 630 (void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets), 631 "%llu", usage->du_opackets); 632 (void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes), 633 "%llu", usage->du_obytes); 634 (void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth), 635 "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); 636 637 if (!state->us_parseable && !state->us_printheader) { 638 print_header(&state->us_print); 639 state->us_printheader = B_TRUE; 640 } 641 642 flowadm_print_output(&state->us_print, state->us_parseable, 643 flowadm_print_field, (void *)&ubuf); 644 645 return (DLADM_STATUS_OK); 646 } 647 648 static boolean_t 649 valid_formatspec(char *formatspec_str) 650 { 651 if (strcmp(formatspec_str, "gnuplot") == 0) 652 return (B_TRUE); 653 return (B_FALSE); 654 } 655 656 /* ARGSUSED */ 657 static void 658 do_show_usage(int argc, char *argv[]) 659 { 660 char *file = NULL; 661 int opt; 662 dladm_status_t status; 663 boolean_t d_arg = B_FALSE; 664 char *stime = NULL; 665 char *etime = NULL; 666 char *resource = NULL; 667 show_usage_state_t state; 668 boolean_t o_arg = B_FALSE; 669 boolean_t F_arg = B_FALSE; 670 char *fields_str = NULL; 671 char *formatspec_str = NULL; 672 print_field_t **fields; 673 uint_t nfields; 674 char *all_fields = 675 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; 676 char *all_l_fields = 677 "flow,start,end,rbytes,obytes,bandwidth"; 678 679 bzero(&state, sizeof (show_usage_state_t)); 680 state.us_parseable = B_FALSE; 681 state.us_printheader = B_FALSE; 682 state.us_plot = B_FALSE; 683 state.us_first = B_TRUE; 684 685 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) { 686 switch (opt) { 687 case 'd': 688 d_arg = B_TRUE; 689 break; 690 case 'a': 691 state.us_showall = B_TRUE; 692 break; 693 case 'f': 694 file = optarg; 695 break; 696 case 's': 697 stime = optarg; 698 break; 699 case 'e': 700 etime = optarg; 701 break; 702 case 'o': 703 o_arg = B_TRUE; 704 fields_str = optarg; 705 break; 706 case 'F': 707 state.us_plot = F_arg = B_TRUE; 708 formatspec_str = optarg; 709 break; 710 default: 711 die_opterr(optopt, opt); 712 } 713 } 714 715 if (file == NULL) 716 die("show-usage requires a file"); 717 718 if (optind == (argc-1)) { 719 dladm_flow_attr_t attr; 720 721 if (!state.us_showall && 722 dladm_flow_info(handle, resource, &attr) != 723 DLADM_STATUS_OK) { 724 die("invalid flow: '%s'", resource); 725 } 726 resource = argv[optind]; 727 } 728 729 if (resource == NULL && stime == NULL && etime == NULL) { 730 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 731 fields_str = all_fields; 732 fields = parse_output_fields(fields_str, usage_fields, 733 USAGE_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 734 } else { 735 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 736 fields_str = all_l_fields; 737 fields = parse_output_fields(fields_str, usage_l_fields, 738 USAGE_L_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 739 } 740 741 if (fields == NULL) { 742 die("invalid fields(s) specified"); 743 return; 744 } 745 state.us_print.ps_fields = fields; 746 state.us_print.ps_nfields = nfields; 747 748 if (F_arg && d_arg) 749 die("incompatible -d and -F options"); 750 751 if (F_arg && valid_formatspec(formatspec_str) == B_FALSE) 752 die("Format specifier %s not supported", formatspec_str); 753 754 if (d_arg) { 755 /* Print log dates */ 756 status = dladm_usage_dates(show_usage_date, 757 DLADM_LOGTYPE_FLOW, file, resource, &state); 758 } else if (resource == NULL && stime == NULL && etime == NULL && 759 !F_arg) { 760 /* Print summary */ 761 status = dladm_usage_summary(show_usage_res, 762 DLADM_LOGTYPE_FLOW, file, &state); 763 } else if (resource != NULL) { 764 /* Print log entries for named resource */ 765 status = dladm_walk_usage_res(show_usage_time, 766 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); 767 } else { 768 /* Print time and information for each link */ 769 status = dladm_walk_usage_time(show_usage_time, 770 DLADM_LOGTYPE_FLOW, file, stime, etime, &state); 771 } 772 773 if (status != DLADM_STATUS_OK) 774 die_dlerr(status, "show-usage"); 775 } 776 777 static void 778 do_add_flow(int argc, char *argv[]) 779 { 780 char devname[MAXLINKNAMELEN]; 781 char *name = NULL; 782 uint_t index; 783 datalink_id_t linkid; 784 785 char option; 786 boolean_t l_arg = B_FALSE; 787 char propstr[DLADM_STRSIZE]; 788 char attrstr[DLADM_STRSIZE]; 789 dladm_arg_list_t *proplist = NULL; 790 dladm_arg_list_t *attrlist = NULL; 791 dladm_status_t status; 792 793 bzero(propstr, DLADM_STRSIZE); 794 bzero(attrstr, DLADM_STRSIZE); 795 796 while ((option = getopt_long(argc, argv, "tR:l:a:p:", 797 prop_longopts, NULL)) != -1) { 798 switch (option) { 799 case 't': 800 t_arg = B_TRUE; 801 break; 802 case 'R': 803 altroot = optarg; 804 break; 805 case 'l': 806 if (strlcpy(devname, optarg, 807 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 808 die("link name too long"); 809 } 810 if (dladm_name2info(handle, devname, &linkid, NULL, 811 NULL, NULL) != DLADM_STATUS_OK) 812 die("invalid link '%s'", devname); 813 l_arg = B_TRUE; 814 break; 815 case 'a': 816 (void) strlcat(attrstr, optarg, DLADM_STRSIZE); 817 if (strlcat(attrstr, ",", DLADM_STRSIZE) >= 818 DLADM_STRSIZE) 819 die("attribute list too long '%s'", attrstr); 820 break; 821 case 'p': 822 (void) strlcat(propstr, optarg, DLADM_STRSIZE); 823 if (strlcat(propstr, ",", DLADM_STRSIZE) >= 824 DLADM_STRSIZE) 825 die("property list too long '%s'", propstr); 826 break; 827 default: 828 die_opterr(optopt, option); 829 } 830 } 831 if (!l_arg) { 832 die("link is required"); 833 } 834 835 opterr = 0; 836 index = optind; 837 838 if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) { 839 die("flow name is required"); 840 } else { 841 /* get flow name; required last argument */ 842 if (strlen(argv[index]) >= MAXFLOWNAMELEN) 843 die("flow name too long"); 844 name = argv[index]; 845 } 846 847 if (dladm_parse_flow_attrs(attrstr, &attrlist, B_FALSE) 848 != DLADM_STATUS_OK) 849 die("invalid flow attribute specified"); 850 if (dladm_parse_flow_props(propstr, &proplist, B_FALSE) 851 != DLADM_STATUS_OK) 852 die("invalid flow property specified"); 853 854 status = dladm_flow_add(handle, linkid, attrlist, proplist, name, 855 t_arg, altroot); 856 if (status != DLADM_STATUS_OK) 857 die_dlerr(status, "add flow failed"); 858 859 dladm_free_attrs(attrlist); 860 dladm_free_props(proplist); 861 } 862 863 static void 864 do_remove_flow(int argc, char *argv[]) 865 { 866 char option; 867 char *flowname = NULL; 868 char linkname[MAXLINKNAMELEN]; 869 datalink_id_t linkid = DATALINK_ALL_LINKID; 870 boolean_t l_arg = B_FALSE; 871 remove_flow_state_t state; 872 dladm_status_t status; 873 874 bzero(&state, sizeof (state)); 875 876 opterr = 0; 877 while ((option = getopt_long(argc, argv, ":tR:l:", 878 longopts, NULL)) != -1) { 879 switch (option) { 880 case 't': 881 t_arg = B_TRUE; 882 break; 883 case 'R': 884 altroot = optarg; 885 break; 886 case 'l': 887 if (strlcpy(linkname, optarg, 888 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 889 die("link name too long"); 890 } 891 if (dladm_name2info(handle, linkname, &linkid, NULL, 892 NULL, NULL) != DLADM_STATUS_OK) { 893 die("invalid link '%s'", linkname); 894 } 895 l_arg = B_TRUE; 896 break; 897 default: 898 die_opterr(optopt, option); 899 break; 900 } 901 } 902 903 /* when link not specified get flow name */ 904 if (!l_arg) { 905 if (optind != (argc-1)) { 906 usage(); 907 } else { 908 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 909 die("flow name too long"); 910 flowname = argv[optind]; 911 } 912 status = dladm_flow_remove(handle, flowname, t_arg, altroot); 913 } else { 914 /* if link is specified then flow name should not be there */ 915 if (optind == argc-1) 916 usage(); 917 /* walk the link to find flows and remove them */ 918 state.fs_tempop = t_arg; 919 state.fs_altroot = altroot; 920 state.fs_status = DLADM_STATUS_OK; 921 status = dladm_walk_flow(remove_flow, handle, linkid, &state, 922 B_FALSE); 923 /* 924 * check if dladm_walk_flow terminated early and see if the 925 * walker function as any status for us 926 */ 927 if (status == DLADM_STATUS_OK) 928 status = state.fs_status; 929 } 930 931 if (status != DLADM_STATUS_OK) 932 die_dlerr(status, "remove flow failed"); 933 } 934 935 /* 936 * Walker function for removing a flow through dladm_walk_flow(); 937 */ 938 static int 939 remove_flow(dladm_flow_attr_t *attr, void *arg) 940 { 941 remove_flow_state_t *state = (remove_flow_state_t *)arg; 942 943 state->fs_status = dladm_flow_remove(handle, attr->fa_flowname, 944 state->fs_tempop, state->fs_altroot); 945 946 if (state->fs_status == DLADM_STATUS_OK) 947 return (DLADM_WALK_CONTINUE); 948 else 949 return (DLADM_WALK_TERMINATE); 950 } 951 952 static char * 953 flowadm_print_field(print_field_t *pf, void *arg) 954 { 955 char *value; 956 957 value = (char *)arg + pf->pf_offset; 958 return (value); 959 } 960 961 /*ARGSUSED*/ 962 static dladm_status_t 963 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr, 964 flow_fields_buf_t *fbuf) 965 { 966 char link[MAXLINKNAMELEN]; 967 dladm_status_t status; 968 969 if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL, 970 NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) { 971 return (status); 972 } 973 974 (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name), 975 "%s", attr->fa_flowname); 976 (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link), 977 "%s", link); 978 979 (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr, 980 sizeof (fbuf->flow_ipaddr)); 981 (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto, 982 sizeof (fbuf->flow_proto)); 983 (void) dladm_flow_attr_port2str(attr, fbuf->flow_port, 984 sizeof (fbuf->flow_port)); 985 (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield, 986 sizeof (fbuf->flow_dsfield)); 987 988 return (DLADM_STATUS_OK); 989 } 990 991 /* 992 * Walker function for showing flow attributes through dladm_walk_flow(). 993 */ 994 static int 995 show_flow(dladm_flow_attr_t *attr, void *arg) 996 { 997 show_flow_state_t *statep = arg; 998 dladm_status_t status; 999 flow_fields_buf_t fbuf; 1000 1001 /* 1002 * first get all the flow attributes into fbuf; 1003 */ 1004 bzero(&fbuf, sizeof (fbuf)); 1005 status = print_flow(statep, attr, &fbuf); 1006 1007 if (status != DLADM_STATUS_OK) 1008 goto done; 1009 1010 if (!statep->fs_parseable && !statep->fs_printheader) { 1011 print_header(&statep->fs_print); 1012 statep->fs_printheader = B_TRUE; 1013 } 1014 1015 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1016 flowadm_print_field, (void *)&fbuf); 1017 1018 done: 1019 statep->fs_status = status; 1020 return (DLADM_WALK_CONTINUE); 1021 } 1022 1023 static void 1024 show_one_flow(void *arg, const char *name) 1025 { 1026 dladm_flow_attr_t attr; 1027 1028 if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK) 1029 die("invalid flow: '%s'", name); 1030 else 1031 (void) show_flow(&attr, arg); 1032 } 1033 1034 /* 1035 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to 1036 * dladm_walk_datalink_id(). Used for showing flow attributes for 1037 * all flows on all links. 1038 */ 1039 static int 1040 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1041 { 1042 show_flow_state_t *state = arg; 1043 1044 (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist); 1045 1046 return (DLADM_WALK_CONTINUE); 1047 } 1048 1049 static void 1050 get_flow_stats(const char *flowname, pktsum_t *stats) 1051 { 1052 kstat_ctl_t *kcp; 1053 kstat_t *ksp; 1054 1055 bzero(stats, sizeof (*stats)); 1056 1057 if ((kcp = kstat_open()) == NULL) { 1058 warn("kstat open operation failed"); 1059 return; 1060 } 1061 1062 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); 1063 1064 if (ksp != NULL) 1065 dladm_get_stats(kcp, ksp, stats); 1066 1067 (void) kstat_close(kcp); 1068 } 1069 1070 1071 static char * 1072 print_flow_stats(print_field_t *pf, void *arg) 1073 { 1074 flow_args_t *fargs = arg; 1075 pktsum_t *diff_stats = fargs->flow_s_psum; 1076 static char buf[DLADM_STRSIZE]; 1077 1078 switch (pf->pf_index) { 1079 case FLOW_S_FLOW: 1080 (void) snprintf(buf, sizeof (buf), "%s", fargs->flow_s_flow); 1081 break; 1082 case FLOW_S_IPKTS: 1083 (void) snprintf(buf, sizeof (buf), "%llu", 1084 diff_stats->ipackets); 1085 break; 1086 case FLOW_S_RBYTES: 1087 (void) snprintf(buf, sizeof (buf), "%llu", 1088 diff_stats->rbytes); 1089 break; 1090 case FLOW_S_IERRORS: 1091 (void) snprintf(buf, sizeof (buf), "%u", 1092 diff_stats->ierrors); 1093 break; 1094 case FLOW_S_OPKTS: 1095 (void) snprintf(buf, sizeof (buf), "%llu", 1096 diff_stats->opackets); 1097 break; 1098 case FLOW_S_OBYTES: 1099 (void) snprintf(buf, sizeof (buf), "%llu", 1100 diff_stats->obytes); 1101 break; 1102 case FLOW_S_OERRORS: 1103 (void) snprintf(buf, sizeof (buf), "%u", 1104 diff_stats->oerrors); 1105 break; 1106 default: 1107 die("invalid input"); 1108 break; 1109 } 1110 return (buf); 1111 } 1112 /* ARGSUSED */ 1113 static int 1114 show_flow_stats(dladm_flow_attr_t *attr, void *arg) 1115 { 1116 show_flow_state_t *state = (show_flow_state_t *)arg; 1117 char *name = attr->fa_flowname; 1118 pktsum_t stats, diff_stats; 1119 flow_args_t fargs; 1120 1121 if (state->fs_firstonly) { 1122 if (state->fs_donefirst) 1123 return (DLADM_WALK_TERMINATE); 1124 state->fs_donefirst = B_TRUE; 1125 } else { 1126 bzero(&state->fs_prevstats, sizeof (state->fs_prevstats)); 1127 } 1128 1129 get_flow_stats(name, &stats); 1130 dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats); 1131 1132 fargs.flow_s_flow = name; 1133 fargs.flow_s_psum = &diff_stats; 1134 flowadm_print_output(&state->fs_print, state->fs_parseable, 1135 print_flow_stats, &fargs); 1136 1137 state->fs_prevstats = stats; 1138 1139 return (DLADM_WALK_CONTINUE); 1140 } 1141 1142 /* 1143 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for 1144 * dladm_walk_datalink_id(). Used for showing flow stats for 1145 * all flows on all links. 1146 */ 1147 static int 1148 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 1149 { 1150 if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE) 1151 == DLADM_STATUS_OK) 1152 return (DLADM_WALK_CONTINUE); 1153 else 1154 return (DLADM_WALK_TERMINATE); 1155 } 1156 1157 /* ARGSUSED */ 1158 static void 1159 flow_stats(const char *flow, datalink_id_t linkid, uint_t interval, 1160 char *fields_str, show_flow_state_t *state) 1161 { 1162 dladm_flow_attr_t attr; 1163 print_field_t **fields; 1164 uint_t nfields; 1165 1166 fields = parse_output_fields(fields_str, flow_s_fields, 1167 FLOW_S_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1168 if (fields == NULL) { 1169 die("invalid field(s) specified"); 1170 return; 1171 } 1172 1173 state->fs_print.ps_fields = fields; 1174 state->fs_print.ps_nfields = nfields; 1175 1176 if (flow != NULL && 1177 dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK) 1178 die("invalid flow %s", flow); 1179 1180 /* 1181 * If an interval is specified, continuously show the stats 1182 * for only the first flow. 1183 */ 1184 state->fs_firstonly = (interval != 0); 1185 1186 if (!state->fs_parseable) 1187 print_header(&state->fs_print); 1188 for (;;) { 1189 state->fs_donefirst = B_FALSE; 1190 1191 /* Show stats for named flow */ 1192 if (flow != NULL) { 1193 state->fs_flow = flow; 1194 (void) show_flow_stats(&attr, state); 1195 1196 /* Show all stats on a link */ 1197 } else if (linkid != DATALINK_INVALID_LINKID) { 1198 (void) dladm_walk_flow(show_flow_stats, handle, linkid, 1199 state, B_FALSE); 1200 1201 /* Show all stats by datalink */ 1202 } else { 1203 (void) dladm_walk_datalink_id(show_link_flow_stats, 1204 handle, state, DATALINK_CLASS_ALL, 1205 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 1206 } 1207 1208 if (interval == 0) 1209 break; 1210 1211 (void) sleep(interval); 1212 } 1213 } 1214 1215 static void 1216 do_show_flow(int argc, char *argv[]) 1217 { 1218 char flowname[MAXFLOWNAMELEN]; 1219 char linkname[MAXLINKNAMELEN]; 1220 datalink_id_t linkid = DATALINK_ALL_LINKID; 1221 int option; 1222 boolean_t s_arg = B_FALSE; 1223 boolean_t S_arg = B_FALSE; 1224 boolean_t i_arg = B_FALSE; 1225 boolean_t l_arg = B_FALSE; 1226 boolean_t o_arg = B_FALSE; 1227 uint32_t interval = 0; 1228 show_flow_state_t state; 1229 char *fields_str = NULL; 1230 print_field_t **fields; 1231 uint_t nfields; 1232 char *all_fields = 1233 "flow,link,ipaddr,proto,port,dsfld"; 1234 char *allstat_fields = 1235 "flow,ipackets,rbytes,ierrors,opackets,obytes,oerrors"; 1236 1237 bzero(&state, sizeof (state)); 1238 1239 opterr = 0; 1240 while ((option = getopt_long(argc, argv, ":pPsSi:l:o:", 1241 longopts, NULL)) != -1) { 1242 switch (option) { 1243 case 'p': 1244 state.fs_parseable = B_TRUE; 1245 break; 1246 case 'P': 1247 state.fs_persist = B_TRUE; 1248 break; 1249 case 's': 1250 if (s_arg) 1251 die_optdup(option); 1252 1253 s_arg = B_TRUE; 1254 break; 1255 case 'S': 1256 if (S_arg) 1257 die_optdup(option); 1258 1259 S_arg = B_TRUE; 1260 break; 1261 case 'o': 1262 if (o_arg) 1263 die_optdup(option); 1264 1265 o_arg = B_TRUE; 1266 fields_str = optarg; 1267 break; 1268 case 'i': 1269 if (i_arg) 1270 die_optdup(option); 1271 1272 i_arg = B_TRUE; 1273 1274 if (!dladm_str2interval(optarg, &interval)) 1275 die("invalid interval value '%s'", optarg); 1276 break; 1277 case 'l': 1278 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 1279 >= MAXLINKNAMELEN) 1280 die("link name too long\n"); 1281 if (dladm_name2info(handle, linkname, &linkid, NULL, 1282 NULL, NULL) != DLADM_STATUS_OK) 1283 die("invalid link '%s'", linkname); 1284 l_arg = B_TRUE; 1285 break; 1286 default: 1287 die_opterr(optopt, option); 1288 break; 1289 } 1290 } 1291 1292 if (i_arg && !(s_arg || S_arg)) 1293 die("the -i option can be used only with -s or -S"); 1294 1295 if (s_arg && S_arg) 1296 die("the -s option cannot be used with -S"); 1297 1298 /* get flow name (optional last argument */ 1299 if (optind == (argc-1)) { 1300 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 1301 >= MAXFLOWNAMELEN) 1302 die("flow name too long"); 1303 state.fs_flow = flowname; 1304 } 1305 1306 if (S_arg) { 1307 dladm_continuous(handle, linkid, state.fs_flow, interval, 1308 FLOW_REPORT); 1309 return; 1310 } 1311 1312 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { 1313 if (s_arg) 1314 fields_str = allstat_fields; 1315 else 1316 fields_str = all_fields; 1317 } 1318 1319 if (s_arg) { 1320 flow_stats(state.fs_flow, linkid, interval, fields_str, &state); 1321 return; 1322 } 1323 1324 fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS, 1325 CMD_TYPE_ANY, &nfields); 1326 1327 if (fields == NULL) { 1328 die("invalid fields(s) specified"); 1329 return; 1330 } 1331 1332 state.fs_print.ps_fields = fields; 1333 state.fs_print.ps_nfields = nfields; 1334 1335 /* Show attributes of one flow */ 1336 if (state.fs_flow != NULL) { 1337 show_one_flow(&state, state.fs_flow); 1338 1339 /* Show attributes of flows on one link */ 1340 } else if (l_arg) { 1341 (void) show_flows_onelink(handle, linkid, &state); 1342 1343 /* Show attributes of all flows on all links */ 1344 } else { 1345 (void) dladm_walk_datalink_id(show_flows_onelink, handle, 1346 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1347 DLADM_OPT_ACTIVE); 1348 } 1349 } 1350 1351 static dladm_status_t 1352 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, 1353 uint_t val_cnt, boolean_t reset) 1354 { 1355 dladm_status_t status; 1356 char *errprop; 1357 1358 status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt, 1359 DLADM_OPT_PERSIST, &errprop); 1360 1361 if (status != DLADM_STATUS_OK) { 1362 warn_dlerr(status, "cannot persistently %s flow " 1363 "property '%s' on '%s'", reset? "reset": "set", 1364 errprop, flow); 1365 } 1366 return (status); 1367 } 1368 1369 static void 1370 set_flowprop(int argc, char **argv, boolean_t reset) 1371 { 1372 int i, option; 1373 char errmsg[DLADM_STRSIZE]; 1374 const char *flow = NULL; 1375 char propstr[DLADM_STRSIZE]; 1376 dladm_arg_list_t *proplist = NULL; 1377 boolean_t temp = B_FALSE; 1378 dladm_status_t status = DLADM_STATUS_OK; 1379 1380 opterr = 0; 1381 bzero(propstr, DLADM_STRSIZE); 1382 1383 while ((option = getopt_long(argc, argv, ":p:R:t", 1384 prop_longopts, NULL)) != -1) { 1385 switch (option) { 1386 case 'p': 1387 (void) strlcat(propstr, optarg, DLADM_STRSIZE); 1388 if (strlcat(propstr, ",", DLADM_STRSIZE) >= 1389 DLADM_STRSIZE) 1390 die("property list too long '%s'", propstr); 1391 break; 1392 case 't': 1393 temp = B_TRUE; 1394 break; 1395 case 'R': 1396 status = dladm_set_rootdir(optarg); 1397 if (status != DLADM_STATUS_OK) { 1398 die_dlerr(status, "invalid directory " 1399 "specified"); 1400 } 1401 break; 1402 default: 1403 die_opterr(optopt, option); 1404 break; 1405 } 1406 } 1407 1408 if (optind == (argc - 1)) { 1409 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1410 die("flow name too long"); 1411 flow = argv[optind]; 1412 } else if (optind != argc) { 1413 usage(); 1414 } 1415 if (flow == NULL) 1416 die("flow name must be specified"); 1417 1418 if (dladm_parse_flow_props(propstr, &proplist, reset) 1419 != DLADM_STATUS_OK) 1420 die("invalid flow property specified"); 1421 1422 if (proplist == NULL) { 1423 char *errprop; 1424 1425 if (!reset) 1426 die("flow property must be specified"); 1427 1428 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0, 1429 DLADM_OPT_ACTIVE, &errprop); 1430 if (status != DLADM_STATUS_OK) { 1431 warn_dlerr(status, "cannot reset flow property '%s' " 1432 "on '%s'", errprop, flow); 1433 } 1434 if (!temp) { 1435 dladm_status_t s; 1436 1437 s = set_flowprop_persist(flow, NULL, NULL, 0, reset); 1438 if (s != DLADM_STATUS_OK) 1439 status = s; 1440 } 1441 goto done; 1442 } 1443 1444 for (i = 0; i < proplist->al_count; i++) { 1445 dladm_arg_info_t *aip = &proplist->al_info[i]; 1446 char **val; 1447 uint_t count; 1448 dladm_status_t s; 1449 1450 if (reset) { 1451 val = NULL; 1452 count = 0; 1453 } else { 1454 val = aip->ai_val; 1455 count = aip->ai_count; 1456 if (count == 0) { 1457 warn("no value specified for '%s'", 1458 aip->ai_name); 1459 status = DLADM_STATUS_BADARG; 1460 continue; 1461 } 1462 } 1463 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count, 1464 DLADM_OPT_ACTIVE, NULL); 1465 if (s == DLADM_STATUS_OK) { 1466 if (!temp) { 1467 s = set_flowprop_persist(flow, 1468 aip->ai_name, val, count, reset); 1469 if (s != DLADM_STATUS_OK) 1470 status = s; 1471 } 1472 continue; 1473 } 1474 status = s; 1475 switch (s) { 1476 case DLADM_STATUS_NOTFOUND: 1477 warn("invalid flow property '%s'", aip->ai_name); 1478 break; 1479 case DLADM_STATUS_BADVAL: { 1480 int j; 1481 char *ptr, *lim; 1482 char **propvals = NULL; 1483 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1484 1485 ptr = malloc((sizeof (char *) + 1486 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 1487 MAX_PROP_LINE); 1488 1489 if (ptr == NULL) 1490 die("insufficient memory"); 1491 propvals = (char **)(void *)ptr; 1492 1493 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 1494 propvals[j] = ptr + sizeof (char *) * 1495 DLADM_MAX_PROP_VALCNT + 1496 j * DLADM_PROP_VAL_MAX; 1497 } 1498 s = dladm_get_flowprop(handle, flow, 1499 DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, 1500 &valcnt); 1501 1502 ptr = errmsg; 1503 lim = ptr + DLADM_STRSIZE; 1504 *ptr = '\0'; 1505 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { 1506 ptr += snprintf(ptr, lim - ptr, "%s,", 1507 propvals[j]); 1508 if (ptr >= lim) 1509 break; 1510 } 1511 if (ptr > errmsg) { 1512 *(ptr - 1) = '\0'; 1513 warn("flow property '%s' must be one of: %s", 1514 aip->ai_name, errmsg); 1515 } else 1516 warn("%s is an invalid value for " 1517 "flow property %s", *val, aip->ai_name); 1518 free(propvals); 1519 break; 1520 } 1521 default: 1522 if (reset) { 1523 warn_dlerr(status, "cannot reset flow property " 1524 "'%s' on '%s'", aip->ai_name, flow); 1525 } else { 1526 warn_dlerr(status, "cannot set flow property " 1527 "'%s' on '%s'", aip->ai_name, flow); 1528 } 1529 break; 1530 } 1531 } 1532 done: 1533 dladm_free_props(proplist); 1534 if (status != DLADM_STATUS_OK) { 1535 dladm_close(handle); 1536 exit(EXIT_FAILURE); 1537 } 1538 } 1539 1540 static void 1541 do_set_flowprop(int argc, char **argv) 1542 { 1543 set_flowprop(argc, argv, B_FALSE); 1544 } 1545 1546 static void 1547 do_reset_flowprop(int argc, char **argv) 1548 { 1549 set_flowprop(argc, argv, B_TRUE); 1550 } 1551 1552 static void 1553 warn(const char *format, ...) 1554 { 1555 va_list alist; 1556 1557 format = gettext(format); 1558 (void) fprintf(stderr, "%s: warning: ", progname); 1559 1560 va_start(alist, format); 1561 (void) vfprintf(stderr, format, alist); 1562 va_end(alist); 1563 1564 (void) putchar('\n'); 1565 } 1566 1567 /* PRINTFLIKE2 */ 1568 static void 1569 warn_dlerr(dladm_status_t err, const char *format, ...) 1570 { 1571 va_list alist; 1572 char errmsg[DLADM_STRSIZE]; 1573 1574 format = gettext(format); 1575 (void) fprintf(stderr, gettext("%s: warning: "), progname); 1576 1577 va_start(alist, format); 1578 (void) vfprintf(stderr, format, alist); 1579 va_end(alist); 1580 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1581 } 1582 1583 /* PRINTFLIKE1 */ 1584 static void 1585 die(const char *format, ...) 1586 { 1587 va_list alist; 1588 1589 format = gettext(format); 1590 (void) fprintf(stderr, "%s: ", progname); 1591 1592 va_start(alist, format); 1593 (void) vfprintf(stderr, format, alist); 1594 va_end(alist); 1595 1596 (void) putchar('\n'); 1597 1598 /* close dladm handle if it was opened */ 1599 if (handle != NULL) 1600 dladm_close(handle); 1601 1602 exit(EXIT_FAILURE); 1603 } 1604 1605 static void 1606 die_optdup(int opt) 1607 { 1608 die("the option -%c cannot be specified more than once", opt); 1609 } 1610 1611 static void 1612 die_opterr(int opt, int opterr) 1613 { 1614 switch (opterr) { 1615 case ':': 1616 die("option '-%c' requires a value", opt); 1617 break; 1618 case '?': 1619 default: 1620 die("unrecognized option '-%c'", opt); 1621 break; 1622 } 1623 } 1624 1625 /* PRINTFLIKE2 */ 1626 static void 1627 die_dlerr(dladm_status_t err, const char *format, ...) 1628 { 1629 va_list alist; 1630 char errmsg[DLADM_STRSIZE]; 1631 1632 format = gettext(format); 1633 (void) fprintf(stderr, "%s: ", progname); 1634 1635 va_start(alist, format); 1636 (void) vfprintf(stderr, format, alist); 1637 va_end(alist); 1638 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1639 1640 /* close dladm handle if it was opened */ 1641 if (handle != NULL) 1642 dladm_close(handle); 1643 1644 exit(EXIT_FAILURE); 1645 } 1646 1647 static void 1648 print_flowprop(const char *flowname, show_flowprop_state_t *statep, 1649 const char *propname, dladm_prop_type_t type, 1650 const char *format, char **pptr) 1651 { 1652 int i; 1653 char *ptr, *lim; 1654 char buf[DLADM_STRSIZE]; 1655 char *unknown = "--", *notsup = ""; 1656 char **propvals = statep->fs_propvals; 1657 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1658 dladm_status_t status; 1659 1660 status = dladm_get_flowprop(handle, flowname, type, propname, propvals, 1661 &valcnt); 1662 if (status != DLADM_STATUS_OK) { 1663 if (status == DLADM_STATUS_TEMPONLY) { 1664 if (type == DLADM_PROP_VAL_MODIFIABLE && 1665 statep->fs_persist) { 1666 valcnt = 1; 1667 propvals = &unknown; 1668 } else { 1669 statep->fs_status = status; 1670 statep->fs_retstatus = status; 1671 return; 1672 } 1673 } else if (status == DLADM_STATUS_NOTSUP || 1674 statep->fs_persist) { 1675 valcnt = 1; 1676 if (type == DLADM_PROP_VAL_CURRENT) 1677 propvals = &unknown; 1678 else 1679 propvals = ¬sup; 1680 } else { 1681 if ((statep->fs_proplist != NULL) && 1682 statep->fs_status == DLADM_STATUS_OK) { 1683 warn("invalid flow property '%s'", propname); 1684 } 1685 statep->fs_status = status; 1686 statep->fs_retstatus = status; 1687 return; 1688 } 1689 } 1690 1691 statep->fs_status = DLADM_STATUS_OK; 1692 1693 ptr = buf; 1694 lim = buf + DLADM_STRSIZE; 1695 for (i = 0; i < valcnt; i++) { 1696 if (propvals[i][0] == '\0' && !statep->fs_parseable) 1697 ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL","); 1698 else 1699 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 1700 if (ptr >= lim) 1701 break; 1702 } 1703 if (valcnt > 0) 1704 buf[strlen(buf) - 1] = '\0'; 1705 1706 lim = statep->fs_line + MAX_PROP_LINE; 1707 if (statep->fs_parseable) { 1708 *pptr += snprintf(*pptr, lim - *pptr, 1709 "%s", buf); 1710 } else { 1711 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 1712 } 1713 } 1714 1715 static char * 1716 flowprop_callback(print_field_t *pf, void *fs_arg) 1717 { 1718 flowprop_args_t *arg = fs_arg; 1719 char *propname = arg->fs_propname; 1720 show_flowprop_state_t *statep = arg->fs_state; 1721 char *ptr = statep->fs_line; 1722 char *lim = ptr + MAX_PROP_LINE; 1723 char *flowname = arg->fs_flowname; 1724 1725 switch (pf->pf_index) { 1726 case FLOWPROP_FLOW: 1727 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); 1728 break; 1729 case FLOWPROP_PROPERTY: 1730 (void) snprintf(ptr, lim - ptr, "%s", propname); 1731 break; 1732 case FLOWPROP_VALUE: 1733 print_flowprop(flowname, statep, propname, 1734 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : 1735 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 1736 /* 1737 * If we failed to query the flow property, for example, query 1738 * the persistent value of a non-persistable flow property, 1739 * simply skip the output. 1740 */ 1741 if (statep->fs_status != DLADM_STATUS_OK) 1742 goto skip; 1743 ptr = statep->fs_line; 1744 break; 1745 case FLOWPROP_DEFAULT: 1746 print_flowprop(flowname, statep, propname, 1747 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 1748 if (statep->fs_status != DLADM_STATUS_OK) 1749 goto skip; 1750 ptr = statep->fs_line; 1751 break; 1752 case FLOWPROP_POSSIBLE: 1753 print_flowprop(flowname, statep, propname, 1754 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 1755 if (statep->fs_status != DLADM_STATUS_OK) 1756 goto skip; 1757 ptr = statep->fs_line; 1758 break; 1759 default: 1760 die("invalid input"); 1761 break; 1762 } 1763 return (ptr); 1764 skip: 1765 if (statep->fs_status != DLADM_STATUS_OK) 1766 return (NULL); 1767 else 1768 return (""); 1769 } 1770 1771 static int 1772 show_one_flowprop(void *arg, const char *propname) 1773 { 1774 show_flowprop_state_t *statep = arg; 1775 flowprop_args_t fs_arg; 1776 1777 bzero(&fs_arg, sizeof (fs_arg)); 1778 fs_arg.fs_state = statep; 1779 fs_arg.fs_propname = (char *)propname; 1780 fs_arg.fs_flowname = (char *)statep->fs_flow; 1781 1782 if (statep->fs_header) { 1783 statep->fs_header = B_FALSE; 1784 if (!statep ->fs_parseable) 1785 print_header(&statep->fs_print); 1786 } 1787 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1788 flowprop_callback, (void *)&fs_arg); 1789 1790 return (DLADM_WALK_CONTINUE); 1791 } 1792 1793 /* Walker function called by dladm_walk_flow to display flow properties */ 1794 static int 1795 show_flowprop(dladm_flow_attr_t *attr, void *arg) 1796 { 1797 show_flowprop_one_flow(arg, attr->fa_flowname); 1798 return (DLADM_WALK_CONTINUE); 1799 } 1800 1801 /* 1802 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it 1803 * usable to dladm_walk_datalink_id() 1804 */ 1805 static int 1806 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1807 { 1808 char name[MAXLINKNAMELEN]; 1809 1810 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name, 1811 sizeof (name)) != DLADM_STATUS_OK) 1812 return (DLADM_WALK_TERMINATE); 1813 1814 (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE); 1815 1816 return (DLADM_WALK_CONTINUE); 1817 } 1818 1819 static void 1820 do_show_flowprop(int argc, char **argv) 1821 { 1822 int option; 1823 boolean_t o_arg = B_FALSE; 1824 dladm_arg_list_t *proplist = NULL; 1825 show_flowprop_state_t state; 1826 char *fields_str = NULL; 1827 print_field_t **fields; 1828 uint_t nfields; 1829 char *all_fields = 1830 "flow,property,value,default,possible"; 1831 1832 fields_str = all_fields; 1833 opterr = 0; 1834 state.fs_propvals = NULL; 1835 state.fs_line = NULL; 1836 state.fs_parseable = B_FALSE; 1837 state.fs_persist = B_FALSE; 1838 state.fs_header = B_TRUE; 1839 state.fs_retstatus = DLADM_STATUS_OK; 1840 state.fs_linkid = DATALINK_INVALID_LINKID; 1841 state.fs_flow = NULL; 1842 1843 while ((option = getopt_long(argc, argv, ":p:cPl:o:", 1844 prop_longopts, NULL)) != -1) { 1845 switch (option) { 1846 case 'p': 1847 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) 1848 != DLADM_STATUS_OK) 1849 die("invalid flow properties specified"); 1850 break; 1851 case 'c': 1852 state.fs_parseable = B_TRUE; 1853 break; 1854 case 'P': 1855 state.fs_persist = B_TRUE; 1856 break; 1857 case 'l': 1858 if (dladm_name2info(handle, optarg, &state.fs_linkid, 1859 NULL, NULL, NULL) != DLADM_STATUS_OK) 1860 die("invalid link '%s'", optarg); 1861 break; 1862 case 'o': 1863 o_arg = B_TRUE; 1864 if (strcasecmp(optarg, "all") == 0) 1865 fields_str = all_fields; 1866 else 1867 fields_str = optarg; 1868 break; 1869 default: 1870 die_opterr(optopt, option); 1871 break; 1872 } 1873 } 1874 1875 if (state.fs_parseable && !o_arg) 1876 die("-p requires -o"); 1877 1878 if (state.fs_parseable && strcasecmp(fields_str, "all") == 0) 1879 die("\"-o all\" is invalid with -p"); 1880 1881 if (optind == (argc - 1)) { 1882 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1883 die("flow name too long"); 1884 state.fs_flow = argv[optind]; 1885 } else if (optind != argc) { 1886 usage(); 1887 } 1888 bzero(&state.fs_print, sizeof (print_state_t)); 1889 state.fs_proplist = proplist; 1890 state.fs_status = DLADM_STATUS_OK; 1891 1892 fields = parse_output_fields(fields_str, flowprop_fields, 1893 FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1894 1895 if (fields == NULL) { 1896 die("invalid field(s) specified"); 1897 return; 1898 } 1899 1900 state.fs_print.ps_fields = fields; 1901 state.fs_print.ps_nfields = nfields; 1902 1903 /* Show properties for one flow */ 1904 if (state.fs_flow != NULL) { 1905 show_flowprop_one_flow(&state, state.fs_flow); 1906 1907 /* Show properties for all flows on one link */ 1908 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { 1909 (void) show_flowprop_onelink(handle, state.fs_linkid, &state); 1910 1911 /* Show properties for all flows on all links */ 1912 } else { 1913 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle, 1914 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1915 DLADM_OPT_ACTIVE); 1916 } 1917 1918 dladm_free_props(proplist); 1919 } 1920 1921 static void 1922 show_flowprop_one_flow(void *arg, const char *flow) 1923 { 1924 int i; 1925 char *buf; 1926 dladm_status_t status; 1927 dladm_arg_list_t *proplist = NULL; 1928 show_flowprop_state_t *statep = arg; 1929 dladm_flow_attr_t attr; 1930 const char *savep; 1931 1932 /* 1933 * Do not print flow props for invalid flows. 1934 */ 1935 if ((status = dladm_flow_info(handle, flow, &attr)) != 1936 DLADM_STATUS_OK) { 1937 die("invalid flow: '%s'", flow); 1938 } 1939 1940 savep = statep->fs_flow; 1941 statep->fs_flow = flow; 1942 1943 proplist = statep->fs_proplist; 1944 1945 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) 1946 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 1947 if (buf == NULL) 1948 die("insufficient memory"); 1949 1950 statep->fs_propvals = (char **)(void *)buf; 1951 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 1952 statep->fs_propvals[i] = buf + 1953 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 1954 i * DLADM_PROP_VAL_MAX; 1955 } 1956 statep->fs_line = buf + 1957 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 1958 1959 /* show only specified flow properties */ 1960 if (proplist != NULL) { 1961 for (i = 0; i < proplist->al_count; i++) { 1962 if (show_one_flowprop(statep, 1963 proplist->al_info[i].ai_name) != DLADM_STATUS_OK) 1964 break; 1965 } 1966 1967 /* show all flow properties */ 1968 } else { 1969 status = dladm_walk_flowprop(show_one_flowprop, flow, statep); 1970 if (status != DLADM_STATUS_OK) 1971 die_dlerr(status, "show-flowprop"); 1972 } 1973 free(buf); 1974 statep->fs_flow = savep; 1975 } 1976 1977 typedef struct { 1978 char *s_buf; 1979 char **s_fields; /* array of pointer to the fields in s_buf */ 1980 uint_t s_nfields; /* the number of fields in s_buf */ 1981 } split_t; 1982 1983 /* 1984 * Free the split_t structure pointed to by `sp'. 1985 */ 1986 static void 1987 splitfree(split_t *sp) 1988 { 1989 free(sp->s_buf); 1990 free(sp->s_fields); 1991 free(sp); 1992 } 1993 1994 /* 1995 * Split `str' into at most `maxfields' fields, each field at most `maxlen' in 1996 * length. Return a pointer to a split_t containing the split fields, or NULL 1997 * on failure. 1998 */ 1999 static split_t * 2000 split(const char *str, uint_t maxfields, uint_t maxlen) 2001 { 2002 char *field, *token, *lasts = NULL; 2003 split_t *sp; 2004 2005 if (*str == '\0' || maxfields == 0 || maxlen == 0) 2006 return (NULL); 2007 2008 sp = calloc(sizeof (split_t), 1); 2009 if (sp == NULL) 2010 return (NULL); 2011 2012 sp->s_buf = strdup(str); 2013 sp->s_fields = malloc(sizeof (char *) * maxfields); 2014 if (sp->s_buf == NULL || sp->s_fields == NULL) 2015 goto fail; 2016 2017 token = sp->s_buf; 2018 while ((field = strtok_r(token, ",", &lasts)) != NULL) { 2019 if (sp->s_nfields == maxfields || strlen(field) > maxlen) 2020 goto fail; 2021 token = NULL; 2022 sp->s_fields[sp->s_nfields++] = field; 2023 } 2024 return (sp); 2025 fail: 2026 splitfree(sp); 2027 return (NULL); 2028 } 2029 2030 static print_field_t ** 2031 parse_output_fields(char *str, print_field_t *template, int max_fields, 2032 uint_t cmdtype, uint_t *countp) 2033 { 2034 split_t *sp; 2035 boolean_t good_match = B_FALSE; 2036 uint_t i, j; 2037 print_field_t **pf = NULL; 2038 2039 sp = split(str, max_fields, MAX_FIELD_LEN); 2040 2041 if (sp == NULL) 2042 return (NULL); 2043 2044 pf = malloc(sp->s_nfields * sizeof (print_field_t *)); 2045 if (pf == NULL) 2046 goto fail; 2047 2048 for (i = 0; i < sp->s_nfields; i++) { 2049 for (j = 0; j < max_fields; j++) { 2050 if (strcasecmp(sp->s_fields[i], 2051 template[j].pf_name) == 0) { 2052 good_match = template[j]. pf_cmdtype & cmdtype; 2053 break; 2054 } 2055 } 2056 if (!good_match) 2057 goto fail; 2058 2059 good_match = B_FALSE; 2060 pf[i] = &template[j]; 2061 } 2062 *countp = i; 2063 splitfree(sp); 2064 return (pf); 2065 fail: 2066 free(pf); 2067 splitfree(sp); 2068 return (NULL); 2069 } 2070 2071 static void 2072 flowadm_print_output(print_state_t *statep, boolean_t parseable, 2073 print_callback_t fn, void *arg) 2074 { 2075 int i; 2076 char *value; 2077 print_field_t **pf; 2078 2079 pf = statep->ps_fields; 2080 for (i = 0; i < statep->ps_nfields; i++) { 2081 statep->ps_lastfield = (i + 1 == statep->ps_nfields); 2082 value = (*fn)(pf[i], arg); 2083 if (value != NULL) 2084 print_field(statep, pf[i], value, parseable); 2085 } 2086 (void) putchar('\n'); 2087 } 2088 2089 static void 2090 print_header(print_state_t *ps) 2091 { 2092 int i; 2093 print_field_t **pf; 2094 2095 pf = ps->ps_fields; 2096 for (i = 0; i < ps->ps_nfields; i++) { 2097 ps->ps_lastfield = (i + 1 == ps->ps_nfields); 2098 print_field(ps, pf[i], pf[i]->pf_header, B_FALSE); 2099 } 2100 (void) putchar('\n'); 2101 } 2102 2103 static void 2104 print_field(print_state_t *statep, print_field_t *pfp, const char *value, 2105 boolean_t parseable) 2106 { 2107 uint_t width = pfp->pf_width; 2108 uint_t valwidth; 2109 uint_t compress; 2110 2111 /* 2112 * Parsable fields are separated by ':'. If such a field contains 2113 * a ':' or '\', this character is prefixed by a '\'. 2114 */ 2115 if (parseable) { 2116 char c; 2117 2118 if (statep->ps_nfields == 1) { 2119 (void) printf("%s", value); 2120 return; 2121 } 2122 while ((c = *value++) != '\0') { 2123 if (c == ':' || c == '\\') 2124 (void) putchar('\\'); 2125 (void) putchar(c); 2126 } 2127 if (!statep->ps_lastfield) 2128 (void) putchar(':'); 2129 return; 2130 } else { 2131 if (value[0] == '\0') 2132 value = STR_UNDEF_VAL; 2133 if (statep->ps_lastfield) { 2134 (void) printf("%s", value); 2135 statep->ps_overflow = 0; 2136 return; 2137 } 2138 2139 valwidth = strlen(value); 2140 if (valwidth > width) { 2141 statep->ps_overflow += valwidth - width; 2142 } else if (valwidth < width && statep->ps_overflow > 0) { 2143 compress = min(statep->ps_overflow, width - valwidth); 2144 statep->ps_overflow -= compress; 2145 width -= compress; 2146 } 2147 (void) printf("%-*s", width, value); 2148 } 2149 2150 if (!statep->ps_lastfield) 2151 (void) putchar(' '); 2152 } 2153