diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile index 6740256b85f9..ecdee19f4a84 100644 --- a/usr.bin/systat/Makefile +++ b/usr.bin/systat/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.38 2016/01/23 21:22:50 christos Exp $ +# $NetBSD: Makefile,v 1.39 2016/08/02 15:56:09 scole Exp $ # @(#)Makefile 8.1 (Berkeley) 6/6/93 .include @@ -11,10 +11,10 @@ PROG= systat CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/vmstat -DSUPPORT_UTMP -DSUPPORT_UTMPX \ -I${NETBSDSRCDIR}/usr.bin/who -D_KMEMUSER CWARNFLAGS+= -Wno-format-y2k -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 +SRCS= bufcache.c cmds.c cmdtab.c convtbl.c disks.c df.c drvstats.c \ + fetch.c globalcmds.c icmp.c ifcmds.c ifstat.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 DPADD= ${LIBCURSES} ${LIBTERMINFO} ${LIBM} ${LIBKVM} LDADD= -lutil -lcurses -lterminfo -lm -lkvm BINGRP= kmem diff --git a/usr.bin/systat/cmds.c b/usr.bin/systat/cmds.c index bda38d9a7725..93b9b04b5189 100644 --- a/usr.bin/systat/cmds.c +++ b/usr.bin/systat/cmds.c @@ -1,4 +1,4 @@ -/* $NetBSD: cmds.c,v 1.28 2004/11/04 07:18:47 dsl Exp $ */ +/* $NetBSD: cmds.c,v 1.29 2016/08/02 15:56:09 scole Exp $ */ /*- * Copyright (c) 1980, 1992, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/29/95"; #endif -__RCSID("$NetBSD: cmds.c,v 1.28 2004/11/04 07:18:47 dsl Exp $"); +__RCSID("$NetBSD: cmds.c,v 1.29 2016/08/02 15:56:09 scole Exp $"); #endif /* not lint */ #include @@ -152,3 +152,15 @@ status(void) { 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'); +} diff --git a/usr.bin/systat/cmdtab.c b/usr.bin/systat/cmdtab.c index e3c90d0be907..ab8f78880a5c 100644 --- a/usr.bin/systat/cmdtab.c +++ b/usr.bin/systat/cmdtab.c @@ -1,4 +1,4 @@ -/* $NetBSD: cmdtab.c,v 1.24 2012/01/06 14:08:08 drochner Exp $ */ +/* $NetBSD: cmdtab.c,v 1.25 2016/08/02 15:56:09 scole Exp $ */ /*- * Copyright (c) 1980, 1992, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93"; #endif -__RCSID("$NetBSD: cmdtab.c,v 1.24 2012/01/06 14:08:08 drochner Exp $"); +__RCSID("$NetBSD: cmdtab.c,v 1.25 2016/08/02 15:56:09 scole Exp $"); #endif /* not lint */ #include "systat.h" @@ -69,6 +69,13 @@ struct command icmp_commands[] = { { .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 @@ struct mode modes[] = { { "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 }, diff --git a/usr.bin/systat/convtbl.c b/usr.bin/systat/convtbl.c new file mode 100644 index 000000000000..1c6c60ef3947 --- /dev/null +++ b/usr.bin/systat/convtbl.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2003, Trent Nelson, . + * 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 +#ifndef lint +__RCSID("$NetBSD: convtbl.c,v 1.1 2016/08/02 15:56:09 scole Exp $"); +#endif /* not lint */ + +#include + +#include +#include + +#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); +} diff --git a/usr.bin/systat/convtbl.h b/usr.bin/systat/convtbl.h new file mode 100644 index 000000000000..7bc2c4437b08 --- /dev/null +++ b/usr.bin/systat/convtbl.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2003, Trent Nelson, . + * 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 +#include + +/* + * 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_ */ diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h index 2b40b0a3248b..93a9dd25d9f7 100644 --- a/usr.bin/systat/extern.h +++ b/usr.bin/systat/extern.h @@ -1,4 +1,4 @@ -/* $NetBSD: extern.h,v 1.44 2015/08/23 18:33:15 mrg Exp $ */ +/* $NetBSD: extern.h,v 1.45 2016/08/02 15:56:09 scole Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -75,6 +75,7 @@ int checkport6(struct in6pcb *); 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 closenetstat(WINDOW *); 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 clearerror(void); 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_boot(char *); 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 @@ ssize_t kvm_ckread(const void *, void *, size_t, const char *); 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 @@ void nlisterr(struct nlist []) __dead; 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 *opennetstat(void); 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); diff --git a/usr.bin/systat/ifcmds.c b/usr.bin/systat/ifcmds.c new file mode 100644 index 000000000000..e90b90c0100b --- /dev/null +++ b/usr.bin/systat/ifcmds.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2003, Trent Nelson, . + * 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 +#ifndef lint +__RCSID("$NetBSD: ifcmds.c,v 1.1 2016/08/02 15:56:09 scole Exp $"); +#endif /* not lint */ + +#include + +#include +#include + +#include "systat.h" +#include "extern.h" +#include "convtbl.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); +} diff --git a/usr.bin/systat/ifstat.c b/usr.bin/systat/ifstat.c new file mode 100644 index 000000000000..96227df073bf --- /dev/null +++ b/usr.bin/systat/ifstat.c @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2003, Trent Nelson, . + * 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 +#ifndef lint +__RCSID("$NetBSD: ifstat.c,v 1.1 2016/08/02 15:56:09 scole Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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(); +} diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1 index eaaef44aa21a..730d9cacb392 100644 --- a/usr.bin/systat/systat.1 +++ b/usr.bin/systat/systat.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: systat.1,v 1.44 2016/03/12 02:39:01 dholland Exp $ +.\" $NetBSD: systat.1,v 1.45 2016/08/02 15:56:09 scole Exp $ .\" .\" Copyright (c) 1985, 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -73,7 +73,7 @@ statistics (a la .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 @@ argument expects to be one of: .Ic all , .Ic bufcache , .Ic df , +.Ic ifstat , .Ic inet.icmp , .Ic inet.ip , .Ic inet.tcp , @@ -226,6 +227,54 @@ kernfs, procfs and null-mounts. .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