Home | History | Annotate | Download | only in toolbox
      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