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 <unistd.h> 43 #include <float.h> 44 #include <err.h> 45 46 #include "systat.h" 47 #include "extern.h" 48 #include "mode.h" 49 #include "convtbl.h" 50 51 /* Column numbers */ 52 53 #define C1 0 /* 0-19 */ 54 #define C2 20 /* 20-39 */ 55 #define C3 40 /* 40-59 */ 56 #define C4 60 /* 60-80 */ 57 #define C5 80 /* Used for label positioning. */ 58 59 static int col0 = 0; 60 static int col1 = C1; 61 static int col2 = C2; 62 static int col3 = C3; 63 static int col4 = C4; 64 static int col5 = C5; 65 66 67 SLIST_HEAD(, if_stat) curlist; 68 SLIST_HEAD(, if_stat_disp) displist; 69 70 struct if_stat { 71 SLIST_ENTRY(if_stat) link; 72 char if_name[IF_NAMESIZE]; 73 struct ifmibdata if_mib; 74 struct timeval tv; 75 struct timeval tv_lastchanged; 76 u_long if_in_curtraffic; 77 u_long if_out_curtraffic; 78 u_long if_in_traffic_peak; 79 u_long if_out_traffic_peak; 80 u_int if_row; /* Index into ifmib sysctl */ 81 u_int if_ypos; /* 0 if not being displayed */ 82 u_int display; 83 }; 84 85 extern u_int curscale; 86 87 static void right_align_string(const struct if_stat *); 88 static void getifmibdata(const int, struct ifmibdata *); 89 static void sort_interface_list(void); 90 static u_int getifnum(void); 91 92 #define IFSTAT_ERR(n, s) do { \ 93 putchar(''); \ 94 closeifstat(wnd); \ 95 err((n), (s)); \ 96 } while (0) 97 98 #define STARTING_ROW (8) 99 #define ROW_SPACING (3) 100 101 #define TOPLINE 5 102 #define TOPLABEL \ 103 " Interface Traffic Peak Total" 104 105 #define CLEAR_LINE(y, x) do { \ 106 wmove(wnd, y, x); \ 107 wclrtoeol(wnd); \ 108 } while (0) 109 110 #define IN_col2 (ifp->if_in_curtraffic) 111 #define OUT_col2 (ifp->if_out_curtraffic) 112 #define IN_col3 (ifp->if_in_traffic_peak) 113 #define OUT_col3 (ifp->if_out_traffic_peak) 114 #define IN_col4 (ifp->if_mib.ifmd_data.ifi_ibytes) 115 #define OUT_col4 (ifp->if_mib.ifmd_data.ifi_obytes) 116 117 #define EMPTY_COLUMN " " 118 #define CLEAR_COLUMN(y, x) mvprintw((y), (x), "%20s", EMPTY_COLUMN); 119 120 #define DOPUTRATE(c, r, d) do { \ 121 CLEAR_COLUMN(r, c); \ 122 mvprintw(r, (c), "%10.3f %s%s ", \ 123 convert(d##_##c, curscale), \ 124 get_string(d##_##c, curscale), \ 125 "/s"); \ 126 } while (0) 127 128 #define DOPUTTOTAL(c, r, d) do { \ 129 CLEAR_COLUMN((r), (c)); \ 130 mvprintw((r), (c), "%12.3f %s ", \ 131 convert(d##_##c, SC_AUTO), \ 132 get_string(d##_##c, SC_AUTO)); \ 133 } while (0) 134 135 #define PUTRATE(c, r) do { \ 136 DOPUTRATE(c, (r), IN); \ 137 DOPUTRATE(c, (r)+1, OUT); \ 138 } while (0) 139 140 #define PUTTOTAL(c, r) do { \ 141 DOPUTTOTAL(c, (r), IN); \ 142 DOPUTTOTAL(c, (r)+1, OUT); \ 143 } while (0) 144 145 #define PUTNAME(p) do { \ 146 mvprintw(p->if_ypos, 0, "%s", p->if_name); \ 147 mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in"); \ 148 mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out"); \ 149 } while (0) 150 151 152 WINDOW * 153 openifstat(void) 154 { 155 return (subwin(stdscr, LINES-1-5, 0, 5, 0)); 156 } 157 158 void 159 closeifstat(WINDOW *w) 160 { 161 struct if_stat *node = NULL; 162 163 while (!SLIST_EMPTY(&curlist)) { 164 node = SLIST_FIRST(&curlist); 165 SLIST_REMOVE_HEAD(&curlist, link); 166 free(node); 167 } 168 169 if (w != NULL) { 170 wclear(w); 171 wrefresh(w); 172 delwin(w); 173 } 174 175 return; 176 } 177 178 179 void 180 labelifstat(void) 181 { 182 183 wmove(wnd, TOPLINE, 0); 184 wclrtoeol(wnd); 185 mvprintw(TOPLINE, 0, "%s", TOPLABEL); 186 187 return; 188 } 189 190 void 191 showifstat(void) 192 { 193 struct if_stat *ifp = NULL; 194 SLIST_FOREACH(ifp, &curlist, link) { 195 if (ifp->display == 0) 196 continue; 197 PUTNAME(ifp); 198 PUTRATE(col2, ifp->if_ypos); 199 PUTRATE(col3, ifp->if_ypos); 200 PUTTOTAL(col4, ifp->if_ypos); 201 } 202 203 return; 204 } 205 206 int 207 initifstat(void) 208 { 209 struct if_stat *p = NULL; 210 u_int n = 0, i = 0; 211 212 n = getifnum(); 213 if (n <= 0) 214 return -1; 215 216 SLIST_INIT(&curlist); 217 218 for (i = 0; i < n; i++) { 219 p = (struct if_stat *)malloc(sizeof(struct if_stat)); 220 if (p == NULL) 221 IFSTAT_ERR(1, "out of memory"); 222 memset((void *)p, 0, sizeof(struct if_stat)); 223 SLIST_INSERT_HEAD(&curlist, p, link); 224 p->if_row = i+1; 225 getifmibdata(p->if_row, &p->if_mib); 226 right_align_string(p); 227 228 /* 229 * Initially, we only display interfaces that have 230 * received some traffic. 231 */ 232 if (p->if_mib.ifmd_data.ifi_ibytes != 0) 233 p->display = 1; 234 } 235 236 sort_interface_list(); 237 238 return 1; 239 } 240 241 void 242 fetchifstat(void) 243 { 244 struct if_stat *ifp = NULL; 245 struct timeval tv, new_tv, old_tv; 246 double elapsed = 0.0; 247 u_int new_inb, new_outb, old_inb, old_outb = 0; 248 u_int error = 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 error = gettimeofday(&new_tv, (struct timezone *)0); 261 if (error) 262 IFSTAT_ERR(2, "error getting time of day"); 263 (void)getifmibdata(ifp->if_row, &ifp->if_mib); 264 265 266 new_inb = ifp->if_mib.ifmd_data.ifi_ibytes; 267 new_outb = ifp->if_mib.ifmd_data.ifi_obytes; 268 269 /* Display interface if it's received some traffic. */ 270 if (new_inb > 0 && old_inb == 0) { 271 ifp->display = 1; 272 we_need_to_sort_interface_list++; 273 } 274 275 /* 276 * The rest is pretty trivial. Calculate the new values 277 * for our current traffic rates, and while we're there, 278 * see if we have new peak rates. 279 */ 280 old_tv = ifp->tv; 281 timersub(&new_tv, &old_tv, &tv); 282 elapsed = tv.tv_sec + (tv.tv_usec * 1e-6); 283 284 ifp->if_in_curtraffic = new_inb - old_inb; 285 ifp->if_out_curtraffic = new_outb - old_outb; 286 287 /* 288 * Rather than divide by the time specified on the comm- 289 * and line, we divide by ``elapsed'' as this is likely 290 * to be more accurate. 291 */ 292 ifp->if_in_curtraffic /= elapsed; 293 ifp->if_out_curtraffic /= elapsed; 294 295 if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak) 296 ifp->if_in_traffic_peak = ifp->if_in_curtraffic; 297 298 if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak) 299 ifp->if_out_traffic_peak = ifp->if_out_curtraffic; 300 301 ifp->tv.tv_sec = new_tv.tv_sec; 302 ifp->tv.tv_usec = new_tv.tv_usec; 303 304 } 305 306 if (we_need_to_sort_interface_list) 307 sort_interface_list(); 308 309 return; 310 } 311 312 /* 313 * We want to right justify our interface names against the first column 314 * (first sixteen or so characters), so we need to do some alignment. 315 */ 316 static void 317 right_align_string(const struct if_stat *ifp) 318 { 319 int str_len = 0, pad_len = 0; 320 char *newstr = NULL, *ptr = NULL; 321 322 if (ifp == NULL || ifp->if_mib.ifmd_name == NULL) 323 return; 324 else { 325 /* string length + '\0' */ 326 str_len = strlen(ifp->if_mib.ifmd_name)+1; 327 pad_len = IF_NAMESIZE-(str_len); 328 329 newstr = (char *)ifp->if_name; 330 ptr = newstr + pad_len; 331 (void)memset((void *)newstr, (int)' ', IF_NAMESIZE); 332 (void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name, 333 str_len); 334 } 335 336 return; 337 } 338 339 /* 340 * This function iterates through our list of interfaces, identifying 341 * those that are to be displayed (ifp->display = 1). For each interf- 342 * rface that we're displaying, we generate an appropriate position for 343 * it on the screen (ifp->if_ypos). 344 * 345 * This function is called any time a change is made to an interface's 346 * ``display'' state. 347 */ 348 void 349 sort_interface_list(void) 350 { 351 struct if_stat *ifp = NULL; 352 u_int y = 0; 353 354 y = STARTING_ROW; 355 SLIST_FOREACH(ifp, &curlist, link) { 356 if (ifp->display) { 357 ifp->if_ypos = y; 358 y += ROW_SPACING; 359 } 360 } 361 } 362 363 static 364 unsigned int 365 getifnum(void) 366 { 367 int error = 0; 368 u_int data = 0; 369 size_t datalen = 0; 370 static int name[] = { CTL_NET, 371 PF_LINK, 372 NETLINK_GENERIC, 373 IFMIB_SYSTEM, 374 IFMIB_IFCOUNT }; 375 376 datalen = sizeof(data); 377 error = sysctl(name, 378 5, 379 (void *)&data, 380 (size_t *)&datalen, 381 (void *)NULL, 382 (size_t)0); 383 if (error) 384 IFSTAT_ERR(1, "sysctl error"); 385 return data; 386 } 387 388 static void 389 getifmibdata(int row, struct ifmibdata *data) 390 { 391 int error = 0; 392 size_t datalen = 0; 393 static int name[] = { CTL_NET, 394 PF_LINK, 395 NETLINK_GENERIC, 396 IFMIB_IFDATA, 397 0, 398 IFDATA_GENERAL }; 399 datalen = sizeof(*data); 400 name[4] = row; 401 402 error = sysctl(name, 403 6, 404 (void *)data, 405 (size_t *)&datalen, 406 (void *)NULL, 407 (size_t)0); 408 if (error) 409 IFSTAT_ERR(2, "sysctl error getting interface data"); 410 } 411 412 int 413 cmdifstat(const char *cmd, const char *args) 414 { 415 int retval = 0; 416 417 retval = ifcmd(cmd, args); 418 /* ifcmd() returns 1 on success */ 419 if (retval == 1) { 420 showifstat(); 421 refresh(); 422 } 423 424 return retval; 425 } 426