summaryrefslogtreecommitdiff
path: root/ifpps.c
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-03-15 10:41:48 +0100
committerDaniel Borkmann <dborkman@redhat.com>2013-03-15 10:41:48 +0100
commit1a9fbac03c684f29cff9ac44875bd9504a89f54e (patch)
tree1b2e40dbe5dc1899ef5b62c4325c9b94c9c450fc /ifpps.c
all: import netsniff-ng 0.5.8-rc0 source
We decided to get rid of the old Git history and start a new one for several reasons: *) Allow / enforce only high-quality commits (which was not the case for many commits in the history), have a policy that is more close to the one from the Linux kernel. With high quality commits, we mean code that is logically split into commits and commit messages that are signed-off and have a proper subject and message body. We do not allow automatic Github merges anymore, since they are total bullshit. However, we will either cherry-pick your patches or pull them manually. *) The old archive was about ~27MB for no particular good reason. This basically derived from the bad decision that also some PDF files where stored there. From this moment onwards, no binary objects are allowed to be stored in this repository anymore. The old archive is not wiped away from the Internet. You will still be able to find it, e.g. on git.cryptoism.org etc. Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Diffstat (limited to 'ifpps.c')
-rw-r--r--ifpps.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/ifpps.c b/ifpps.c
new file mode 100644
index 0000000..517ee23
--- /dev/null
+++ b/ifpps.c
@@ -0,0 +1,949 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Copyright 2009 - 2013 Daniel Borkmann.
+ * Subject to the GPL, version 2.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <curses.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/fsuid.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "die.h"
+#include "xmalloc.h"
+#include "xutils.h"
+#include "xio.h"
+#include "built_in.h"
+
+struct wifi_stat {
+ uint32_t bitrate;
+ int16_t link_qual, link_qual_max;
+ int signal_level /*, noise_level*/;
+};
+
+struct ifstat {
+ long long unsigned int rx_bytes, rx_packets, rx_drops, rx_errors;
+ long long unsigned int rx_fifo, rx_frame, rx_multi;
+ long long unsigned int tx_bytes, tx_packets, tx_drops, tx_errors;
+ long long unsigned int tx_fifo, tx_colls, tx_carrier;
+ long long unsigned int irqs[MAX_CPUS], irqs_srx[MAX_CPUS], irqs_stx[MAX_CPUS];
+ int64_t cpu_user[MAX_CPUS], cpu_nice[MAX_CPUS], cpu_sys[MAX_CPUS];
+ int64_t cpu_idle[MAX_CPUS], cpu_iow[MAX_CPUS], mem_free, mem_total;
+ uint32_t irq_nr, procs_run, procs_iow, cswitch, forks;
+ struct wifi_stat wifi;
+};
+
+volatile sig_atomic_t sigint = 0;
+
+static struct ifstat stats_old, stats_new, stats_delta;
+
+static int stats_loop = 0;
+
+static WINDOW *stats_screen = NULL;
+
+static const char *short_options = "d:t:vhclp";
+static const struct option long_options[] = {
+ {"dev", required_argument, NULL, 'd'},
+ {"interval", required_argument, NULL, 't'},
+ {"promisc", no_argument, NULL, 'p'},
+ {"csv", no_argument, NULL, 'c'},
+ {"loop", no_argument, NULL, 'l'},
+ {"version", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+};
+
+static void signal_handler(int number)
+{
+ switch (number) {
+ case SIGINT:
+ sigint = 1;
+ break;
+ case SIGHUP:
+ default:
+ break;
+ }
+}
+
+static inline char *snr_to_str(int level)
+{
+ if (level > 40)
+ return "very good signal";
+ if (level > 25 && level <= 40)
+ return "good signal";
+ if (level > 15 && level <= 25)
+ return "poor signal";
+ if (level > 10 && level <= 15)
+ return "very poor signal";
+ if (level <= 10)
+ return "no signal";
+
+ return "unknown";
+}
+
+static inline int iswireless(const struct ifstat *stats)
+{
+ return stats->wifi.bitrate > 0;
+}
+
+static void help(void)
+{
+ printf("\nifpps %s, top-like kernel networking and system statistics\n",
+ VERSION_STRING);
+ puts("http://www.netsniff-ng.org\n\n"
+ "Usage: ifpps [options] || ifpps <netdev>\n"
+ "Options:\n"
+ " -d|--dev <netdev> Device to fetch statistics for e.g., eth0\n"
+ " -t|--interval <time> Refresh time in ms (default 1000 ms)\n"
+ " -p|--promisc Promiscuous mode\n"
+ " -c|--csv Output to terminal as Gnuplot-ready data\n"
+ " -l|--loop Continuous CSV output\n"
+ " -v|--version Print version\n"
+ " -h|--help Print this help\n\n"
+ "Examples:\n"
+ " ifpps eth0\n"
+ " ifpps -pd eth0\n"
+ " ifpps -lpcd wlan0 > plot.dat\n\n"
+ "Note:\n"
+ " On 10G cards, RX/TX statistics are usually accumulated each > 1sec.\n"
+ " Thus, in those situations, it's good to use a -t of 10sec.\n\n"
+ "Please report bugs to <bugs@netsniff-ng.org>\n"
+ "Copyright (C) 2009-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>\n"
+ "Swiss federal institute of technology (ETH Zurich)\n"
+ "License: GNU GPL version 2.0\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+ die();
+}
+
+static void version(void)
+{
+ printf("\nifpps %s, top-like kernel networking and system statistics\n",
+ VERSION_STRING);
+ puts("http://www.netsniff-ng.org\n\n"
+ "Please report bugs to <bugs@netsniff-ng.org>\n"
+ "Copyright (C) 2009-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>\n"
+ "Swiss federal institute of technology (ETH Zurich)\n"
+ "License: GNU GPL version 2.0\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n");
+ die();
+}
+
+static int stats_proc_net_dev(const char *ifname, struct ifstat *stats)
+{
+ int ret = -EINVAL;
+ char buff[256];
+ FILE *fp;
+
+ fp = fopen("/proc/net/dev", "r");
+ if (!fp)
+ panic("Cannot open /proc/net/dev!\n");
+
+ if (fgets(buff, sizeof(buff), fp)) { ; }
+ if (fgets(buff, sizeof(buff), fp)) { ; }
+
+ memset(buff, 0, sizeof(buff));
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ buff[sizeof(buff) -1] = 0;
+
+ if (strstr(buff, ifname) == NULL)
+ continue;
+
+ if (sscanf(buff, "%*[a-z0-9 .-]:%llu%llu%llu%llu%llu%llu"
+ "%llu%*u%llu%llu%llu%llu%llu%llu%llu",
+ &stats->rx_bytes, &stats->rx_packets,
+ &stats->rx_errors, &stats->rx_drops,
+ &stats->rx_fifo, &stats->rx_frame,
+ &stats->rx_multi, &stats->tx_bytes,
+ &stats->tx_packets, &stats->tx_errors,
+ &stats->tx_drops, &stats->tx_fifo,
+ &stats->tx_colls, &stats->tx_carrier) == 14) {
+ ret = 0;
+ break;
+ }
+
+ memset(buff, 0, sizeof(buff));
+ }
+
+ fclose(fp);
+ return ret;
+}
+
+static int stats_proc_interrupts(char *ifname, struct ifstat *stats)
+{
+ int ret = -EINVAL, i, cpus, try = 0;
+ char *ptr, buff[256];
+ struct ethtool_drvinfo drvinf;
+ FILE *fp;
+
+ fp = fopen("/proc/interrupts", "r");
+ if (!fp)
+ panic("Cannot open /proc/interrupts!\n");
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+retry:
+ fseek(fp, 0, SEEK_SET);
+ memset(buff, 0, sizeof(buff));
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ buff[sizeof(buff) - 1] = 0;
+ ptr = buff;
+
+ if (strstr(buff, ifname) == NULL)
+ continue;
+
+ stats->irq_nr = strtol(ptr, &ptr, 10);
+ bug_on(stats->irq_nr == 0);
+
+ if (ptr)
+ ptr++;
+ for (i = 0; i < cpus && ptr; ++i) {
+ stats->irqs[i] = strtol(ptr, &ptr, 10);
+ if (i == cpus - 1) {
+ ret = 0;
+ goto done;
+ }
+ }
+
+ memset(buff, 0, sizeof(buff));
+ }
+
+ if (ret == -EINVAL && try == 0) {
+ memset(&drvinf, 0, sizeof(drvinf));
+ if (ethtool_drvinf(ifname, &drvinf) < 0)
+ goto done;
+
+ ifname = drvinf.driver;
+ try++;
+
+ goto retry;
+ }
+done:
+ fclose(fp);
+ return ret;
+}
+
+static int stats_proc_softirqs(struct ifstat *stats)
+{
+ int i, cpus;
+ char *ptr, buff[256];
+ FILE *fp;
+ enum {
+ softirqs_net_rx,
+ softirqs_net_tx,
+ softirqs_net_none,
+ } net_type = softirqs_net_none;
+
+ fp = fopen("/proc/softirqs", "r");
+ if (!fp)
+ panic("Cannot open /proc/softirqs!\n");
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+
+ memset(buff, 0, sizeof(buff));
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ buff[sizeof(buff) - 1] = 0;
+
+ if ((ptr = strstr(buff, "NET_TX:")))
+ net_type = softirqs_net_tx;
+ else if ((ptr = strstr(buff, "NET_RX:")))
+ net_type = softirqs_net_rx;
+ else
+ continue;
+
+ for (ptr += strlen("NET_xX:"), i = 0; i < cpus; ++i) {
+ switch (net_type) {
+ case softirqs_net_tx:
+ stats->irqs_stx[i] = strtol(ptr, &ptr, 10);
+ break;
+ case softirqs_net_rx:
+ stats->irqs_srx[i] = strtol(ptr, &ptr, 10);
+ break;
+ default:
+ bug();
+ }
+ }
+
+ memset(buff, 0, sizeof(buff));
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int stats_proc_memory(struct ifstat *stats)
+{
+ char *ptr, buff[256];
+ FILE *fp;
+
+ fp = fopen("/proc/meminfo", "r");
+ if (!fp)
+ panic("Cannot open /proc/meminfo!\n");
+
+ memset(buff, 0, sizeof(buff));
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ buff[sizeof(buff) - 1] = 0;
+
+ if ((ptr = strstr(buff, "MemTotal:"))) {
+ ptr += strlen("MemTotal:");
+ stats->mem_total = strtol(ptr, &ptr, 10);
+ } else if ((ptr = strstr(buff, "MemFree:"))) {
+ ptr += strlen("MemFree:");
+ stats->mem_free = strtol(ptr, &ptr, 10);
+ }
+
+ memset(buff, 0, sizeof(buff));
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int stats_proc_system(struct ifstat *stats)
+{
+ int cpu, cpus;
+ char *ptr, buff[256];
+ FILE *fp;
+
+ fp = fopen("/proc/stat", "r");
+ if (!fp)
+ panic("Cannot open /proc/stat!\n");
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+
+ memset(buff, 0, sizeof(buff));
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ buff[sizeof(buff) - 1] = 0;
+
+ if ((ptr = strstr(buff, "cpu"))) {
+ ptr += strlen("cpu");
+ if (isblank(*ptr))
+ goto next;
+
+ cpu = strtol(ptr, &ptr, 10);
+ bug_on(cpu > cpus);
+
+ if (sscanf(ptr, "%lu%lu%lu%lu%lu",
+ &stats->cpu_user[cpu],
+ &stats->cpu_nice[cpu],
+ &stats->cpu_sys[cpu],
+ &stats->cpu_idle[cpu],
+ &stats->cpu_iow[cpu]) != 5)
+ goto next;
+ } else if ((ptr = strstr(buff, "ctxt"))) {
+ ptr += strlen("ctxt");
+ stats->cswitch = strtoul(ptr, &ptr, 10);
+ } else if ((ptr = strstr(buff, "processes"))) {
+ ptr += strlen("processes");
+ stats->forks = strtoul(ptr, &ptr, 10);
+ } else if ((ptr = strstr(buff, "procs_running"))) {
+ ptr += strlen("procs_running");
+ stats->procs_run = strtoul(ptr, &ptr, 10);
+ } else if ((ptr = strstr(buff, "procs_blocked"))) {
+ ptr += strlen("procs_blocked");
+ stats->procs_iow = strtoul(ptr, &ptr, 10);
+ }
+next:
+ memset(buff, 0, sizeof(buff));
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int adjust_dbm_level(int in_dbm, int dbm_val)
+{
+ if (!in_dbm)
+ return dbm_val;
+
+ return dbm_val - 0x100;
+}
+
+static int stats_wireless(const char *ifname, struct ifstat *stats)
+{
+ int ret;
+ struct iw_statistics ws;
+
+ ret = wireless_sigqual(ifname, &ws);
+ if (ret != 0) {
+ stats->wifi.bitrate = 0;
+ return -EINVAL;
+ }
+
+ stats->wifi.bitrate = wireless_bitrate(ifname);
+
+ stats->wifi.signal_level =
+ adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM, ws.qual.level);
+
+ stats->wifi.link_qual = ws.qual.qual;
+ stats->wifi.link_qual_max = wireless_rangemax_sigqual(ifname);
+
+ return ret;
+}
+
+#define DIFF1(member) do { diff->member = new->member - old->member; } while (0)
+#define DIFF(member) do { \
+ if (sizeof(diff->member) != sizeof(new->member) || \
+ sizeof(diff->member) != sizeof(old->member)) \
+ bug(); \
+ bug_on((new->member - old->member) > (new->member)); \
+ DIFF1(member); \
+ } while (0)
+
+static void stats_diff(struct ifstat *old, struct ifstat *new,
+ struct ifstat *diff)
+{
+ int cpus, i;
+
+ DIFF(rx_bytes);
+ DIFF(rx_packets);
+ DIFF(rx_drops);
+ DIFF(rx_errors);
+ DIFF(rx_fifo);
+ DIFF(rx_frame);
+ DIFF(rx_multi);
+
+ DIFF(tx_bytes);
+ DIFF(tx_bytes);
+ DIFF(tx_packets);
+ DIFF(tx_drops);
+ DIFF(tx_errors);
+ DIFF(tx_fifo);
+ DIFF(tx_colls);
+ DIFF(tx_carrier);
+
+ DIFF1(procs_run);
+ DIFF1(procs_iow);
+
+ DIFF1(wifi.signal_level);
+ DIFF1(wifi.link_qual);
+
+ DIFF1(cswitch);
+ DIFF1(forks);
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+
+ for (i = 0; i < cpus; ++i) {
+ DIFF(irqs[i]);
+ DIFF(irqs_srx[i]);
+ DIFF(irqs_stx[i]);
+
+ DIFF1(cpu_user[i]);
+ DIFF1(cpu_nice[i]);
+ DIFF1(cpu_sys[i]);
+ DIFF1(cpu_idle[i]);
+ DIFF1(cpu_iow[i]);
+ }
+}
+
+static void stats_fetch(const char *ifname, struct ifstat *stats)
+{
+ if (stats_proc_net_dev(ifname, stats) < 0)
+ panic("Cannot fetch device stats!\n");
+ if (stats_proc_softirqs(stats) < 0)
+ panic("Cannot fetch software interrupts!\n");
+ if (stats_proc_memory(stats) < 0)
+ panic("Cannot fetch memory stats!\n");
+ if (stats_proc_system(stats) < 0)
+ panic("Cannot fetch system stats!\n");
+
+ stats_proc_interrupts((char *) ifname, stats);
+
+ stats_wireless(ifname, stats);
+}
+
+static void stats_sample_generic(const char *ifname, uint64_t ms_interval)
+{
+ memset(&stats_old, 0, sizeof(stats_old));
+ memset(&stats_new, 0, sizeof(stats_new));
+ memset(&stats_delta, 0, sizeof(stats_delta));
+
+ stats_fetch(ifname, &stats_old);
+ usleep(ms_interval * 1000);
+ stats_fetch(ifname, &stats_new);
+
+ stats_diff(&stats_old, &stats_new, &stats_delta);
+}
+
+static void screen_init(WINDOW **screen)
+{
+ (*screen) = initscr();
+
+ raw();
+ noecho();
+ cbreak();
+ nodelay((*screen), TRUE);
+
+ keypad(stdscr, TRUE);
+
+ refresh();
+ wrefresh((*screen));
+}
+
+static void screen_header(WINDOW *screen, const char *ifname, int *voff,
+ uint64_t ms_interval)
+{
+ size_t len = 0;
+ char buff[64];
+ struct ethtool_drvinfo drvinf;
+ u32 rate = device_bitrate(ifname);
+ int link = ethtool_link(ifname);
+
+ memset(&drvinf, 0, sizeof(drvinf));
+ ethtool_drvinf(ifname, &drvinf);
+
+ memset(buff, 0, sizeof(buff));
+ if (rate)
+ len += snprintf(buff + len, sizeof(buff) - len, " %uMbit/s", rate);
+ if (link >= 0)
+ len += snprintf(buff + len, sizeof(buff) - len, " link:%s",
+ link == 0 ? "no" : "yes");
+
+ mvwprintw(screen, (*voff)++, 2,
+ "Kernel net/sys statistics for %s (%s%s), t=%lums"
+ " ",
+ ifname, drvinf.driver, buff, ms_interval);
+}
+
+static void screen_net_dev_rel(WINDOW *screen, const struct ifstat *rel,
+ int *voff)
+{
+ attron(A_REVERSE);
+
+ mvwprintw(screen, (*voff)++, 0,
+ " RX: %16.3llf MiB/t "
+ "%10llu pkts/t "
+ "%10llu drops/t "
+ "%10llu errors/t ",
+ ((long double) rel->rx_bytes) / (1LLU << 20),
+ rel->rx_packets, rel->rx_drops, rel->rx_errors);
+
+ mvwprintw(screen, (*voff)++, 0,
+ " TX: %16.3llf MiB/t "
+ "%10llu pkts/t "
+ "%10llu drops/t "
+ "%10llu errors/t ",
+ ((long double) rel->tx_bytes) / (1LLU << 20),
+ rel->tx_packets, rel->tx_drops, rel->tx_errors);
+
+ attroff(A_REVERSE);
+}
+
+static void screen_net_dev_abs(WINDOW *screen, const struct ifstat *abs,
+ int *voff)
+{
+ mvwprintw(screen, (*voff)++, 2,
+ "RX: %16.3llf MiB "
+ "%10llu pkts "
+ "%10llu drops "
+ "%10llu errors",
+ ((long double) abs->rx_bytes) / (1LLU << 20),
+ abs->rx_packets, abs->rx_drops, abs->rx_errors);
+
+ mvwprintw(screen, (*voff)++, 2,
+ "TX: %16.3llf MiB "
+ "%10llu pkts "
+ "%10llu drops "
+ "%10llu errors",
+ ((long double) abs->tx_bytes) / (1LLU << 20),
+ abs->tx_packets, abs->tx_drops, abs->tx_errors);
+}
+
+static void screen_sys_mem(WINDOW *screen, const struct ifstat *rel,
+ const struct ifstat *abs, int *voff)
+{
+ mvwprintw(screen, (*voff)++, 2,
+ "SYS: %14u cs/t "
+ "%10.1lf%% mem "
+ "%13u running "
+ "%10u iowait",
+ rel->cswitch,
+ (100.0 * (abs->mem_total - abs->mem_free)) / abs->mem_total,
+ abs->procs_run, abs->procs_iow);
+}
+
+static void screen_percpu_states(WINDOW *screen, const struct ifstat *rel,
+ int cpus, int *voff)
+{
+ int i;
+ uint64_t all;
+
+ for (i = 0; i < cpus; ++i) {
+ all = rel->cpu_user[i] + rel->cpu_nice[i] + rel->cpu_sys[i] +
+ rel->cpu_idle[i] + rel->cpu_iow[i];
+
+ mvwprintw(screen, (*voff)++, 2,
+ "CPU%d: %13.1lf%% usr/t "
+ "%9.1lf%% sys/t "
+ "%10.1lf%% idl/t "
+ "%11.1lf%% iow/t ", i,
+ 100.0 * (rel->cpu_user[i] + rel->cpu_nice[i]) / all,
+ 100.0 * rel->cpu_sys[i] / all,
+ 100.0 * rel->cpu_idle[i] / all,
+ 100.0 * rel->cpu_iow[i] / all);
+ }
+}
+
+static void screen_percpu_irqs_rel(WINDOW *screen, const struct ifstat *rel,
+ int cpus, int *voff)
+{
+ int i;
+
+ for (i = 0; i < cpus; ++i) {
+ mvwprintw(screen, (*voff)++, 2,
+ "CPU%d: %14llu irqs/t "
+ "%15llu soirq RX/t "
+ "%15llu soirq TX/t ", i,
+ rel->irqs[i],
+ rel->irqs_srx[i],
+ rel->irqs_stx[i]);
+ }
+}
+
+static void screen_percpu_irqs_abs(WINDOW *screen, const struct ifstat *abs,
+ int cpus, int *voff)
+{
+ int i;
+
+ for (i = 0; i < cpus; ++i) {
+ mvwprintw(screen, (*voff)++, 2,
+ "CPU%d: %14llu irqs", i,
+ abs->irqs[i]);
+ }
+}
+
+static void screen_wireless(WINDOW *screen, const struct ifstat *rel,
+ const struct ifstat *abs, int *voff)
+{
+ if (iswireless(abs)) {
+ mvwprintw(screen, (*voff)++, 2,
+ "LinkQual: %7d/%d (%d/t) ",
+ abs->wifi.link_qual,
+ abs->wifi.link_qual_max,
+ rel->wifi.link_qual);
+
+ mvwprintw(screen, (*voff)++, 2,
+ "Signal: %8d dBm (%d dBm/t) ",
+ abs->wifi.signal_level,
+ rel->wifi.signal_level);
+ }
+}
+
+static void screen_update(WINDOW *screen, const char *ifname, const struct ifstat *rel,
+ const struct ifstat *abs, int *first, uint64_t ms_interval)
+{
+ int cpus, voff = 1, cvoff = 2;
+
+ curs_set(0);
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+
+ screen_header(screen, ifname, &voff, ms_interval);
+
+ voff++;
+ screen_net_dev_rel(screen, rel, &voff);
+
+ voff++;
+ screen_net_dev_abs(screen, abs, &voff);
+
+ voff++;
+ screen_sys_mem(screen, rel, abs, &voff);
+
+ voff++;
+ screen_percpu_states(screen, rel, cpus, &voff);
+
+ voff++;
+ screen_percpu_irqs_rel(screen, rel, cpus, &voff);
+
+ voff++;
+ screen_percpu_irqs_abs(screen, abs, cpus, &voff);
+
+ voff++;
+ screen_wireless(screen, rel, abs, &voff);
+
+ if (*first) {
+ mvwprintw(screen, cvoff, 2, "Collecting data ...");
+ *first = 0;
+ } else {
+ mvwprintw(screen, cvoff, 2, " ");
+ }
+
+ wrefresh(screen);
+ refresh();
+}
+
+static void screen_end(void)
+{
+ endwin();
+}
+
+static int screen_main(const char *ifname, uint64_t ms_interval)
+{
+ int first = 1, key;
+
+ screen_init(&stats_screen);
+
+ while (!sigint) {
+ key = getch();
+ if (key == 'q' || key == 0x1b || key == KEY_F(10))
+ break;
+
+ screen_update(stats_screen, ifname, &stats_delta, &stats_new,
+ &first, ms_interval);
+
+ stats_sample_generic(ifname, ms_interval);
+ }
+
+ screen_end();
+
+ return 0;
+}
+
+static void term_csv(const char *ifname, const struct ifstat *rel,
+ const struct ifstat *abs, uint64_t ms_interval)
+{
+ int cpus, i;
+
+ printf("%ld ", time(0));
+
+ printf("%llu ", rel->rx_bytes);
+ printf("%llu ", rel->rx_packets);
+ printf("%llu ", rel->rx_drops);
+ printf("%llu ", rel->rx_errors);
+
+ printf("%llu ", abs->rx_bytes);
+ printf("%llu ", abs->rx_packets);
+ printf("%llu ", abs->rx_drops);
+ printf("%llu ", abs->rx_errors);
+
+ printf("%llu ", rel->tx_bytes);
+ printf("%llu ", rel->tx_packets);
+ printf("%llu ", rel->tx_drops);
+ printf("%llu ", rel->tx_errors);
+
+ printf("%llu ", abs->tx_bytes);
+ printf("%llu ", abs->tx_packets);
+ printf("%llu ", abs->tx_drops);
+ printf("%llu ", abs->tx_errors);
+
+ printf("%u ", rel->cswitch);
+ printf("%lu ", abs->mem_free);
+ printf("%lu ", abs->mem_total - abs->mem_free);
+ printf("%lu ", abs->mem_total);
+ printf("%u ", abs->procs_run);
+ printf("%u ", abs->procs_iow);
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+
+ for (i = 0; i < cpus; ++i) {
+ printf("%lu ", rel->cpu_user[i]);
+ printf("%lu ", rel->cpu_nice[i]);
+ printf("%lu ", rel->cpu_sys[i]);
+ printf("%lu ", rel->cpu_idle[i]);
+ printf("%lu ", rel->cpu_iow[i]);
+
+ printf("%llu ", rel->irqs[i]);
+ printf("%llu ", abs->irqs[i]);
+
+ printf("%llu ", rel->irqs_srx[i]);
+ printf("%llu ", abs->irqs_srx[i]);
+
+ printf("%llu ", rel->irqs_stx[i]);
+ printf("%llu ", abs->irqs_stx[i]);
+ }
+
+ if (iswireless(abs)) {
+ printf("%u ", rel->wifi.link_qual);
+ printf("%u ", abs->wifi.link_qual);
+ printf("%u ", abs->wifi.link_qual_max);
+
+ printf("%d ", rel->wifi.signal_level);
+ printf("%d ", abs->wifi.signal_level);
+ }
+
+ puts("");
+ fflush(stdout);
+}
+
+static void term_csv_header(const char *ifname, const struct ifstat *abs,
+ uint64_t ms_interval)
+{
+ int cpus, i, j = 1;
+
+ printf("# gnuplot dump (#col:description)\n");
+ printf("# networking interface: %s\n", ifname);
+ printf("# sampling interval (t): %lu ms\n", ms_interval);
+ printf("# %d:unixtime ", j++);
+
+ printf("%d:rx-bytes-per-t ", j++);
+ printf("%d:rx-pkts-per-t ", j++);
+ printf("%d:rx-drops-per-t ", j++);
+ printf("%d:rx-errors-per-t ", j++);
+
+ printf("%d:rx-bytes ", j++);
+ printf("%d:rx-pkts ", j++);
+ printf("%d:rx-drops ", j++);
+ printf("%d:rx-errors ", j++);
+
+ printf("%d:tx-bytes-per-t ", j++);
+ printf("%d:tx-pkts-per-t ", j++);
+ printf("%d:tx-drops-per-t ", j++);
+ printf("%d:tx-errors-per-t ", j++);
+
+ printf("%d:tx-bytes ", j++);
+ printf("%d:tx-pkts ", j++);
+ printf("%d:tx-drops ", j++);
+ printf("%d:tx-errors ", j++);
+
+ printf("%d:context-switches-per-t ", j++);
+ printf("%d:mem-free ", j++);
+ printf("%d:mem-used ", j++);
+ printf("%d:mem-total ", j++);
+ printf("%d:procs-in-run ", j++);
+ printf("%d:procs-in-iow ", j++);
+
+ cpus = get_number_cpus();
+ bug_on(cpus > MAX_CPUS);
+
+ for (i = 0, j = 22; i < cpus; ++i) {
+ printf("%d:cpu%i-usr-per-t ", j++, i);
+ printf("%d:cpu%i-nice-per-t ", j++, i);
+ printf("%d:cpu%i-sys-per-t ", j++, i);
+ printf("%d:cpu%i-idle-per-t ", j++, i);
+ printf("%d:cpu%i-iow-per-t ", j++, i);
+
+ printf("%d:cpu%i-net-irqs-per-t ", j++, i);
+ printf("%d:cpu%i-net-irqs ", j++, i);
+
+ printf("%d:cpu%i-net-rx-soft-irqs-per-t ", j++, i);
+ printf("%d:cpu%i-net-rx-soft-irqs ", j++, i);
+ printf("%d:cpu%i-net-tx-soft-irqs-per-t ", j++, i);
+ printf("%d:cpu%i-net-tx-soft-irqs ", j++, i);
+ }
+
+ if (iswireless(abs)) {
+ printf("%d:wifi-link-qual-per-t ", j++);
+ printf("%d:wifi-link-qual ", j++);
+ printf("%d:wifi-link-qual-max ", j++);
+
+ printf("%d:wifi-signal-dbm-per-t ", j++);
+ printf("%d:wifi-signal-dbm ", j++);
+ }
+
+ puts("");
+ printf("# data:\n");
+ fflush(stdout);
+}
+
+static int term_main(const char *ifname, uint64_t ms_interval)
+{
+ int first = 1;
+
+ do {
+ stats_sample_generic(ifname, ms_interval);
+
+ if (first) {
+ first = 0;
+ term_csv_header(ifname, &stats_new, ms_interval);
+ }
+
+ term_csv(ifname, &stats_delta, &stats_new, ms_interval);
+ } while (stats_loop && !sigint);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ short ifflags = 0;
+ int c, opt_index, ret, promisc = 0;
+ uint64_t interval = 1000;
+ char *ifname = NULL;
+ int (*func_main)(const char *ifname, uint64_t ms_interval) = screen_main;
+
+ setfsuid(getuid());
+ setfsgid(getgid());
+
+ while ((c = getopt_long(argc, argv, short_options, long_options,
+ &opt_index)) != EOF) {
+ switch (c) {
+ case 'h':
+ help();
+ break;
+ case 'v':
+ version();
+ break;
+ case 'd':
+ ifname = xstrndup(optarg, IFNAMSIZ);
+ break;
+ case 't':
+ interval = strtol(optarg, NULL, 10);
+ break;
+ case 'l':
+ stats_loop = 1;
+ break;
+ case 'p':
+ promisc = 1;
+ break;
+ case 'c':
+ func_main = term_main;
+ break;
+ case '?':
+ switch (optopt) {
+ case 'd':
+ case 't':
+ panic("Option -%c requires an argument!\n",
+ optopt);
+ default:
+ if (isprint(optopt))
+ printf("Unknown option character `0x%X\'!\n", optopt);
+ die();
+ }
+ default:
+ break;
+ }
+ }
+
+ if (argc == 1)
+ help();
+
+ if (argc == 2)
+ ifname = xstrndup(argv[1], IFNAMSIZ);
+ if (ifname == NULL)
+ panic("No networking device given!\n");
+
+ if (!strncmp("lo", ifname, IFNAMSIZ))
+ panic("lo is not supported!\n");
+ if (device_mtu(ifname) == 0)
+ panic("This is no networking device!\n");
+
+ register_signal(SIGINT, signal_handler);
+ register_signal(SIGHUP, signal_handler);
+
+ if (promisc)
+ ifflags = enter_promiscuous_mode(ifname);
+ ret = func_main(ifname, interval);
+ if (promisc)
+ leave_promiscuous_mode(ifname, ifflags);
+
+ xfree(ifname);
+ return ret;
+}