blob: 800c0f0e528ce8b6a3fd69542655663523e7ae10 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
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
54struct 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
70static 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
100static 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
188static 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
196static 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
221static void usage(const char *cmd)
222{
223 fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd);
224}
225
226int 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}