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