1 /* 2 * Copyright (c) 2003, Trent Nelson, <trent@arpa.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTIFSTAT_ERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <sys/sysctl.h> 34 #include <sys/time.h> 35 #include <sys/queue.h> 36 #include <net/if.h> 37 #include <net/if_mib.h> 38 #include <net/if_types.h> /* For IFT_ETHER */ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <float.h> 45 #include <err.h> 46 47 #include "systat.h" 48 #include "extern.h" 49 #include "mode.h" 50 #include "convtbl.h" 51 52 /* Column numbers */ 53 54 #define C1 0 /* 0-19 */ 55 #define C2 20 /* 20-39 */ 56 #define C3 40 /* 40-59 */ 57 #define C4 60 /* 60-80 */ 58 #define C5 80 /* Used for label positioning. */ 59 60 static const int col0 = 0; 61 static const int col1 = C1; 62 static const int col2 = C2; 63 static const int col3 = C3; 64 static const int col4 = C4; 65 static const int col5 = C5; 66 67 68 SLIST_HEAD(, if_stat) curlist; 69 SLIST_HEAD(, if_stat_disp) displist; 70 71 struct if_stat { 72 SLIST_ENTRY(if_stat) link; 73 char if_name[IF_NAMESIZE]; 74 struct ifmibdata if_mib; 75 struct timeval tv; 76 struct timeval tv_lastchanged; 77 u_long if_in_curtraffic; 78 u_long if_out_curtraffic; 79 u_long if_in_traffic_peak; 80 u_long if_out_traffic_peak; 81 u_int if_row; /* Index into ifmib sysctl */ 82 u_int if_ypos; /* 0 if not being displayed */ 83 u_int display; 84 }; 85 86 extern u_int curscale; 87 88 static void right_align_string(const struct if_stat *); 89 static void getifmibdata(const int, struct ifmibdata *); 90 static void sort_interface_list(void); 91 static u_int getifnum(void); 92 93 #define IFSTAT_ERR(n, s) do { \ 94 putchar(''); \ 95 closeifstat(wnd); \ 96 err((n), (s)); \ 97 } while (0) 98 99 #define TOPLINE 3 100 #define TOPLABEL \ 101 " Interface Traffic Peak Total" 102 103 #define STARTING_ROW (TOPLINE + 1) 104 #define ROW_SPACING (3) 105 106 #define CLEAR_LINE(y, x) do { \ 107 wmove(wnd, y, x); \ 108 wclrtoeol(wnd); \ 109 } while (0) 110 111 #define IN_col2 (ifp->if_in_curtraffic) 112 #define OUT_col2 (ifp->if_out_curtraffic) 113 #define IN_col3 (ifp->if_in_traffic_peak) 114 #define OUT_col3 (ifp->if_out_traffic_peak) 115 #define IN_col4 (ifp->if_mib.ifmd_data.ifi_ibytes) 116 #define OUT_col4 (ifp->if_mib.ifmd_data.ifi_obytes) 117 118 #define EMPTY_COLUMN " " 119 #define CLEAR_COLUMN(y, x) mvprintw((y), (x), "%20s", EMPTY_COLUMN); 120 121 #define DOPUTRATE(c, r, d) do { \ 122 CLEAR_COLUMN(r, c); \ 123 mvprintw(r, (c), "%10.3f %s%s ", \ 124 convert(d##_##c, curscale), \ 125 get_string(d##_##c, curscale), \ 126 "/s"); \ 127 } while (0) 128 129 #define DOPUTTOTAL(c, r, d) do { \ 130 CLEAR_COLUMN((r), (c)); \ 131 mvprintw((r), (c), "%12.3f %s ", \ 132 convert(d##_##c, SC_AUTO), \ 133 get_string(d##_##c, SC_AUTO)); \ 134 } while (0) 135 136 #define PUTRATE(c, r) do { \ 137 DOPUTRATE(c, (r), IN); \ 138 DOPUTRATE(c, (r)+1, OUT); \ 139 } while (0) 140 141 #define PUTTOTAL(c, r) do { \ 142 DOPUTTOTAL(c, (r), IN); \ 143 DOPUTTOTAL(c, (r)+1, OUT); \ 144 } while (0) 145 146 #define PUTNAME(p) do { \ 147 mvprintw(p->if_ypos, 0, "%s", p->if_name); \ 148 mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in"); \ 149 mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out"); \ 150 } while (0) 151 152 153 WINDOW * 154 openifstat(void) 155 { 156 return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); 157 } 158 159 void 160 closeifstat(WINDOW *w) 161 { 162 struct if_stat *node = NULL; 163 164 while (!SLIST_EMPTY(&curlist)) { 165 node = SLIST_FIRST(&curlist); 166 SLIST_REMOVE_HEAD(&curlist, link); 167 free(node); 168 } 169 170 if (w != NULL) { 171 wclear(w); 172 wrefresh(w); 173 delwin(w); 174 } 175 176 return; 177 } 178 179 180 void 181 labelifstat(void) 182 { 183 184 wmove(wnd, TOPLINE, 0); 185 wclrtoeol(wnd); 186 mvprintw(TOPLINE, 0, "%s", TOPLABEL); 187 188 return; 189 } 190 191 void 192 showifstat(void) 193 { 194 struct if_stat *ifp = NULL; 195 SLIST_FOREACH(ifp, &curlist, link) { 196 if (ifp->display == 0) 197 continue; 198 PUTNAME(ifp); 199 PUTRATE(col2, ifp->if_ypos); 200 PUTRATE(col3, ifp->if_ypos); 201 PUTTOTAL(col4, ifp->if_ypos); 202 } 203 204 return; 205 } 206 207 int 208 initifstat(void) 209 { 210 struct if_stat *p = NULL; 211 u_int n = 0, i = 0; 212 213 n = getifnum(); 214 if (n <= 0) 215 return -1; 216 217 SLIST_INIT(&curlist); 218 219 for (i = 0; i < n; i++) { 220 p = (struct if_stat *)malloc(sizeof(struct if_stat)); 221 if (p == NULL) 222 IFSTAT_ERR(1, "out of memory"); 223 memset((void *)p, 0, sizeof(struct if_stat)); 224 SLIST_INSERT_HEAD(&curlist, p, link); 225 p->if_row = i+1; 226 getifmibdata(p->if_row, &p->if_mib); 227 right_align_string(p); 228 229 /* 230 * Initially, we only display interfaces that have 231 * received some traffic. 232 */ 233 if (p->if_mib.ifmd_data.ifi_ibytes != 0) 234 p->display = 1; 235 } 236 237 sort_interface_list(); 238 239 return 1; 240 } 241 242 void 243 fetchifstat(void) 244 { 245 struct if_stat *ifp = NULL; 246 struct timeval tv, new_tv, old_tv; 247 double elapsed = 0.0; 248 u_int new_inb, new_outb, old_inb, old_outb = 0; 249 u_int we_need_to_sort_interface_list = 0; 250 251 SLIST_FOREACH(ifp, &curlist, link) { 252 /* 253 * Grab a copy of the old input/output values before we 254 * call getifmibdata(). 255 */ 256 old_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 257 old_outb = ifp->if_mib.ifmd_data.ifi_obytes; 258 ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange; 259 260 if (gettimeofday(&new_tv, (struct timezone *)0) != 0) 261 IFSTAT_ERR(2, "error getting time of day"); 262 (void)getifmibdata(ifp->if_row, &ifp->if_mib); 263 264 265 new_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 266 new_outb = ifp->if_mib.ifmd_data.ifi_obytes; 267 268 /* Display interface if it's received some traffic. */ 269 if (new_inb > 0 && old_inb == 0) { 270 ifp->display = 1; 271 we_need_to_sort_interface_list++; 272 } 273 274 /* 275 * The rest is pretty trivial. Calculate the new values 276 * for our current traffic rates, and while we're there, 277 * see if we have new peak rates. 278 */ 279 old_tv = ifp->tv; 280 timersub(&new_tv, &old_tv, &tv); 281 elapsed = tv.tv_sec + (tv.tv_usec * 1e-6); 282 283 ifp->if_in_curtraffic = new_inb - old_inb; 284 ifp->if_out_curtraffic = new_outb - old_outb; 285 286 /* 287 * Rather than divide by the time specified on the comm- 288 * and line, we divide by ``elapsed'' as this is likely 289 * to be more accurate. 290 */ 291 ifp->if_in_curtraffic /= elapsed; 292 ifp->if_out_curtraffic /= elapsed; 293 294 if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak) 295 ifp->if_in_traffic_peak = ifp->if_in_curtraffic; 296 297 if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak) 298 ifp->if_out_traffic_peak = ifp->if_out_curtraffic; 299 300 ifp->tv.tv_sec = new_tv.tv_sec; 301 ifp->tv.tv_usec = new_tv.tv_usec; 302 303 } 304 305 if (we_need_to_sort_interface_list) 306 sort_interface_list(); 307 308 return; 309 } 310 311 /* 312 * We want to right justify our interface names against the first column 313 * (first sixteen or so characters), so we need to do some alignment. 314 */ 315 static void 316 right_align_string(const struct if_stat *ifp) 317 { 318 int str_len = 0, pad_len = 0; 319 char *newstr = NULL, *ptr = NULL; 320 321 if (ifp == NULL || ifp->if_mib.ifmd_name == NULL) 322 return; 323 else { 324 /* string length + '\0' */ 325 str_len = strlen(ifp->if_mib.ifmd_name)+1; 326 pad_len = IF_NAMESIZE-(str_len); 327 328 newstr = (char *)ifp->if_name; 329 ptr = newstr + pad_len; 330 (void)memset((void *)newstr, (int)' ', IF_NAMESIZE); 331 (void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name, 332 str_len); 333 } 334 335 return; 336 } 337 338 /* 339 * This function iterates through our list of interfaces, identifying 340 * those that are to be displayed (ifp->display = 1). For each interf- 341 * rface that we're displaying, we generate an appropriate position for 342 * it on the screen (ifp->if_ypos). 343 * 344 * This function is called any time a change is made to an interface's 345 * ``display'' state. 346 */ 347 void 348 sort_interface_list(void) 349 { 350 struct if_stat *ifp = NULL; 351 u_int y = 0; 352 353 y = STARTING_ROW; 354 SLIST_FOREACH(ifp, &curlist, link) { 355 if (ifp->display) { 356 ifp->if_ypos = y; 357 y += ROW_SPACING; 358 } 359 } 360 } 361 362 static 363 unsigned int 364 getifnum(void) 365 { 366 u_int data = 0; 367 size_t datalen = 0; 368 static int name[] = { CTL_NET, 369 PF_LINK, 370 NETLINK_GENERIC, 371 IFMIB_SYSTEM, 372 IFMIB_IFCOUNT }; 373 374 datalen = sizeof(data); 375 if (sysctl(name, 5, (void *)&data, (size_t *)&datalen, (void *)NULL, 376 (size_t)0) != 0) 377 IFSTAT_ERR(1, "sysctl error"); 378 return data; 379 } 380 381 static void 382 getifmibdata(int row, struct ifmibdata *data) 383 { 384 size_t datalen = 0; 385 static int name[] = { CTL_NET, 386 PF_LINK, 387 NETLINK_GENERIC, 388 IFMIB_IFDATA, 389 0, 390 IFDATA_GENERAL }; 391 datalen = sizeof(*data); 392 name[4] = row; 393 394 if (sysctl(name, 6, (void *)data, (size_t *)&datalen, (void *)NULL, 395 (size_t)0) != 0) 396 IFSTAT_ERR(2, "sysctl error getting interface data"); 397 } 398 399 int 400 cmdifstat(const char *cmd, const char *args) 401 { 402 int retval = 0; 403 404 retval = ifcmd(cmd, args); 405 /* ifcmd() returns 1 on success */ 406 if (retval == 1) { 407 showifstat(); 408 refresh(); 409 } 410 411 return retval; 412 } 413