NetBSD Problem Report #51204

From www@NetBSD.org  Tue May 31 15:15:49 2016
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id ACCFC7A3DB
	for <gnats-bugs@gnats.NetBSD.org>; Tue, 31 May 2016 15:15:49 +0000 (UTC)
Message-Id: <20160531151548.89A737AA9E@mollari.NetBSD.org>
Date: Tue, 31 May 2016 15:15:48 +0000 (UTC)
From: scole_mail@gmx.com
Reply-To: scole_mail@gmx.com
To: gnats-bugs@NetBSD.org
Subject: add ifstat to systat command
X-Send-Pr-Version: www-1.0

>Number:         51204
>Category:       bin
>Synopsis:       add ifstat to systat command
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    scole
>State:          closed
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue May 31 15:20:00 +0000 2016
>Closed-Date:    Wed Aug 10 12:08:11 +0000 2016
>Last-Modified:  Wed Aug 10 12:08:11 +0000 2016
>Originator:     scole_mail
>Release:        7.0.1
>Organization:
none
>Environment:
NetBSD dstar 7.0.1_PATCH NetBSD 7.0.1_PATCH (GENERIC) #0: Sat May 28 11:24:19 EDT 2016  scole@dstar:/home/scole/nbsd/7_0/obj/sys/arch/i386/compile/GENERIC i386
>Description:
I ported the ifstat part of systat from freebsd 10.1.  This displays
network traffic statistics for active interfaces in a curses window.
Here is a screen shot:

shell> systat -w 1 ifstat


                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average

      Interface           Traffic               Peak                Total
            lo0  in      0.054 KB/s          0.221 KB/s          111.380 KB
                 out     0.054 KB/s          0.221 KB/s          111.380 KB

          athn0  in     22.062 KB/s         27.065 KB/s           47.030 MB
                 out    22.003 KB/s         26.978 KB/s           34.109 MB

            re1  in      0.000 KB/s          0.000 KB/s            1.111 KB
                 out     0.000 KB/s          0.000 KB/s            0.494 KB

            re0  in     50.894 KB/s         51.068 KB/s          797.001 KB
                 out    50.894 KB/s         51.040 KB/s          787.297 KB


To get interface information, I wasn't sure which method to use from
sysctl/prog_sysctl/kvm.  So I used getifaddrs/freeifaddrs which seemed
the simplest to understand and code.

If new interfaces are added while running, the code handles that
automagically and will add them to the display.  If any interfaces are
removed, then it display an error message and exit.  I didn't see a
clean way around that, as you can just restart the gui and that
shouldn't be a common occurrence I would think.

These patches apply to current, but they worked on 7.0.1 fine.

Feel free to change these changes...

new files copied from freebsd:
	convtbl.c	(unchanged)
	convtbl.h	(unchanged)
	ifcmds.c	(unchanged)
	ifstat.c	(modified for netbsd)

modified:
	Makefile
	cmds.c
	cmdtab.c
	extern.h
	systat.1

Thanks

>How-To-Repeat:
systat -w 1 ifstat
>Fix:

>Release-Note:

>Audit-Trail:
From: scole_mail@gmx.com
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/51204: add
Date: Tue, 31 May 2016 11:24:34 -0400

 Sorry about the subject line, that was supposed to be
 "add ifstat to systat command"

 Here is a patch:

 --- /dev/null	2016-05-31 11:09:13.000000000 -0400
 +++ convtbl.c	2014-11-11 15:03:22.000000000 -0500
 @@ -0,0 +1,150 @@
 +/*
 + * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>.
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + * 3. The name of the author may not be used to endorse or promote products
 + *    derived from this software without specific prior written permission.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 + * SUCH DAMAGE.
 + *
 + * $FreeBSD: releng/10.1/usr.bin/systat/convtbl.c 175387 2008-01-16 19:27:43Z delphij $
 + */
 +
 +#include <sys/types.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include "convtbl.h"
 +
 +#define BIT		(8)
 +#define BITS		(1)
 +#define KILOBIT		(1000LL)
 +#define MEGABIT		(KILOBIT * 1000)
 +#define GIGABIT		(MEGABIT * 1000)
 +#define TERABIT		(GIGABIT * 1000)
 +
 +#define BYTE		(1)
 +#define BYTES		(1)
 +#define KILOBYTE	(1024LL)
 +#define MEGABYTE	(KILOBYTE * 1024)
 +#define GIGABYTE	(MEGABYTE * 1024)
 +#define TERABYTE	(GIGABYTE * 1024)
 +
 +struct convtbl {
 +	uintmax_t	 mul;
 +	uintmax_t	 scale;
 +	const char	*str;
 +	const char	*name;
 +};
 +
 +static struct convtbl convtbl[] = {
 +	/* mul, scale, str, name */
 +	[SC_BYTE] =	{ BYTE, BYTES, "B", "byte" },
 +	[SC_KILOBYTE] =	{ BYTE, KILOBYTE, "KB", "kbyte" },
 +	[SC_MEGABYTE] =	{ BYTE, MEGABYTE, "MB", "mbyte" },
 +	[SC_GIGABYTE] =	{ BYTE, GIGABYTE, "GB", "gbyte" },
 +	[SC_TERABYTE] =	{ BYTE, TERABYTE, "TB", "tbyte" },
 +
 +	[SC_BIT] =	{ BIT, BITS, "b", "bit" },
 +	[SC_KILOBIT] =	{ BIT, KILOBIT, "Kb", "kbit" },
 +	[SC_MEGABIT] =	{ BIT, MEGABIT, "Mb", "mbit" },
 +	[SC_GIGABIT] =	{ BIT, GIGABIT, "Gb", "gbit" },
 +	[SC_TERABIT] =	{ BIT, TERABIT, "Tb", "tbit" },
 +
 +	[SC_AUTO] =	{ 0, 0, "", "auto" }
 +};
 +
 +static
 +struct convtbl *
 +get_tbl_ptr(const uintmax_t size, const int scale)
 +{
 +	uintmax_t	 tmp;
 +	int		 idx;
 +
 +	/* If our index is out of range, default to auto-scaling. */
 +	idx = scale < SC_AUTO ? scale : SC_AUTO;
 +
 +	if (idx == SC_AUTO)
 +		/*
 +		 * Simple but elegant algorithm.  Count how many times
 +		 * we can shift our size value right by a factor of ten,
 +		 * incrementing an index each time.  We then use the
 +		 * index as the array index into the conversion table.
 +		 */
 +		for (tmp = size, idx = SC_KILOBYTE;
 +		     tmp >= MEGABYTE && idx < SC_BIT - 1;
 +		     tmp >>= 10, idx++);
 +
 +	return (&convtbl[idx]);
 +}
 +
 +double
 +convert(const uintmax_t size, const int scale)
 +{
 +	struct convtbl	*tp;
 +
 +	tp = get_tbl_ptr(size, scale);
 +	return ((double)size * tp->mul / tp->scale);
 +
 +}
 +
 +const char *
 +get_string(const uintmax_t size, const int scale)
 +{
 +	struct convtbl	*tp;
 +
 +	tp = get_tbl_ptr(size, scale);
 +	return (tp->str);
 +}
 +
 +int
 +get_scale(const char *name)
 +{
 +	int i;
 +
 +	for (i = 0; i <= SC_AUTO; i++)
 +		if (strcmp(convtbl[i].name, name) == 0)
 +			return (i);
 +	return (-1);
 +}
 +
 +const char *
 +get_helplist(void)
 +{
 +	int i;
 +	size_t len;
 +	static char *buf;
 +
 +	if (buf == NULL) {
 +		len = 0;
 +		for (i = 0; i <= SC_AUTO; i++)
 +			len += strlen(convtbl[i].name) + 2;
 +		if ((buf = malloc(len)) != NULL) {
 +			buf[0] = '\0';
 +			for (i = 0; i <= SC_AUTO; i++) {
 +				strcat(buf, convtbl[i].name);
 +				if (i < SC_AUTO)
 +					strcat(buf, ", ");
 +			}
 +		} else
 +			return ("");
 +	}
 +	return (buf);
 +}
 --- /dev/null	2016-05-31 11:09:13.000000000 -0400
 +++ convtbl.h	2014-11-11 15:03:22.000000000 -0500
 @@ -0,0 +1,59 @@
 +/*
 + * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>.
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + * 3. The name of the author may not be used to endorse or promote products
 + *    derived from this software without specific prior written permission.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 + * SUCH DAMAGE.
 + *
 + * $FreeBSD: releng/10.1/usr.bin/systat/convtbl.h 164675 2006-11-27 16:33:44Z yar $
 + */
 +
 +#ifndef _CONVTBL_H_
 +#define _CONVTBL_H_
 +
 +#include <sys/types.h>
 +#include <stdint.h>
 +
 +/*
 + * Keep the order in the enum.
 + */
 +enum scale {
 +	SC_BYTE,
 +	SC_KILOBYTE,
 +	SC_MEGABYTE,
 +	SC_GIGABYTE,
 +	SC_TERABYTE,
 +	SC_BIT,
 +	SC_KILOBIT,
 +	SC_MEGABIT,
 +	SC_GIGABIT,
 +	SC_TERABIT,
 +	SC_AUTO		/* KEEP THIS LAST */
 +};
 +
 +extern	double		 convert(const uintmax_t, const int);
 +extern	const char	*get_helplist(void);
 +extern	int		 get_scale(const char *);
 +extern	const char	*get_string(const uintmax_t, const int);
 +
 +#endif		/* ! _CONVTBL_H_ */
 --- /dev/null	2016-05-31 11:09:13.000000000 -0400
 +++ ifcmds.c	2016-05-31 10:30:20.000000000 -0400
 @@ -0,0 +1,77 @@
 +/*
 + * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>.
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + * 3. The name of the author may not be used to endorse or promote products
 + *    derived from this software without specific prior written permission.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 + * SUCH DAMAGE.
 + *
 + * $FreeBSD: releng/10.1/usr.bin/systat/ifcmds.c 247037 2013-02-20 14:19:09Z melifaro $
 + */
 +
 +#include <sys/types.h>
 +
 +#include "systat.h"
 +#include "extern.h"
 +#include "convtbl.h"
 +
 +#include <stdlib.h>
 +#include <string.h>
 +
 +int curscale = SC_AUTO;
 +char *matchline = NULL;
 +int showpps = 0;
 +int needsort = 0;
 +
 +int
 +ifcmd(const char *cmd, const char *args)
 +{
 +	int scale;
 +
 +	if (prefix(cmd, "scale")) {
 +		if ((scale = get_scale(args)) != -1)
 +			curscale = scale;
 +		else {
 +			move(CMDLINE, 0);
 +			clrtoeol();
 +			addstr("what scale? ");
 +			addstr(get_helplist());
 +		}
 +	} else if (prefix(cmd, "match")) {
 +		if (args != NULL && *args != '\0' && memcmp(args, "*", 2) != 0) {
 +			/* We got a valid match line */
 +			if (matchline != NULL)
 +				free(matchline);
 +			needsort = 1;
 +			matchline = strdup(args);
 +		} else {
 +			/* Empty or * pattern, turn filtering off */
 +			if (matchline != NULL)
 +				free(matchline);
 +			needsort = 1;
 +			matchline = NULL;
 +		}
 +	} else if (prefix(cmd, "pps"))
 +		showpps = !showpps;
 +
 +	return (1);
 +}
 --- /dev/null	2016-05-31 11:09:13.000000000 -0400
 +++ ifstat.c	2016-05-31 10:57:57.000000000 -0400
 @@ -0,0 +1,539 @@
 +/*
 + * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>.
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + * 3. The name of the author may not be used to endorse or promote products
 + *    derived from this software without specific prior written permission.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTIFSTAT_ERRUPTION)
 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 + * SUCH DAMAGE.
 + *
 + * $FreeBSD: releng/10.1/usr.bin/systat/ifstat.c 247037 2013-02-20 14:19:09Z melifaro $
 + */
 +
 +#include <sys/types.h>
 +#include <sys/socket.h>
 +#include <sys/sysctl.h>
 +#include <sys/time.h>
 +#include <net/if.h>
 +
 +#include <ifaddrs.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <err.h>
 +#include <errno.h>
 +#include <fnmatch.h>
 +
 +#include "systat.h"
 +#include "extern.h"
 +#include "convtbl.h"
 +
 +				/* Column numbers */
 +
 +#define C1	0		/*  0-19 */
 +#define C2	20		/* 20-39 */
 +#define C3	40		/* 40-59 */
 +#define C4	60		/* 60-80 */
 +#define C5	80		/* Used for label positioning. */
 +
 +static const int col0 = 0;
 +static const int col1 = C1;
 +static const int col2 = C2;
 +static const int col3 = C3;
 +static const int col4 = C4;
 +static const int col5 = C5;
 +
 +SLIST_HEAD(, if_stat)		curlist;
 +
 +struct if_stat {
 +	SLIST_ENTRY(if_stat)	 link;
 +  	char	if_name[IF_NAMESIZE];
 +	struct  ifdatareq if_mib;
 +	struct	timeval tv;
 +	struct	timeval tv_lastchanged;
 +	u_long	if_in_curtraffic;
 +	u_long	if_out_curtraffic;
 +	u_long	if_in_traffic_peak;
 +	u_long	if_out_traffic_peak;
 +	u_long	if_in_curpps;
 +	u_long	if_out_curpps;
 +	u_long	if_in_pps_peak;
 +	u_long	if_out_pps_peak;
 +	u_int	if_row;			/* Index into ifmib sysctl */
 +	u_int	if_ypos;		/* 0 if not being displayed */
 +	u_int	display;
 +	u_int	match;
 +};
 +
 +extern	 int curscale;
 +extern	 char *matchline;
 +extern	 int showpps;
 +extern	 int needsort;
 +
 +static	 int needclear = 0;
 +
 +static	 void  right_align_string(struct if_stat *);
 +static	 void  getifmibdata(const int, struct ifdatareq *);
 +static	 void  sort_interface_list(void);
 +static	 u_int getifnum(void);
 +
 +#define IFSTAT_ERR(n, s)	do {					\
 +	putchar('\014');						\
 +	closeifstat(wnd);						\
 +	err((n), (s));							\
 +} while (0)
 +
 +#define TOPLINE 5
 +#define TOPLABEL \
 +"      Interface           Traffic               Peak                Total"
 +
 +#define STARTING_ROW	(TOPLINE + 1)
 +#define ROW_SPACING	(3)
 +
 +#define IN_col2		(showpps ? ifp->if_in_curpps : ifp->if_in_curtraffic)
 +#define OUT_col2	(showpps ? ifp->if_out_curpps : ifp->if_out_curtraffic)
 +#define IN_col3		(showpps ? \
 +		ifp->if_in_pps_peak : ifp->if_in_traffic_peak)
 +#define OUT_col3	(showpps ? \
 +		ifp->if_out_pps_peak : ifp->if_out_traffic_peak)
 +#define IN_col4		(showpps ?				\
 +	ifp->if_mib.ifdr_data.ifi_ipackets : ifp->if_mib.ifdr_data.ifi_ibytes)
 +#define OUT_col4	(showpps ?					\
 +	ifp->if_mib.ifdr_data.ifi_opackets : ifp->if_mib.ifdr_data.ifi_obytes)
 +
 +#define EMPTY_COLUMN 	"                    "
 +#define CLEAR_COLUMN(y, x)	mvprintw((y), (x), "%20s", EMPTY_COLUMN);
 +
 +#define DOPUTRATE(c, r, d)	do {					\
 +	CLEAR_COLUMN(r, c);						\
 +	if (showpps) {							\
 +		mvprintw(r, (c), "%10.3f %cp%s  ",			\
 +			 convert(d##_##c, curscale),			\
 +			 *get_string(d##_##c, curscale),		\
 +			 "/s");						\
 +	}								\
 +	else {								\
 +		mvprintw(r, (c), "%10.3f %s%s  ",			\
 +			 convert(d##_##c, curscale),			\
 +			 get_string(d##_##c, curscale),			\
 +			 "/s");						\
 +	}								\
 +} while (0)
 +
 +#define DOPUTTOTAL(c, r, d)	do {					\
 +	CLEAR_COLUMN((r), (c));						\
 +	if (showpps) {							\
 +		mvprintw((r), (c), "%12.3f %cp  ",			\
 +			 convert(d##_##c, SC_AUTO),			\
 +			 *get_string(d##_##c, SC_AUTO));		\
 +	}								\
 +	else {								\
 +		mvprintw((r), (c), "%12.3f %s  ",			\
 +			 convert(d##_##c, SC_AUTO),			\
 +			 get_string(d##_##c, SC_AUTO));			\
 +	}								\
 +} while (0)
 +
 +#define PUTRATE(c, r)	do {						\
 +	DOPUTRATE(c, (r), IN);						\
 +	DOPUTRATE(c, (r)+1, OUT);					\
 +} while (0)
 +
 +#define PUTTOTAL(c, r)	do {						\
 +	DOPUTTOTAL(c, (r), IN);						\
 +	DOPUTTOTAL(c, (r)+1, OUT);					\
 +} while (0)
 +
 +#define PUTNAME(p) do {							\
 +	mvprintw(p->if_ypos, 0, "%s", p->if_name);			\
 +	mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in");		\
 +	mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out");	\
 +} while (0)
 +
 +WINDOW *
 +openifstat(void)
 +{
 +	return (subwin(stdscr, -1, 0, 5, 0));
 +}
 +
 +void
 +closeifstat(WINDOW *w)
 +{
 +	struct if_stat	*node = NULL;
 +
 +	while (!SLIST_EMPTY(&curlist)) {
 +		node = SLIST_FIRST(&curlist);
 +		SLIST_REMOVE_HEAD(&curlist, link);
 +		free(node);
 +	}
 +
 +	if (w != NULL) {
 +		wclear(w);
 +		wrefresh(w);
 +		delwin(w);
 +	}
 +
 +	return;
 +}
 +
 +void
 +labelifstat(void)
 +{
 +
 +	wmove(wnd, TOPLINE, 0);
 +	wclrtoeol(wnd);
 +	mvprintw(TOPLINE, 0, "%s", TOPLABEL);
 +
 +	return;
 +}
 +
 +void
 +showifstat(void)
 +{
 +	struct	if_stat *ifp = NULL;
 +	
 +	SLIST_FOREACH(ifp, &curlist, link) {
 +		if (ifp->display == 0 || (ifp->match == 0) ||
 +		    ifp->if_ypos > (u_int)(LINES - 3 - 1))
 +			continue;
 +		PUTNAME(ifp);
 +		PUTRATE(col2, ifp->if_ypos);
 +		PUTRATE(col3, ifp->if_ypos);
 +		PUTTOTAL(col4, ifp->if_ypos);
 +	}
 +
 +	return;
 +}
 +
 +int
 +initifstat(void)
 +{
 +	struct   if_stat *p = NULL;
 +	u_int	 n = 0, i = 0;
 +
 +	n = getifnum();
 +	if (n <= 0)
 +		return (-1);
 +
 +	SLIST_INIT(&curlist);
 +
 +	for (i = 0; i < n; i++) {
 +		p = (struct if_stat *)calloc(1, sizeof(struct if_stat));
 +		if (p == NULL)
 +			IFSTAT_ERR(1, "out of memory");
 +		SLIST_INSERT_HEAD(&curlist, p, link);
 +		p->if_row = i+1;
 +		getifmibdata(p->if_row, &p->if_mib);
 +		right_align_string(p);
 +		p->match = 1;
 +
 +		/*
 +		 * Initially, we only display interfaces that have
 +		 * received some traffic.
 +		 */
 +		if (p->if_mib.ifdr_data.ifi_ibytes != 0)
 +			p->display = 1;
 +	}
 +
 +	sort_interface_list();
 +
 +	return (1);
 +}
 +
 +void
 +fetchifstat(void)
 +{
 +	struct	if_stat *ifp = NULL;
 +	struct	timeval tv, new_tv, old_tv;
 +	double	elapsed = 0.0;
 +	u_int	new_inb, new_outb, old_inb, old_outb = 0;
 +	u_int	new_inp, new_outp, old_inp, old_outp = 0;
 +
 +	SLIST_FOREACH(ifp, &curlist, link) {
 +		/*
 +		 * Grab a copy of the old input/output values before we
 +		 * call getifmibdata().
 +		 */
 +		old_inb = ifp->if_mib.ifdr_data.ifi_ibytes;
 +		old_outb = ifp->if_mib.ifdr_data.ifi_obytes;
 +		old_inp = ifp->if_mib.ifdr_data.ifi_ipackets;
 +		old_outp = ifp->if_mib.ifdr_data.ifi_opackets;
 +		TIMESPEC_TO_TIMEVAL(&ifp->tv_lastchanged, &ifp->if_mib.ifdr_data.ifi_lastchange);
 +
 +		(void)gettimeofday(&new_tv, NULL);
 +		(void)getifmibdata(ifp->if_row, &ifp->if_mib);
 +
 +		new_inb = ifp->if_mib.ifdr_data.ifi_ibytes;
 +		new_outb = ifp->if_mib.ifdr_data.ifi_obytes;
 +		new_inp = ifp->if_mib.ifdr_data.ifi_ipackets;
 +		new_outp = ifp->if_mib.ifdr_data.ifi_opackets;
 +
 +		/* Display interface if it's received some traffic. */
 +		if (new_inb > 0 && old_inb == 0) {
 +			ifp->display = 1;
 +			needsort = 1;
 +		}
 +
 +		/*
 +		 * The rest is pretty trivial.  Calculate the new values
 +		 * for our current traffic rates, and while we're there,
 +		 * see if we have new peak rates.
 +		 */
 +		old_tv = ifp->tv;
 +		timersub(&new_tv, &old_tv, &tv);
 +		elapsed = tv.tv_sec + (tv.tv_usec * 1e-6);
 +
 +		ifp->if_in_curtraffic = new_inb - old_inb;
 +		ifp->if_out_curtraffic = new_outb - old_outb;
 +
 +		ifp->if_in_curpps = new_inp - old_inp;
 +		ifp->if_out_curpps = new_outp - old_outp;
 +
 +		/*
 +		 * Rather than divide by the time specified on the comm-
 +		 * and line, we divide by ``elapsed'' as this is likely
 +		 * to be more accurate.
 +		 */
 +		ifp->if_in_curtraffic /= elapsed;
 +		ifp->if_out_curtraffic /= elapsed;
 +		ifp->if_in_curpps /= elapsed;
 +		ifp->if_out_curpps /= elapsed;
 +
 +		if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak)
 +			ifp->if_in_traffic_peak = ifp->if_in_curtraffic;
 +
 +		if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak)
 +			ifp->if_out_traffic_peak = ifp->if_out_curtraffic;
 +
 +		if (ifp->if_in_curpps > ifp->if_in_pps_peak)
 +			ifp->if_in_pps_peak = ifp->if_in_curpps;
 +
 +		if (ifp->if_out_curpps > ifp->if_out_pps_peak)
 +			ifp->if_out_pps_peak = ifp->if_out_curpps;
 +
 +		ifp->tv.tv_sec = new_tv.tv_sec;
 +		ifp->tv.tv_usec = new_tv.tv_usec;
 +
 +	}
 +
 +	if (needsort)
 +		sort_interface_list();
 +
 +	return;
 +}
 +
 +/*
 + * We want to right justify our interface names against the first column
 + * (first sixteen or so characters), so we need to do some alignment.
 + */
 +static void
 +right_align_string(struct if_stat *ifp)
 +{
 +	int	 str_len = 0, pad_len = 0;
 +	char	*newstr = NULL, *ptr = NULL;
 +
 +	if (ifp == NULL || ifp->if_mib.ifdr_name == NULL)
 +		return;
 +	else {
 +		/* string length + '\0' */
 +		str_len = strlen(ifp->if_mib.ifdr_name)+1;
 +		pad_len = IF_NAMESIZE-(str_len);
 +
 +		newstr = ifp->if_name;
 +		ptr = newstr + pad_len;
 +		(void)memset((void *)newstr, (int)' ', IF_NAMESIZE);
 +		(void)strncpy(ptr, (const char *)&ifp->if_mib.ifdr_name,
 +			      str_len);
 +	}
 +
 +	return;
 +}
 +
 +static int
 +check_match(const char *ifname) 
 +{
 +	char *p = matchline, *c, t;
 +	int match = 0, mlen;
 +	
 +	if (matchline == NULL)
 +		return (0);
 +
 +	/* Strip leading whitespaces */
 +	while (*p == ' ')
 +		p ++;
 +
 +	c = p;
 +	while ((mlen = strcspn(c, " ;,")) != 0) {
 +		p = c + mlen;
 +		t = *p;
 +		if (p - c > 0) {
 +			*p = '\0';
 +			if (fnmatch(c, ifname, FNM_CASEFOLD) == 0) {
 +				*p = t;
 +				return (1);
 +			}
 +			*p = t;
 +			c = p + strspn(p, " ;,");
 +		}
 +		else {
 +			c = p + strspn(p, " ;,");
 +		}
 +	}
 +
 +	return (match);
 +}
 +
 +/*
 + * This function iterates through our list of interfaces, identifying
 + * those that are to be displayed (ifp->display = 1).  For each interf-
 + * rface that we're displaying, we generate an appropriate position for
 + * it on the screen (ifp->if_ypos).
 + *
 + * This function is called any time a change is made to an interface's
 + * ``display'' state.
 + */
 +void
 +sort_interface_list(void)
 +{
 +	struct	if_stat	*ifp = NULL;
 +	u_int	y = STARTING_ROW;
 +	
 +	SLIST_FOREACH(ifp, &curlist, link) {
 +		if (matchline && !check_match(ifp->if_mib.ifdr_name))
 +			ifp->match = 0;
 +		else
 +			ifp->match = 1;
 +		if (ifp->display && ifp->match) {
 +			ifp->if_ypos = y;
 +			y += ROW_SPACING;
 +		}
 +	}
 +	
 +	needsort = 0;
 +	needclear = 1;
 +}
 +
 +static
 +unsigned int
 +getifnum(void)
 +{
 +	struct ifaddrs *ifaddrs = NULL;
 +	struct ifaddrs *ifa = NULL;
 +	int num = 0;
 +
 +	if (getifaddrs(&ifaddrs) == 0) {
 +	  for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
 +	    if (ifa->ifa_addr &&
 +		ifa->ifa_addr->sa_family == AF_LINK)
 +	      num++;
 +	  }
 +  
 +	  freeifaddrs(ifaddrs);
 +	}
 +
 +	return num;
 +}
 +
 +static void
 +getifmibdata(int row, struct ifdatareq *data)
 +{
 +	struct ifaddrs *ifaddrs = NULL;
 +	struct ifaddrs *ifa = NULL;
 +	int found = 0;
 +	int num = 0;
 +
 +	if (getifaddrs(&ifaddrs) != 0) {
 +	  IFSTAT_ERR(2, "getifmibdata() error getting interface data");
 +	  return;
 +	}
 +
 +	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
 +	  if (ifa->ifa_addr &&
 +	      ifa->ifa_addr->sa_family == AF_LINK) {
 +	    num++;
 +
 +	    /*
 +	     * expecting rows to start with 1 not 0, 
 +	     * see freebsd "man ifmib"
 +	     */
 +	    if (num == row) {
 +	      found = 1;
 +	      data->ifdr_data = *(struct if_data *)ifa->ifa_data;
 +	      strncpy(data->ifdr_name, ifa->ifa_name, IF_NAMESIZE);
 +	      break;
 +	    }
 +	  }
 +	}
 +
 +	freeifaddrs(ifaddrs);
 +
 +	if (!found) {
 +	  IFSTAT_ERR(2, "getifmibdata() error finding row");
 +	}
 +}
 +
 +int
 +cmdifstat(const char *cmd, const char *args)
 +{
 +	int	retval = 0;
 +
 +	retval = ifcmd(cmd, args);
 +	/* ifcmd() returns 1 on success */
 +	if (retval == 1) {
 +		showifstat();
 +		refresh();
 +		if (needclear) {
 +			werase(wnd);
 +			labelifstat();
 +			needclear = 0;
 +		}
 +	}
 +
 +	return (retval);
 +}
 +
 +void
 +ifstat_scale(char* args)
 +{
 +	cmdifstat("scale", args ? args : "");
 +}
 +
 +void
 +ifstat_pps(char* args)
 +{
 +	cmdifstat("pps", "");
 +}
 +
 +void
 +ifstat_match(char* args)
 +{
 +	cmdifstat("match", args ? args : "");
 +
 +	/*
 +	 * force erase after match command because it is possible for
 +	 * another command to be sent in the interval before the window
 +	 * finishes redrawing completely.  That stale data remains in window
 +	 * and never gets overwritten because there are fewer interfaces
 +	 * being drawn on screen.  Only an issue for match command because
 +	 * pps and scale don't change the number of interfaces being drawn.
 +	 */
 +	werase(wnd);
 +	labelifstat();
 +}
 Index: Makefile
 ===================================================================
 RCS file: /cvsroot/src/usr.bin/systat/Makefile,v
 retrieving revision 1.38
 diff -b -u -r1.38 Makefile
 --- Makefile	23 Jan 2016 21:22:50 -0000	1.38
 +++ Makefile	31 May 2016 15:09:13 -0000
 @@ -14,7 +14,7 @@
  SRCS=	bufcache.c cmds.c cmdtab.c disks.c df.c drvstats.c fetch.c \
  	globalcmds.c icmp.c iostat.c ip.c keyboard.c main.c mbufs.c \
  	netcmds.c netstat.c pigs.c ps.c swap.c tcp.c vmstat.c utmpentry.c \
 -	syscall.c
 +	ifcmds.c convtbl.c ifstat.c syscall.c
  DPADD=	${LIBCURSES} ${LIBTERMINFO} ${LIBM} ${LIBKVM}
  LDADD=	-lutil -lcurses -lterminfo -lm -lkvm
  BINGRP=	kmem
 Index: cmds.c
 ===================================================================
 RCS file: /cvsroot/src/usr.bin/systat/cmds.c,v
 retrieving revision 1.28
 diff -b -u -r1.28 cmds.c
 --- cmds.c	4 Nov 2004 07:18:47 -0000	1.28
 +++ cmds.c	31 May 2016 15:09:13 -0000
 @@ -152,3 +152,15 @@
  {
  	error("Showing %s, refresh every %d seconds.", curmode->c_name, naptime);
  }
 +
 +int
 +prefix(const char *s1, const char *s2)
 +{
 +
 +	while (*s1 == *s2) {
 +		if (*s1 == '\0')
 +			return (1);
 +		s1++, s2++;
 +	}
 +	return (*s1 == '\0');
 +}
 Index: cmdtab.c
 ===================================================================
 RCS file: /cvsroot/src/usr.bin/systat/cmdtab.c,v
 retrieving revision 1.24
 diff -b -u -r1.24 cmdtab.c
 --- cmdtab.c	6 Jan 2012 14:08:08 -0000	1.24
 +++ cmdtab.c	31 May 2016 15:09:13 -0000
 @@ -69,6 +69,13 @@
  	{ .c_name = NULL }
  };

 +struct command ifstat_commands[] = {
 +	{ "scale",	ifstat_scale,	"modify scale of display"},
 +	{ "pps",	ifstat_pps, 	"toggle packets per second display"},
 +	{ "match",	ifstat_match,   "display matching interfaces"},
 +	{ .c_name = NULL }
 +};
 +
  struct command	iostat_commands[] = {
  	{ "bars",	iostat_bars,	"show io stats as a bar graph"},
  	{ "numbers",	iostat_numbers,	"show io stats numerically"},
 @@ -159,6 +166,9 @@
  	{ "df",         showdf,  	fetchdf,	labeldf,
  	  initdf,	opendf,		closedf,	df_commands,
  	  CF_LOADAV },
 +	{ "ifstat",	showifstat,	fetchifstat,	labelifstat,
 +	  initifstat,	openifstat,	closeifstat,	ifstat_commands,
 +	  CF_LOADAV },
  	{ "inet.icmp",	showicmp,	fetchicmp,	labelicmp,
  	  initicmp,	openicmp,	closeicmp,	icmp_commands,
  	  CF_LOADAV },
 Index: extern.h
 ===================================================================
 RCS file: /cvsroot/src/usr.bin/systat/extern.h,v
 retrieving revision 1.44
 diff -b -u -r1.44 extern.h
 --- extern.h	23 Aug 2015 18:33:15 -0000	1.44
 +++ extern.h	31 May 2016 15:09:13 -0000
 @@ -75,6 +75,7 @@
  void	 closebufcache(WINDOW *);
  void	 closedf(WINDOW *);
  void	 closeicmp(WINDOW *);
 +void	 closeifstat(WINDOW *);
  void	 closeiostat(WINDOW *);
  void	 closeip(WINDOW *);
  void	 closevmstat(WINDOW *);
 @@ -84,6 +85,7 @@
  void	 closepigs(WINDOW *);
  void	 closeswap(WINDOW *);
  void	 closetcp(WINDOW *);
 +int	 cmdifstat(const char *, const char *);
  void	 command(char *);
  void	 df_all(char *);
  void	 df_some(char *);
 @@ -97,6 +99,7 @@
  void	 fetchbufcache(void);
  void	 fetchdf(void);
  void	 fetchicmp(void);
 +void	 fetchifstat(void);
  void	 fetchiostat(void);
  void	 fetchip(void);
  void	 fetchvmstat(void);
 @@ -116,9 +119,14 @@
  void	 icmp_run(char *);
  void	 icmp_time(char *);
  void	 icmp_zero(char *);
 +int	 ifcmd(const char *cmd, const char *args);
 +void	 ifstat_match(char*);
 +void	 ifstat_pps(char*);
 +void	 ifstat_scale(char*);
  int	 initbufcache(void);
  int	 initdf(void);
  int	 initicmp(void);
 +int	 initifstat(void);
  int	 initiostat(void);
  int	 initip(void);
  int	 initvmstat(void);
 @@ -142,6 +150,7 @@
  void	 labelbufcache(void);
  void	 labeldf(void);
  void	 labelicmp(void);
 +void	 labelifstat(void);
  void	 labeliostat(void);
  void	 labelip(void);
  void	 labelvmstat(void);
 @@ -167,6 +176,7 @@
  WINDOW	*openbufcache(void);
  WINDOW	*opendf(void);
  WINDOW	*openicmp(void);
 +WINDOW	*openifstat(void);
  WINDOW	*openiostat(void);
  WINDOW	*openip(void);
  WINDOW	*openvmstat(void);
 @@ -176,11 +186,13 @@
  WINDOW	*openpigs(void);
  WINDOW	*openswap(void);
  WINDOW	*opentcp(void);
 +int	 prefix(const char *, const char *);
  void	 ps_user(char *);
  void	 redraw(void);
  void	 showbufcache(void);
  void	 showdf(void);
  void	 showicmp(void);
 +void	 showifstat(void);
  void	 showiostat(void);
  void	 showip(void);
  void	 showvmstat(void);
 Index: systat.1
 ===================================================================
 RCS file: /cvsroot/src/usr.bin/systat/systat.1,v
 retrieving revision 1.44
 diff -b -u -r1.44 systat.1
 --- systat.1	12 Mar 2016 02:39:01 -0000	1.44
 +++ systat.1	31 May 2016 15:09:13 -0000
 @@ -73,7 +73,7 @@
  .Xr iostat 8 ) ,
  virtual memory statistics (a la
  .Xr vmstat 1 ) ,
 -network ``mbuf'' utilization, and network connections (a la
 +network ``mbuf'' utilization, network 'ifstat' traffic, and network connections (a la
  .Xr netstat 1 ) .
  .Pp
  Input is interpreted at two different levels.
 @@ -115,6 +115,7 @@
  .Ic all ,
  .Ic bufcache ,
  .Ic df ,
 +.Ic ifstat ,
  .Ic inet.icmp ,
  .Ic inet.ip ,
  .Ic inet.tcp ,
 @@ -226,6 +227,54 @@
  .It Cm some
  Suppress information about procfs, kernfs and null-mounts (default).
  .El
 +.It Ic ifstat
 +Display the network traffic going through active interfaces on the
 +system.
 +Idle interfaces will not be displayed until they receive some
 +traffic.
 +.Pp
 +For each interface being displayed, the current, peak and total
 +statistics are displayed for incoming and outgoing traffic.
 +By default,
 +the
 +.Ic ifstat
 +display will automatically scale the units being used so that they are
 +in a human-readable format.
 +The scaling units used for the current and
 +peak
 +traffic columns can be altered by the
 +.Ic scale
 +command.
 +.Bl -tag -width ".Cm scale Op Ar units"
 +.It Cm scale Op Ar units
 +Modify the scale used to display the current and peak traffic over all
 +interfaces.
 +The following units are recognised: kbit, kbyte, mbit,
 +mbyte, gbit, gbyte and auto.
 +.It Cm pps
 +Show statistics in packets per second instead of bytes/bits per second.
 +A subsequent call of
 +.Ic pps
 +switches this mode off.
 +.It Cm match Op Ar patterns
 +Display only interfaces that match pattern provided as an argument.
 +Patterns should be in shell syntax separated by whitespaces or commas.
 +If this command is called without arguments then all interfaces are displayed.
 +For example:
 +.Pp
 +.Dl match re0, bge1
 +.Pp
 +This will display re0 and bge1 interfaces.
 +.Pp
 +.Dl match re*, bge*, lo0
 +.Pp
 +This will display all
 +.Ic re
 +interfaces, all
 +.Ic bge
 +interfaces and the loopback interface.
 +.El
 +.Pp
  .It Ic inet.icmp
  Display ICMP statistics.
  .It Ic inet.ip

From: scole_mail@gmx.com
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/51204: add ifstat to systat command
Date: Tue, 31 May 2016 11:29:44 -0400

 One more time...

 Thanks

Responsible-Changed-From-To: bin-bug-people->scole
Responsible-Changed-By: scole@NetBSD.org
Responsible-Changed-When: Tue, 02 Aug 2016 09:38:50 -0400
Responsible-Changed-Why:
Take


From: "Sean Cole" <scole@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/51204 CVS commit: src/usr.bin/systat
Date: Tue, 2 Aug 2016 15:56:09 +0000

 Module Name:	src
 Committed By:	scole
 Date:		Tue Aug  2 15:56:09 UTC 2016

 Modified Files:
 	src/usr.bin/systat: Makefile cmds.c cmdtab.c extern.h systat.1
 Added Files:
 	src/usr.bin/systat: convtbl.c convtbl.h ifcmds.c ifstat.c

 Log Message:
 PR bin/51204

 Add ifstat command to systat.

 Imported from FreeBSD


 To generate a diff of this commit:
 cvs rdiff -u -r1.38 -r1.39 src/usr.bin/systat/Makefile
 cvs rdiff -u -r1.28 -r1.29 src/usr.bin/systat/cmds.c
 cvs rdiff -u -r1.24 -r1.25 src/usr.bin/systat/cmdtab.c
 cvs rdiff -u -r0 -r1.1 src/usr.bin/systat/convtbl.c \
     src/usr.bin/systat/convtbl.h src/usr.bin/systat/ifcmds.c \
     src/usr.bin/systat/ifstat.c
 cvs rdiff -u -r1.44 -r1.45 src/usr.bin/systat/extern.h \
     src/usr.bin/systat/systat.1

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: open->closed
State-Changed-By: scole@NetBSD.org
State-Changed-When: Wed, 10 Aug 2016 08:08:11 -0400
State-Changed-Why:
Checked into current


>Unformatted:

 Fixed Synopsis per submitter's intention -- wiz.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.