From 83f11c231626f79433a021c526ffa1ac7521ae36 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Sat, 27 May 2017 18:30:08 +0300 Subject: flowtop: Improve and unify up/down scrolling Move scrolling logic to the ui.c module, it requires to have some data iteration provided in flowtop.c and delegated to ui.c part. So approach is that now flowtop provides 2 additional callbacks for: 1) Iterate over flows/procs list 2) Draw flow/proc on each iteration which is controlled from ui.c it allows to unify scrolling logic and delegate it to the ui.c, in the future it should allow to easy handle press event on selected row and drow some additional information, or draw a cursor line per selected row. Also fixed case when down scrolling was bigger that printed rows, not it is handled by ui part. Signed-off-by: Vadim Kochan Signed-off-by: Tobias Klauser --- flowtop.c | 166 ++++++++++++++++++++++++++++---------------------------------- ui.c | 70 +++++++++++++++++++++++++- ui.h | 13 +++++ 3 files changed, 157 insertions(+), 92 deletions(-) diff --git a/flowtop.c b/flowtop.c index 1caae9e..00937e1 100644 --- a/flowtop.c +++ b/flowtop.c @@ -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) diff --git a/ui.c b/ui.c index e6d1825..6eeff83 100644 --- a/ui.c +++ b/ui.c @@ -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) diff --git a/ui.h b/ui.h index 40bf4f1..fbc0eba 100644 --- a/ui.h +++ b/ui.h @@ -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); -- cgit v1.2.3-54-g00ecf