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 dladm_arg_list_t *proplist = NULL; 788 dladm_arg_list_t *attrlist = NULL; 789 dladm_status_t status; 790 791 while ((option = getopt_long(argc, argv, "tR:l:a:p:", 792 prop_longopts, NULL)) != -1) { 793 switch (option) { 794 case 't': 795 t_arg = B_TRUE; 796 break; 797 case 'R': 798 altroot = optarg; 799 break; 800 case 'l': 801 if (strlcpy(devname, optarg, 802 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 803 die("link name too long"); 804 } 805 if (dladm_name2info(handle, devname, &linkid, NULL, 806 NULL, NULL) != DLADM_STATUS_OK) 807 die("invalid link '%s'", devname); 808 l_arg = B_TRUE; 809 break; 810 case 'a': 811 if (dladm_parse_flow_attrs(optarg, &attrlist, B_FALSE) 812 != DLADM_STATUS_OK) 813 die("invalid flow attribute specified"); 814 break; 815 case 'p': 816 if (dladm_parse_flow_props(optarg, &proplist, B_FALSE) 817 != DLADM_STATUS_OK) 818 die("invalid flow property specified"); 819 break; 820 default: 821 die_opterr(optopt, option); 822 } 823 } 824 if (!l_arg) { 825 die("link is required"); 826 } 827 828 opterr = 0; 829 index = optind; 830 831 if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) { 832 die("flow name is required"); 833 } else { 834 /* get flow name; required last argument */ 835 if (strlen(argv[index]) >= MAXFLOWNAMELEN) 836 die("flow name too long"); 837 name = argv[index]; 838 } 839 840 status = dladm_flow_add(handle, linkid, attrlist, proplist, name, 841 t_arg, altroot); 842 if (status != DLADM_STATUS_OK) 843 die_dlerr(status, "add flow failed"); 844 845 dladm_free_attrs(attrlist); 846 dladm_free_props(proplist); 847 } 848 849 static void 850 do_remove_flow(int argc, char *argv[]) 851 { 852 char option; 853 char *flowname = NULL; 854 char linkname[MAXLINKNAMELEN]; 855 datalink_id_t linkid = DATALINK_ALL_LINKID; 856 boolean_t l_arg = B_FALSE; 857 remove_flow_state_t state; 858 dladm_status_t status; 859 860 bzero(&state, sizeof (state)); 861 862 opterr = 0; 863 while ((option = getopt_long(argc, argv, ":tR:l:", 864 longopts, NULL)) != -1) { 865 switch (option) { 866 case 't': 867 t_arg = B_TRUE; 868 break; 869 case 'R': 870 altroot = optarg; 871 break; 872 case 'l': 873 if (strlcpy(linkname, optarg, 874 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 875 die("link name too long"); 876 } 877 if (dladm_name2info(handle, linkname, &linkid, NULL, 878 NULL, NULL) != DLADM_STATUS_OK) { 879 die("invalid link '%s'", linkname); 880 } 881 l_arg = B_TRUE; 882 break; 883 default: 884 die_opterr(optopt, option); 885 break; 886 } 887 } 888 889 /* when link not specified get flow name */ 890 if (!l_arg) { 891 if (optind != (argc-1)) { 892 usage(); 893 } else { 894 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 895 die("flow name too long"); 896 flowname = argv[optind]; 897 } 898 status = dladm_flow_remove(handle, flowname, t_arg, altroot); 899 } else { 900 /* if link is specified then flow name should not be there */ 901 if (optind == argc-1) 902 usage(); 903 /* walk the link to find flows and remove them */ 904 state.fs_tempop = t_arg; 905 state.fs_altroot = altroot; 906 state.fs_status = DLADM_STATUS_OK; 907 status = dladm_walk_flow(remove_flow, handle, linkid, &state, 908 B_FALSE); 909 /* 910 * check if dladm_walk_flow terminated early and see if the 911 * walker function as any status for us 912 */ 913 if (status == DLADM_STATUS_OK) 914 status = state.fs_status; 915 } 916 917 if (status != DLADM_STATUS_OK) 918 die_dlerr(status, "remove flow failed"); 919 } 920 921 /* 922 * Walker function for removing a flow through dladm_walk_flow(); 923 */ 924 static int 925 remove_flow(dladm_flow_attr_t *attr, void *arg) 926 { 927 remove_flow_state_t *state = (remove_flow_state_t *)arg; 928 929 state->fs_status = dladm_flow_remove(handle, attr->fa_flowname, 930 state->fs_tempop, state->fs_altroot); 931 932 if (state->fs_status == DLADM_STATUS_OK) 933 return (DLADM_WALK_CONTINUE); 934 else 935 return (DLADM_WALK_TERMINATE); 936 } 937 938 static char * 939 flowadm_print_field(print_field_t *pf, void *arg) 940 { 941 char *value; 942 943 value = (char *)arg + pf->pf_offset; 944 return (value); 945 } 946 947 /*ARGSUSED*/ 948 static dladm_status_t 949 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr, 950 flow_fields_buf_t *fbuf) 951 { 952 char link[MAXLINKNAMELEN]; 953 dladm_status_t status; 954 955 if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL, 956 NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) { 957 return (status); 958 } 959 960 (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name), 961 "%s", attr->fa_flowname); 962 (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link), 963 "%s", link); 964 965 (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr, 966 sizeof (fbuf->flow_ipaddr)); 967 (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto, 968 sizeof (fbuf->flow_proto)); 969 (void) dladm_flow_attr_port2str(attr, fbuf->flow_port, 970 sizeof (fbuf->flow_port)); 971 (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield, 972 sizeof (fbuf->flow_dsfield)); 973 974 return (DLADM_STATUS_OK); 975 } 976 977 /* 978 * Walker function for showing flow attributes through dladm_walk_flow(). 979 */ 980 static int 981 show_flow(dladm_flow_attr_t *attr, void *arg) 982 { 983 show_flow_state_t *statep = arg; 984 dladm_status_t status; 985 flow_fields_buf_t fbuf; 986 987 /* 988 * first get all the flow attributes into fbuf; 989 */ 990 bzero(&fbuf, sizeof (fbuf)); 991 status = print_flow(statep, attr, &fbuf); 992 993 if (status != DLADM_STATUS_OK) 994 goto done; 995 996 if (!statep->fs_parseable && !statep->fs_printheader) { 997 print_header(&statep->fs_print); 998 statep->fs_printheader = B_TRUE; 999 } 1000 1001 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1002 flowadm_print_field, (void *)&fbuf); 1003 1004 done: 1005 statep->fs_status = status; 1006 return (DLADM_WALK_CONTINUE); 1007 } 1008 1009 static void 1010 show_one_flow(void *arg, const char *name) 1011 { 1012 dladm_flow_attr_t attr; 1013 1014 if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK) 1015 die("invalid flow: '%s'", name); 1016 else 1017 (void) show_flow(&attr, arg); 1018 } 1019 1020 /* 1021 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to 1022 * dladm_walk_datalink_id(). Used for showing flow attributes for 1023 * all flows on all links. 1024 */ 1025 static int 1026 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1027 { 1028 show_flow_state_t *state = arg; 1029 1030 (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist); 1031 1032 return (DLADM_WALK_CONTINUE); 1033 } 1034 1035 static void 1036 get_flow_stats(const char *flowname, pktsum_t *stats) 1037 { 1038 kstat_ctl_t *kcp; 1039 kstat_t *ksp; 1040 1041 bzero(stats, sizeof (*stats)); 1042 1043 if ((kcp = kstat_open()) == NULL) { 1044 warn("kstat open operation failed"); 1045 return; 1046 } 1047 1048 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); 1049 1050 if (ksp != NULL) 1051 dladm_get_stats(kcp, ksp, stats); 1052 1053 (void) kstat_close(kcp); 1054 } 1055 1056 1057 static char * 1058 print_flow_stats(print_field_t *pf, void *arg) 1059 { 1060 flow_args_t *fargs = arg; 1061 pktsum_t *diff_stats = fargs->flow_s_psum; 1062 static char buf[DLADM_STRSIZE]; 1063 1064 switch (pf->pf_index) { 1065 case FLOW_S_FLOW: 1066 (void) snprintf(buf, sizeof (buf), "%s", fargs->flow_s_flow); 1067 break; 1068 case FLOW_S_IPKTS: 1069 (void) snprintf(buf, sizeof (buf), "%llu", 1070 diff_stats->ipackets); 1071 break; 1072 case FLOW_S_RBYTES: 1073 (void) snprintf(buf, sizeof (buf), "%llu", 1074 diff_stats->rbytes); 1075 break; 1076 case FLOW_S_IERRORS: 1077 (void) snprintf(buf, sizeof (buf), "%u", 1078 diff_stats->ierrors); 1079 break; 1080 case FLOW_S_OPKTS: 1081 (void) snprintf(buf, sizeof (buf), "%llu", 1082 diff_stats->opackets); 1083 break; 1084 case FLOW_S_OBYTES: 1085 (void) snprintf(buf, sizeof (buf), "%llu", 1086 diff_stats->obytes); 1087 break; 1088 case FLOW_S_OERRORS: 1089 (void) snprintf(buf, sizeof (buf), "%u", 1090 diff_stats->oerrors); 1091 break; 1092 default: 1093 die("invalid input"); 1094 break; 1095 } 1096 return (buf); 1097 } 1098 /* ARGSUSED */ 1099 static int 1100 show_flow_stats(dladm_flow_attr_t *attr, void *arg) 1101 { 1102 show_flow_state_t *state = (show_flow_state_t *)arg; 1103 char *name = attr->fa_flowname; 1104 pktsum_t stats, diff_stats; 1105 flow_args_t fargs; 1106 1107 if (state->fs_firstonly) { 1108 if (state->fs_donefirst) 1109 return (DLADM_WALK_TERMINATE); 1110 state->fs_donefirst = B_TRUE; 1111 } else { 1112 bzero(&state->fs_prevstats, sizeof (state->fs_prevstats)); 1113 } 1114 1115 get_flow_stats(name, &stats); 1116 dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats); 1117 1118 fargs.flow_s_flow = name; 1119 fargs.flow_s_psum = &diff_stats; 1120 flowadm_print_output(&state->fs_print, state->fs_parseable, 1121 print_flow_stats, &fargs); 1122 1123 state->fs_prevstats = stats; 1124 1125 return (DLADM_WALK_CONTINUE); 1126 } 1127 1128 /* 1129 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for 1130 * dladm_walk_datalink_id(). Used for showing flow stats for 1131 * all flows on all links. 1132 */ 1133 static int 1134 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 1135 { 1136 if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE) 1137 == DLADM_STATUS_OK) 1138 return (DLADM_WALK_CONTINUE); 1139 else 1140 return (DLADM_WALK_TERMINATE); 1141 } 1142 1143 /* ARGSUSED */ 1144 static void 1145 flow_stats(const char *flow, datalink_id_t linkid, uint_t interval, 1146 char *fields_str, show_flow_state_t *state) 1147 { 1148 dladm_flow_attr_t attr; 1149 print_field_t **fields; 1150 uint_t nfields; 1151 1152 fields = parse_output_fields(fields_str, flow_s_fields, 1153 FLOW_S_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1154 if (fields == NULL) { 1155 die("invalid field(s) specified"); 1156 return; 1157 } 1158 1159 state->fs_print.ps_fields = fields; 1160 state->fs_print.ps_nfields = nfields; 1161 1162 if (flow != NULL && 1163 dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK) 1164 die("invalid flow %s", flow); 1165 1166 /* 1167 * If an interval is specified, continuously show the stats 1168 * for only the first flow. 1169 */ 1170 state->fs_firstonly = (interval != 0); 1171 1172 if (!state->fs_parseable) 1173 print_header(&state->fs_print); 1174 for (;;) { 1175 state->fs_donefirst = B_FALSE; 1176 1177 /* Show stats for named flow */ 1178 if (flow != NULL) { 1179 state->fs_flow = flow; 1180 (void) show_flow_stats(&attr, state); 1181 1182 /* Show all stats on a link */ 1183 } else if (linkid != DATALINK_INVALID_LINKID) { 1184 (void) dladm_walk_flow(show_flow_stats, handle, linkid, 1185 state, B_FALSE); 1186 1187 /* Show all stats by datalink */ 1188 } else { 1189 (void) dladm_walk_datalink_id(show_link_flow_stats, 1190 handle, state, DATALINK_CLASS_ALL, 1191 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 1192 } 1193 1194 if (interval == 0) 1195 break; 1196 1197 (void) sleep(interval); 1198 } 1199 } 1200 1201 static void 1202 do_show_flow(int argc, char *argv[]) 1203 { 1204 char flowname[MAXFLOWNAMELEN]; 1205 char linkname[MAXLINKNAMELEN]; 1206 datalink_id_t linkid = DATALINK_ALL_LINKID; 1207 int option; 1208 boolean_t s_arg = B_FALSE; 1209 boolean_t S_arg = B_FALSE; 1210 boolean_t i_arg = B_FALSE; 1211 boolean_t l_arg = B_FALSE; 1212 boolean_t o_arg = B_FALSE; 1213 uint32_t interval = 0; 1214 char *endp = NULL; 1215 show_flow_state_t state; 1216 char *fields_str = NULL; 1217 print_field_t **fields; 1218 uint_t nfields; 1219 char *all_fields = 1220 "flow,link,ipaddr,proto,port,dsfld"; 1221 char *allstat_fields = 1222 "flow,ipackets,rbytes,ierrors,opackets,obytes,oerrors"; 1223 1224 bzero(&state, sizeof (state)); 1225 1226 opterr = 0; 1227 while ((option = getopt_long(argc, argv, ":pPsSi:l:o:", 1228 longopts, NULL)) != -1) { 1229 switch (option) { 1230 case 'p': 1231 state.fs_parseable = B_TRUE; 1232 break; 1233 case 'P': 1234 state.fs_persist = B_TRUE; 1235 break; 1236 case 's': 1237 if (s_arg) 1238 die_optdup(option); 1239 1240 s_arg = B_TRUE; 1241 break; 1242 case 'S': 1243 if (S_arg) 1244 die_optdup(option); 1245 1246 S_arg = B_TRUE; 1247 break; 1248 case 'o': 1249 if (o_arg) 1250 die_optdup(option); 1251 1252 o_arg = B_TRUE; 1253 fields_str = optarg; 1254 break; 1255 case 'i': 1256 if (i_arg) 1257 die_optdup(option); 1258 1259 i_arg = B_TRUE; 1260 1261 errno = 0; 1262 interval = (int)strtol(optarg, &endp, 10); 1263 if (errno != 0 || interval == 0 || *endp != '\0') 1264 die("invalid interval value" " '%d'\n", 1265 interval); 1266 break; 1267 case 'l': 1268 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 1269 >= MAXLINKNAMELEN) 1270 die("link name too long\n"); 1271 if (dladm_name2info(handle, linkname, &linkid, NULL, 1272 NULL, NULL) != DLADM_STATUS_OK) 1273 die("invalid link '%s'", linkname); 1274 l_arg = B_TRUE; 1275 break; 1276 default: 1277 die_opterr(optopt, option); 1278 break; 1279 } 1280 } 1281 1282 if (i_arg && !(s_arg || S_arg)) 1283 die("the -i option can be used only with -s or -S"); 1284 1285 if (s_arg && S_arg) 1286 die("the -s option cannot be used with -S"); 1287 1288 /* get flow name (optional last argument */ 1289 if (optind == (argc-1)) { 1290 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 1291 >= MAXFLOWNAMELEN) 1292 die("flow name too long"); 1293 state.fs_flow = flowname; 1294 } 1295 1296 if (S_arg) { 1297 dladm_continuous(handle, linkid, state.fs_flow, interval, 1298 FLOW_REPORT); 1299 return; 1300 } 1301 1302 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { 1303 if (s_arg) 1304 fields_str = allstat_fields; 1305 else 1306 fields_str = all_fields; 1307 } 1308 1309 if (s_arg) { 1310 flow_stats(state.fs_flow, linkid, interval, fields_str, &state); 1311 return; 1312 } 1313 1314 fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS, 1315 CMD_TYPE_ANY, &nfields); 1316 1317 if (fields == NULL) { 1318 die("invalid fields(s) specified"); 1319 return; 1320 } 1321 1322 state.fs_print.ps_fields = fields; 1323 state.fs_print.ps_nfields = nfields; 1324 1325 /* Show attributes of one flow */ 1326 if (state.fs_flow != NULL) { 1327 show_one_flow(&state, state.fs_flow); 1328 1329 /* Show attributes of flows on one link */ 1330 } else if (l_arg) { 1331 (void) show_flows_onelink(handle, linkid, &state); 1332 1333 /* Show attributes of all flows on all links */ 1334 } else { 1335 (void) dladm_walk_datalink_id(show_flows_onelink, handle, 1336 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1337 DLADM_OPT_ACTIVE); 1338 } 1339 } 1340 1341 static dladm_status_t 1342 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, 1343 uint_t val_cnt, boolean_t reset) 1344 { 1345 dladm_status_t status; 1346 char *errprop; 1347 1348 status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt, 1349 DLADM_OPT_PERSIST, &errprop); 1350 1351 if (status != DLADM_STATUS_OK) { 1352 warn_dlerr(status, "cannot persistently %s flow " 1353 "property '%s' on '%s'", reset? "reset": "set", 1354 errprop, flow); 1355 } 1356 return (status); 1357 } 1358 1359 static void 1360 set_flowprop(int argc, char **argv, boolean_t reset) 1361 { 1362 int i, option; 1363 char errmsg[DLADM_STRSIZE]; 1364 const char *flow = NULL; 1365 dladm_arg_list_t *proplist = NULL; 1366 boolean_t temp = B_FALSE; 1367 dladm_status_t status = DLADM_STATUS_OK; 1368 1369 opterr = 0; 1370 while ((option = getopt_long(argc, argv, ":p:R:t", 1371 prop_longopts, NULL)) != -1) { 1372 switch (option) { 1373 case 'p': 1374 if (dladm_parse_flow_props(optarg, &proplist, reset) 1375 != DLADM_STATUS_OK) 1376 die("invalid flow property specified"); 1377 break; 1378 case 't': 1379 temp = B_TRUE; 1380 break; 1381 case 'R': 1382 status = dladm_set_rootdir(optarg); 1383 if (status != DLADM_STATUS_OK) { 1384 die_dlerr(status, "invalid directory " 1385 "specified"); 1386 } 1387 break; 1388 default: 1389 die_opterr(optopt, option); 1390 break; 1391 } 1392 } 1393 1394 if (optind == (argc - 1)) { 1395 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1396 die("flow name too long"); 1397 flow = argv[optind]; 1398 } else if (optind != argc) { 1399 usage(); 1400 } 1401 if (flow == NULL) 1402 die("flow name must be specified"); 1403 1404 if (proplist == NULL) { 1405 char *errprop; 1406 1407 if (!reset) 1408 die("flow property must be specified"); 1409 1410 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0, 1411 DLADM_OPT_ACTIVE, &errprop); 1412 if (status != DLADM_STATUS_OK) { 1413 warn_dlerr(status, "cannot reset flow property '%s' " 1414 "on '%s'", errprop, flow); 1415 } 1416 if (!temp) { 1417 dladm_status_t s; 1418 1419 s = set_flowprop_persist(flow, NULL, NULL, 0, reset); 1420 if (s != DLADM_STATUS_OK) 1421 status = s; 1422 } 1423 goto done; 1424 } 1425 1426 for (i = 0; i < proplist->al_count; i++) { 1427 dladm_arg_info_t *aip = &proplist->al_info[i]; 1428 char **val; 1429 uint_t count; 1430 dladm_status_t s; 1431 1432 if (reset) { 1433 val = NULL; 1434 count = 0; 1435 } else { 1436 val = aip->ai_val; 1437 count = aip->ai_count; 1438 if (count == 0) { 1439 warn("no value specified for '%s'", 1440 aip->ai_name); 1441 status = DLADM_STATUS_BADARG; 1442 continue; 1443 } 1444 } 1445 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count, 1446 DLADM_OPT_ACTIVE, NULL); 1447 if (s == DLADM_STATUS_OK) { 1448 if (!temp) { 1449 s = set_flowprop_persist(flow, 1450 aip->ai_name, val, count, reset); 1451 if (s != DLADM_STATUS_OK) 1452 status = s; 1453 } 1454 continue; 1455 } 1456 status = s; 1457 switch (s) { 1458 case DLADM_STATUS_NOTFOUND: 1459 warn("invalid flow property '%s'", aip->ai_name); 1460 break; 1461 case DLADM_STATUS_BADVAL: { 1462 int j; 1463 char *ptr, *lim; 1464 char **propvals = NULL; 1465 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1466 1467 ptr = malloc((sizeof (char *) + 1468 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 1469 MAX_PROP_LINE); 1470 1471 if (ptr == NULL) 1472 die("insufficient memory"); 1473 propvals = (char **)(void *)ptr; 1474 1475 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 1476 propvals[j] = ptr + sizeof (char *) * 1477 DLADM_MAX_PROP_VALCNT + 1478 j * DLADM_PROP_VAL_MAX; 1479 } 1480 s = dladm_get_flowprop(handle, flow, 1481 DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, 1482 &valcnt); 1483 1484 ptr = errmsg; 1485 lim = ptr + DLADM_STRSIZE; 1486 *ptr = '\0'; 1487 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { 1488 ptr += snprintf(ptr, lim - ptr, "%s,", 1489 propvals[j]); 1490 if (ptr >= lim) 1491 break; 1492 } 1493 if (ptr > errmsg) { 1494 *(ptr - 1) = '\0'; 1495 warn("flow property '%s' must be one of: %s", 1496 aip->ai_name, errmsg); 1497 } else 1498 warn("%s is an invalid value for " 1499 "flow property %s", *val, aip->ai_name); 1500 free(propvals); 1501 break; 1502 } 1503 default: 1504 if (reset) { 1505 warn_dlerr(status, "cannot reset flow property " 1506 "'%s' on '%s'", aip->ai_name, flow); 1507 } else { 1508 warn_dlerr(status, "cannot set flow property " 1509 "'%s' on '%s'", aip->ai_name, flow); 1510 } 1511 break; 1512 } 1513 } 1514 done: 1515 dladm_free_props(proplist); 1516 if (status != DLADM_STATUS_OK) { 1517 dladm_close(handle); 1518 exit(EXIT_FAILURE); 1519 } 1520 } 1521 1522 static void 1523 do_set_flowprop(int argc, char **argv) 1524 { 1525 set_flowprop(argc, argv, B_FALSE); 1526 } 1527 1528 static void 1529 do_reset_flowprop(int argc, char **argv) 1530 { 1531 set_flowprop(argc, argv, B_TRUE); 1532 } 1533 1534 static void 1535 warn(const char *format, ...) 1536 { 1537 va_list alist; 1538 1539 format = gettext(format); 1540 (void) fprintf(stderr, "%s: warning: ", progname); 1541 1542 va_start(alist, format); 1543 (void) vfprintf(stderr, format, alist); 1544 va_end(alist); 1545 1546 (void) putchar('\n'); 1547 } 1548 1549 /* PRINTFLIKE2 */ 1550 static void 1551 warn_dlerr(dladm_status_t err, const char *format, ...) 1552 { 1553 va_list alist; 1554 char errmsg[DLADM_STRSIZE]; 1555 1556 format = gettext(format); 1557 (void) fprintf(stderr, gettext("%s: warning: "), progname); 1558 1559 va_start(alist, format); 1560 (void) vfprintf(stderr, format, alist); 1561 va_end(alist); 1562 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1563 } 1564 1565 /* PRINTFLIKE1 */ 1566 static void 1567 die(const char *format, ...) 1568 { 1569 va_list alist; 1570 1571 format = gettext(format); 1572 (void) fprintf(stderr, "%s: ", progname); 1573 1574 va_start(alist, format); 1575 (void) vfprintf(stderr, format, alist); 1576 va_end(alist); 1577 1578 (void) putchar('\n'); 1579 1580 /* close dladm handle if it was opened */ 1581 if (handle != NULL) 1582 dladm_close(handle); 1583 1584 exit(EXIT_FAILURE); 1585 } 1586 1587 static void 1588 die_optdup(int opt) 1589 { 1590 die("the option -%c cannot be specified more than once", opt); 1591 } 1592 1593 static void 1594 die_opterr(int opt, int opterr) 1595 { 1596 switch (opterr) { 1597 case ':': 1598 die("option '-%c' requires a value", opt); 1599 break; 1600 case '?': 1601 default: 1602 die("unrecognized option '-%c'", opt); 1603 break; 1604 } 1605 } 1606 1607 /* PRINTFLIKE2 */ 1608 static void 1609 die_dlerr(dladm_status_t err, const char *format, ...) 1610 { 1611 va_list alist; 1612 char errmsg[DLADM_STRSIZE]; 1613 1614 format = gettext(format); 1615 (void) fprintf(stderr, "%s: ", progname); 1616 1617 va_start(alist, format); 1618 (void) vfprintf(stderr, format, alist); 1619 va_end(alist); 1620 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1621 1622 /* close dladm handle if it was opened */ 1623 if (handle != NULL) 1624 dladm_close(handle); 1625 1626 exit(EXIT_FAILURE); 1627 } 1628 1629 static void 1630 print_flowprop(const char *flowname, show_flowprop_state_t *statep, 1631 const char *propname, dladm_prop_type_t type, 1632 const char *format, char **pptr) 1633 { 1634 int i; 1635 char *ptr, *lim; 1636 char buf[DLADM_STRSIZE]; 1637 char *unknown = "--", *notsup = ""; 1638 char **propvals = statep->fs_propvals; 1639 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1640 dladm_status_t status; 1641 1642 status = dladm_get_flowprop(handle, flowname, type, propname, propvals, 1643 &valcnt); 1644 if (status != DLADM_STATUS_OK) { 1645 if (status == DLADM_STATUS_TEMPONLY) { 1646 if (type == DLADM_PROP_VAL_MODIFIABLE && 1647 statep->fs_persist) { 1648 valcnt = 1; 1649 propvals = &unknown; 1650 } else { 1651 statep->fs_status = status; 1652 statep->fs_retstatus = status; 1653 return; 1654 } 1655 } else if (status == DLADM_STATUS_NOTSUP || 1656 statep->fs_persist) { 1657 valcnt = 1; 1658 if (type == DLADM_PROP_VAL_CURRENT) 1659 propvals = &unknown; 1660 else 1661 propvals = ¬sup; 1662 } else { 1663 if ((statep->fs_proplist != NULL) && 1664 statep->fs_status == DLADM_STATUS_OK) { 1665 warn("invalid flow property '%s'", propname); 1666 } 1667 statep->fs_status = status; 1668 statep->fs_retstatus = status; 1669 return; 1670 } 1671 } 1672 1673 statep->fs_status = DLADM_STATUS_OK; 1674 1675 ptr = buf; 1676 lim = buf + DLADM_STRSIZE; 1677 for (i = 0; i < valcnt; i++) { 1678 if (propvals[i][0] == '\0' && !statep->fs_parseable) 1679 ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL","); 1680 else 1681 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 1682 if (ptr >= lim) 1683 break; 1684 } 1685 if (valcnt > 0) 1686 buf[strlen(buf) - 1] = '\0'; 1687 1688 lim = statep->fs_line + MAX_PROP_LINE; 1689 if (statep->fs_parseable) { 1690 *pptr += snprintf(*pptr, lim - *pptr, 1691 "%s", buf); 1692 } else { 1693 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 1694 } 1695 } 1696 1697 static char * 1698 flowprop_callback(print_field_t *pf, void *fs_arg) 1699 { 1700 flowprop_args_t *arg = fs_arg; 1701 char *propname = arg->fs_propname; 1702 show_flowprop_state_t *statep = arg->fs_state; 1703 char *ptr = statep->fs_line; 1704 char *lim = ptr + MAX_PROP_LINE; 1705 char *flowname = arg->fs_flowname; 1706 1707 switch (pf->pf_index) { 1708 case FLOWPROP_FLOW: 1709 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); 1710 break; 1711 case FLOWPROP_PROPERTY: 1712 (void) snprintf(ptr, lim - ptr, "%s", propname); 1713 break; 1714 case FLOWPROP_VALUE: 1715 print_flowprop(flowname, statep, propname, 1716 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : 1717 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 1718 /* 1719 * If we failed to query the flow property, for example, query 1720 * the persistent value of a non-persistable flow property, 1721 * simply skip the output. 1722 */ 1723 if (statep->fs_status != DLADM_STATUS_OK) 1724 goto skip; 1725 ptr = statep->fs_line; 1726 break; 1727 case FLOWPROP_DEFAULT: 1728 print_flowprop(flowname, statep, propname, 1729 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 1730 if (statep->fs_status != DLADM_STATUS_OK) 1731 goto skip; 1732 ptr = statep->fs_line; 1733 break; 1734 case FLOWPROP_POSSIBLE: 1735 print_flowprop(flowname, statep, propname, 1736 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 1737 if (statep->fs_status != DLADM_STATUS_OK) 1738 goto skip; 1739 ptr = statep->fs_line; 1740 break; 1741 default: 1742 die("invalid input"); 1743 break; 1744 } 1745 return (ptr); 1746 skip: 1747 if (statep->fs_status != DLADM_STATUS_OK) 1748 return (NULL); 1749 else 1750 return (""); 1751 } 1752 1753 static int 1754 show_one_flowprop(void *arg, const char *propname) 1755 { 1756 show_flowprop_state_t *statep = arg; 1757 flowprop_args_t fs_arg; 1758 1759 bzero(&fs_arg, sizeof (fs_arg)); 1760 fs_arg.fs_state = statep; 1761 fs_arg.fs_propname = (char *)propname; 1762 fs_arg.fs_flowname = (char *)statep->fs_flow; 1763 1764 if (statep->fs_header) { 1765 statep->fs_header = B_FALSE; 1766 if (!statep ->fs_parseable) 1767 print_header(&statep->fs_print); 1768 } 1769 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1770 flowprop_callback, (void *)&fs_arg); 1771 1772 return (DLADM_WALK_CONTINUE); 1773 } 1774 1775 /* Walker function called by dladm_walk_flow to display flow properties */ 1776 static int 1777 show_flowprop(dladm_flow_attr_t *attr, void *arg) 1778 { 1779 show_flowprop_one_flow(arg, attr->fa_flowname); 1780 return (DLADM_WALK_CONTINUE); 1781 } 1782 1783 /* 1784 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it 1785 * usable to dladm_walk_datalink_id() 1786 */ 1787 static int 1788 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1789 { 1790 char name[MAXLINKNAMELEN]; 1791 1792 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name, 1793 sizeof (name)) != DLADM_STATUS_OK) 1794 return (DLADM_WALK_TERMINATE); 1795 1796 (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE); 1797 1798 return (DLADM_WALK_CONTINUE); 1799 } 1800 1801 static void 1802 do_show_flowprop(int argc, char **argv) 1803 { 1804 int option; 1805 boolean_t o_arg = B_FALSE; 1806 dladm_arg_list_t *proplist = NULL; 1807 show_flowprop_state_t state; 1808 char *fields_str = NULL; 1809 print_field_t **fields; 1810 uint_t nfields; 1811 char *all_fields = 1812 "flow,property,value,default,possible"; 1813 1814 fields_str = all_fields; 1815 opterr = 0; 1816 state.fs_propvals = NULL; 1817 state.fs_line = NULL; 1818 state.fs_parseable = B_FALSE; 1819 state.fs_persist = B_FALSE; 1820 state.fs_header = B_TRUE; 1821 state.fs_retstatus = DLADM_STATUS_OK; 1822 state.fs_linkid = DATALINK_INVALID_LINKID; 1823 state.fs_flow = NULL; 1824 1825 while ((option = getopt_long(argc, argv, ":p:cPl:o:", 1826 prop_longopts, NULL)) != -1) { 1827 switch (option) { 1828 case 'p': 1829 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) 1830 != DLADM_STATUS_OK) 1831 die("invalid flow properties specified"); 1832 break; 1833 case 'c': 1834 state.fs_parseable = B_TRUE; 1835 break; 1836 case 'P': 1837 state.fs_persist = B_TRUE; 1838 break; 1839 case 'l': 1840 if (dladm_name2info(handle, optarg, &state.fs_linkid, 1841 NULL, NULL, NULL) != DLADM_STATUS_OK) 1842 die("invalid link '%s'", optarg); 1843 break; 1844 case 'o': 1845 o_arg = B_TRUE; 1846 if (strcasecmp(optarg, "all") == 0) 1847 fields_str = all_fields; 1848 else 1849 fields_str = optarg; 1850 break; 1851 default: 1852 die_opterr(optopt, option); 1853 break; 1854 } 1855 } 1856 1857 if (state.fs_parseable && !o_arg) 1858 die("-p requires -o"); 1859 1860 if (state.fs_parseable && strcasecmp(fields_str, "all") == 0) 1861 die("\"-o all\" is invalid with -p"); 1862 1863 if (optind == (argc - 1)) { 1864 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1865 die("flow name too long"); 1866 state.fs_flow = argv[optind]; 1867 } else if (optind != argc) { 1868 usage(); 1869 } 1870 bzero(&state.fs_print, sizeof (print_state_t)); 1871 state.fs_proplist = proplist; 1872 state.fs_status = DLADM_STATUS_OK; 1873 1874 fields = parse_output_fields(fields_str, flowprop_fields, 1875 FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1876 1877 if (fields == NULL) { 1878 die("invalid field(s) specified"); 1879 return; 1880 } 1881 1882 state.fs_print.ps_fields = fields; 1883 state.fs_print.ps_nfields = nfields; 1884 1885 /* Show properties for one flow */ 1886 if (state.fs_flow != NULL) { 1887 show_flowprop_one_flow(&state, state.fs_flow); 1888 1889 /* Show properties for all flows on one link */ 1890 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { 1891 (void) show_flowprop_onelink(handle, state.fs_linkid, &state); 1892 1893 /* Show properties for all flows on all links */ 1894 } else { 1895 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle, 1896 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1897 DLADM_OPT_ACTIVE); 1898 } 1899 1900 dladm_free_props(proplist); 1901 } 1902 1903 static void 1904 show_flowprop_one_flow(void *arg, const char *flow) 1905 { 1906 int i; 1907 char *buf; 1908 dladm_status_t status; 1909 dladm_arg_list_t *proplist = NULL; 1910 show_flowprop_state_t *statep = arg; 1911 dladm_flow_attr_t attr; 1912 const char *savep; 1913 1914 /* 1915 * Do not print flow props for invalid flows. 1916 */ 1917 if ((status = dladm_flow_info(handle, flow, &attr)) != 1918 DLADM_STATUS_OK) { 1919 die("invalid flow: '%s'", flow); 1920 } 1921 1922 savep = statep->fs_flow; 1923 statep->fs_flow = flow; 1924 1925 proplist = statep->fs_proplist; 1926 1927 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) 1928 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 1929 if (buf == NULL) 1930 die("insufficient memory"); 1931 1932 statep->fs_propvals = (char **)(void *)buf; 1933 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 1934 statep->fs_propvals[i] = buf + 1935 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 1936 i * DLADM_PROP_VAL_MAX; 1937 } 1938 statep->fs_line = buf + 1939 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 1940 1941 /* show only specified flow properties */ 1942 if (proplist != NULL) { 1943 for (i = 0; i < proplist->al_count; i++) { 1944 if (show_one_flowprop(statep, 1945 proplist->al_info[i].ai_name) != DLADM_STATUS_OK) 1946 break; 1947 } 1948 1949 /* show all flow properties */ 1950 } else { 1951 status = dladm_walk_flowprop(show_one_flowprop, flow, statep); 1952 if (status != DLADM_STATUS_OK) 1953 die_dlerr(status, "show-flowprop"); 1954 } 1955 free(buf); 1956 statep->fs_flow = savep; 1957 } 1958 1959 typedef struct { 1960 char *s_buf; 1961 char **s_fields; /* array of pointer to the fields in s_buf */ 1962 uint_t s_nfields; /* the number of fields in s_buf */ 1963 } split_t; 1964 1965 /* 1966 * Free the split_t structure pointed to by `sp'. 1967 */ 1968 static void 1969 splitfree(split_t *sp) 1970 { 1971 free(sp->s_buf); 1972 free(sp->s_fields); 1973 free(sp); 1974 } 1975 1976 /* 1977 * Split `str' into at most `maxfields' fields, each field at most `maxlen' in 1978 * length. Return a pointer to a split_t containing the split fields, or NULL 1979 * on failure. 1980 */ 1981 static split_t * 1982 split(const char *str, uint_t maxfields, uint_t maxlen) 1983 { 1984 char *field, *token, *lasts = NULL; 1985 split_t *sp; 1986 1987 if (*str == '\0' || maxfields == 0 || maxlen == 0) 1988 return (NULL); 1989 1990 sp = calloc(sizeof (split_t), 1); 1991 if (sp == NULL) 1992 return (NULL); 1993 1994 sp->s_buf = strdup(str); 1995 sp->s_fields = malloc(sizeof (char *) * maxfields); 1996 if (sp->s_buf == NULL || sp->s_fields == NULL) 1997 goto fail; 1998 1999 token = sp->s_buf; 2000 while ((field = strtok_r(token, ",", &lasts)) != NULL) { 2001 if (sp->s_nfields == maxfields || strlen(field) > maxlen) 2002 goto fail; 2003 token = NULL; 2004 sp->s_fields[sp->s_nfields++] = field; 2005 } 2006 return (sp); 2007 fail: 2008 splitfree(sp); 2009 return (NULL); 2010 } 2011 2012 static print_field_t ** 2013 parse_output_fields(char *str, print_field_t *template, int max_fields, 2014 uint_t cmdtype, uint_t *countp) 2015 { 2016 split_t *sp; 2017 boolean_t good_match = B_FALSE; 2018 uint_t i, j; 2019 print_field_t **pf = NULL; 2020 2021 sp = split(str, max_fields, MAX_FIELD_LEN); 2022 2023 if (sp == NULL) 2024 return (NULL); 2025 2026 pf = malloc(sp->s_nfields * sizeof (print_field_t *)); 2027 if (pf == NULL) 2028 goto fail; 2029 2030 for (i = 0; i < sp->s_nfields; i++) { 2031 for (j = 0; j < max_fields; j++) { 2032 if (strcasecmp(sp->s_fields[i], 2033 template[j].pf_name) == 0) { 2034 good_match = template[j]. pf_cmdtype & cmdtype; 2035 break; 2036 } 2037 } 2038 if (!good_match) 2039 goto fail; 2040 2041 good_match = B_FALSE; 2042 pf[i] = &template[j]; 2043 } 2044 *countp = i; 2045 splitfree(sp); 2046 return (pf); 2047 fail: 2048 free(pf); 2049 splitfree(sp); 2050 return (NULL); 2051 } 2052 2053 static void 2054 flowadm_print_output(print_state_t *statep, boolean_t parseable, 2055 print_callback_t fn, void *arg) 2056 { 2057 int i; 2058 char *value; 2059 print_field_t **pf; 2060 2061 pf = statep->ps_fields; 2062 for (i = 0; i < statep->ps_nfields; i++) { 2063 statep->ps_lastfield = (i + 1 == statep->ps_nfields); 2064 value = (*fn)(pf[i], arg); 2065 if (value != NULL) 2066 print_field(statep, pf[i], value, parseable); 2067 } 2068 (void) putchar('\n'); 2069 } 2070 2071 static void 2072 print_header(print_state_t *ps) 2073 { 2074 int i; 2075 print_field_t **pf; 2076 2077 pf = ps->ps_fields; 2078 for (i = 0; i < ps->ps_nfields; i++) { 2079 ps->ps_lastfield = (i + 1 == ps->ps_nfields); 2080 print_field(ps, pf[i], pf[i]->pf_header, B_FALSE); 2081 } 2082 (void) putchar('\n'); 2083 } 2084 2085 static void 2086 print_field(print_state_t *statep, print_field_t *pfp, const char *value, 2087 boolean_t parseable) 2088 { 2089 uint_t width = pfp->pf_width; 2090 uint_t valwidth; 2091 uint_t compress; 2092 2093 /* 2094 * Parsable fields are separated by ':'. If such a field contains 2095 * a ':' or '\', this character is prefixed by a '\'. 2096 */ 2097 if (parseable) { 2098 char c; 2099 2100 if (statep->ps_nfields == 1) { 2101 (void) printf("%s", value); 2102 return; 2103 } 2104 while ((c = *value++) != '\0') { 2105 if (c == ':' || c == '\\') 2106 (void) putchar('\\'); 2107 (void) putchar(c); 2108 } 2109 if (!statep->ps_lastfield) 2110 (void) putchar(':'); 2111 return; 2112 } else { 2113 if (value[0] == '\0') 2114 value = STR_UNDEF_VAL; 2115 if (statep->ps_lastfield) { 2116 (void) printf("%s", value); 2117 statep->ps_overflow = 0; 2118 return; 2119 } 2120 2121 valwidth = strlen(value); 2122 if (valwidth > width) { 2123 statep->ps_overflow += valwidth - width; 2124 } else if (valwidth < width && statep->ps_overflow > 0) { 2125 compress = min(statep->ps_overflow, width - valwidth); 2126 statep->ps_overflow -= compress; 2127 width -= compress; 2128 } 2129 (void) printf("%-*s", width, value); 2130 } 2131 2132 if (!statep->ps_lastfield) 2133 (void) putchar(' '); 2134 } 2135