Add '-d, --dump' option which dumps flows to stdout only.
The table layout is same like in interactive mode, the difference
is only if '-s' option is enabled that in stdout mode the rows
are separated with empty line for better separation.

Interval option is also supported but only for delay to collect
such dynamic info like rate. By default interval set to 0 if --dump
option is selected.

Signed-off-by: Vadim Kochan <vadi...@gmail.com>
---
 flowtop.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 113 insertions(+), 21 deletions(-)

diff --git a/flowtop.c b/flowtop.c
index 65a8ad4..5e529ad 100644
--- a/flowtop.c
+++ b/flowtop.c
@@ -133,12 +133,13 @@ static struct sysctl_params_ctx sysctl = { -1, -1 };
 
 static unsigned int cols, rows;
 
-static unsigned int interval = 1;
+static unsigned int interval;
 static bool show_src = false;
 static bool resolve_dns = true;
 static bool resolve_geoip = true;
 static enum rate_units rate_type = RATE_BYTES;
 static bool show_active_only = false;
+static bool do_dump = false;
 
 enum tbl_flow_col {
        TBL_FLOW_PROCESS,
@@ -155,7 +156,7 @@ enum tbl_flow_col {
 
 static struct ui_table flows_tbl;
 
-static const char *short_options = "vhTUsDIS46ut:nGb";
+static const char *short_options = "vhTUsDIS46ut:nGbd";
 static const struct option long_options[] = {
        {"ipv4",        no_argument,            NULL, '4'},
        {"ipv6",        no_argument,            NULL, '6'},
@@ -169,6 +170,7 @@ static const struct option long_options[] = {
        {"show-src",    no_argument,            NULL, 's'},
        {"bits",        no_argument,            NULL, 'b'},
        {"update",      no_argument,            NULL, 'u'},
+       {"dump",        no_argument,            NULL, 'd'},
        {"interval",    required_argument,      NULL, 't'},
        {"version",     no_argument,            NULL, 'v'},
        {"help",        no_argument,            NULL, 'h'},
@@ -252,6 +254,10 @@ static const struct nfct_filter_ipv6 filter_ipv6 = {
        .mask = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
 };
 
+static void collector_dump_flows(void);
+static struct nfct_handle *collector_create_updater(void);
+static void collector_refresh_flows(struct nfct_handle *handle);
+
 static int64_t time_after_us(struct timeval *tv)
 {
        struct timeval now;
@@ -300,6 +306,7 @@ static void help(void)
             "  -s|--show-src          Also show source, not only dest\n"
             "  -b|--bits              Show rates in bits/s instead of 
bytes/s\n"
             "  -u|--update            Update GeoIP databases\n"
+            "  -d|--dump              Only dump flows to stdout\n"
             "  -t|--interval <time>   Refresh time in seconds (default 1s)\n"
             "  -v|--version           Print version and exit\n"
             "  -h|--help              Print this help and exit\n\n"
@@ -456,12 +463,28 @@ static void flow_list_destroy_entry(struct flow_list *fl,
        }
 }
 
+static void flow_list_write_lock(struct flow_list *fl)
+{
+       if (do_dump)
+               return;
+
+       synchronize_rcu();
+       spinlock_lock(&fl->lock);
+}
+
+static void flow_list_unlock(struct flow_list *fl)
+{
+       if (do_dump)
+               return;
+
+       spinlock_unlock(&fl->lock);
+}
+
 static void flow_list_destroy(struct flow_list *fl)
 {
        struct flow_entry *n;
 
-       synchronize_rcu();
-       spinlock_lock(&flow_list.lock);
+       flow_list_write_lock(fl);
 
        while (fl->head != NULL) {
                n = rcu_dereference(fl->head->next);
@@ -471,7 +494,7 @@ static void flow_list_destroy(struct flow_list *fl)
                rcu_assign_pointer(fl->head, n);
        }
 
-       spinlock_unlock(&flow_list.lock);
+       flow_list_unlock(fl);
 }
 
 static int walk_process(unsigned int pid, struct flow_entry *n)
@@ -1020,6 +1043,10 @@ static void draw_flow_entry(const struct flow_entry *n)
        if (show_src) {
                ui_table_row_add(&flows_tbl);
 
+               ui_table_row_print(&flows_tbl, TBL_FLOW_PROCESS, " ");
+               ui_table_row_print(&flows_tbl, TBL_FLOW_PID, " ");
+               ui_table_row_print(&flows_tbl, TBL_FLOW_PROTO, " ");
+               ui_table_row_print(&flows_tbl, TBL_FLOW_STATE, " ");
                ui_table_row_print(&flows_tbl, TBL_FLOW_TIME, "-->");
 
                print_flow_peer_info(n, FLOW_DIR_DST);
@@ -1294,6 +1321,8 @@ static void presenter_curses(void)
        INIT_COLOR(GREEN, BLACK);
        INIT_COLOR(BLACK, GREEN);
 
+       ui_init(UI_CURSES);
+
         flows_table_init(&flows_tbl);
 
        rcu_register_thread();
@@ -1368,12 +1397,54 @@ static void presenter_curses(void)
        screen_end();
 }
 
+static void presenter_stdout(void)
+{
+       struct flow_entry *n;
+
+       ui_init(UI_STDOUT);
+
+        flows_table_init(&flows_tbl);
+       ui_table_header_print(&flows_tbl);
+
+       collector_dump_flows();
+
+       if (interval > 0) {
+               struct nfct_handle *ct_event;
+
+               ct_event = collector_create_updater();
+
+               usleep(USEC_PER_SEC * interval);
+
+               collector_refresh_flows(ct_event);
+               nfct_close(ct_event);
+       }
+
+       for (n = flow_list.head; n; n = n->next) {
+               if (!n->is_visible)
+                       continue;
+               if (presenter_flow_wrong_state(n))
+                       continue;
+
+               draw_flow_entry(n);
+
+               if (show_src)
+                       ui_table_row_add(&flows_tbl);
+
+       }
+
+       printf("\n");
+       fflush(stdout);
+}
+
 static void presenter(void)
 {
        lookup_init(LT_PORTS_TCP);
        lookup_init(LT_PORTS_UDP);
 
-       presenter_curses();
+       if (do_dump)
+               presenter_stdout();
+       else
+               presenter_curses();
 
        lookup_cleanup(LT_PORTS_UDP);
        lookup_cleanup(LT_PORTS_TCP);
@@ -1463,8 +1534,7 @@ static int flow_event_cb(enum nf_conntrack_msg_type type,
        if (sigint)
                return NFCT_CB_STOP;
 
-       synchronize_rcu();
-       spinlock_lock(&flow_list.lock);
+       flow_list_write_lock(&flow_list);
 
        switch (type) {
        case NFCT_T_NEW:
@@ -1480,7 +1550,7 @@ static int flow_event_cb(enum nf_conntrack_msg_type type,
                break;
        }
 
-       spinlock_unlock(&flow_list.lock);
+       flow_list_unlock(&flow_list);
 
        if (sigint)
                return NFCT_CB_STOP;
@@ -1548,8 +1618,7 @@ static int flow_dump_cb(enum nf_conntrack_msg_type type,
        if (sigint)
                return NFCT_CB_STOP;
 
-       synchronize_rcu();
-       spinlock_lock(&flow_list.lock);
+       flow_list_write_lock(&flow_list);
 
        if (!(what & ~(INCLUDE_IPV4 | INCLUDE_IPV6)))
                goto check_addr;
@@ -1606,7 +1675,7 @@ check_addr:
        flow_list_new_entry(&flow_list, ct);
 
 skip_flow:
-       spinlock_unlock(&flow_list.lock);
+       flow_list_unlock(&flow_list);
        return NFCT_CB_CONTINUE;
 }
 
@@ -1633,12 +1702,9 @@ static void collector_dump_flows(void)
        nfct_close(nfct);
 }
 
-static void *collector(void *null __maybe_unused)
+static struct nfct_handle *collector_create_updater(void)
 {
        struct nfct_handle *ct_event;
-       struct pollfd poll_fd[1];
-
-       flow_list_init(&flow_list);
 
        ct_event = nfct_open(CONNTRACK, NF_NETLINK_CONNTRACK_NEW |
                                      NF_NETLINK_CONNTRACK_UPDATE |
@@ -1650,6 +1716,16 @@ static void *collector(void *null __maybe_unused)
 
        nfct_callback_register(ct_event, NFCT_T_ALL, flow_event_cb, NULL);
 
+       return ct_event;
+}
+
+static void *collector(void *null __maybe_unused)
+{
+       struct nfct_handle *ct_event;
+       struct pollfd poll_fd[1];
+
+       ct_event = collector_create_updater();
+
        poll_fd[0].fd = nfct_fd(ct_event);
        poll_fd[0].events = POLLIN;
 
@@ -1693,7 +1769,6 @@ static void *collector(void *null __maybe_unused)
 
        rcu_unregister_thread();
 
-       flow_list_destroy(&flow_list);
        spinlock_destroy(&flow_list.lock);
 
        nfct_close(ct_event);
@@ -1705,6 +1780,7 @@ int main(int argc, char **argv)
 {
        pthread_t tid;
        int ret, c, opt_index, what_cmd = 0;
+       int delay = -1;
 
        setfsuid(getuid());
        setfsgid(getgid());
@@ -1744,7 +1820,7 @@ int main(int argc, char **argv)
                        die();
                        break;
                case 't':
-                       interval = strtoul(optarg, NULL, 10);
+                       delay = strtoul(optarg, NULL, 10);
                        break;
                case 'n':
                        resolve_dns = false;
@@ -1752,6 +1828,9 @@ int main(int argc, char **argv)
                case 'G':
                        resolve_geoip = false;
                        break;
+               case 'd':
+                       do_dump = true;
+                       break;
                case 'h':
                        help();
                        break;
@@ -1763,6 +1842,13 @@ int main(int argc, char **argv)
                }
        }
 
+       if (!do_dump && delay == -1)
+               interval = 1;
+       else if (delay == -1)
+               interval = 0;
+       else
+               interval = delay;
+
        if (what_cmd > 0) {
                what = what_cmd;
 
@@ -1785,12 +1871,18 @@ int main(int argc, char **argv)
        if (resolve_geoip)
                init_geoip(1);
 
-       ret = pthread_create(&tid, NULL, collector, NULL);
-       if (ret < 0)
-               panic("Cannot create phthread!\n");
+       flow_list_init(&flow_list);
+
+       if (!do_dump) {
+               ret = pthread_create(&tid, NULL, collector, NULL);
+               if (ret < 0)
+                       panic("Cannot create phthread!\n");
+       }
 
        presenter();
 
+       flow_list_destroy(&flow_list);
+
        if (resolve_geoip)
                destroy_geoip();
 
-- 
2.6.3

-- 
You received this message because you are subscribed to the Google Groups 
"netsniff-ng" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to netsniff-ng+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to