1 /* 2 * Copyright (c) 2008, The Android Open Source Project 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 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google, Inc. nor the names of its contributors 15 * may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <unistd.h> 33 #include <stdlib.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <fcntl.h> 39 #include <sys/ioctl.h> 40 #include <sys/socket.h> 41 #include <net/if.h> 42 43 #define PROC_NET_DEV "/proc/net/dev" 44 45 #define MAX_IF 8 /* max interfaces we can handle */ 46 47 #ifndef PAGE_SIZE 48 # define PAGE_SIZE 4096 49 #endif 50 51 #define _STR(s) #s 52 #define STR(s) _STR(s) 53 54 struct if_stats { 55 char name[IFNAMSIZ]; 56 57 unsigned int mtu; 58 59 unsigned int rx_bytes; 60 unsigned int rx_packets; 61 unsigned int rx_errors; 62 unsigned int rx_dropped; 63 64 unsigned int tx_bytes; 65 unsigned int tx_packets; 66 unsigned int tx_errors; 67 unsigned int tx_dropped; 68 }; 69 70 static int get_mtu(const char *if_name) 71 { 72 struct ifreq ifr; 73 int s, ret; 74 75 s = socket(AF_INET, SOCK_DGRAM, 0); 76 if (s < 0) { 77 perror("socket"); 78 exit(EXIT_FAILURE); 79 } 80 81 memset(&ifr, 0, sizeof(struct ifreq)); 82 ifr.ifr_addr.sa_family = AF_INET; 83 strcpy(ifr.ifr_name, if_name); 84 85 ret = ioctl(s, SIOCGIFMTU, &ifr); 86 if (ret < 0) { 87 perror("ioctl"); 88 exit(EXIT_FAILURE); 89 } 90 91 ret = close(s); 92 if (ret < 0) { 93 perror("close"); 94 exit(EXIT_FAILURE); 95 } 96 97 return ifr.ifr_mtu; 98 } 99 100 static int get_interfaces(struct if_stats *ifs) 101 { 102 char buf[PAGE_SIZE]; 103 char *p; 104 int ret, nr, fd; 105 106 fd = open(PROC_NET_DEV, O_RDONLY); 107 if (fd < 0) { 108 perror("open"); 109 exit(EXIT_FAILURE); 110 } 111 112 ret = read(fd, buf, sizeof(buf) - 1); 113 if (ret < 0) { 114 perror("read"); 115 exit(EXIT_FAILURE); 116 } else if (!ret) { 117 fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n"); 118 exit(EXIT_FAILURE); 119 } 120 buf[ret] = '\0'; 121 122 /* skip down to the third line */ 123 p = strchr(buf, '\n'); 124 if (!p) { 125 fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); 126 exit(EXIT_FAILURE); 127 } 128 p = strchr(p + 1, '\n'); 129 if (!p) { 130 fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); 131 exit(EXIT_FAILURE); 132 } 133 p += 1; 134 135 /* 136 * Key: 137 * if: (Rx) bytes packets errs drop fifo frame compressed multicast \ 138 * (Tx) bytes packets errs drop fifo colls carrier compressed 139 */ 140 for (nr = 0; nr < MAX_IF; nr++) { 141 char *c; 142 143 ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name); 144 if (ret != 1) { 145 fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); 146 exit(EXIT_FAILURE); 147 } 148 149 /* 150 * This works around a bug in the proc file where large interface names 151 * or Rx byte counts eat the delimiter, breaking sscanf. 152 */ 153 c = strchr(ifs->name, ':'); 154 if (c) 155 *c = '\0'; 156 157 p = strchr(p, ':') + 1; 158 159 ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u " 160 "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets, 161 &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes, 162 &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped); 163 if (ret != 8) { 164 fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); 165 exit(EXIT_FAILURE); 166 } 167 168 ifs->mtu = get_mtu(ifs->name); 169 170 p = strchr(p, '\n') + 1; 171 if (*p == '\0') { 172 nr++; 173 break; 174 } 175 176 ifs++; 177 } 178 179 ret = close(fd); 180 if (ret) { 181 perror("close"); 182 exit(EXIT_FAILURE); 183 } 184 185 return nr; 186 } 187 188 static void print_header(void) 189 { 190 printf(" Rx Tx\n"); 191 printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n", 192 "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes", 193 "packets", "errs", "drpd"); 194 } 195 196 static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr) 197 { 198 int i = 0; 199 200 while (nr--) { 201 if (old->rx_packets || old->tx_packets) { 202 printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n", 203 new->name, new->mtu, 204 new->rx_bytes - old->rx_bytes, 205 new->rx_packets - old->rx_packets, 206 new->rx_errors - old->rx_errors, 207 new->rx_dropped - old->rx_dropped, 208 new->tx_bytes - old->tx_bytes, 209 new->tx_packets - old->tx_packets, 210 new->tx_errors - old->tx_errors, 211 new->tx_dropped - old->tx_dropped); 212 i++; 213 } 214 old++; 215 new++; 216 } 217 218 return i; 219 } 220 221 static void usage(const char *cmd) 222 { 223 fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd); 224 } 225 226 int iftop_main(int argc, char *argv[]) 227 { 228 struct if_stats ifs[2][MAX_IF]; 229 int count = 0, header_interval = 22, delay = 1, i; 230 unsigned int toggle = 0; 231 232 for (i = 1; i < argc; i++) { 233 if (!strcmp(argv[i], "-d")) { 234 if (i >= argc - 1) { 235 fprintf(stderr, "Option -d requires an argument.\n"); 236 exit(EXIT_FAILURE); 237 } 238 delay = atoi(argv[i++]); 239 if (!delay) 240 delay = 1; 241 continue; 242 } 243 if (!strcmp(argv[i], "-r")) { 244 if (i >= argc - 1) { 245 fprintf(stderr, "Option -r requires an argument.\n"); 246 exit(EXIT_FAILURE); 247 } 248 header_interval = atoi(argv[i++]); 249 if (header_interval < MAX_IF) 250 header_interval = MAX_IF; 251 continue; 252 } 253 if (!strcmp(argv[i], "-h")) { 254 usage(argv[0]); 255 exit(EXIT_SUCCESS); 256 } 257 usage(argv[0]); 258 exit(EXIT_FAILURE); 259 } 260 261 get_interfaces(ifs[!toggle]); 262 if (header_interval) 263 print_header(); 264 while (1) { 265 int nr; 266 267 sleep(delay); 268 nr = get_interfaces(ifs[toggle]); 269 if (header_interval && count + nr > header_interval) { 270 print_header(); 271 count = 0; 272 } 273 count += print_interfaces(ifs[!toggle], ifs[toggle], nr); 274 toggle = !toggle; 275 } 276 277 return 0; 278 } 279