#include #include #include #include #include #include #include #include #include #include #include #include #include #define PROC_NET_DEV "/proc/net/dev" struct iface { char interface[IFNAMSIZ]; struct timeval timestamp; unsigned long long rx_bytes; unsigned long long tx_bytes; }; volatile bool q = false; bool use_bits = false; struct iface *interfaces = NULL; size_t no_iface = 0; void endHandler(int signum) { q = true; } void print_bps(double bytesPerSecond) { const char* prefixes[] = { "", "k", "M", "G", "T", "P", "E", "Z", "Y" }; // Calculate the value double value; const char* unit; if(use_bits) { value = bytesPerSecond * 8; unit = "b/s"; } else { value = bytesPerSecond; unit = "B/s"; } // Choose the prefix unsigned prefix = 0; while (prefix < (sizeof(prefixes) / sizeof(const char*)) && value > 1000.) { value /= 1000.; ++prefix; } // Format the string printf("%.2f %s%s", value, prefixes[prefix], unit); } void print_stats() { // Open /proc/net/dev FILE *dev = fopen(PROC_NET_DEV, "r"); if (!dev) { fprintf(stderr, "cannot open %s - %s\n", PROC_NET_DEV, strerror(errno)); exit(EXIT_FAILURE); } // Read /proc/dev/net while (!feof(dev)) { char buf[512]; char *name, *statistics_string; long long unsigned rx_bytes, tx_bytes; struct timezone unused_timezone; struct timeval timestamp; // Read a line fgets(buf, sizeof(buf), dev); // get timestamp gettimeofday(×tamp, &unused_timezone); // Find the colon separating the name from the statistics statistics_string = strchr(buf, ':'); if (!statistics_string) continue; // Get a pointer to the statistics *statistics_string = 0; ++statistics_string; // Remove leading whitespace from the name name = buf; ptrdiff_t len = 0; while (*name == ' ' || *name == '\t') { ++name; } len = name - buf; // Parse the statistics, alls well, then get this shit going, otherwise f it. if (sscanf(statistics_string, "%Lu %*Lu %*Lu %*Lu %*Lu %*Lu %*Lu %*Lu %Lu", &rx_bytes, &tx_bytes) == 2) { size_t i = 0; bool found = false; if(interfaces == NULL) { goto skip; } for(i = 0; i < no_iface; i++) { // if it exists in our interfaces, great. reuse the iface struct if(strncmp(name, interfaces[i].interface, len) == 0) { found = true; break; } } skip: if(found) { double timeDelta, rx_speed, tx_speed = 0; // print our new speeds timeDelta = (timestamp.tv_sec - interfaces[i].timestamp.tv_sec) * 1.0 + (timestamp.tv_usec - interfaces[i].timestamp.tv_usec) * .000001; rx_speed = (rx_bytes - interfaces[i].rx_bytes) / timeDelta; tx_speed = (tx_bytes - interfaces[i].tx_bytes) / timeDelta; printf("%s (", interfaces[i].interface); print_bps(rx_speed); printf(", "); print_bps(tx_speed); printf(")\t"); // update our interface strcpy(interfaces[i].interface, name); interfaces[i].rx_bytes = rx_bytes; interfaces[i].timestamp = timestamp; interfaces[i].tx_bytes = tx_bytes; } else { interfaces = realloc(interfaces, sizeof(struct iface) * (no_iface + 1)); if(interfaces == NULL) { fprintf(stderr, "Error: %s\n", strerror(errno)); exit(1); } strcpy(interfaces[no_iface].interface, name); interfaces[no_iface].rx_bytes = rx_bytes; interfaces[no_iface].tx_bytes = tx_bytes; no_iface++; } } } // Close the file fclose(dev); } int main(int argc, char **argv) { // parse the command line int c; int interval = 10 * 100000; while ((c = getopt (argc, argv, "bi:hv")) != -1) switch (c) { case 'b': use_bits = true; break; case 'i': // Magic numbers? fuck it that's not a magic number, that's a 'real' number. interval = atoi(optarg) * 100000; break; case 'h': printf("USAGE: fsbm, -b specifies bits, -i sets interval in 10ths of seconds, 1 sec default\n"); exit(EXIT_SUCCESS); break; case 'v': printf("Version 1\n"); exit(EXIT_SUCCESS); break; case '?': fprintf(stderr,"bad flag\n"); break; default: exit(EXIT_FAILURE); } // Catch SIGINT signal(SIGINT, endHandler); // populate first. print_stats(); while (!q) { print_stats(); printf("\n"); usleep(interval); } return EXIT_SUCCESS; }