[BACK]Return to if.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / systat

File: [local] / src / usr.bin / systat / if.c (download)

Revision 1.12, Thu Jun 12 22:26:01 2008 UTC (15 years, 11 months ago) by canacar
Branch: MAIN
CVS Tags: OPENBSD_4_5_BASE, OPENBSD_4_5, OPENBSD_4_4_BASE, OPENBSD_4_4
Changes since 1.11: +164 -103 lines

New display engine for systat, based on pftop. Adds new views for pf
(status, state, rule, queue). While all displays work, some keyboard
comands are not implemented yet. Other features include better handling
of display resize and scrolling for long views. Committing now to fix
the remaining issues in the tree.
Testing and comments by otto@ and harding@, ok deraadt@

/*	$OpenBSD: if.c,v 1.12 2008/06/12 22:26:01 canacar Exp $ */
/*
 * Copyright (c) 2004 Markus Friedl <markus@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>

#include <stdlib.h>
#include <string.h>

#include "systat.h"

static  enum state { BOOT, TIME, RUN } state = TIME;

struct ifcount {
	u_int64_t	ifc_ib;			/* input bytes */
	u_int64_t	ifc_ip;			/* input packets */
	u_int64_t	ifc_ie;			/* input errors */
	u_int64_t	ifc_ob;			/* output bytes */
	u_int64_t	ifc_op;			/* output packets */
	u_int64_t	ifc_oe;			/* output errors */
	u_int64_t	ifc_co;			/* collisions */
	int		ifc_flags;		/* up / down */
	int		ifc_state;		/* link state */
} sum;

struct ifstat {
	char		ifs_name[IFNAMSIZ];	/* interface name */
	struct ifcount	ifs_cur;
	struct ifcount	ifs_old;
	struct ifcount	ifs_now;
} *ifstats;

static	int nifs = 0;
static int num_ifs = 0;

void print_if(void);
int read_if(void);
int select_if(void);
int if_keyboard_callback(int);

static void fetchifstat(void);
static void showifstat(struct ifstat *);
static void showtotal(void);


/* Define fields */
field_def fields_if[] = {
	{"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
	{"STATE", 10, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
	{"IPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"IBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"IERRS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"OPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"OBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"OERRS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"COLLS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
};


#define FIELD_ADDR(x) (&fields_if[x])

#define FLD_IF_IFACE	FIELD_ADDR(0)
#define FLD_IF_STATE	FIELD_ADDR(1)
#define FLD_IF_IPKTS	FIELD_ADDR(2)
#define FLD_IF_IBYTES	FIELD_ADDR(3)
#define FLD_IF_IERRS	FIELD_ADDR(4)
#define FLD_IF_OPKTS	FIELD_ADDR(5)
#define FLD_IF_OBYTES	FIELD_ADDR(6)
#define FLD_IF_OERRS	FIELD_ADDR(7)
#define FLD_IF_COLLS	FIELD_ADDR(8)


/* Define views */
field_def *view_if_0[] = {
	FLD_IF_IFACE, FLD_IF_STATE, FLD_IF_IPKTS, FLD_IF_IBYTES,
	FLD_IF_IERRS, FLD_IF_OPKTS, FLD_IF_OBYTES, FLD_IF_OERRS,
	FLD_IF_COLLS, NULL
};

/* Define view managers */

struct view_manager ifstat_mgr = {
	"Ifstat", select_if, read_if, NULL, print_header,
	print_if, if_keyboard_callback, NULL, NULL
};

field_view views_if[] = {
	{view_if_0, "ifstat", '1', &ifstat_mgr},
	{NULL, NULL, 0, NULL}
};


int
initifstat(void)
{
	field_view *v;
	read_if();
	for (v = views_if; v->name != NULL; v++)
		add_view(v);

	return(1);
}

#define UPDATE(x, y) do { \
		ifs->ifs_now.x = ifm.y; \
		ifs->ifs_cur.x = ifs->ifs_now.x - ifs->ifs_old.x; \
		if (state == TIME) {\
			ifs->ifs_old.x = ifs->ifs_now.x; \
			ifs->ifs_cur.x /= naptime; \
		} \
		sum.x += ifs->ifs_cur.x; \
	} while(0)


void
rt_getaddrinfo(struct sockaddr *sa, int addrs, struct sockaddr **info)
{
	int i;

	for (i = 0; i < RTAX_MAX; i++) {
		if (addrs & (1 << i)) {
			info[i] = sa;
			sa = (struct sockaddr *) ((char *)(sa) +
			    roundup(sa->sa_len, sizeof(long)));
		} else
			info[i] = NULL;
	}
}



int
select_if(void)
{
	num_disp = num_ifs + 1;
	return (0);
}

int
read_if(void)
{
	fetchifstat();
	num_disp = num_ifs + 1;

	return 0;
}

void
print_if(void)
{
	int n, i, count = 0;

	for (n = 0, i = 0; n < nifs; n++) {
		if (ifstats[n].ifs_name[0] == '\0')
			continue;
		if (i++ < dispstart)
			continue;
		if (i == num_disp)
			break;
		showifstat(ifstats + n);
		if (maxprint > 0 && ++count >= maxprint)
			return;
	}
	showtotal();
}


static void
fetchifstat(void)
{
	struct ifstat *newstats, *ifs;
	struct if_msghdr ifm;
	struct sockaddr *info[RTAX_MAX];
	struct sockaddr_dl *sdl;
	char *buf, *next, *lim;
	int mib[6];
	size_t need;

	mib[0] = CTL_NET;
	mib[1] = AF_ROUTE;
	mib[2] = 0;
	mib[3] = 0;
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;

	if (sysctl(mib, 6, NULL, &need, NULL, 0) == -1)
		return;
	if ((buf = malloc(need)) == NULL)
		return;
	if (sysctl(mib, 6, buf, &need, NULL, 0) == -1) {
		free(buf);
		return;
	}

	bzero(&sum, sizeof(sum));
	num_ifs = 0;

	lim = buf + need;
	for (next = buf; next < lim; next += ifm.ifm_msglen) {
		bcopy(next, &ifm, sizeof ifm);
		if (ifm.ifm_type != RTM_IFINFO ||
		   !(ifm.ifm_addrs & RTA_IFP))
			continue;
		if (ifm.ifm_index >= nifs) {
			if ((newstats = realloc(ifstats, (ifm.ifm_index + 4)
			    * sizeof(struct ifstat))) == NULL)
				continue;
			ifstats = newstats;
			for (; nifs < ifm.ifm_index + 4; nifs++)
				bzero(&ifstats[nifs], sizeof(*ifstats));
		}
		ifs = &ifstats[ifm.ifm_index];
		if (ifs->ifs_name[0] == '\0') {
			bzero(&info, sizeof(info));
			rt_getaddrinfo(
			    (struct sockaddr *)((struct if_msghdr *)next + 1),
			    ifm.ifm_addrs, info);
			if ((sdl = (struct sockaddr_dl *)info[RTAX_IFP])) {
				if (sdl->sdl_family == AF_LINK &&
				    sdl->sdl_nlen > 0) {
					bcopy(sdl->sdl_data, ifs->ifs_name,
					    sdl->sdl_nlen);
					ifs->ifs_name[sdl->sdl_nlen] = '\0';
				}
			}
			if (ifs->ifs_name[0] == '\0')
				continue;
		}
		num_ifs++;
		UPDATE(ifc_ip, ifm_data.ifi_ipackets);
		UPDATE(ifc_ib, ifm_data.ifi_ibytes);
		UPDATE(ifc_ie, ifm_data.ifi_ierrors);
		UPDATE(ifc_op, ifm_data.ifi_opackets);
		UPDATE(ifc_ob, ifm_data.ifi_obytes);
		UPDATE(ifc_oe, ifm_data.ifi_oerrors);
		UPDATE(ifc_co, ifm_data.ifi_collisions);
		ifs->ifs_cur.ifc_flags = ifm.ifm_flags;
		ifs->ifs_cur.ifc_state = ifm.ifm_data.ifi_link_state;
	}
	free(buf);
}


static void
showifstat(struct ifstat *ifs)
{
	print_fld_str(FLD_IF_IFACE, ifs->ifs_name);

	tb_start();
	tbprintf("%s", ifs->ifs_cur.ifc_flags & IFF_UP ?
		 "up" : "dn");

	switch (ifs->ifs_cur.ifc_state) {
	case LINK_STATE_UP:
	case LINK_STATE_HALF_DUPLEX:
	case LINK_STATE_FULL_DUPLEX:
		tbprintf(":U");
		break;
	case LINK_STATE_DOWN:
		tbprintf (":D");
		break;
	}

	print_fld_tb(FLD_IF_STATE);

	print_fld_size(FLD_IF_IBYTES, ifs->ifs_cur.ifc_ib);
	print_fld_size(FLD_IF_IPKTS, ifs->ifs_cur.ifc_ip);
	print_fld_size(FLD_IF_IERRS, ifs->ifs_cur.ifc_ie);

	print_fld_size(FLD_IF_OBYTES, ifs->ifs_cur.ifc_ob);
	print_fld_size(FLD_IF_OPKTS, ifs->ifs_cur.ifc_op);
	print_fld_size(FLD_IF_OERRS, ifs->ifs_cur.ifc_oe);

	print_fld_size(FLD_IF_COLLS, ifs->ifs_cur.ifc_co);

	end_line();
}

static void
showtotal(void)
{
	print_fld_str(FLD_IF_IFACE, "Totals");

	print_fld_size(FLD_IF_IBYTES, sum.ifc_ib);
	print_fld_size(FLD_IF_IPKTS, sum.ifc_ip);
	print_fld_size(FLD_IF_IERRS, sum.ifc_ie);

	print_fld_size(FLD_IF_OBYTES, sum.ifc_ob);
	print_fld_size(FLD_IF_OPKTS, sum.ifc_op);
	print_fld_size(FLD_IF_OERRS, sum.ifc_oe);

	print_fld_size(FLD_IF_COLLS, sum.ifc_co);

	end_line();

}

int
if_keyboard_callback(int ch)
{
	struct ifstat *ifs;

	switch (ch) {
	case 'r':
		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
			ifs->ifs_old = ifs->ifs_now;
		state = RUN;
		gotsig_alarm = 1;

		break;
	case 'b':
		state = BOOT;
		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
			bzero(&ifs->ifs_old, sizeof(ifs->ifs_old));
		gotsig_alarm = 1;
		break;
	case 't':
		state = TIME;
		gotsig_alarm = 1;
		break;
	default:
		return keyboard_callback(ch);
	};

	return 1;
}