/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <locale.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stropts.h>
#include <errno.h>
#include <strings.h>
#include <getopt.h>
#include <unistd.h>
#include <priv.h>
#include <netdb.h>
#include <libintl.h>
#include <libdlflow.h>
#include <libdllink.h>
#include <libdlstat.h>
#include <sys/ethernet.h>
#include <stddef.h>
#include <ofmt.h>
typedef struct flow_chain_s {
} flow_chain_t;
typedef struct show_flow_state {
char fs_unit;
typedef struct show_history_state_s {
static void do_show_history(int, char **);
static void die(const char *, ...);
static void die_optdup(int);
static void die_opterr(int, int, const char *);
static void die_dlerr(dladm_status_t, const char *, ...);
static void warn(const char *, ...);
/* callback functions for printing output */
/*
* structures for flowstat (printing live statistics)
*/
typedef enum {
/* name, field width, index, callback */
;
typedef struct flow_args_s {
char *flow_s_flow;
char flow_s_unit;
} flow_args_t;
/*
* structures for 'flowstat -h'
*/
typedef struct history_fields_buf_s {
/* name, field width, offset */
{ "FLOW", 13,
{ "DURATION", 11,
{ "IPACKETS", 10,
{ "RBYTES", 11,
{ "OPACKETS", 10,
{ "OBYTES", 11,
{ "BANDWIDTH", 15,
;
typedef struct history_l_fields_buf_s {
/* name, field width, offset */
{ "FLOW", 13,
{ "START", 14,
{ "END", 14,
{ "RBYTES", 9,
{ "OBYTES", 9,
{ "BANDWIDTH", 15,
;
static char *progname;
/*
* Handle to libdladm. Opened in main() before the sub-command
* specific function is called.
*/
"[-l link] [flow]\n"
" flowstat [-A] [-i interval] [-p] [ -o field[,...]]\n"
" [-u R|K|M|G|T|P] [-l link] [flow]\n"
" flowstat -h [-a] [-d] [-F format]"
"[<flow>]";
static void
usage(void)
{
/* close dladm handle if it was opened */
exit(1);
}
{
return (B_TRUE);
}
return (B_FALSE);
}
void
{
if (parsable) {
return;
}
if (unit == '\0') {
int index;
;
switch (index) {
case 0:
unit = '\0';
break;
case 1:
unit = 'K';
break;
case 2:
unit = 'M';
break;
case 3:
unit = 'G';
break;
case 4:
unit = 'T';
break;
case 5:
/* Largest unit supported */
default:
unit = 'P';
break;
}
} else {
switch (unit) {
case 'R':
/* Already raw numbers */
unit = '\0';
break;
case 'K':
num /= 1000;
break;
case 'M':
break;
case 'G':
break;
case 'T':
break;
case 'P':
/* Largest unit supported */
default:
break;
}
}
if (unit == '\0')
else
}
{
/* Scan prev flowname list and look for entry matching this entry */
break;
}
/* New flow, add it */
goto done;
}
done:
return (flow_curr);
}
/*
* Number of flows may change while flowstat -i is executing.
* Free memory allocated for flows that are no longer there.
* Prepare for next iteration by marking visited = false for
* existing stat entries.
*/
static void
{
/* Delete all nodes from the list that have fc_visited marked false */
if (fcurr->fc_visited) {
continue;
}
/* Is it head of the list? */
else
/* fprev remains the same */
/* Free stats memory for the removed flow */
}
}
static boolean_t
{
case FLOW_S_FLOW:
break;
case FLOW_S_IPKTS:
parsable);
break;
case FLOW_S_RBYTES:
parsable);
break;
case FLOW_S_IERRORS:
parsable);
break;
case FLOW_S_OPKTS:
parsable);
break;
case FLOW_S_OBYTES:
parsable);
break;
case FLOW_S_OERRORS:
parsable);
break;
default:
die("invalid input");
break;
}
return (B_TRUE);
}
/* ARGSUSED */
static int
{
/* Get previous stats for the flow */
goto done;
/* Query library for current stats */
goto done;
/* current stats - prev iteration stats */
/* Free prev stats */
/* Prev <- curr stats */
goto done;
/* Print stats */
/* Free diff stats */
done:
return (DLADM_WALK_CONTINUE);
}
/*
* Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
* dladm_walk_datalink_id(). Used for showing flow stats for
* all flows on all links.
*/
static int
{
== DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
else
return (DLADM_WALK_TERMINATE);
}
void
{
}
}
/* ARGSUSED */
static int
{
void *stat;
goto done;
done:
return (DLADM_WALK_CONTINUE);
}
/*
* Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
* dladm_walk_datalink_id(). Used for showing flow stats for
* all flows on all links.
*/
static int
{
== DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
else
return (DLADM_WALK_TERMINATE);
}
static void
{
/* Show stats for named flow */
if (flow_arg) {
/* Show stats for flows on one link */
} else if (linkid != DATALINK_INVALID_LINKID) {
/* Show stats for all flows on all links */
} else {
}
}
int
{
int option;
char *total_stat_fields =
"flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs";
char *rx_stat_fields =
"flow,ipkts,rbytes,ierrs";
char *tx_stat_fields =
"flow,opkts,obytes,oerrs";
#if !defined(TEXT_DOMAIN)
#endif
(void) textdomain(TEXT_DOMAIN);
/* Open the libdladm handle */
opterr = 0;
switch (option) {
case 'r':
if (r_arg)
break;
case 't':
if (t_arg)
break;
case 'A':
if (A_arg)
break;
case 'p':
if (p_arg)
break;
case 'i':
if (i_arg)
break;
case 'o':
break;
case 'u':
if (u_arg)
die("invalid unit value '%s',"
"unit must be R|K|M|G|T|P", optarg);
break;
case 'l':
>= MAXLINKNAMELEN)
die("link name too long\n");
break;
case 'h':
die("the option -h is not compatible with "
"-r, -t, -p, -o, -u, -i, -A");
}
return (0);
break;
default:
break;
}
}
die("the option -t and -r are not compatible");
die("the option -u and -p are not compatible");
die("-p requires -o");
die("\"-o all\" is invalid with -p");
if (A_arg &&
die("the option -A is not compatible with "
"-r, -t, -p, -o, -u, -i");
/* get flow name (optional last argument) */
>= MAXFLOWNAMELEN)
die("flow name too long");
usage();
}
if (flow_arg &&
if (A_arg) {
return (0);
}
if (state.fs_parsable)
if (r_arg)
else if (t_arg)
else
if (o_arg) {
}
for (;;) {
/* Show stats for named flow */
if (flow_arg) {
/* Show stats for flows on one link */
} else if (linkid != DATALINK_INVALID_LINKID) {
/* Show stats for all flows on all links */
} else {
}
if (interval == 0)
break;
}
return (0);
}
/* ARGSUSED */
static int
{
/*
* Only show historical information for existing flows unless '-a'
* is specified.
*/
return (status);
}
return (DLADM_STATUS_OK);
}
static int
{
double bw;
/*
* Only show historical information for existing flows unless '-a'
* is specified.
*/
return (status);
}
if (!state->us_printheader) {
(void) printf("# Time");
}
(void) printf("\n");
}
} else {
}
(void) printf("\n");
}
}
return (DLADM_STATUS_OK);
}
"%s", buf);
"%s", buf);
return (DLADM_STATUS_OK);
}
static int
{
/*
* Only show historical information for existing flows unless '-a'
* is specified.
*/
return (status);
}
return (DLADM_STATUS_OK);
}
static boolean_t
{
}
/* ARGSUSED */
static void
{
int opt;
char *all_fields =
"flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
char *all_l_fields =
"flow,start,end,rbytes,obytes,bandwidth";
switch (opt) {
case 'd':
break;
case 'a':
break;
case 'f':
break;
case 's':
break;
case 'e':
break;
case 'o':
fields_str = optarg;
break;
case 'F':
break;
default:
}
}
die("-h requires a file");
if (!state.us_showall &&
}
}
if (state.us_parsable)
0, &ofmt);
} else {
0, &ofmt);
}
die("incompatible -d and -F options");
if (d_arg) {
/* Print log dates */
!F_arg) {
/* Print summary */
/* Print log entries for named resource */
} else {
/* Print time and information for each flow */
}
if (status != DLADM_STATUS_OK)
}
static void
{
}
/* PRINTFLIKE1 */
static void
{
/* close dladm handle if it was opened */
}
static void
{
}
static void
{
switch (opterr) {
case ':':
break;
case '?':
default:
break;
}
}
/* PRINTFLIKE2 */
static void
{
/* close dladm handle if it was opened */
}
/*
* default output callback function that, when invoked from dladm_print_output,
* prints string which is offset by of_arg->ofmt_id within buf.
*/
static boolean_t
{
char *value;
return (B_TRUE);
}
static void
{
if (oferr == OFMT_SUCCESS)
return;
/*
* All errors are considered fatal in parsable mode.
* NOMEM errors are always fatal, regardless of mode.
* For other errors, we print diagnostics in human-readable
* mode and processs what we can.
*/
} else {
}
}