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