diff options
-rw-r--r-- | flowtop.c | 166 | ||||
-rw-r--r-- | ui.c | 70 | ||||
-rw-r--r-- | ui.h | 13 |
3 files changed, 157 insertions, 92 deletions
@@ -118,8 +118,6 @@ enum flow_direction { # define ATTR_TIMESTAMP_STOP 64 #endif -#define SCROLL_MAX 1000 - #define INCLUDE_IPV4 (1 << 0) #define INCLUDE_IPV6 (1 << 1) #define INCLUDE_UDP (1 << 2) @@ -156,7 +154,6 @@ static struct sysctl_params_ctx sysctl = { -1, -1 }; static unsigned int cols, rows; static WINDOW *screen; -static int skip_lines; static unsigned int interval = 1; static bool show_src = false; @@ -190,12 +187,25 @@ enum tbl_proc_col { static struct ui_table flows_tbl; static struct ui_table procs_tbl; +static struct ui_table *curr_tbl; enum tab_entry { TAB_FLOWS, TAB_PROCS, }; +#define list_first_or_next(__ptr, __head, __entry) \ +({ \ + struct cds_list_head *h; \ + if (!__ptr) \ + h = rcu_dereference((__head)->next); \ + else if (rcu_dereference(__ptr->__entry.next) == (__head)) \ + return NULL; \ + else \ + h = rcu_dereference(__ptr->__entry.next); \ + cds_list_entry(h, __typeof(* (__ptr)), __entry); \ +}) + static const char *short_options = "vhTUsDIS46ut:nGb"; static const struct option long_options[] = { {"ipv4", no_argument, NULL, '4'}, @@ -1013,45 +1023,45 @@ static void print_flow_peer_info(const struct flow_entry *n, enum flow_direction tmp, sizeof(tmp) - 1)); } -static void draw_flow_entry(const struct flow_entry *n) +static void draw_flow_entry(struct ui_table *tbl, const void *data) { + const struct flow_entry *n = data; char tmp[128]; - ui_table_row_add(&flows_tbl); + ui_table_row_add(tbl); /* Application */ - ui_table_row_col_set(&flows_tbl, TBL_FLOW_PROCESS, - n->proc ? n->proc->name : ""); + ui_table_row_col_set(tbl, TBL_FLOW_PROCESS, n->proc ? n->proc->name : ""); /* PID */ slprintf(tmp, sizeof(tmp), "%.d", n->proc ? n->proc->pid : 0); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_PID, tmp); + ui_table_row_col_set(tbl, TBL_FLOW_PID, tmp); /* L4 protocol */ - ui_table_row_col_set(&flows_tbl, TBL_FLOW_PROTO, l4proto2str[n->l4_proto]); + ui_table_row_col_set(tbl, TBL_FLOW_PROTO, l4proto2str[n->l4_proto]); /* L4 protocol state */ - ui_table_row_col_set(&flows_tbl, TBL_FLOW_STATE, flow_state2str(n)); + ui_table_row_col_set(tbl, TBL_FLOW_STATE, flow_state2str(n)); /* Time */ time2str(n->timestamp_start, tmp, sizeof(tmp)); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_TIME, tmp); + ui_table_row_col_set(tbl, TBL_FLOW_TIME, tmp); print_flow_peer_info(n, show_src ? FLOW_DIR_SRC : FLOW_DIR_DST); - ui_table_row_show(&flows_tbl); + ui_table_row_show(tbl); if (show_src) { - ui_table_row_add(&flows_tbl); + ui_table_row_add(tbl); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_PROCESS, ""); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_PID, ""); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_PROTO, ""); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_STATE, ""); - ui_table_row_col_set(&flows_tbl, TBL_FLOW_TIME, ""); + ui_table_row_col_set(tbl, TBL_FLOW_PROCESS, ""); + ui_table_row_col_set(tbl, TBL_FLOW_PID, ""); + ui_table_row_col_set(tbl, TBL_FLOW_PROTO, ""); + ui_table_row_col_set(tbl, TBL_FLOW_STATE, ""); + ui_table_row_col_set(tbl, TBL_FLOW_TIME, ""); print_flow_peer_info(n, FLOW_DIR_DST); - ui_table_row_show(&flows_tbl); + ui_table_row_show(tbl); } } @@ -1115,10 +1125,10 @@ static inline bool presenter_flow_wrong_state(struct flow_entry *n) return true; } -static void draw_filter_status(char *title, int count, int skip_lines) +static void draw_filter_status(struct ui_table *tbl, char *title) { mvwprintw(screen, 1, 0, "%*s", COLS - 1, " "); - mvwprintw(screen, 1, 2, "%s(%u) for ", title, count); + mvwprintw(screen, 1, 2, "%s(%u) for ", title, ui_table_data_count(tbl)); if (what & INCLUDE_IPV4) printw("IPv4,"); @@ -1139,60 +1149,31 @@ static void draw_filter_status(char *title, int count, int skip_lines) if (show_active_only) printw("Active,"); - printw(" [+%d]", skip_lines); + printw(" [+%d]", ui_table_scroll_height(tbl)); if (is_flow_collecting) printw(" [Collecting flows ...]"); } -static void draw_flows(WINDOW *screen, struct flow_list *fl, - int skip_lines) +static void draw_flows(WINDOW *screen, struct flow_list *fl) { - int row_width = show_src ? 2 : 1; - unsigned int flows = 0; - unsigned int line = 4; - int skip = skip_lines; - struct flow_entry *n; - rcu_read_lock(); if (cds_list_empty(&fl->head)) - mvwprintw(screen, line, 2, "(No sessions! " + mvwprintw(screen, 4, 2, "(No sessions! " "Is netfilter running?)"); - ui_table_clear(&flows_tbl); - ui_table_header_print(&flows_tbl); - - cds_list_for_each_entry_rcu(n, &fl->head, entry) { - if (!n->is_visible) - continue; - if (presenter_flow_wrong_state(n)) - continue; - - /* count only flows which might be showed */ - flows++; - - if (line + row_width >= rows) - continue; - if (--skip >= 0) - continue; - - draw_flow_entry(n); - line += row_width; - } + ui_table_data_bind(&flows_tbl); rcu_read_unlock(); - mvwprintw(screen, 1, 0, "%*s", COLS - 1, " "); - mvwprintw(screen, 1, 2, "Kernel netfilter flows(%u) for ", flows); - - draw_filter_status("Kernel netfilter flows", flows, skip_lines); + draw_filter_status(&flows_tbl, "Kernel netfilter flows"); } -static void draw_proc_entry(struct proc_entry *p) +static void draw_proc_entry(struct ui_table *tbl, const void *data) { - struct ui_table *tbl = &procs_tbl;; + const struct proc_entry *p = data; char tmp[128]; ui_table_row_add(tbl); @@ -1227,33 +1208,15 @@ static void draw_proc_entry(struct proc_entry *p) ui_table_row_show(tbl); } -static void draw_procs(WINDOW *screen, struct flow_list *fl, - int skip_lines) +static void draw_procs(WINDOW *screen, struct flow_list *fl) { - struct proc_entry *proc, *tmp; - unsigned int line = 4; - int skip = skip_lines; - int procs = 0; - rcu_read_lock(); - ui_table_clear(&procs_tbl); - ui_table_header_print(&procs_tbl); - - cds_list_for_each_entry_safe(proc, tmp, &proc_list.head, entry) { - if (line + 1 >= rows) - continue; - if (--skip >= 0) - continue; - - draw_proc_entry(proc); - procs++; - line++; - } + ui_table_data_bind(&procs_tbl); rcu_read_unlock(); - draw_filter_status("Processes", procs, skip_lines); + draw_filter_status(&procs_tbl, "Processes"); } static void draw_help(void) @@ -1355,6 +1318,17 @@ static void show_option_toggle(int opt) } } +void * flows_iter(void *data) +{ + struct flow_entry *n = data; + + do { + n = list_first_or_next(n, &flow_list.head, entry); + } while (n && (!n->is_visible || presenter_flow_wrong_state(n))); + + return n; +} + static void flows_table_init(struct ui_table *tbl) { ui_table_init(tbl); @@ -1382,6 +1356,16 @@ static void flows_table_init(struct ui_table *tbl) ui_table_col_color_set(tbl, TBL_FLOW_STATE, COLOR(YELLOW, BLACK)); ui_table_header_color_set(&flows_tbl, COLOR(BLACK, GREEN)); + + ui_table_data_bind_set(tbl, draw_flow_entry); + ui_table_data_iter_set(tbl, flows_iter); +} + +void * procs_iter(void *data) +{ + struct proc_entry *p = data; + + return list_first_or_next(p, &proc_list.head, entry); } static void procs_table_init(struct ui_table *tbl) @@ -1413,6 +1397,9 @@ static void procs_table_init(struct ui_table *tbl) ui_table_col_color_set(tbl, TBL_PROC_RATE_DST, COLOR(BLUE, BLACK)); ui_table_header_color_set(tbl, COLOR(BLACK, GREEN)); + + ui_table_data_bind_set(tbl, draw_proc_entry); + ui_table_data_iter_set(tbl, procs_iter); } static void tab_main_on_open(struct ui_tab *tab, enum ui_tab_event_t evt, uint32_t id) @@ -1420,10 +1407,13 @@ static void tab_main_on_open(struct ui_tab *tab, enum ui_tab_event_t evt, uint32 if (evt != UI_TAB_EVT_OPEN) return; - if (id == TAB_FLOWS) - draw_flows(screen, &flow_list, skip_lines); - else if (id == TAB_PROCS) - draw_procs(screen, &flow_list, skip_lines); + if (id == TAB_FLOWS) { + draw_flows(screen, &flow_list); + curr_tbl = &flows_tbl; + } else if (id == TAB_PROCS) { + draw_procs(screen, &flow_list); + curr_tbl = &procs_tbl; + } } static void presenter(void) @@ -1470,24 +1460,20 @@ static void presenter(void) case KEY_UP: case 'u': case 'k': - skip_lines--; - if (skip_lines < 0) - skip_lines = 0; + ui_table_event_send(curr_tbl, UI_EVT_SCROLL_UP); break; case KEY_DOWN: case 'd': case 'j': - skip_lines++; - if (skip_lines > SCROLL_MAX) - skip_lines = SCROLL_MAX; + ui_table_event_send(curr_tbl, UI_EVT_SCROLL_DOWN); break; case KEY_LEFT: case 'h': - ui_table_event_send(&flows_tbl, UI_EVT_SCROLL_LEFT); + ui_table_event_send(curr_tbl, UI_EVT_SCROLL_LEFT); break; case KEY_RIGHT: case 'l': - ui_table_event_send(&flows_tbl, UI_EVT_SCROLL_RIGHT); + ui_table_event_send(curr_tbl, UI_EVT_SCROLL_RIGHT); break; case 'b': if (rate_type == RATE_BYTES) @@ -214,13 +214,79 @@ void ui_table_header_print(struct ui_table *tbl) void ui_table_event_send(struct ui_table *tbl, enum ui_event_id evt_id) { - if (evt_id == UI_EVT_SCROLL_RIGHT) { + switch (evt_id) { + case UI_EVT_SCROLL_RIGHT: tbl->scroll_x += SCROLL_X_STEP; - } else if (evt_id == UI_EVT_SCROLL_LEFT) { + break; + + case UI_EVT_SCROLL_LEFT: tbl->scroll_x -= SCROLL_X_STEP; if (tbl->scroll_x < 0) tbl->scroll_x = 0; + break; + + case UI_EVT_SCROLL_UP: + tbl->scroll_y--; + if (tbl->scroll_y < 0) + tbl->scroll_y = 0; + break; + + case UI_EVT_SCROLL_DOWN: + tbl->scroll_y++; + break; + + default: /* pass the rest events */ + return; + } +} + +void ui_table_data_iter_set(struct ui_table *tbl, void * (* iter)(void *data)) +{ + tbl->data_iter = iter; +} + +void ui_table_data_bind_set(struct ui_table *tbl, + void (* bind)(struct ui_table *tbl, const void *data)) +{ + tbl->data_bind = bind; +} + +void ui_table_data_bind(struct ui_table *tbl) +{ + void *data; + int i = 0; + + bug_on(!tbl); + bug_on(!tbl->data_iter); + bug_on(!tbl->data_bind); + + ui_table_clear(tbl); + ui_table_header_print(tbl); + + tbl->data_count = 0; + + data = tbl->data_iter(NULL); + for (; data; data = tbl->data_iter(data)) { + tbl->data_count++; + + if (i++ < tbl->scroll_y) + continue; + + tbl->data_bind(tbl, data); } + + if (tbl->scroll_y > i) + tbl->scroll_y = i; +} + +int ui_table_data_count(struct ui_table *tbl) +{ + return tbl->data_count; +} + +int ui_table_scroll_height(struct ui_table *tbl) +{ + return tbl->scroll_y; } struct ui_tab *ui_tab_create(void) @@ -9,6 +9,8 @@ enum ui_event_id { UI_EVT_SCROLL_LEFT, UI_EVT_SCROLL_RIGHT, + UI_EVT_SCROLL_UP, + UI_EVT_SCROLL_DOWN, UI_EVT_SELECT_NEXT, }; @@ -44,7 +46,12 @@ struct ui_table { int width; int height; int scroll_x; + int scroll_y; const char *delim; + int data_count; + + void * (* data_iter)(void *data); + void (* data_bind)(struct ui_table *tbl, const void *data); }; struct ui_tab; @@ -86,6 +93,12 @@ extern void ui_table_header_color_set(struct ui_table *tbl, int color); extern void ui_table_header_print(struct ui_table *tbl); extern void ui_table_event_send(struct ui_table *tbl, enum ui_event_id id); +extern void ui_table_data_iter_set(struct ui_table *tbl, void * (* iter)(void *data)); +extern void ui_table_data_bind_set(struct ui_table *tbl, + void (* bind)(struct ui_table *tbl, const void *data)); +extern void ui_table_data_bind(struct ui_table *tbl); +extern int ui_table_data_count(struct ui_table *tbl); +extern int ui_table_scroll_height(struct ui_table *tbl); extern struct ui_tab *ui_tab_create(void); extern void ui_tab_destroy(struct ui_tab *tab); |