ifstat.c revision 126775
1169689Skan/*
2169689Skan * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>.
3169689Skan * All rights reserved.
490075Sobrien *
590075Sobrien * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
790075Sobrien * are met:
890075Sobrien * 1. Redistributions of source code must retain the above copyright
990075Sobrien *    notice, this list of conditions and the following disclaimer.
1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1190075Sobrien *    notice, this list of conditions and the following disclaimer in the
1290075Sobrien *    documentation and/or other materials provided with the distribution.
1390075Sobrien * 3. The name of the author may not be used to endorse or promote products
1490075Sobrien *    derived from this software without specific prior written permission.
1590075Sobrien *
1690075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1790075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1890075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19220755Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20220755Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21220755Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2290075Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTIFSTAT_ERRUPTION)
2390075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2490075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2590075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2690075Sobrien * SUCH DAMAGE.
2790075Sobrien *
28132718Skan * $FreeBSD: head/usr.bin/systat/ifstat.c 126775 2004-03-09 11:57:28Z dwmalone $
2990075Sobrien */
3090075Sobrien
3190075Sobrien#include <sys/types.h>
3290075Sobrien#include <sys/socket.h>
3390075Sobrien#include <sys/sysctl.h>
3490075Sobrien#include <sys/time.h>
3590075Sobrien#include <sys/queue.h>
36169689Skan#include <net/if.h>
3790075Sobrien#include <net/if_mib.h>
3890075Sobrien#include <net/if_types.h>	/* For IFT_ETHER */
3990075Sobrien
40117395Skan#include <stdio.h>
4190075Sobrien#include <stdlib.h>
4290075Sobrien#include <string.h>
4390075Sobrien#include <unistd.h>
4490075Sobrien#include <float.h>
4590075Sobrien#include <err.h>
4690075Sobrien
4790075Sobrien#include "systat.h"
4890075Sobrien#include "extern.h"
4990075Sobrien#include "mode.h"
5090075Sobrien#include "convtbl.h"
5190075Sobrien
5290075Sobrien                                /* Column numbers */
5390075Sobrien
5490075Sobrien#define C1	0		/*  0-19 */
5590075Sobrien#define C2	20		/* 20-39 */
5690075Sobrien#define C3	40		/* 40-59 */
5790075Sobrien#define C4	60		/* 60-80 */
5890075Sobrien#define C5	80		/* Used for label positioning. */
5990075Sobrien
6090075Sobrienstatic const int col0 = 0;
61260918Spfgstatic const int col1 = C1;
62260918Spfgstatic const int col2 = C2;
63260918Spfgstatic const int col3 = C3;
6490075Sobrienstatic const int col4 = C4;
6590075Sobrienstatic const int col5 = C5;
6690075Sobrien
6790075Sobrien
6890075SobrienSLIST_HEAD(, if_stat)		curlist;
6990075SobrienSLIST_HEAD(, if_stat_disp)	displist;
7090075Sobrien
7190075Sobrienstruct if_stat {
7290075Sobrien	SLIST_ENTRY(if_stat)	 link;
7390075Sobrien	char	if_name[IF_NAMESIZE];
7490075Sobrien	struct	ifmibdata if_mib;
7590075Sobrien	struct	timeval tv;
7690075Sobrien	struct	timeval tv_lastchanged;
77169689Skan	u_long	if_in_curtraffic;
78169689Skan	u_long	if_out_curtraffic;
79169689Skan	u_long	if_in_traffic_peak;
80169689Skan	u_long	if_out_traffic_peak;
8190075Sobrien	u_int	if_row;			/* Index into ifmib sysctl */
8290075Sobrien	u_int	if_ypos;		/* 0 if not being displayed */
83169689Skan	u_int	display;
8490075Sobrien};
8590075Sobrien
86117395Skanextern	 u_int curscale;
87255107Spfg
88261188Spfgstatic	 void  right_align_string(const struct if_stat *);
89261188Spfgstatic	 void  getifmibdata(const int, struct ifmibdata *);
9090075Sobrienstatic	 void  sort_interface_list(void);
9190075Sobrienstatic	 u_int getifnum(void);
9290075Sobrien
9390075Sobrien#define IFSTAT_ERR(n, s)	do {					\
9490075Sobrien	putchar('');							\
9590075Sobrien	closeifstat(wnd);						\
9690075Sobrien	err((n), (s));							\
9790075Sobrien} while (0)
9890075Sobrien
9990075Sobrien#define STARTING_ROW	(8)
10090075Sobrien#define ROW_SPACING	(3)
10190075Sobrien
10290075Sobrien#define TOPLINE 5
10390075Sobrien#define TOPLABEL \
10490075Sobrien"      Interface           Traffic               Peak                Total"
10590075Sobrien
10690075Sobrien#define CLEAR_LINE(y, x)	do {					\
10790075Sobrien	wmove(wnd, y, x);						\
10890075Sobrien	wclrtoeol(wnd);							\
109132718Skan} while (0)
11090075Sobrien
11190075Sobrien#define IN_col2		(ifp->if_in_curtraffic)
11290075Sobrien#define OUT_col2	(ifp->if_out_curtraffic)
11390075Sobrien#define IN_col3		(ifp->if_in_traffic_peak)
114132718Skan#define OUT_col3	(ifp->if_out_traffic_peak)
11590075Sobrien#define IN_col4		(ifp->if_mib.ifmd_data.ifi_ibytes)
11690075Sobrien#define OUT_col4	(ifp->if_mib.ifmd_data.ifi_obytes)
11790075Sobrien
11890075Sobrien#define EMPTY_COLUMN 	"                    "
11990075Sobrien#define CLEAR_COLUMN(y, x)	mvprintw((y), (x), "%20s", EMPTY_COLUMN);
12090075Sobrien
12190075Sobrien#define DOPUTRATE(c, r, d)	do {					\
12290075Sobrien	CLEAR_COLUMN(r, c);						\
12390075Sobrien	mvprintw(r, (c), "%10.3f %s%s  ",				\
12490075Sobrien		 convert(d##_##c, curscale),				\
12590075Sobrien		 get_string(d##_##c, curscale),				\
12690075Sobrien		 "/s");							\
12790075Sobrien} while (0)
12890075Sobrien
12990075Sobrien#define DOPUTTOTAL(c, r, d)	do {					\
13090075Sobrien 	CLEAR_COLUMN((r), (c));						\
131132718Skan 	mvprintw((r), (c), "%12.3f %s  ",				\
13290075Sobrien 		 convert(d##_##c, SC_AUTO),				\
133132718Skan		 get_string(d##_##c, SC_AUTO));				\
13490075Sobrien} while (0)
13590075Sobrien
13690075Sobrien#define PUTRATE(c, r)	do {						\
13790075Sobrien	DOPUTRATE(c, (r), IN);						\
13890075Sobrien	DOPUTRATE(c, (r)+1, OUT);					\
139132718Skan} while (0)
14090075Sobrien
14190075Sobrien#define PUTTOTAL(c, r)	do {						\
142132718Skan	DOPUTTOTAL(c, (r), IN);						\
14390075Sobrien	DOPUTTOTAL(c, (r)+1, OUT);					\
14490075Sobrien} while (0)
145132718Skan
14690075Sobrien#define PUTNAME(p) do {							\
14790075Sobrien	mvprintw(p->if_ypos, 0, "%s", p->if_name);			\
14890075Sobrien	mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in");		\
14990075Sobrien	mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out");	\
15090075Sobrien} while (0)
15190075Sobrien
152104752Skan
15390075SobrienWINDOW *
154132718Skanopenifstat(void)
155132718Skan{
156169689Skan	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
15790075Sobrien}
158132718Skan
159132718Skanvoid
16090075Sobriencloseifstat(WINDOW *w)
161132718Skan{
162132718Skan	struct if_stat	*node = NULL;
16390075Sobrien
16490075Sobrien	while (!SLIST_EMPTY(&curlist)) {
165132718Skan		node = SLIST_FIRST(&curlist);
166132718Skan		SLIST_REMOVE_HEAD(&curlist, link);
167132718Skan		free(node);
168132718Skan	}
16990075Sobrien
170132718Skan	if (w != NULL) {
171132718Skan		wclear(w);
172132718Skan		wrefresh(w);
173132718Skan		delwin(w);
174132718Skan	}
175132718Skan
176132718Skan	return;
17790075Sobrien}
178132718Skan
179132718Skan
180132718Skanvoid
181132718Skanlabelifstat(void)
182132718Skan{
183132718Skan
184132718Skan	wmove(wnd, TOPLINE, 0);
185132718Skan	wclrtoeol(wnd);
186132718Skan	mvprintw(TOPLINE, 0, "%s", TOPLABEL);
187132718Skan
188132718Skan	return;
18990075Sobrien}
190132718Skan
191132718Skanvoid
192132718Skanshowifstat(void)
193132718Skan{
19490075Sobrien	struct	if_stat *ifp = NULL;
19590075Sobrien	SLIST_FOREACH(ifp, &curlist, link) {
19690075Sobrien		if (ifp->display == 0)
19790075Sobrien			continue;
19890075Sobrien		PUTNAME(ifp);
19990075Sobrien		PUTRATE(col2, ifp->if_ypos);
20090075Sobrien		PUTRATE(col3, ifp->if_ypos);
201169689Skan		PUTTOTAL(col4, ifp->if_ypos);
202169689Skan	}
203169689Skan
204169689Skan	return;
205169689Skan}
206169689Skan
207169689Skanint
208169689Skaninitifstat(void)
209169689Skan{
210169689Skan	struct   if_stat *p = NULL;
211169689Skan	u_int	 n = 0, i = 0;
212169689Skan
213169689Skan	n = getifnum();
214169689Skan	if (n <= 0)
215169689Skan		return -1;
216169689Skan
217169689Skan	SLIST_INIT(&curlist);
218169689Skan
219169689Skan	for (i = 0; i < n; i++) {
220169689Skan		p = (struct if_stat *)malloc(sizeof(struct if_stat));
221169689Skan		if (p == NULL)
222169689Skan			IFSTAT_ERR(1, "out of memory");
223169689Skan		memset((void *)p, 0, sizeof(struct if_stat));
22490075Sobrien		SLIST_INSERT_HEAD(&curlist, p, link);
22590075Sobrien		p->if_row = i+1;
22690075Sobrien		getifmibdata(p->if_row, &p->if_mib);
22790075Sobrien		right_align_string(p);
22890075Sobrien
229132718Skan		/*
230169689Skan		 * Initially, we only display interfaces that have
231132718Skan		 * received some traffic.
232132718Skan		 */
23390075Sobrien		if (p->if_mib.ifmd_data.ifi_ibytes != 0)
23490075Sobrien			p->display = 1;
23590075Sobrien	}
236132718Skan
23790075Sobrien	sort_interface_list();
238132718Skan
23990075Sobrien	return 1;
24090075Sobrien}
24190075Sobrien
24290075Sobrienvoid
243132718Skanfetchifstat(void)
244117395Skan{
245132718Skan	struct	if_stat *ifp = NULL;
24690075Sobrien	struct	timeval tv, new_tv, old_tv;
247132718Skan	double	elapsed = 0.0;
248132718Skan	u_int	new_inb, new_outb, old_inb, old_outb = 0;
24990075Sobrien	u_int	we_need_to_sort_interface_list = 0;
25090075Sobrien
25190075Sobrien	SLIST_FOREACH(ifp, &curlist, link) {
25290075Sobrien		/*
25390075Sobrien		 * Grab a copy of the old input/output values before we
254132718Skan		 * call getifmibdata().
255132718Skan		 */
256132718Skan		old_inb = ifp->if_mib.ifmd_data.ifi_ibytes;
257132718Skan		old_outb = ifp->if_mib.ifmd_data.ifi_obytes;
258132718Skan		ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange;
259132718Skan
26090075Sobrien		if (gettimeofday(&new_tv, (struct timezone *)0) != 0)
261132718Skan			IFSTAT_ERR(2, "error getting time of day");
262132718Skan		(void)getifmibdata(ifp->if_row, &ifp->if_mib);
263132718Skan
264132718Skan
265132718Skan                new_inb = ifp->if_mib.ifmd_data.ifi_ibytes;
266132718Skan                new_outb = ifp->if_mib.ifmd_data.ifi_obytes;
267132718Skan
268132718Skan		/* Display interface if it's received some traffic. */
269132718Skan		if (new_inb > 0 && old_inb == 0) {
270132718Skan			ifp->display = 1;
271132718Skan			we_need_to_sort_interface_list++;
272132718Skan		}
273132718Skan
274132718Skan		/*
275132718Skan		 * The rest is pretty trivial.  Calculate the new values
276132718Skan		 * for our current traffic rates, and while we're there,
277132718Skan		 * see if we have new peak rates.
278132718Skan		 */
279132718Skan                old_tv = ifp->tv;
280132718Skan                timersub(&new_tv, &old_tv, &tv);
28190075Sobrien                elapsed = tv.tv_sec + (tv.tv_usec * 1e-6);
28290075Sobrien
28390075Sobrien		ifp->if_in_curtraffic = new_inb - old_inb;
28490075Sobrien		ifp->if_out_curtraffic = new_outb - old_outb;
28590075Sobrien
28690075Sobrien		/*
28790075Sobrien		 * Rather than divide by the time specified on the comm-
28890075Sobrien		 * and line, we divide by ``elapsed'' as this is likely
28990075Sobrien		 * to be more accurate.
29090075Sobrien		 */
29190075Sobrien                ifp->if_in_curtraffic /= elapsed;
29290075Sobrien                ifp->if_out_curtraffic /= elapsed;
29390075Sobrien
29490075Sobrien		if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak)
29590075Sobrien			ifp->if_in_traffic_peak = ifp->if_in_curtraffic;
296132718Skan
29790075Sobrien		if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak)
298132718Skan			ifp->if_out_traffic_peak = ifp->if_out_curtraffic;
299132718Skan
300132718Skan		ifp->tv.tv_sec = new_tv.tv_sec;
30190075Sobrien		ifp->tv.tv_usec = new_tv.tv_usec;
30290075Sobrien
30390075Sobrien	}
30490075Sobrien
30590075Sobrien	if (we_need_to_sort_interface_list)
30690075Sobrien		sort_interface_list();
30790075Sobrien
30890075Sobrien	return;
30990075Sobrien}
31090075Sobrien
31190075Sobrien/*
31290075Sobrien * We want to right justify our interface names against the first column
313132718Skan * (first sixteen or so characters), so we need to do some alignment.
31490075Sobrien */
315117395Skanstatic void
31690075Sobrienright_align_string(const struct if_stat *ifp)
317132718Skan{
31890075Sobrien	int	 str_len = 0, pad_len = 0;
31990075Sobrien	char	*newstr = NULL, *ptr = NULL;
32090075Sobrien
32190075Sobrien	if (ifp == NULL || ifp->if_mib.ifmd_name == NULL)
32290075Sobrien		return;
32390075Sobrien	else {
32490075Sobrien		/* string length + '\0' */
325132718Skan		str_len = strlen(ifp->if_mib.ifmd_name)+1;
32690075Sobrien		pad_len = IF_NAMESIZE-(str_len);
327132718Skan
32890075Sobrien		newstr = (char *)ifp->if_name;
32990075Sobrien		ptr = newstr + pad_len;
33090075Sobrien		(void)memset((void *)newstr, (int)' ', IF_NAMESIZE);
33190075Sobrien		(void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name,
33290075Sobrien			      str_len);
33390075Sobrien	}
33490075Sobrien
335132718Skan	return;
33690075Sobrien}
337132718Skan
33890075Sobrien/*
33990075Sobrien * This function iterates through our list of interfaces, identifying
34090075Sobrien * those that are to be displayed (ifp->display = 1).  For each interf-
341132718Skan * rface that we're displaying, we generate an appropriate position for
34290075Sobrien * it on the screen (ifp->if_ypos).
343132718Skan *
34490075Sobrien * This function is called any time a change is made to an interface's
34590075Sobrien * ``display'' state.
34690075Sobrien */
34790075Sobrienvoid
34890075Sobriensort_interface_list(void)
34990075Sobrien{
35090075Sobrien	struct	if_stat	*ifp = NULL;
35190075Sobrien	u_int	y = 0;
35290075Sobrien
35390075Sobrien	y = STARTING_ROW;
35490075Sobrien	SLIST_FOREACH(ifp, &curlist, link) {
35590075Sobrien		if (ifp->display) {
35690075Sobrien			ifp->if_ypos = y;
35790075Sobrien			y += ROW_SPACING;
35890075Sobrien		}
35990075Sobrien	}
36090075Sobrien}
36190075Sobrien
36290075Sobrienstatic
36390075Sobrienunsigned int
36490075Sobriengetifnum(void)
365132718Skan{
36690075Sobrien	u_int	data    = 0;
36790075Sobrien	size_t	datalen = 0;
36890075Sobrien	static	int name[] = { CTL_NET,
369132718Skan			       PF_LINK,
37090075Sobrien			       NETLINK_GENERIC,
37190075Sobrien			       IFMIB_SYSTEM,
37290075Sobrien			       IFMIB_IFCOUNT };
37390075Sobrien
37490075Sobrien	datalen = sizeof(data);
37590075Sobrien	if (sysctl(name, 5, (void *)&data, (size_t *)&datalen, (void *)NULL,
37690075Sobrien	    (size_t)0) != 0)
37790075Sobrien		IFSTAT_ERR(1, "sysctl error");
37890075Sobrien	return data;
37990075Sobrien}
38090075Sobrien
38190075Sobrienstatic void
38290075Sobriengetifmibdata(int row, struct ifmibdata *data)
383261188Spfg{
384261188Spfg	size_t	datalen = 0;
385261188Spfg	static	int name[] = { CTL_NET,
386261188Spfg			       PF_LINK,
387261188Spfg			       NETLINK_GENERIC,
388261188Spfg			       IFMIB_IFDATA,
389261188Spfg			       0,
390261188Spfg			       IFDATA_GENERAL };
39190075Sobrien	datalen = sizeof(*data);
392132718Skan	name[4] = row;
39390075Sobrien
39490075Sobrien	if (sysctl(name, 6, (void *)data, (size_t *)&datalen, (void *)NULL,
39590075Sobrien	    (size_t)0) != 0)
39690075Sobrien		IFSTAT_ERR(2, "sysctl error getting interface data");
39790075Sobrien}
39890075Sobrien
39990075Sobrienint
40090075Sobriencmdifstat(const char *cmd, const char *args)
401132718Skan{
40290075Sobrien	int	retval = 0;
40390075Sobrien
40490075Sobrien	retval = ifcmd(cmd, args);
40590075Sobrien	/* ifcmd() returns 1 on success */
40690075Sobrien	if (retval == 1) {
40790075Sobrien		showifstat();
408132718Skan		refresh();
40990075Sobrien	}
41090075Sobrien
41190075Sobrien	return retval;
41290075Sobrien}
41390075Sobrien