"Fossies" - the Fresh Open Source Software Archive

Member "sysdig-0.26.1/userspace/libsinsp/cursesui.cpp" (24 May 2019, 70064 Bytes) of package /linux/misc/sysdig-0.26.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cursesui.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.24.2_vs_0.25.

    1 /*
    2 Copyright (C) 2013-2018 Draios Inc dba Sysdig.
    3 
    4 This file is part of sysdig.
    5 
    6 Licensed under the Apache License, Version 2.0 (the "License");
    7 you may not use this file except in compliance with the License.
    8 You may obtain a copy of the License at
    9 
   10     http://www.apache.org/licenses/LICENSE-2.0
   11 
   12 Unless required by applicable law or agreed to in writing, software
   13 distributed under the License is distributed on an "AS IS" BASIS,
   14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15 See the License for the specific language governing permissions and
   16 limitations under the License.
   17 
   18 */
   19 
   20 #include <iostream>
   21 #include "sinsp.h"
   22 #include "sinsp_int.h"
   23 #include "../../driver/ppm_ringbuffer.h"
   24 #include "filter.h"
   25 #include "filterchecks.h"
   26 
   27 #ifdef CSYSDIG
   28 
   29 #ifndef _WIN32
   30 #include <curses.h>
   31 #else
   32 #include <conio.h>
   33 #define getch _getch
   34 #endif
   35 #include "table.h"
   36 #include "cursescomponents.h"
   37 #include "cursestable.h"
   38 #include "cursesspectro.h"
   39 #include "ctext.h"
   40 #include "cursesui.h"
   41 
   42 extern int32_t g_csysdig_screen_w;
   43 extern bool g_filterchecks_force_raw_times;
   44 
   45 #ifndef NOCURSESUI
   46 #define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
   47 #endif
   48 
   49 #ifndef _WIN32
   50 static int do_sleep(useconds_t usec)
   51 {
   52     return usleep(usec);
   53 }
   54 #else
   55 int do_sleep(DWORD usec)
   56 {
   57     ASSERT(usec >= 1000);
   58     Sleep(DWORD(usec / 1000));
   59     return 0;
   60 }
   61 #endif
   62 
   63 ///////////////////////////////////////////////////////////////////////////////
   64 // json_spy_renderer implementation
   65 ///////////////////////////////////////////////////////////////////////////////
   66 json_spy_renderer::json_spy_renderer(sinsp* inspector, 
   67     sinsp_cursesui* parent,
   68     int32_t viz_type, 
   69     spy_text_renderer::sysdig_output_type sotype, 
   70     bool print_containers,
   71     sinsp_evt::param_fmt text_fmt)
   72 {
   73     m_inspector = inspector;
   74     m_filter = NULL;
   75     m_root = Json::Value(Json::arrayValue);
   76     m_linecnt = 0;
   77 
   78     m_json_spy_renderer = new spy_text_renderer(inspector, 
   79         parent,
   80         viz_type, 
   81         sotype, 
   82         print_containers,
   83         text_fmt);
   84 }
   85 
   86 json_spy_renderer::~json_spy_renderer()
   87 {
   88     delete m_json_spy_renderer;
   89 
   90     if(m_filter != NULL)
   91     {
   92         delete m_filter;
   93     }
   94 }
   95 
   96 void json_spy_renderer::set_filter(string filter)
   97 {
   98     if(filter != "")
   99     {
  100         sinsp_filter_compiler compiler(m_inspector, filter);
  101         m_filter = compiler.compile();
  102     }
  103 }
  104 
  105 void json_spy_renderer::process_event_spy(sinsp_evt* evt, int32_t next_res)
  106 {
  107     int64_t len;
  108     const char* argstr = m_json_spy_renderer->process_event_spy(evt, &len);
  109 
  110     if(argstr != NULL)
  111     {
  112         Json::Value line;
  113         m_linecnt++;
  114 
  115         uint64_t ts = evt->get_ts(); 
  116         line["ta"] = to_string(ts);
  117         line["td"] = to_string(ts - m_inspector->m_firstevent_ts);
  118 
  119         ppm_event_flags eflags = evt->get_info_flags();
  120         if(eflags & EF_READS_FROM_FD)
  121         {
  122             line["d"] = "<";
  123         }
  124         else if(eflags & EF_WRITES_TO_FD)
  125         {
  126             line["d"] = ">";
  127         }
  128 
  129         line["v"] = argstr;
  130         line["l"] = to_string(len);
  131 
  132         string fdname = evt->get_fd_info()->m_name;
  133         string tc;
  134         tc.push_back(evt->get_fd_info()->get_typechar());
  135         int64_t fdnum = evt->get_fd_num();
  136 
  137         line["fd"] = to_string(fdnum);
  138         line["ft"] = string(tc);
  139 
  140         if(fdname != "")
  141         {
  142             sanitize_string(fdname);
  143             line["f"] = to_string(fdnum) + "(<" + string(tc) + ">" + fdname + ")";
  144             line["fn"] = fdname;
  145         }
  146         else
  147         {
  148             line["f"] = to_string(fdnum) + "(<" + string(tc) + ">)";
  149         }
  150 
  151         sinsp_threadinfo* tinfo = evt->get_thread_info();
  152         ASSERT(tinfo);
  153 
  154         line["p"] = tinfo->m_comm;
  155 
  156         if(!tinfo->m_container_id.empty())
  157         {
  158             const sinsp_container_info *container_info =
  159                 m_inspector->m_container_manager.get_container(tinfo->m_container_id);
  160             if(container_info)
  161             {
  162                 if(!container_info->m_name.empty())
  163                 {
  164                     line["c"] = container_info->m_name;
  165                 }
  166             }
  167         }
  168 
  169         m_root.append(line);
  170     }
  171 }
  172 
  173 void json_spy_renderer::process_event_dig(sinsp_evt* evt, int32_t next_res)
  174 {
  175     if(!m_inspector->is_debug_enabled() && evt->get_category() & EC_INTERNAL)
  176     {
  177         return;
  178     }
  179 
  180     string line;
  181 
  182     m_json_spy_renderer->m_formatter->tostring(evt, &line);
  183     m_root.append(line);
  184     m_linecnt++;
  185 }
  186 
  187 void json_spy_renderer::process_event(sinsp_evt* evt, int32_t next_res)
  188 {
  189     //
  190     // Filter the event
  191     //
  192     if(m_filter)
  193     {
  194         if(!m_filter->run(evt))
  195         {
  196             return;
  197         }
  198     }
  199 
  200     //
  201     // Render the output
  202     //
  203     if(m_json_spy_renderer->m_viz_type == VIEW_ID_SPY)
  204     {
  205         process_event_spy(evt, next_res);
  206     }
  207     else
  208     {
  209         process_event_dig(evt, next_res);
  210     }
  211 }
  212 
  213 string json_spy_renderer::get_data()
  214 {
  215     Json::FastWriter writer;
  216 
  217     string res = writer.write(m_root);
  218 
  219     m_root.clear();
  220 
  221     return res;
  222 }
  223 
  224 uint64_t json_spy_renderer::get_count()
  225 {
  226     return m_linecnt;
  227 }
  228 
  229 ///////////////////////////////////////////////////////////////////////////////
  230 // sinsp_cursesui implementation
  231 ///////////////////////////////////////////////////////////////////////////////
  232 sinsp_cursesui::sinsp_cursesui(sinsp* inspector,
  233     string event_source_name,
  234     string cmdline_capture_filter,
  235     uint64_t refresh_interval_ns,
  236     bool print_containers,
  237     sinsp_table::output_type output_type,
  238     bool is_mousedrag_available,
  239     int32_t json_first_row, int32_t json_last_row,
  240     int32_t sorting_col,
  241     sinsp_evt::param_fmt json_spy_text_fmt)
  242 {
  243     m_inspector = inspector;
  244     m_event_source_name = event_source_name;
  245     m_selected_view = 0;
  246     m_prev_selected_view = 0;
  247     m_selected_view_sidemenu_entry = 0;
  248     m_selected_action_sidemenu_entry = 0;
  249     m_datatable = NULL;
  250     m_cmdline_capture_filter = cmdline_capture_filter;
  251     m_paused = false;
  252     m_last_input_check_ts = 0;
  253     m_output_filtering = false;
  254     m_output_searching = false;
  255     m_is_filter_sysdig = false;
  256     m_eof = 0;
  257     m_offline_replay = false;
  258     m_last_progress_evt = 0;
  259     m_input_check_period_ns = UI_USER_INPUT_CHECK_PERIOD_NS;
  260     m_search_nomatch = false;
  261     m_chart = NULL;
  262     m_n_evts_in_file = 0;
  263     m_1st_evt_ts = 0;
  264     m_last_evt_ts = 0;
  265     m_evt_ts_delta = 0;
  266     m_timedelta_formatter = new sinsp_filter_check_reference();
  267     m_refresh_interval_ns = refresh_interval_ns;
  268     m_print_containers = print_containers;
  269     m_output_type = output_type;
  270     m_truncated_input = false;
  271     m_view_depth = 0;
  272     m_interactive = false;
  273     m_json_first_row = json_first_row;
  274     m_json_last_row = json_last_row;
  275     m_sorting_col = sorting_col;
  276     m_json_spy_renderer = NULL;
  277     m_json_spy_text_fmt = json_spy_text_fmt;
  278 
  279     if(output_type == sinsp_table::OT_JSON)
  280     {
  281         g_filterchecks_force_raw_times = true;
  282     }
  283 
  284 #ifndef NOCURSESUI
  285     m_viz = NULL;
  286     m_spectro = NULL;
  287     m_spybox_text_format = sinsp_evt::PF_NORMAL;
  288     m_view_sidemenu = NULL;
  289     m_action_sidemenu = NULL;
  290     m_spy_box = NULL;
  291     m_search_caller_interface = NULL;
  292     m_viewinfo_page = NULL;
  293     m_mainhelp_page = NULL;
  294     m_is_mousedrag_available = is_mousedrag_available;
  295 
  296     for (int i = 0; i < 8; i++)
  297     {
  298         for (int j = 0; j < 8; j++)
  299         {
  300             init_pair((7-i)*8+j, i, (j==0?-1:j));
  301         }
  302     }
  303 
  304     m_view_sort_sidemenu = NULL;
  305     m_selected_view_sort_sidemenu_entry = 0;
  306 
  307     if(output_type == sinsp_table::OT_CURSES)
  308     {
  309         //
  310         // Colors initialization
  311         //
  312         m_colors[RESET_COLOR] = ColorPair(COLOR_WHITE,COLOR_BLACK);
  313         m_colors[DEFAULT_COLOR] = ColorPair(COLOR_WHITE,COLOR_BLACK);
  314         m_colors[FUNCTION_BAR] = ColorPair(COLOR_BLACK,COLOR_YELLOW);
  315         m_colors[FUNCTION_KEY] = ColorPair( COLOR_WHITE,COLOR_BLACK);
  316         m_colors[PANEL_HEADER_FOCUS] = ColorPair(COLOR_BLACK,COLOR_GREEN);
  317         m_colors[PANEL_HEADER_UNFOCUS] = ColorPair(COLOR_BLACK,COLOR_GREEN);
  318         m_colors[PANEL_HIGHLIGHT_FOCUS] = ColorPair(COLOR_BLACK,COLOR_CYAN);
  319         m_colors[PANEL_HIGHLIGHT_UNFOCUS] = ColorPair(COLOR_BLACK, COLOR_WHITE);
  320         m_colors[PANEL_HEADER_LIST_FOCUS] = ColorPair(COLOR_BLACK,COLOR_YELLOW);
  321         m_colors[PANEL_HEADER_LIST_HIGHLIGHT] = ColorPair(COLOR_BLACK,COLOR_GREEN);
  322         m_colors[FAILED_SEARCH] = ColorPair(COLOR_RED,COLOR_CYAN);
  323         m_colors[UPTIME] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK);
  324         m_colors[BATTERY] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK);
  325         m_colors[LARGE_NUMBER] = A_BOLD | ColorPair(COLOR_RED,COLOR_BLACK);
  326         m_colors[METER_TEXT] = ColorPair(COLOR_CYAN,COLOR_BLACK);
  327         m_colors[METER_VALUE] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK);
  328         m_colors[LED_COLOR] = ColorPair(COLOR_GREEN,COLOR_BLACK);
  329         m_colors[TASKS_RUNNING] = A_BOLD | ColorPair(COLOR_GREEN,COLOR_BLACK);
  330         m_colors[PROCESS] = A_NORMAL;
  331         m_colors[PROCESS_SHADOW] = A_BOLD | ColorPair(COLOR_BLACK,COLOR_BLACK);
  332         m_colors[PROCESS_TAG] = A_BOLD | ColorPair(COLOR_YELLOW,COLOR_BLACK);
  333         m_colors[PROCESS_MEGABYTES] = ColorPair(COLOR_CYAN,COLOR_BLACK);
  334         m_colors[PROCESS_BASENAME] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK);
  335         m_colors[PROCESS_TREE] = ColorPair(COLOR_CYAN,COLOR_BLACK);
  336         m_colors[PROCESS_R_STATE] = ColorPair(COLOR_GREEN,COLOR_BLACK);
  337         m_colors[PROCESS_D_STATE] = A_BOLD | ColorPair(COLOR_RED,COLOR_BLACK);
  338         m_colors[PROCESS_HIGH_PRIORITY] = ColorPair(COLOR_RED,COLOR_BLACK);
  339         m_colors[PROCESS_LOW_PRIORITY] = ColorPair(COLOR_RED,COLOR_BLACK);
  340         m_colors[PROCESS_THREAD] = ColorPair(COLOR_GREEN,COLOR_BLACK);
  341         m_colors[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(COLOR_GREEN,COLOR_BLACK);
  342         m_colors[BAR_BORDER] = A_BOLD;
  343         m_colors[BAR_SHADOW] = A_BOLD | ColorPair(COLOR_BLACK,COLOR_BLACK);
  344         m_colors[SWAP] = ColorPair(COLOR_RED,COLOR_BLACK);
  345         m_colors[GRAPH_BLACK] = ColorPair(COLOR_BLACK,COLOR_BLACK);
  346         m_colors[GRAPH_WHITE] = ColorPair(COLOR_WHITE,COLOR_WHITE);
  347         m_colors[GRAPH_WHITE_D] = ColorPair(COLOR_GREEN,COLOR_WHITE);
  348         m_colors[GRAPH_GREEN_L] = ColorPair(COLOR_WHITE,COLOR_GREEN);
  349         m_colors[GRAPH_GREEN] = ColorPair(COLOR_WHITE,COLOR_GREEN);
  350         m_colors[GRAPH_GREEN_D] = ColorPair(COLOR_YELLOW,COLOR_GREEN);
  351         m_colors[GRAPH_YELLOW_L] = ColorPair(COLOR_GREEN,COLOR_YELLOW);
  352         m_colors[GRAPH_YELLOW] = ColorPair(COLOR_WHITE,COLOR_YELLOW);
  353         m_colors[GRAPH_YELLOW_D] = ColorPair(COLOR_RED,COLOR_YELLOW);
  354         m_colors[GRAPH_RED_L] = ColorPair(COLOR_YELLOW,COLOR_RED);
  355         m_colors[GRAPH_RED] = ColorPair(COLOR_WHITE,COLOR_RED);
  356         m_colors[GRAPH_RED_D] = ColorPair(COLOR_MAGENTA,COLOR_RED);
  357         m_colors[GRAPH_MAGENTA_L] = ColorPair(COLOR_RED,COLOR_MAGENTA);
  358         m_colors[GRAPH_MAGENTA] = ColorPair(COLOR_MAGENTA,COLOR_MAGENTA);
  359         m_colors[MEMORY_USED] = ColorPair(COLOR_GREEN,COLOR_BLACK);
  360         m_colors[MEMORY_BUFFERS] = ColorPair(COLOR_BLUE,COLOR_BLACK);
  361         m_colors[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(COLOR_BLUE,COLOR_BLACK);
  362         m_colors[MEMORY_CACHE] = ColorPair(COLOR_YELLOW,COLOR_BLACK);
  363         m_colors[LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(COLOR_BLACK,COLOR_BLACK);
  364         m_colors[LOAD_AVERAGE_FIVE] = A_NORMAL;
  365         m_colors[LOAD_AVERAGE_ONE] = A_BOLD;
  366         m_colors[LOAD] = A_BOLD;
  367         m_colors[HELP_BOLD] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK);
  368         m_colors[CLOCK] = A_BOLD;
  369         m_colors[CHECK_BOX] = ColorPair(COLOR_CYAN,COLOR_BLACK);
  370         m_colors[CHECK_MARK] = A_BOLD;
  371         m_colors[CHECK_TEXT] = A_NORMAL;
  372         m_colors[HOSTNAME] = A_BOLD;
  373         m_colors[CPU_NICE] = ColorPair(COLOR_BLUE,COLOR_BLACK);
  374         m_colors[CPU_NICE_TEXT] = A_BOLD | ColorPair(COLOR_BLUE,COLOR_BLACK);
  375         m_colors[CPU_NORMAL] = ColorPair(COLOR_GREEN,COLOR_BLACK);
  376         m_colors[CPU_KERNEL] = ColorPair(COLOR_RED,COLOR_BLACK);
  377         m_colors[CPU_IOWAIT] = A_BOLD | ColorPair(COLOR_BLACK, COLOR_BLACK);
  378         m_colors[CPU_IRQ] = ColorPair(COLOR_YELLOW,COLOR_BLACK);
  379         m_colors[CPU_SOFTIRQ] = ColorPair(COLOR_MAGENTA,COLOR_BLACK);
  380         m_colors[SPY_READ] = ColorPair(COLOR_RED,COLOR_BLACK);
  381         m_colors[SPY_WRITE] = ColorPair(COLOR_CYAN,COLOR_BLACK);
  382 
  383         //
  384         // Populate the main menu entries
  385         //
  386         m_menuitems.push_back(sinsp_menuitem_info("F1", "Help", sinsp_menuitem_info::ALL, KEY_F(1)));
  387         m_menuitems.push_back(sinsp_menuitem_info("F2", "Views", sinsp_menuitem_info::ALL, KEY_F(2)));
  388         m_menuitems.push_back(sinsp_menuitem_info("F4", "Filter", sinsp_menuitem_info::ALL, KEY_F(4)));
  389         m_menuitems.push_back(sinsp_menuitem_info("F5", "Echo", sinsp_menuitem_info::TABLE, KEY_F(5)));
  390         m_menuitems.push_back(sinsp_menuitem_info("F6", "Dig", sinsp_menuitem_info::TABLE, KEY_F(6)));
  391         m_menuitems.push_back(sinsp_menuitem_info("F7", "Legend", sinsp_menuitem_info::ALL, KEY_F(7)));
  392         m_menuitems.push_back(sinsp_menuitem_info("F8", "Actions", sinsp_menuitem_info::ALL, KEY_F(8)));
  393         m_menuitems.push_back(sinsp_menuitem_info("F9", "Sort", sinsp_menuitem_info::ALL, KEY_F(9)));
  394         m_menuitems.push_back(sinsp_menuitem_info("F12", "Spectro", sinsp_menuitem_info::ALL, KEY_F(12)));
  395         m_menuitems.push_back(sinsp_menuitem_info("CTRL+F", "Search", sinsp_menuitem_info::ALL, 6));
  396         m_menuitems.push_back(sinsp_menuitem_info("p", "Pause", sinsp_menuitem_info::ALL, 'p'));
  397         m_menuitems.push_back(sinsp_menuitem_info("c", "Clear", sinsp_menuitem_info::LIST, 'c'));
  398 
  399         m_menuitems_spybox.push_back(sinsp_menuitem_info("F1", "Help", sinsp_menuitem_info::ALL, KEY_F(1)));
  400         m_menuitems_spybox.push_back(sinsp_menuitem_info("F2", "View As", sinsp_menuitem_info::ALL, KEY_F(2)));
  401         m_menuitems_spybox.push_back(sinsp_menuitem_info("CTRL+F", "Search", sinsp_menuitem_info::ALL, 6));
  402         m_menuitems_spybox.push_back(sinsp_menuitem_info("p", "Pause", sinsp_menuitem_info::ALL, 'p'));
  403         m_menuitems_spybox.push_back(sinsp_menuitem_info("Bak", "Back", sinsp_menuitem_info::ALL, KEY_BACKSPACE));
  404         m_menuitems_spybox.push_back(sinsp_menuitem_info("c", "Clear", sinsp_menuitem_info::ALL, 'c'));
  405         m_menuitems_spybox.push_back(sinsp_menuitem_info("CTRL+G", "Goto", sinsp_menuitem_info::ALL, 7));
  406 
  407         //
  408         // Get screen dimensions
  409         //
  410         getmaxyx(stdscr, m_screenh, m_screenw);
  411         g_csysdig_screen_w = m_screenw;
  412     }
  413 #endif
  414 }
  415 
  416 sinsp_cursesui::~sinsp_cursesui()
  417 {
  418     if(m_datatable != NULL)
  419     {
  420         delete m_datatable;
  421     }
  422 
  423     if(m_json_spy_renderer != NULL)
  424     {
  425         delete m_json_spy_renderer;
  426     }
  427 
  428 #ifndef NOCURSESUI
  429     if(m_output_type == sinsp_table::OT_CURSES)
  430     {
  431         if(m_viz != NULL)
  432         {
  433             delete m_viz;
  434         }
  435 
  436         if(m_spectro != NULL)
  437         {
  438             delete m_spectro;
  439         }
  440 
  441         if(m_view_sidemenu != NULL)
  442         {
  443             delete m_view_sidemenu;
  444         }
  445 
  446         if(m_action_sidemenu != NULL)
  447         {
  448             delete m_action_sidemenu;
  449         }
  450 
  451         if(m_viewinfo_page != NULL)
  452         {
  453             delete m_viewinfo_page;
  454         }
  455 
  456         if(m_mainhelp_page != NULL)
  457         {
  458             delete m_mainhelp_page;
  459         }
  460 
  461         if(m_spy_box)
  462         {
  463             delete m_spy_box;
  464         }
  465     }
  466 #endif
  467 
  468     delete m_timedelta_formatter;
  469 }
  470 
  471 void sinsp_cursesui::configure(sinsp_view_manager* views)
  472 {
  473     if(views == NULL)
  474     {
  475         ASSERT(false);
  476         throw sinsp_exception("trying to configure the command line UI with no views");
  477     }
  478 
  479     //
  480     // Copy the input views
  481     //
  482     m_views = *views;
  483 
  484     //
  485     // Determine which view is the starting one
  486     //
  487     m_selected_view = m_views.get_selected_view();
  488     m_selected_view_sidemenu_entry = m_selected_view;
  489     m_selected_action_sidemenu_entry = 0;
  490     m_selected_view_sort_sidemenu_entry = 0;
  491     m_sidemenu_sorting_col = -1;
  492 }
  493 
  494 void sinsp_cursesui::start(bool is_drilldown, bool is_spy_switch)
  495 {
  496     //
  497     // Input validation
  498     //
  499     if(m_selected_view >= 0)
  500     {
  501         if(m_selected_view >= (int32_t)m_views.size())
  502         {
  503             if(m_views.size() == 0)
  504             {
  505                 throw sinsp_exception("no views loaded");
  506             }
  507             else
  508             {
  509                 ASSERT(false);
  510                 throw sinsp_exception("invalid view");
  511             }
  512         }
  513     }
  514 
  515     //
  516     // Delete the previous table and visualizations
  517     //
  518     if(m_datatable != NULL)
  519     {
  520         delete m_datatable;
  521         m_datatable = NULL;
  522     }
  523 
  524     if(m_json_spy_renderer != NULL)
  525     {
  526         delete m_json_spy_renderer;
  527         m_json_spy_renderer = NULL;
  528     }
  529 
  530 #ifndef NOCURSESUI
  531     spy_text_renderer::sysdig_output_type dig_otype = spy_text_renderer::OT_NORMAL;
  532 
  533     if(m_output_type == sinsp_table::OT_CURSES)
  534     {
  535         if(m_viz != NULL)
  536         {
  537             delete m_viz;
  538             m_viz = NULL;
  539         }
  540 
  541         if(m_spectro != NULL)
  542         {
  543             delete m_spectro;
  544             m_spectro = NULL;
  545             if(m_views.at(m_prev_selected_view)->m_drilldown_target == "dig_app")
  546             {
  547                 dig_otype = spy_text_renderer::OT_LATENCY_APP;
  548             }
  549             else
  550             {
  551                 dig_otype = spy_text_renderer::OT_LATENCY;
  552             }
  553         }
  554 
  555         if(m_spy_box && !is_spy_switch)
  556         {
  557             delete m_spy_box;
  558             m_spy_box = NULL;
  559         }
  560 
  561         m_chart = NULL;
  562     }
  563 #endif
  564 
  565     //
  566     // Update the filter based on what's selected
  567     //
  568     create_complete_filter(false);
  569 
  570     //
  571     // If we need a new datatable, allocate it and set it up
  572     //
  573     sinsp_view_info* wi = NULL;
  574     sinsp_table::tabletype ty = sinsp_table::TT_NONE;
  575 
  576     if(m_selected_view >= 0)
  577     {
  578         wi = m_views.at(m_selected_view);
  579 
  580         if(wi->m_type == sinsp_view_info::T_TABLE)
  581         {
  582             ty = sinsp_table::TT_TABLE;
  583             m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, 
  584                 m_output_type, m_json_first_row, m_json_last_row);
  585         }
  586         else if(wi->m_type == sinsp_view_info::T_LIST)
  587         {
  588             ty = sinsp_table::TT_LIST;
  589             m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, 
  590                 m_output_type, m_json_first_row, m_json_last_row);
  591         }
  592         else if(wi->m_type == sinsp_view_info::T_SPECTRO)
  593         {
  594             ty = sinsp_table::TT_TABLE;
  595 
  596             //
  597             // Accelerate the refresh rate to 1/2s
  598             //
  599             if(m_refresh_interval_ns == 2000000000)
  600             {
  601                 m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns / 4, 
  602                     m_output_type, m_json_first_row, m_json_last_row);
  603             }
  604             else
  605             {
  606                 m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, 
  607                     m_output_type, m_json_first_row, m_json_last_row);
  608             }
  609         }
  610         else
  611         {
  612             ASSERT(false);
  613         }
  614 
  615         try
  616         {
  617             m_datatable->configure(&wi->m_columns, 
  618                 m_complete_filter,
  619                 wi->m_use_defaults,
  620                 m_view_depth);
  621         }
  622         catch(...)
  623         {
  624             delete m_datatable;
  625             m_datatable = NULL;
  626             throw;
  627         }
  628 
  629         if(m_sorting_col != -1 && m_sorting_col < (int32_t)wi->m_columns.size())
  630         {
  631             m_datatable->set_sorting_col(m_sorting_col);
  632         }
  633         else
  634         {
  635             m_datatable->set_sorting_col(wi->m_sortingcol);
  636         }
  637     }
  638     else
  639     {
  640         //
  641         // Create the visualization component
  642         //
  643         if(m_output_type == sinsp_table::OT_JSON)
  644         {
  645             m_json_spy_renderer= new json_spy_renderer(m_inspector,
  646                 this,
  647                 m_selected_view,
  648                 spy_text_renderer::OT_NORMAL,
  649                 m_print_containers,
  650                 m_json_spy_text_fmt);
  651 
  652             m_json_spy_renderer->set_filter(m_complete_filter);
  653         }
  654 #ifndef NOCURSESUI
  655         else
  656         {
  657             m_spy_box = new curses_textbox(m_inspector, this, m_selected_view, dig_otype);
  658             m_spy_box->reset();
  659             m_chart = m_spy_box;
  660             m_spy_box->set_filter(m_complete_filter);
  661         }
  662 #endif
  663     }
  664 
  665 #ifndef NOCURSESUI
  666     if(m_output_type != sinsp_table::OT_CURSES)
  667     {
  668         return;
  669     }
  670 
  671     //
  672     // If we need a table or spectrogram visualization, allocate it and set it up
  673     //
  674     if(m_output_type != sinsp_table::OT_JSON)
  675     {
  676         if(m_selected_view >= 0)
  677         {
  678             if(wi != NULL && wi->m_type == sinsp_view_info::T_SPECTRO)
  679             {
  680                 ASSERT(ty == sinsp_table::TT_TABLE);
  681                 m_spectro = new curses_spectro(this, 
  682                     m_inspector, 
  683                     m_views.at(m_selected_view)->m_id == "spectro_traces");
  684                 m_viz = NULL;
  685                 m_chart = m_spectro;
  686             }
  687             else
  688             {
  689                 ASSERT(ty != sinsp_table::TT_NONE);
  690                 m_viz = new curses_table(this, m_inspector, ty);
  691                 m_spectro = NULL;
  692                 m_chart = m_viz;
  693             }
  694 
  695             vector<int32_t> colsizes;
  696             vector<string> colnames;
  697 
  698             ASSERT(wi != NULL);
  699 
  700             wi->get_col_names_and_sizes(&colnames, &colsizes);
  701 
  702             if(m_viz)
  703             {
  704                 ASSERT(m_spectro == NULL);
  705                 m_viz->configure(m_datatable, &colsizes, &colnames);
  706             }
  707             else
  708             {
  709                 ASSERT(m_spectro != NULL);
  710                 m_spectro->configure(m_datatable);
  711             }
  712 
  713             if(!is_drilldown)
  714             {
  715                 populate_view_sidemenu("", &m_sidemenu_viewlist);
  716             }
  717         }
  718     }
  719 #endif
  720 
  721     m_prev_selected_view = m_selected_view;
  722 }
  723 
  724 #ifndef NOCURSESUI
  725 void sinsp_cursesui::render_header()
  726 {
  727     uint32_t j = 0;
  728     uint32_t k = 0;
  729 
  730     //
  731     // Show the 'viewing' line
  732     //
  733     attrset(m_colors[HELP_BOLD]);
  734     move(0, 0);
  735     for(j = 0; j < m_screenw; j++)
  736     {
  737         addch(' ');
  738     }
  739 
  740     mvaddstr(0, 0, "Viewing:");
  741     k += sizeof("Viewing: ") - 1;
  742  
  743     attrset(m_colors[sinsp_cursesui::PROCESS]);
  744 
  745     string vs;
  746 
  747     if(m_selected_view >= 0)
  748     {
  749         sinsp_view_info* sv = get_selected_view();
  750         const char* vcs = sv->m_name.c_str();
  751         vs = vcs;
  752     }
  753     else
  754     {
  755         if(m_selected_view == VIEW_ID_SPY)
  756         {
  757             vs = "I/O activity";
  758         }
  759         else if(m_selected_view == VIEW_ID_DIG)
  760         {
  761             vs = "sysdig output";
  762         }
  763         else
  764         {
  765             ASSERT(false);
  766         }
  767     }
  768 
  769     mvaddstr(0, k, vs.c_str());
  770 
  771     k+= vs.size() + 1;
  772 
  773     attrset(m_colors[HELP_BOLD]);
  774     mvaddstr(0, k, "For: ");
  775     k += sizeof("For: ") - 1;
  776 
  777     attrset(m_colors[sinsp_cursesui::PROCESS]);
  778 
  779     if(m_sel_hierarchy.size() != 0)
  780     {
  781         vs = "";
  782 
  783         for(j = 0; j < m_sel_hierarchy.size(); j++)
  784         {
  785             uint32_t pv = m_sel_hierarchy.at(j)->m_prev_selected_view;
  786 
  787             if(m_sel_hierarchy.at(j)->m_field == "")
  788             {
  789                 continue;
  790             }
  791 
  792             if(m_views.at(pv)->m_type == sinsp_view_info::T_SPECTRO)
  793             {
  794                 //vs += m_sel_hierarchy.at(j)->m_prev_manual_filter.c_str();
  795                 vs += "spectrogram area";
  796             }
  797             else
  798             {
  799                 vs += m_sel_hierarchy.at(j)->m_field;
  800                 vs += "=";
  801                 vs += m_sel_hierarchy.at(j)->m_val;
  802             }
  803 
  804             if(j < m_sel_hierarchy.size() - 1)
  805             {
  806                 vs += " and ";
  807             }
  808         }
  809 
  810         if(vs == "")
  811         {
  812             vs = "whole machine";
  813         }
  814     }
  815     else
  816     {
  817         vs = "whole machine";
  818     }
  819 
  820     mvaddstr(0, k, vs.c_str());
  821 
  822     if(m_paused)
  823     {
  824         string wstr = "PAUSED";
  825         attrset(m_colors[sinsp_cursesui::LARGE_NUMBER]);
  826         mvprintw(0,
  827             m_screenw / 2 - wstr.size() / 2, 
  828             wstr.c_str());  
  829     }
  830 
  831     //
  832     // Show the 'filter' line
  833     //
  834     attrset(m_colors[HELP_BOLD]);
  835 
  836     move(1, 0);
  837     for(uint32_t j = 0; j < m_screenw; j++)
  838     {
  839         addch(' ');
  840     }
  841 
  842     attrset(m_colors[HELP_BOLD]);
  843 
  844     mvaddstr(1, 0, "Source:");
  845     k = sizeof("Source: ") - 1;
  846 
  847     attrset(m_colors[sinsp_cursesui::PROCESS]);
  848     
  849     string srcstr;
  850     
  851     if(m_inspector->is_live())
  852     {
  853         srcstr = "Live System";
  854     }
  855     else
  856     {
  857         if(m_n_evts_in_file == 0)
  858         {
  859             m_n_evts_in_file = m_inspector->get_num_events();
  860             m_evt_ts_delta = m_last_evt_ts - m_1st_evt_ts;
  861         }
  862 
  863         srcstr = m_inspector->get_input_filename();
  864         srcstr += " (" + to_string(m_n_evts_in_file) + " evts, ";
  865 
  866         if(m_truncated_input)
  867         {
  868             srcstr += " truncated, ";
  869         }
  870 
  871         m_timedelta_formatter->set_val(PT_RELTIME, 
  872             (uint8_t*)&m_evt_ts_delta,
  873             8,
  874             0,
  875             ppm_print_format::PF_DEC);
  876 
  877             srcstr += string(m_timedelta_formatter->tostring_nice(NULL, 0, 0)) + ")";
  878     }
  879 
  880     mvaddnstr(1, k, srcstr.c_str(), m_screenw - k - 1);
  881 
  882     k += srcstr.size() + 1;
  883     m_filterstring_start_x = k;
  884 
  885     attrset(m_colors[HELP_BOLD]);
  886 
  887     mvaddstr(1, k, "Filter:");
  888     k += sizeof("Filter: ") - 1;
  889 
  890     attrset(m_colors[sinsp_cursesui::PROCESS]);
  891 
  892     string sflt;
  893     if(m_complete_filter != "")
  894     {
  895         sflt = m_complete_filter.c_str();
  896     }
  897     else
  898     {
  899         sflt = "none";
  900     }
  901 
  902     mvaddnstr(1, k, sflt.c_str(), m_screenw - k - 1);
  903     
  904     k += sflt.size();
  905     m_filterstring_end_x = k;
  906 }
  907 
  908 void sinsp_cursesui::turn_search_on(search_caller_interface* ifc, string header_text)
  909 {
  910     ASSERT(m_spy_box != NULL);
  911     m_search_header_text = header_text;
  912     m_spy_box->get_offset(&m_search_start_x, &m_search_start_y);
  913 
  914     m_search_caller_interface = ifc;
  915     m_output_searching = false;
  916     m_output_filtering = false;
  917     m_cursor_pos = 0;
  918     curs_set(1);
  919     render();
  920 }
  921 
  922 void sinsp_cursesui::draw_bottom_menu(vector<sinsp_menuitem_info>* items, bool istable)
  923 {
  924     uint32_t j = 0;
  925     uint32_t k = 0;
  926 
  927     //
  928     // Clear the line
  929     //
  930     move(m_screenh - 1, 0);
  931     for(uint32_t j = 0; j < m_screenw; j++)
  932     {
  933         addch(' ');
  934     }
  935 
  936     m_mouse_to_key_list.clear();
  937 
  938     for(j = 0; j < items->size(); j++)
  939     {
  940         if(istable && ((items->at(j).m_type & sinsp_menuitem_info::TABLE) == 0))
  941         {
  942             continue;
  943         }
  944 
  945         if((!istable) && ((items->at(j).m_type & sinsp_menuitem_info::LIST) == 0))
  946         {
  947             continue;
  948         }
  949 
  950         uint32_t startx = k;
  951 
  952         attrset(m_colors[PROCESS]);
  953         string fks = items->at(j).m_key;
  954         mvaddnstr(m_screenh - 1, k, fks.c_str(), MAX(fks.size(), 2));
  955         k += MAX(fks.size(), 2);
  956 
  957         attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
  958         fks = items->at(j).m_desc;
  959         
  960         if(fks.size() < 6)
  961         {
  962             fks.resize(6, ' ');
  963         }
  964         
  965         mvaddnstr(m_screenh - 1, k, fks.c_str(), fks.size());
  966         k += fks.size();
  967         
  968         m_mouse_to_key_list.add(sinsp_mouse_to_key_list_entry(startx,
  969             m_screenh - 1,
  970             k - 1,
  971             m_screenh - 1,
  972             items->at(j).m_keyboard_equivalent));
  973     }
  974 }
  975 
  976 void sinsp_cursesui::render_default_main_menu()
  977 {
  978     bool istable;
  979 
  980     if(m_datatable != NULL && m_datatable->m_type == sinsp_table::TT_TABLE)
  981     {
  982         istable = true;
  983     }
  984     else
  985     {
  986         istable = false;
  987     }
  988 
  989     draw_bottom_menu(&m_menuitems, istable);
  990 }
  991 
  992 void sinsp_cursesui::render_spy_main_menu()
  993 {
  994     draw_bottom_menu(&m_menuitems_spybox, false);
  995 }
  996 
  997 void sinsp_cursesui::render_filtersearch_main_menu()
  998 {
  999     uint32_t k = 0;
 1000     string* str = 0;
 1001 
 1002     //
 1003     // Pick the right string based on what we're doing
 1004     //
 1005     if(m_output_filtering)
 1006     {
 1007         str = &m_manual_filter;
 1008 
 1009         if(*str == "" && m_is_filter_sysdig && m_complete_filter != "")
 1010         {
 1011             *str = m_complete_filter;
 1012         }
 1013     }
 1014     else if(m_output_searching)
 1015     {
 1016         str = &m_manual_search_text;
 1017     }
 1018     else
 1019     {
 1020         if(m_search_caller_interface)
 1021         {
 1022             str = m_search_caller_interface->get_last_search_string();
 1023         }
 1024         else
 1025         {
 1026             ASSERT(false);
 1027         }
 1028     }
 1029 
 1030     //
 1031     // Only clear the line if this is the first refresh, to prevent deleting the
 1032     // text that the user is typing
 1033     //
 1034     if(m_cursor_pos == 0)
 1035     {
 1036         move(m_screenh - 1, 0);
 1037         for(uint32_t j = 0; j < m_screenw; j++)
 1038         {
 1039             addch(' ');
 1040         }
 1041     }
 1042 
 1043     attrset(m_colors[PROCESS]);
 1044     string fks = "F1";
 1045     mvaddnstr(m_screenh - 1, k, fks.c_str(), 10);
 1046     k += fks.size();
 1047     attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
 1048     fks = "Help";
 1049     fks.resize(6, ' ');
 1050     mvaddnstr(m_screenh - 1, k, fks.c_str(), 6);
 1051     k += 6;
 1052 
 1053     if(m_output_filtering)
 1054     {
 1055         attrset(m_colors[PROCESS]);
 1056         fks = "F2";
 1057         mvaddnstr(m_screenh - 1, k, fks.c_str(), 10);
 1058         k += fks.size();
 1059         attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
 1060         if(m_is_filter_sysdig)
 1061         {
 1062             fks = "Text";
 1063         }
 1064         else
 1065         {
 1066             fks = "sysdig";
 1067         }
 1068         fks.resize(6, ' ');
 1069         mvaddnstr(m_screenh - 1, k, fks.c_str(), 6);
 1070         k += 6;
 1071     }
 1072 
 1073     attrset(m_colors[PROCESS]);
 1074     fks = "Enter";
 1075     mvaddnstr(m_screenh - 1, k, fks.c_str(), 10);
 1076     k += fks.size();
 1077 
 1078     attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
 1079     fks = "Done";
 1080     fks.resize(6, ' ');
 1081     mvaddnstr(m_screenh - 1, k, fks.c_str(), 6);
 1082     k += 6;
 1083 
 1084     attrset(m_colors[PROCESS]);
 1085     fks = "Esc";
 1086     mvaddnstr(m_screenh - 1, k, fks.c_str(), 10);
 1087     k += fks.size();
 1088 
 1089     attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
 1090     fks = "Clear";
 1091     fks.resize(6, ' ');
 1092     mvaddnstr(m_screenh - 1, k, fks.c_str(), 6);
 1093     k += 6;
 1094 
 1095     k++;
 1096     attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
 1097     if(m_is_filter_sysdig)
 1098     {
 1099         fks = "Expression: ";
 1100     }
 1101     else
 1102     {
 1103         if(m_search_header_text == "")
 1104         {
 1105             fks = "Text to match: ";
 1106         }
 1107         else
 1108         {
 1109             fks = m_search_header_text + ": ";
 1110         }
 1111     }
 1112     mvaddnstr(m_screenh - 1, k, fks.c_str(), 20);
 1113     k += fks.size();
 1114 
 1115     uint32_t cursor_pos = k;
 1116 
 1117     if(m_cursor_pos == 0)
 1118     {
 1119         for(; k < m_screenw; k++)
 1120         {
 1121             addch(' ');
 1122         }
 1123 
 1124         m_cursor_pos = cursor_pos;
 1125 
 1126         mvprintw(m_screenh - 1, m_cursor_pos, str->c_str());
 1127 
 1128         m_cursor_pos += str->size();
 1129     }
 1130 
 1131     move(m_screenh - 1, m_cursor_pos);
 1132 }
 1133 
 1134 void sinsp_cursesui::render_position_info()
 1135 {
 1136     if(m_chart == NULL)
 1137     {
 1138         return;
 1139     }
 1140 
 1141     int32_t pos;
 1142     int32_t totlines;
 1143     float percent;
 1144     bool truncated;
 1145     if(m_chart->get_position(&pos, &totlines, &percent, &truncated))
 1146     {
 1147         char prstr[128];
 1148         string trs;
 1149         uint32_t csize = 18;
 1150 
 1151         attrset(m_colors[sinsp_cursesui::PROCESS_MEGABYTES]);
 1152 
 1153         move(m_screenh - 1, m_screenw - csize);
 1154         for(uint32_t k = 0; k < csize; k++)
 1155         {
 1156             addch(' ');
 1157         }
 1158 
 1159         if(truncated)
 1160         {
 1161             trs = "(truncated)";
 1162         }
 1163 
 1164         if(percent != 0)
 1165         {
 1166             sprintf(prstr, "%d/%d(%.1f%%)%s", (int)pos, (int)totlines, percent * 100, trs.c_str());
 1167         }
 1168         else
 1169         {
 1170             sprintf(prstr, "%d/%d(0.0%%)%s", (int)pos, (int)totlines, trs.c_str());
 1171         }
 1172 
 1173         mvaddstr(m_screenh - 1, 
 1174             m_screenw - strlen(prstr),
 1175             prstr);
 1176     }
 1177 }
 1178 
 1179 void sinsp_cursesui::render_main_menu()
 1180 {
 1181     if(m_output_filtering || m_output_searching || m_search_caller_interface != NULL)
 1182     {
 1183         render_filtersearch_main_menu();
 1184     }
 1185     else if(m_spy_box != NULL)
 1186     {
 1187         render_spy_main_menu();
 1188     }
 1189     else
 1190     {
 1191         render_default_main_menu();
 1192     }
 1193 }
 1194 
 1195 void sinsp_cursesui::render()
 1196 {
 1197     if(m_spectro && !m_view_sidemenu)
 1198     {
 1199         return;
 1200     }
 1201 
 1202     //
 1203     // Draw the header at the top of the page
 1204     //
 1205     render_header();
 1206 
 1207     //
 1208     // Print the position in the chart
 1209     //
 1210     if(m_output_filtering || m_output_searching || m_search_caller_interface != NULL)
 1211     {
 1212         render_position_info();
 1213     }
 1214 
 1215     //
 1216     // Draw the menu at the bottom of the screen
 1217     //
 1218     render_main_menu();
 1219 
 1220     //
 1221     // If required, draw the side menu
 1222     //
 1223     if(m_view_sidemenu)
 1224     {
 1225         m_view_sidemenu->render();
 1226     }
 1227 
 1228     if(m_view_sort_sidemenu)
 1229     {
 1230         m_view_sort_sidemenu->render();
 1231     }
 1232 
 1233     if(m_action_sidemenu)
 1234     {
 1235         m_action_sidemenu->render();
 1236     }
 1237 
 1238     //
 1239     // Print the position in the chart
 1240     //
 1241     if(!(m_output_filtering || m_output_searching || m_search_caller_interface != NULL))
 1242     {
 1243         render_position_info();
 1244     }
 1245 }
 1246 #endif
 1247 
 1248 sinsp_view_info* sinsp_cursesui::get_selected_view()
 1249 {
 1250     if(m_selected_view < 0)
 1251     {
 1252         return NULL;
 1253     }
 1254 
 1255     ASSERT(m_selected_view < (int32_t)m_views.size());
 1256     return m_views.at(m_selected_view);
 1257 }
 1258 
 1259 sinsp_view_info* sinsp_cursesui::get_prev_selected_view()
 1260 {
 1261     if(m_prev_selected_view < 0)
 1262     {
 1263         return NULL;
 1264     }
 1265 
 1266     ASSERT(m_prev_selected_view < (int32_t)m_views.size());
 1267     return m_views.at(m_prev_selected_view);
 1268 }
 1269 
 1270 #ifndef NOCURSESUI
 1271 void sinsp_cursesui::populate_view_sidemenu(string field, vector<sidemenu_list_entry>* viewlist)
 1272 {
 1273     uint32_t k = 0;
 1274 
 1275     viewlist->clear();
 1276     uint64_t bpos = field.find('[');
 1277     if(bpos != string::npos)
 1278     {
 1279         field = field.substr(0, bpos);
 1280     }
 1281 
 1282     for(uint32_t j = 0; j < m_views.size(); ++j)
 1283     {
 1284         auto it = m_views.at(j);
 1285 
 1286         for(auto atit = it->m_applies_to.begin(); atit != it->m_applies_to.end(); ++atit)
 1287         {
 1288             if(*atit == field)
 1289             {
 1290                 viewlist->push_back(sidemenu_list_entry(it->m_name, j));
 1291 
 1292                 if(it->m_name == m_views.at(m_selected_view)->m_name)
 1293                 {
 1294                     m_selected_view_sidemenu_entry = k;
 1295 
 1296                     if(m_view_sidemenu != NULL)
 1297                     {
 1298                         m_view_sidemenu->m_selct = k;
 1299                     }
 1300                 }
 1301 
 1302                 k++;
 1303             }
 1304         }
 1305     }
 1306 
 1307     if(m_view_sidemenu != NULL)
 1308     {
 1309         m_view_sidemenu->set_entries(viewlist);
 1310     }
 1311 }
 1312 
 1313 void sinsp_cursesui::populate_view_cols_sidemenu()
 1314 {
 1315     int32_t k = 0;
 1316 
 1317     vector<sidemenu_list_entry> viewlist;
 1318     sinsp_view_info* vinfo = get_selected_view();
 1319 
 1320     for(auto it : vinfo->m_columns)
 1321     {
 1322         if(it.m_name != "NA") 
 1323         {
 1324             if(m_sidemenu_sorting_col == k) 
 1325             {
 1326                 viewlist.push_back(sidemenu_list_entry(it.m_name, k++));
 1327                 continue;
 1328             }
 1329             viewlist.push_back(sidemenu_list_entry(it.m_name, k++));
 1330         }
 1331     }
 1332 
 1333     if(viewlist.size() == 0)
 1334     {
 1335         viewlist.push_back(sidemenu_list_entry("<NO COLUMNS>", 0));
 1336     }
 1337 
 1338     if(m_view_sort_sidemenu != NULL)
 1339     {
 1340         m_view_sort_sidemenu->set_entries(&viewlist);
 1341     }
 1342 }
 1343 
 1344 
 1345 
 1346 void sinsp_cursesui::populate_action_sidemenu()
 1347 {
 1348     uint32_t k = 0;
 1349     vector<sidemenu_list_entry> viewlist;
 1350 
 1351     m_selected_action_sidemenu_entry = 0;
 1352 
 1353     sinsp_view_info* vinfo = get_selected_view();
 1354 
 1355     for(auto hk : vinfo->m_actions)
 1356     {
 1357         string str = string("(") + hk.m_hotkey + ") " + hk.m_description;
 1358         viewlist.push_back(sidemenu_list_entry(str, k++));
 1359     }
 1360 
 1361     if(viewlist.size() == 0)
 1362     {
 1363         viewlist.push_back(sidemenu_list_entry("<NO ACTIONS>", 0));
 1364     }
 1365 
 1366     if(m_action_sidemenu != NULL)
 1367     {
 1368         m_action_sidemenu->m_selct = 0;
 1369         m_action_sidemenu->set_entries(&viewlist);
 1370     }
 1371 }
 1372 #endif // NOCURSESUI
 1373 
 1374 string combine_filters(string flt1, string flt2)
 1375 {
 1376     if(flt1 == "")
 1377     {
 1378         return flt2;
 1379     }
 1380     else
 1381     {
 1382         if(flt2 == "")
 1383         {
 1384             return flt1;
 1385         }
 1386     }
 1387 
 1388     string res = "(" + flt1 + ") and (" + flt2 + ")";
 1389     return res;
 1390 }
 1391 
 1392 Json::Value sinsp_cursesui::generate_json_info_section()
 1393 {
 1394     Json::Value jinfo;
 1395     Json::Value jlegend;
 1396 
 1397     sinsp_view_info* wi = NULL;
 1398 
 1399     if(m_selected_view >= 0)
 1400     {
 1401         wi = m_views.at(m_selected_view);
 1402         vector<int32_t> colsizes;
 1403         vector<string> colnames;
 1404 
 1405         ASSERT(wi != NULL);
 1406 
 1407         jinfo["sortingCol"] = wi->m_sortingcol;
 1408 
 1409         for(auto av : wi->m_applies_to)
 1410         {
 1411             jinfo["appliesTo"].append(av);
 1412         }
 1413 
 1414         sinsp_view_column_info* kinfo = wi->get_key();
 1415         if(kinfo)
 1416         {
 1417             jinfo["drillDownKeyField"] = kinfo->m_field;
 1418             jinfo["canDrillDown"] = true;
 1419         }
 1420         else
 1421         {
 1422             jinfo["canDrillDown"] = false;
 1423         }
 1424 
 1425         wi->get_col_names_and_sizes(&colnames, &colsizes);
 1426 
 1427         uint32_t off;
 1428         if(colnames.size() == m_datatable->m_types->size() - 1)
 1429         {
 1430             off = 1;
 1431         }
 1432         else
 1433         {
 1434             off = 0;
 1435         }
 1436 
 1437         vector<filtercheck_field_info>* tlegend = m_datatable->get_legend();
 1438         ASSERT(tlegend->size() == colnames.size());
 1439 
 1440         for(uint32_t j = 1; j < colnames.size(); j++)
 1441         {
 1442             Json::Value jcinfo;
 1443 
 1444             jcinfo["name"] = colnames[j];
 1445             jcinfo["size"] = colsizes[j];
 1446             jcinfo["type"] = param_type_to_string(m_datatable->m_types->at(j + off));
 1447             jcinfo["format"] = print_format_to_string(tlegend->at(j).m_print_format);
 1448             
 1449             jlegend.append(jcinfo);
 1450         }
 1451     }
 1452 
 1453     jinfo["legend"] = jlegend;
 1454     return jinfo;
 1455 }
 1456 
 1457 void sinsp_cursesui::handle_end_of_sample(sinsp_evt* evt, int32_t next_res)
 1458 {
 1459     vector<sinsp_sample_row>* sample;
 1460     m_datatable->flush(evt);
 1461 
 1462     //
 1463     // It's time to refresh the data for this chart.
 1464     // First of all, create the data for the chart
 1465     //
 1466     if(m_output_type == sinsp_table::OT_JSON && (m_inspector->is_live() || (m_eof > 0)))
 1467     {
 1468         printf("{\"progress\": 100, ");
 1469 
 1470         sample = m_datatable->get_sample(get_time_delta());
 1471 
 1472         printf("\"count\": %" PRIu64 ", ", 
 1473             m_datatable->m_json_output_lines_count);
 1474 
 1475         Json::Value root = generate_json_info_section();
 1476 
 1477         if(m_views.at(m_selected_view)->m_type == sinsp_view_info::T_TABLE)
 1478         {
 1479             bool res;
 1480             execute_table_action(STA_DRILLDOWN_TEMPLATE, 0, &res);
 1481             create_complete_filter(true);
 1482 
 1483             root["filterTemplateF"] = m_complete_filter;
 1484             root["filterTemplate"] = m_complete_filter_noview;
 1485         }
 1486 
 1487         Json::FastWriter writer;
 1488         string jstr = writer.write(root);
 1489         printf("\"info\": %s", jstr.substr(0, jstr.size() - 1).c_str());
 1490 
 1491         printf("}\n");
 1492         //printf("%c", EOF);
 1493     }
 1494     else
 1495     {
 1496         if(m_output_type != sinsp_table::OT_JSON)
 1497         {
 1498             sample = m_datatable->get_sample(get_time_delta());
 1499         }
 1500     }
 1501 
 1502 #ifndef NOCURSESUI
 1503     if(m_output_type == sinsp_table::OT_CURSES)
 1504     {
 1505         //
 1506         // If the help page has been shown, don't update the screen
 1507         //
 1508         if(m_viewinfo_page != NULL || m_mainhelp_page != NULL)
 1509         {
 1510             return;
 1511         }
 1512 
 1513         //
 1514         // Now refresh the UI.
 1515         //
 1516         if(!m_paused)
 1517         {
 1518             if(m_viz)
 1519             {
 1520                 ASSERT(m_spectro == NULL);
 1521                 m_viz->update_data(sample);
 1522 
 1523                 if(m_datatable->m_type == sinsp_table::TT_LIST && m_inspector->is_live())
 1524                 {
 1525                     m_viz->follow_end();
 1526                 }
 1527 
 1528                 m_viz->render(true);
 1529             }
 1530             else if(m_spectro)
 1531             {
 1532                 ASSERT(m_viz == NULL);
 1533                 m_spectro->update_data(sample);
 1534                 m_spectro->render(true);
 1535             }
 1536         }
 1537 
 1538         render();
 1539     }
 1540 #endif
 1541     //
 1542     // If this is a trace file, check if we reached the end of the file.
 1543     // Or, if we are in replay mode, wait for a key press before processing
 1544     // the next sample.
 1545     //
 1546     if(!m_inspector->is_live())
 1547     {
 1548 #ifndef NOCURSESUI
 1549 /*
 1550         if(m_output_type == sinsp_table::OT_CURSES)
 1551         {
 1552             if(m_offline_replay)
 1553             {
 1554                 while(getch() != ' ')
 1555                 {
 1556                     usleep(10000);
 1557                 }
 1558             }
 1559         }
 1560 */      
 1561 #endif
 1562     }
 1563 }
 1564 
 1565 void sinsp_cursesui::restart_capture(bool is_spy_switch)
 1566 {
 1567     if(!m_inspector->is_live() && m_n_evts_in_file == 0)
 1568     {
 1569         m_n_evts_in_file = m_inspector->get_num_events();
 1570         m_evt_ts_delta = m_last_evt_ts - m_1st_evt_ts;
 1571     }
 1572 
 1573     m_inspector->close();
 1574     start(true, is_spy_switch);
 1575     m_inspector->open(m_event_source_name);
 1576 }
 1577 
 1578 void sinsp_cursesui::create_complete_filter(bool templated)
 1579 {
 1580     if(m_is_filter_sysdig)
 1581     {
 1582         if(m_manual_filter != "")
 1583         {
 1584             m_complete_filter = m_manual_filter;
 1585         }
 1586 
 1587         m_complete_filter_noview = m_complete_filter;
 1588     }
 1589     else
 1590     {
 1591         m_complete_filter = m_cmdline_capture_filter;
 1592         m_complete_filter = combine_filters(m_complete_filter, m_sel_hierarchy.tofilter(templated));
 1593 
 1594         m_complete_filter_noview = m_complete_filter;
 1595 
 1596         //
 1597         // Note: m_selected_view is smaller than 0 when there's no view, because we're doing
 1598         //       non-view stuff like spying.
 1599         //
 1600         if(m_selected_view >= 0)
 1601         {
 1602             m_complete_filter = combine_filters(m_complete_filter, 
 1603                 m_views.at(m_selected_view)->get_filter(m_view_depth));
 1604         }
 1605     }
 1606 }
 1607 
 1608 void sinsp_cursesui::switch_view(bool is_spy_switch)
 1609 {
 1610 #ifndef NOCURSESUI
 1611     if(m_output_type == sinsp_table::OT_CURSES)
 1612     {
 1613         //
 1614         // Clear the screen to make sure all the crap is removed
 1615         //
 1616         clear();
 1617 
 1618         //
 1619         // If we're currently visualizing the spy box, reset it and return immediately
 1620         //
 1621         if(is_spy_switch)
 1622         {
 1623             if(m_spy_box)
 1624             {
 1625                 m_spy_box->reset();
 1626             }
 1627         }
 1628     }
 1629 #endif
 1630 
 1631     //
 1632     // Put the current view in the hierarchy stack
 1633     //
 1634 #if 1
 1635     sinsp_view_info* psv = get_prev_selected_view();
 1636 
 1637     if(psv != NULL)
 1638     {
 1639         if(m_sel_hierarchy.size() > 0)
 1640         {
 1641             sinsp_ui_selection_info* psinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 1);
 1642 
 1643             m_sel_hierarchy.push_back(psinfo->m_field, psinfo->m_val,
 1644                 psv->get_key(), psinfo->m_view_filter,
 1645                 m_prev_selected_view, m_selected_view_sidemenu_entry, 
 1646                 NULL, psv->m_sortingcol, m_manual_filter, m_is_filter_sysdig,
 1647                 m_datatable->is_sorting_ascending(), false);
 1648         }
 1649         else
 1650         {
 1651             m_sel_hierarchy.push_back("", "",
 1652                 psv->get_key(), "",
 1653                 m_prev_selected_view, m_selected_view_sidemenu_entry, 
 1654                 NULL, psv->m_sortingcol, m_manual_filter, m_is_filter_sysdig,
 1655                 m_datatable->is_sorting_ascending(), false);
 1656         }
 1657     }
 1658 #endif
 1659 
 1660     //
 1661     // Clear the manual filter, but not if this is a sysdig filter and we're in the same
 1662     // view (applying sysdig filters causes the same view to the reloaded, and in that
 1663     // case we want to preserve the filter).
 1664     //
 1665     if(m_prev_selected_view != m_selected_view)
 1666     {
 1667         m_manual_filter = "";
 1668     }
 1669 
 1670     //
 1671     // If this is a file, we need to restart the capture.
 1672     // If it's a live capture, we restart only if start() fails, which usually
 1673     // happens in case one of the filter fields requested thread state.
 1674     //
 1675     if(!m_inspector->is_live())
 1676     {
 1677         m_eof = 0;
 1678         m_last_progress_evt = 0;
 1679         restart_capture(is_spy_switch);
 1680     }
 1681     else
 1682     {
 1683         //
 1684         // When live, also make sure to unpause the viz, otherwise the screen 
 1685         // will stay empty.
 1686         //
 1687         if(m_paused)
 1688         {
 1689             pause();
 1690         }
 1691 
 1692         try
 1693         {
 1694             start(true, is_spy_switch);
 1695         }
 1696         catch(...)
 1697         {
 1698             restart_capture(is_spy_switch);
 1699         }
 1700     }
 1701 
 1702 #ifndef NOCURSESUI
 1703     if(m_output_type == sinsp_table::OT_CURSES)
 1704     {
 1705         delete m_view_sidemenu;
 1706         m_view_sidemenu = NULL;
 1707 
 1708         delete m_action_sidemenu;
 1709         m_action_sidemenu = NULL;
 1710   
 1711         delete m_view_sort_sidemenu;
 1712         m_view_sort_sidemenu = NULL;
 1713 
 1714         if(m_viz != NULL)
 1715         {
 1716             m_viz->render(true);
 1717         }
 1718         else if(m_spectro != NULL)
 1719         {
 1720             m_spectro->render(true);
 1721         }
 1722 
 1723         render();
 1724     }
 1725 #endif
 1726 }
 1727 
 1728 void sinsp_cursesui::spy_selection(string field, string val, 
 1729     sinsp_view_column_info* column_info,
 1730     bool is_dig)
 1731 {
 1732     uint32_t srtcol;
 1733     sinsp_table_field rowkeybak;
 1734 
 1735 #ifdef NOCURSESUI
 1736     if(true)
 1737 #else
 1738     if(m_viz)
 1739 #endif
 1740     {
 1741 #ifndef NOCURSESUI
 1742         sinsp_table_field* rowkey = m_datatable->get_row_key(m_viz->m_selct);
 1743 #else
 1744         sinsp_table_field* rowkey = NULL;
 1745 #endif
 1746         if(rowkey != NULL)
 1747         {
 1748             rowkeybak.m_val = new uint8_t[rowkey->m_len];
 1749             memcpy(rowkeybak.m_val, rowkey->m_val, rowkey->m_len);
 1750             rowkeybak.m_len = rowkey->m_len;
 1751         }
 1752 
 1753         srtcol = m_datatable->get_sorting_col();
 1754     }
 1755 #ifndef NOCURSESUI
 1756     else if(m_spectro)
 1757     {
 1758         m_is_filter_sysdig = true;
 1759         m_manual_filter = m_spectro->m_selection_filter;
 1760         srtcol = 0;
 1761         rowkeybak.m_val = NULL;
 1762         rowkeybak.m_len = 0;
 1763         srtcol = 2;
 1764     }
 1765     else
 1766     {
 1767         ASSERT(false);
 1768         return;
 1769     }
 1770 #endif
 1771 
 1772     ASSERT(m_selected_view < (int32_t)m_views.size());
 1773 
 1774     if(m_views.at(m_selected_view)->m_drilldown_increase_depth)
 1775     {
 1776         m_view_depth++;
 1777     }
 1778 
 1779     string vfilter;
 1780     if(m_views.at(m_selected_view)->m_propagate_filter)
 1781     {
 1782         vfilter = m_views.at(m_selected_view)->get_filter(m_view_depth);
 1783     }
 1784     else
 1785     {
 1786         vfilter = "";
 1787     }
 1788 
 1789     m_sel_hierarchy.push_back(field, val, column_info, 
 1790         vfilter,
 1791         m_selected_view, m_selected_view_sidemenu_entry, 
 1792         &rowkeybak, srtcol, m_manual_filter, m_is_filter_sysdig, 
 1793         m_datatable->is_sorting_ascending(), true);
 1794 
 1795     if(is_dig)
 1796     {
 1797         m_selected_view = VIEW_ID_DIG;
 1798     }
 1799     else
 1800     {
 1801         m_selected_view = VIEW_ID_SPY;
 1802     }
 1803 
 1804     if(!m_inspector->is_live())
 1805     {
 1806         m_eof = 0;
 1807         m_last_progress_evt = 0;
 1808         restart_capture(false);
 1809     }
 1810     else
 1811     {
 1812         try
 1813         {
 1814             start(true, false);
 1815         }
 1816         catch(...)
 1817         {
 1818             restart_capture(false);
 1819         }
 1820     }
 1821 
 1822 #ifndef NOCURSESUI
 1823     render();
 1824 #endif
 1825 }
 1826 
 1827 // returns false if there is no suitable drill down view for this field
 1828 bool sinsp_cursesui::do_drilldown(string field, string val, 
 1829     sinsp_view_column_info* column_info,
 1830     uint32_t new_view_num, filtercheck_field_info* info,
 1831     bool dont_restart)
 1832 {
 1833     //
 1834     // unpause the thing if it's paused
 1835     //
 1836     if(m_paused)
 1837     {
 1838         pause();
 1839     }
 1840 
 1841     //
 1842     //  escape string parameters
 1843     //
 1844     if(info != NULL && info->m_type & PT_CHARBUF)
 1845     {
 1846         string escape = "\"";
 1847         val = escape + val + escape;
 1848     }
 1849 
 1850     //
 1851     // Do the drilldown
 1852     //
 1853     sinsp_table_field* rowkey = NULL;
 1854 
 1855 #ifndef NOCURSESUI
 1856     if(m_viz != NULL)
 1857     {
 1858         rowkey = m_datatable->get_row_key(m_viz->m_selct);
 1859     }
 1860 #endif
 1861     sinsp_table_field rowkeybak;
 1862     if(rowkey != NULL)
 1863     {
 1864         rowkeybak.m_val = new uint8_t[rowkey->m_len];
 1865         memcpy(rowkeybak.m_val, rowkey->m_val, rowkey->m_len);
 1866         rowkeybak.m_len = rowkey->m_len;
 1867     }
 1868 
 1869     uint32_t srtcol;
 1870     srtcol = m_datatable->get_sorting_col();
 1871 
 1872     if(m_views.at(m_selected_view)->m_drilldown_increase_depth)
 1873     {
 1874         if(m_views.at(new_view_num)->m_id != "spectro_tracers")
 1875         {
 1876             m_view_depth++;
 1877         }
 1878     }
 1879 
 1880     string vfilter;
 1881     if(m_views.at(m_selected_view)->m_propagate_filter)
 1882     {
 1883         vfilter = m_views.at(m_selected_view)->get_filter(m_view_depth);
 1884     }
 1885     else
 1886     {
 1887         vfilter = "";
 1888     }
 1889 
 1890     m_sel_hierarchy.push_back(field, val, 
 1891         column_info, vfilter,
 1892         m_selected_view, m_selected_view_sidemenu_entry, 
 1893         &rowkeybak, srtcol, m_manual_filter, m_is_filter_sysdig,
 1894         m_datatable->is_sorting_ascending(), true);
 1895 
 1896     m_selected_view = new_view_num;
 1897 
 1898     //
 1899     // Reset the filter
 1900     //
 1901 #ifndef NOCURSESUI
 1902     if(m_output_type != sinsp_table::OT_JSON)
 1903     {
 1904         if(m_viz != NULL)
 1905         {
 1906             m_manual_filter = "";
 1907             m_is_filter_sysdig = false;
 1908         }
 1909         else
 1910         {
 1911             ASSERT(m_spectro != NULL);
 1912             m_is_filter_sysdig = true;
 1913             m_manual_filter = m_spectro->m_selection_filter;
 1914         }
 1915     }
 1916 #endif
 1917 
 1918     if(!dont_restart)
 1919     {
 1920         if(!m_inspector->is_live())
 1921         {
 1922             m_eof = 0;
 1923             m_last_progress_evt = 0;
 1924             restart_capture(false);
 1925         }
 1926         else
 1927         {
 1928             try
 1929             {
 1930                 start(true, false);
 1931             }
 1932             catch(...)
 1933             {
 1934                 restart_capture(false);
 1935             }
 1936         }
 1937 
 1938 #ifndef NOCURSESUI
 1939         clear();
 1940         populate_view_sidemenu(field, &m_sidemenu_viewlist);
 1941         populate_action_sidemenu();
 1942 
 1943         if(m_viz)
 1944         {
 1945             m_viz->render(true);
 1946         }
 1947         else if(m_spectro)
 1948         {
 1949             m_spectro->render(true);
 1950         }
 1951         render();
 1952 #endif
 1953     }
 1954 
 1955     return true;
 1956 }
 1957 
 1958 // returns false if there is no suitable drill down view for this field
 1959 bool sinsp_cursesui::drilldown(string field, string val, 
 1960     sinsp_view_column_info* column_info,
 1961     filtercheck_field_info* info, bool dont_restart)
 1962 {
 1963     uint32_t j = 0;
 1964 
 1965     for(j = 0; j < m_views.size(); ++j)
 1966     {
 1967         if(m_views.at(j)->m_id == m_views.at(m_selected_view)->m_drilldown_target)
 1968         {
 1969             return do_drilldown(field, val, column_info, j, info, dont_restart);            
 1970         }
 1971     }
 1972 
 1973     for(j = 0; j < m_views.size(); ++j)
 1974     {
 1975         auto it = m_views.at(j);
 1976 
 1977         for(auto atit = it->m_applies_to.begin(); atit != it->m_applies_to.end(); ++atit)
 1978         {
 1979             if(*atit == field)
 1980             {
 1981                 return do_drilldown(field, val, column_info, j, info, dont_restart);
 1982             }
 1983         }
 1984     }
 1985 
 1986     return false;
 1987 }
 1988 
 1989 bool sinsp_cursesui::spectro_selection(string field, string val,
 1990     sinsp_view_column_info* column_info,
 1991     filtercheck_field_info* info, sysdig_table_action ta)
 1992 {
 1993     uint32_t j = 0;
 1994     string spectro_name;
 1995 
 1996     if(m_views.at(m_selected_view)->m_spectro_type == "tracers")
 1997     {
 1998         spectro_name = "spectro_traces";
 1999     }
 2000     else
 2001     {
 2002         if(ta == STA_SPECTRO)
 2003         {
 2004             spectro_name = "spectro_all";
 2005         }
 2006         else
 2007         {
 2008             spectro_name = "spectro_file";
 2009         }
 2010     }
 2011 
 2012     for(j = 0; j < m_views.size(); ++j)
 2013     {
 2014         if(m_views.at(j)->m_id == spectro_name)
 2015         {
 2016             return do_drilldown(field, val, column_info, j, info, false);
 2017         }
 2018     }
 2019 
 2020     return false;
 2021 }
 2022 
 2023 bool sinsp_cursesui::drillup()
 2024 {
 2025     if(m_sel_hierarchy.size() > 0)
 2026     {
 2027         //
 2028         // unpause the thing if it's paused
 2029         //
 2030         if(m_paused)
 2031         {
 2032             pause();
 2033         }
 2034 
 2035         //
 2036         // Do the drillup
 2037         //
 2038         string field;
 2039         sinsp_ui_selection_info* psinfo = NULL;
 2040 
 2041         sinsp_ui_selection_info* sinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 1);
 2042         bool is_spctro_app = false;
 2043 
 2044         if(m_selected_view > 0 && m_views.at(m_selected_view)->m_id == "spectro_tracers")
 2045         {
 2046             is_spctro_app = true;
 2047         }
 2048 
 2049         m_manual_filter = "";
 2050 
 2051         if(m_sel_hierarchy.size() > 1)
 2052         {
 2053             psinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 2);
 2054             field = psinfo->m_field;
 2055         }
 2056 
 2057         sinsp_table_field rowkey = sinfo->m_rowkey;
 2058 
 2059         m_selected_view = sinfo->m_prev_selected_view;
 2060         m_selected_view_sidemenu_entry = sinfo->m_prev_selected_sidemenu_entry;
 2061 
 2062         if(m_views.at(m_selected_view)->m_drilldown_increase_depth &&
 2063             !is_spctro_app)
 2064         {
 2065             if(sinfo != NULL && sinfo->m_is_drilldown)
 2066             {
 2067                 m_view_depth--;
 2068             }
 2069         }
 2070 
 2071         if(m_views.at(m_selected_view)->m_type == sinsp_view_info::T_SPECTRO)
 2072         {
 2073             m_is_filter_sysdig = false;
 2074         }
 2075         else
 2076         {
 2077             m_manual_filter = sinfo->m_prev_manual_filter;
 2078             m_is_filter_sysdig = sinfo->m_prev_is_filter_sysdig;
 2079         }
 2080         
 2081         bool is_sorting_ascending = sinfo->m_prev_is_sorting_ascending;
 2082 
 2083         ASSERT(m_selected_view < (int32_t)m_views.size());
 2084 
 2085         m_sel_hierarchy.pop_back();
 2086         //m_views[m_selected_view].m_filter = m_sel_hierarchy.tofilter();
 2087 
 2088         m_complete_filter = m_cmdline_capture_filter;
 2089         m_complete_filter = combine_filters(m_complete_filter, m_sel_hierarchy.tofilter(false));
 2090 
 2091         if(!m_inspector->is_live())
 2092         {
 2093             m_eof = 0;
 2094             m_last_progress_evt = 0;
 2095             restart_capture(false);
 2096         }
 2097         else
 2098         {
 2099             try
 2100             {
 2101                 start(true, false);
 2102             }
 2103             catch(...)
 2104             {
 2105                 restart_capture(false);
 2106             }
 2107         }
 2108 #ifndef NOCURSESUI
 2109         if(m_viz)
 2110         {
 2111             if(rowkey.m_val != NULL)
 2112             {
 2113                 m_viz->m_last_key.copy(&rowkey);
 2114                 m_viz->m_last_key.m_isvalid = true;
 2115                 m_viz->m_selection_changed = true;
 2116             }
 2117             else
 2118             {
 2119                 m_viz->m_last_key.m_isvalid = false;
 2120             }
 2121 
 2122             m_viz->m_drilled_up = true;
 2123         }
 2124 
 2125         populate_view_sidemenu(field, &m_sidemenu_viewlist);
 2126         populate_action_sidemenu();
 2127 
 2128         //
 2129         // If sorting is different from the default one, restore it
 2130         //
 2131         if(sinfo->m_prev_sorting_col != m_views.at(m_selected_view)->m_sortingcol)
 2132         {
 2133             m_datatable->set_sorting_col(sinfo->m_prev_sorting_col);
 2134         }
 2135 
 2136         m_datatable->set_is_sorting_ascending(is_sorting_ascending);
 2137 
 2138         //
 2139         // If filtering is different from the default one, apply it
 2140         //
 2141         if(m_manual_filter != "" && !m_is_filter_sysdig)
 2142         {
 2143             m_datatable->set_freetext_filter(m_manual_filter);
 2144         }
 2145 
 2146         clear();
 2147         if(m_viz)
 2148         {
 2149             m_viz->render(true);
 2150         }
 2151         else if(m_spectro)
 2152         {
 2153             m_spectro->render(true);
 2154         }
 2155 
 2156         render();
 2157 #endif
 2158 
 2159         if(rowkey.m_val)
 2160         {
 2161             delete[] rowkey.m_val;
 2162         }
 2163         return true;
 2164     }
 2165 
 2166     return false;
 2167 }
 2168 
 2169 void sinsp_cursesui::pause()
 2170 {
 2171     m_paused = !m_paused;
 2172     if(m_datatable != NULL)
 2173     {
 2174         m_datatable->set_paused(m_paused);
 2175     }
 2176 #ifndef NOCURSESUI
 2177     if(m_spectro == NULL)
 2178     {
 2179         render_header();
 2180     }
 2181 #endif
 2182 }
 2183 
 2184 #ifndef NOCURSESUI
 2185 void sinsp_cursesui::print_progress(double progress)
 2186 {
 2187     attrset(m_colors[sinsp_cursesui::PROCESS]);
 2188 
 2189     string wstr = "Processing File";
 2190     mvprintw(m_screenh / 2,
 2191         m_screenw / 2 - wstr.size() / 2, 
 2192         wstr.c_str());  
 2193 
 2194     //
 2195     // Using sprintf because to_string doesn't support setting the precision 
 2196     //
 2197     char numbuf[64];
 2198     sprintf(numbuf, "%.2lf", progress);
 2199     wstr = "Progress: " + string(numbuf);
 2200     mvprintw(m_screenh / 2 + 1,
 2201         m_screenw / 2 - wstr.size() / 2, 
 2202         wstr.c_str());
 2203 
 2204     refresh();
 2205 }
 2206 
 2207 sysdig_table_action sinsp_cursesui::handle_textbox_input(int ch)
 2208 {
 2209     bool closing = false;
 2210     string* str = NULL;
 2211     bool handled = true;
 2212 
 2213     //
 2214     // Pick the right string based on what we're doing
 2215     //
 2216     if(m_output_filtering)
 2217     {
 2218         str = &m_manual_filter;
 2219     }
 2220     else if(m_output_searching)
 2221     {
 2222         str = &m_manual_search_text;
 2223     }
 2224     else
 2225     {
 2226         if(m_search_caller_interface)
 2227         {
 2228             str = m_search_caller_interface->get_last_search_string();
 2229         }
 2230         else
 2231         {
 2232             ASSERT(false);
 2233         }
 2234     }
 2235 
 2236     switch(ch)
 2237     {
 2238         case KEY_F(1):
 2239             m_mainhelp_page = new curses_mainhelp_page(this);
 2240             return STA_NONE;
 2241         case KEY_F(2):
 2242             m_is_filter_sysdig = !m_is_filter_sysdig;
 2243             *str = "";
 2244             m_cursor_pos = 0;
 2245             render();
 2246             return STA_NONE;
 2247         case KEY_DOWN:
 2248         case KEY_UP:
 2249         case KEY_PPAGE:
 2250         case KEY_NPAGE:
 2251             if(m_spy_box != NULL)
 2252             {
 2253                 m_spy_box->handle_input(ch);
 2254             }
 2255             else
 2256             {
 2257                 if(m_viz)
 2258                 {
 2259                     m_viz->handle_input(ch);
 2260                 }
 2261                 else if(m_spectro)
 2262                 {
 2263                     ASSERT(false);
 2264                 }
 2265             }
 2266             return STA_NONE;
 2267         case 27: // ESC
 2268             *str = "";
 2269 
 2270             if(m_spy_box != NULL)
 2271             {
 2272                 m_spy_box->scroll_to(m_search_start_x, m_search_start_y);
 2273                 m_spy_box->up();
 2274             }
 2275             // FALL THROUGH
 2276         case '\n':
 2277         case '\r':
 2278         case KEY_ENTER:
 2279         case 6: // CTRL+F
 2280         case KEY_F(4):
 2281             closing = true;
 2282             curs_set(0);
 2283 
 2284             if(m_is_filter_sysdig && !m_output_searching)
 2285             {
 2286                 if(*str != "")
 2287                 {
 2288                     sinsp_filter_compiler compiler(m_inspector, *str);
 2289                     sinsp_filter* f;
 2290 
 2291                     try
 2292                     {
 2293                         f = compiler.compile();
 2294                     }
 2295                     catch(sinsp_exception e)
 2296                     {
 2297                         //
 2298                         // Backup the cursor position
 2299                         //
 2300                         int cx, cy;
 2301                         getyx(stdscr, cy, cx);
 2302 
 2303                         //
 2304                         // Print the error string
 2305                         //
 2306                         string wstr = "Invalid sysdig filter";
 2307 
 2308                         attrset(m_colors[sinsp_cursesui::FAILED_SEARCH]);
 2309                         mvprintw(m_screenh / 2,
 2310                             m_screenw / 2 - wstr.size() / 2, 
 2311                             wstr.c_str());  
 2312 
 2313                         //
 2314                         // Restore the cursor
 2315                         //
 2316                         attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]);
 2317                         move(cy, cx);
 2318                         curs_set(1);
 2319                         closing = false;
 2320                         break;
 2321                     }
 2322 
 2323                     delete f;
 2324                 }
 2325             }
 2326 
 2327             break;
 2328         case KEY_BACKSPACE:
 2329         case 127:
 2330             if(str->size() > 0)
 2331             {
 2332                 m_cursor_pos--;
 2333                 move(m_screenh - 1, m_cursor_pos);
 2334                 addch(' ');
 2335                 move(m_screenh - 1, m_cursor_pos);
 2336                 *str = str->substr(0, str->size() - 1);
 2337 
 2338                 if(str->size() < 2)
 2339                 {
 2340                     if(m_spy_box != NULL)
 2341                     {
 2342                         m_spy_box->scroll_to(m_search_start_x, m_search_start_y); 
 2343                     }
 2344                 }
 2345 
 2346                 break;
 2347             }
 2348             else
 2349             {
 2350                 return STA_NONE;
 2351             }
 2352         case KEY_F(3):
 2353             if(m_search_caller_interface)
 2354             {
 2355                 if(m_search_caller_interface->on_search_next())
 2356                 {
 2357                     render();
 2358                 }
 2359                 else
 2360                 {
 2361                     string wstr = "  NOT FOUND ";
 2362                     attrset(m_colors[sinsp_cursesui::FAILED_SEARCH]);
 2363 
 2364                     mvprintw(m_screenh / 2,
 2365                         m_screenw / 2 - wstr.size() / 2, 
 2366                         wstr.c_str());
 2367 
 2368                     render();
 2369                 }
 2370             }
 2371 
 2372             break;
 2373         default:
 2374             handled = false;
 2375             break;
 2376     }
 2377 
 2378     if(ch >= ' ' && ch <= '~')
 2379     {
 2380         addch(ch);
 2381         *str += ch;
 2382         m_cursor_pos++;
 2383     }
 2384     else
 2385     {
 2386         if(!handled)
 2387         {
 2388             return STA_NONE;
 2389         }
 2390     }
 2391 
 2392     if(m_output_filtering)
 2393     {
 2394         if(!m_is_filter_sysdig)
 2395         {
 2396             //
 2397             // Update the filter in the datatable
 2398             //
 2399             m_datatable->set_freetext_filter(*str);
 2400 
 2401             //
 2402             // Refresh the data and the visualization
 2403             //
 2404             m_viz->update_data(m_datatable->get_sample(get_time_delta()), true);
 2405             m_viz->render(true);
 2406         }
 2407     }
 2408     else if(m_output_searching)
 2409     {
 2410         sinsp_table_field* skey = m_datatable->search_in_sample(*str);
 2411 
 2412         if(skey != NULL)
 2413         {
 2414             int32_t selct = m_datatable->get_row_from_key(skey);
 2415             m_viz->goto_row(selct);
 2416             m_search_nomatch = false;
 2417         }
 2418         else
 2419         {
 2420             m_search_nomatch = true;
 2421             m_viz->render(true);
 2422         }
 2423     }
 2424     else
 2425     {
 2426         if(m_search_caller_interface)
 2427         {
 2428             if(m_search_caller_interface->on_search_key_pressed(*str))
 2429             {
 2430                 render();
 2431             }
 2432             else
 2433             {
 2434                 string wstr = "  NOT FOUND ";
 2435                 attrset(m_colors[sinsp_cursesui::FAILED_SEARCH]);
 2436 
 2437                 mvprintw(m_screenh / 2,
 2438                     m_screenw / 2 - wstr.size() / 2, 
 2439                     wstr.c_str());
 2440 
 2441                 render();
 2442             }
 2443 
 2444             render();
 2445         }
 2446         else
 2447         {
 2448             ASSERT(false);
 2449         }
 2450     }
 2451 
 2452     if(closing)
 2453     {
 2454         sysdig_table_action res = STA_NONE;
 2455 
 2456         if(m_is_filter_sysdig && !m_output_searching)
 2457         {
 2458             res = STA_SWITCH_VIEW;
 2459         }
 2460 
 2461         m_search_nomatch = false;
 2462         m_output_filtering = false;
 2463         m_output_searching = false;
 2464         m_search_caller_interface = NULL;
 2465         render();
 2466 
 2467         if(res != STA_NONE)
 2468         {
 2469             return res;
 2470         }
 2471     }
 2472 
 2473     return STA_NONE;
 2474 }
 2475 
 2476 sysdig_table_action sinsp_cursesui::handle_input(int ch)
 2477 {
 2478     //
 2479     // Avoid parsing keys during file load
 2480     //
 2481     if((!m_inspector->is_live()) && !is_eof() && 
 2482         (m_spectro != NULL && !m_spectro->m_scroll_paused))
 2483     {
 2484         if(ch != KEY_BACKSPACE &&
 2485             ch != 127 &&
 2486             ch != 'q' &&
 2487             ch != KEY_F(10))
 2488         {
 2489             return STA_NONE;
 2490         }
 2491     }
 2492 
 2493     if(m_mainhelp_page != NULL)
 2494     {
 2495         sysdig_table_action actn = m_mainhelp_page->handle_input(ch);
 2496 
 2497         if(actn == STA_DESTROY_CHILD)
 2498         {
 2499             delete m_mainhelp_page;
 2500             m_mainhelp_page = NULL;
 2501 
 2502             if(m_spy_box)
 2503             {
 2504                 m_spy_box->render();
 2505             }
 2506 
 2507             if(m_viz != NULL)
 2508             {
 2509                 m_viz->render(true);
 2510             }
 2511             else if(m_spectro)
 2512             {
 2513                 switch_view(false);
 2514             }
 2515 
 2516             if(m_viewinfo_page)
 2517             {
 2518                 m_viewinfo_page->render();
 2519             }
 2520 
 2521             render();
 2522             return STA_NONE;
 2523         }
 2524         else if(actn != STA_PARENT_HANDLE)
 2525         {
 2526             return actn;            
 2527         }
 2528     }
 2529 
 2530     if(m_view_sidemenu != NULL)
 2531     {
 2532         ASSERT(m_action_sidemenu == NULL);
 2533 
 2534         sysdig_table_action ta = m_view_sidemenu->handle_input(ch);
 2535         if(ta == STA_SWITCH_VIEW)
 2536         {
 2537             if(m_viewinfo_page)
 2538             {
 2539                 delete m_viewinfo_page;
 2540                 m_viewinfo_page = NULL;
 2541             }
 2542             return ta;
 2543         }
 2544         else if(ta != STA_PARENT_HANDLE)
 2545         {
 2546             return STA_NONE;
 2547         }
 2548     }
 2549     else
 2550     {
 2551         if(m_action_sidemenu != NULL)
 2552         {
 2553             sysdig_table_action ta = m_action_sidemenu->handle_input(ch);
 2554             if(ta == STA_SWITCH_VIEW)
 2555             {
 2556                 sinsp_view_info* vinfo = get_selected_view();
 2557 
 2558                 g_logger.format("running action %d %s", m_selected_action_sidemenu_entry,
 2559                     vinfo->m_name.c_str());
 2560                 if(vinfo->m_actions.size() != 0)
 2561                 {
 2562                     ASSERT(m_selected_action_sidemenu_entry < vinfo->m_actions.size());
 2563                     run_action(&vinfo->m_actions[m_selected_action_sidemenu_entry]);
 2564                 }
 2565 
 2566                 return ta;
 2567             }
 2568             else if(ta == STA_DESTROY_CHILD)
 2569             {
 2570                 if(m_viz)
 2571                 {
 2572                     m_viz->set_x_start(0);
 2573                     delete m_action_sidemenu;
 2574                     m_action_sidemenu = NULL;
 2575                     m_viz->set_x_start(0);
 2576                     m_viz->recreate_win(m_screenh - 3);
 2577                     m_viz->render(true);
 2578                     m_viz->render(true);
 2579                 }
 2580                 else if(m_spectro)
 2581                 {
 2582                     delete m_action_sidemenu;
 2583                     m_action_sidemenu = NULL;
 2584                     m_spectro->recreate_win(m_screenh - 3);
 2585                     m_spectro->render(true);
 2586                     m_spectro->render(true);
 2587 
 2588                 }               
 2589                 render();
 2590             }
 2591             else if(ta != STA_PARENT_HANDLE)
 2592             {
 2593                 return STA_NONE;
 2594             }
 2595         }
 2596 
 2597         if(m_view_sort_sidemenu != NULL)
 2598         {
 2599             sysdig_table_action ta = m_view_sort_sidemenu->handle_input(ch);
 2600             if(ta == STA_SWITCH_VIEW || ta == STA_DESTROY_CHILD)
 2601             {
 2602                 if(ta == STA_SWITCH_VIEW) 
 2603                 {
 2604                     ASSERT(m_selected_view_sort_sidemenu_entry < get_selected_view()->m_columns.size());
 2605                     m_datatable->set_sorting_col(m_selected_view_sort_sidemenu_entry+1);
 2606                     m_datatable->sort_sample();
 2607                     m_viz->update_data(m_viz->m_data);
 2608                 }
 2609                 delete m_view_sort_sidemenu;
 2610                 m_view_sort_sidemenu = NULL;
 2611                 m_viz->set_x_start(0);
 2612                 m_viz->recreate_win(m_screenh - 3);
 2613                 m_viz->render(true);
 2614                 render();               
 2615                 if(ta == STA_SWITCH_VIEW) 
 2616                 {
 2617                     return STA_NONE;
 2618                 }
 2619             }
 2620             else if(ta != STA_PARENT_HANDLE)
 2621             {
 2622                 return STA_NONE;
 2623             }
 2624         }
 2625 
 2626     }
 2627 
 2628     if(m_output_filtering || m_output_searching || m_search_caller_interface != NULL)
 2629     {
 2630         ASSERT(m_view_sidemenu == NULL);
 2631         ASSERT(m_action_sidemenu == NULL);
 2632         return handle_textbox_input(ch);
 2633     }
 2634 
 2635     if(m_spy_box != NULL)
 2636     {
 2637         ASSERT(m_view_sidemenu == NULL);
 2638         ASSERT(m_action_sidemenu == NULL);
 2639         ASSERT(m_output_filtering == false);
 2640         ASSERT(m_output_searching == false);
 2641         sysdig_table_action actn = m_spy_box->handle_input(ch);
 2642 
 2643         if(actn != STA_PARENT_HANDLE)
 2644         {
 2645             return actn;
 2646         }
 2647     }
 2648 
 2649     //
 2650     // Note: the info page doesn't handle input when the sidemenu is on, because in that
 2651     //       case it's just going to passively show the info for the selected view
 2652     //
 2653     if(m_viewinfo_page && m_view_sidemenu == NULL)
 2654     {
 2655         ASSERT(m_view_sidemenu == NULL);
 2656 
 2657         sysdig_table_action actn = m_viewinfo_page->handle_input(ch);
 2658 
 2659         if(actn == STA_DESTROY_CHILD)
 2660         {
 2661             delete m_viewinfo_page;
 2662             m_viewinfo_page = NULL;
 2663             if(m_viz != NULL)
 2664             {
 2665                 m_viz->render(true);
 2666             }
 2667 
 2668             render();
 2669             return STA_NONE;
 2670         }
 2671 
 2672         return actn;
 2673     }
 2674 
 2675     //
 2676     // Pass the event to the table viz
 2677     //
 2678     if(m_viz)
 2679     {
 2680         sysdig_table_action actn = m_viz->handle_input(ch);
 2681         if(actn != STA_PARENT_HANDLE)
 2682         {
 2683             return actn;
 2684         }
 2685     }
 2686     else if(m_spectro)
 2687     {
 2688         sysdig_table_action actn = m_spectro->handle_input(ch);
 2689         if(actn != STA_PARENT_HANDLE)
 2690         {
 2691             return actn;
 2692         }
 2693     }
 2694 
 2695     switch(ch)
 2696     {
 2697         case '?':
 2698         case 'h':
 2699         case KEY_F(1):
 2700             m_mainhelp_page = new curses_mainhelp_page(this);
 2701             break;
 2702         case KEY_F(10):
 2703         case 'q':
 2704             return STA_QUIT;
 2705         case 'p':
 2706             pause();
 2707             break;
 2708         case KEY_F(2):
 2709             if(m_action_sidemenu != NULL)
 2710             {
 2711                 break;
 2712             }
 2713 
 2714             if(m_view_sidemenu == NULL)
 2715             {
 2716                 if(m_viz)
 2717                 {
 2718                     m_viz->set_x_start(VIEW_SIDEMENU_WIDTH);
 2719                 }
 2720                 else if(m_spectro)
 2721                 {
 2722                     m_spectro->set_x_start(VIEW_SIDEMENU_WIDTH);
 2723                 }
 2724 
 2725                 m_view_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_VIEWS,
 2726                     this, m_selected_view_sidemenu_entry, VIEW_SIDEMENU_WIDTH);
 2727 
 2728                 m_view_sidemenu->set_entries(&m_sidemenu_viewlist);
 2729                 m_view_sidemenu->set_title("Select View");
 2730                 render();
 2731 
 2732                 m_viewinfo_page = new curses_viewinfo_page(this, 
 2733                     m_selected_view,
 2734                     TABLE_Y_START,
 2735                     VIEW_SIDEMENU_WIDTH,
 2736                     m_screenh - TABLE_Y_START - 1,
 2737                     m_screenw - VIEW_SIDEMENU_WIDTH);
 2738 
 2739                 if(m_spectro)
 2740                 {
 2741                     render();
 2742                 }
 2743             }
 2744             else
 2745             {
 2746                 if(m_viewinfo_page)
 2747                 {
 2748                     delete m_viewinfo_page;
 2749                     m_viewinfo_page = NULL;
 2750                 }
 2751 
 2752                 delete m_view_sidemenu;
 2753                 m_view_sidemenu = NULL;
 2754 
 2755                 if(m_viz)
 2756                 {
 2757                     m_viz->set_x_start(0);
 2758                     m_viz->recreate_win(m_screenh - 3);
 2759                 }
 2760                 else if(m_spectro)
 2761                 {
 2762                     switch_view(false);                 
 2763                 }
 2764 
 2765                 render();
 2766             }
 2767 
 2768             break;
 2769         case '/':
 2770         case 6: // CTRL+F
 2771             m_search_caller_interface = NULL;
 2772             m_output_searching = true;
 2773             //m_manual_search_text = "";
 2774             m_cursor_pos = 0;
 2775             curs_set(1);
 2776             render();
 2777             break;
 2778         case KEY_F(9):
 2779         case '>':       // sort columns
 2780             if(m_view_sidemenu != NULL)
 2781             {
 2782                 break;
 2783             }
 2784             if(m_view_sort_sidemenu == NULL) 
 2785             {
 2786                 m_viz->set_x_start(VIEW_SIDEMENU_WIDTH);
 2787                 m_sidemenu_sorting_col = m_datatable->get_sorting_col() -1;
 2788                 m_view_sort_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_COLUMNS,
 2789                 this, m_sidemenu_sorting_col, VIEW_SIDEMENU_WIDTH);
 2790 
 2791                 populate_view_cols_sidemenu();
 2792                 m_view_sort_sidemenu->set_title("Select sort column");
 2793 
 2794                 m_viz->set_x_start(VIEW_SIDEMENU_WIDTH);
 2795                 m_viz->recreate_win(m_screenh - 3);
 2796                 render();
 2797                 m_viewinfo_page = NULL;
 2798             }
 2799             else
 2800             {
 2801                 m_viz->set_x_start(0);
 2802                 delete m_view_sort_sidemenu;
 2803                 m_view_sort_sidemenu = NULL;
 2804                 m_viz->set_x_start(0);
 2805                 m_viz->recreate_win(m_screenh - 3);
 2806                 m_viz->render(true);
 2807                 m_viz->render(true);
 2808                 render();
 2809             }
 2810 
 2811             break;
 2812         case '\\':
 2813         case KEY_F(4):
 2814             m_search_caller_interface = NULL;
 2815             m_output_filtering = true;
 2816             m_cursor_pos = 0;
 2817             curs_set(1);
 2818             render();
 2819             break;
 2820         case KEY_F(5):
 2821         case 'e':
 2822             if(m_datatable == NULL)
 2823             {
 2824                 //
 2825                 // No F5 for non table displays
 2826                 //
 2827                 return STA_NONE;
 2828             }
 2829             else if(m_datatable->m_type == sinsp_table::TT_LIST)
 2830             {
 2831                 //
 2832                 // No F5 for list tables
 2833                 //
 2834                 return STA_NONE;
 2835             }
 2836 
 2837             if(m_datatable->m_sample_data != NULL && m_datatable->m_sample_data->size() != 0)
 2838             {
 2839                 m_selected_view_sidemenu_entry = 0;
 2840                 m_selected_action_sidemenu_entry = 0;
 2841                 return STA_SPY;
 2842             }
 2843             break;
 2844         case KEY_F(6):
 2845         case 'd':
 2846             if(m_datatable == NULL)
 2847             {
 2848                 //
 2849                 // No F5 for non table displays
 2850                 //
 2851                 return STA_NONE;
 2852             }
 2853             else if(m_datatable->m_type == sinsp_table::TT_LIST)
 2854             {
 2855                 //
 2856                 // No F5 for list tables
 2857                 //
 2858                 return STA_NONE;
 2859             }
 2860 
 2861             if(m_datatable->m_sample_data != NULL && m_datatable->m_sample_data->size() != 0)
 2862             {
 2863                 m_selected_view_sidemenu_entry = 0;
 2864                 m_selected_action_sidemenu_entry = 0;
 2865                 return STA_DIG;
 2866             }
 2867 
 2868             break;
 2869         case KEY_F(7):
 2870             m_viewinfo_page = new curses_viewinfo_page(this,
 2871                 m_selected_view,
 2872                 0,
 2873                 0,
 2874                 m_screenh,
 2875                 m_screenw);
 2876             break;
 2877         case KEY_F(8):
 2878             if(m_view_sidemenu != NULL)
 2879             {
 2880                 break;
 2881             }
 2882 
 2883             if(!m_viz)
 2884             {
 2885                 ASSERT(false);
 2886                 break;
 2887             }
 2888 
 2889             if(m_action_sidemenu == NULL)
 2890             {
 2891                 m_viz->set_x_start(ACTION_SIDEMENU_WIDTH);
 2892                 m_action_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_ACTIONS, 
 2893                     this, m_selected_action_sidemenu_entry, ACTION_SIDEMENU_WIDTH);
 2894                 populate_action_sidemenu();
 2895                 m_action_sidemenu->set_title("Select Action");
 2896 
 2897                 m_viz->set_x_start(ACTION_SIDEMENU_WIDTH);
 2898                 m_viz->recreate_win(m_screenh - 3);
 2899 
 2900                 render();
 2901 
 2902                 m_viewinfo_page = NULL;
 2903             }
 2904             else
 2905             {
 2906                 m_viz->set_x_start(0);
 2907                 delete m_action_sidemenu;
 2908                 m_action_sidemenu = NULL;
 2909                 m_viz->set_x_start(0);
 2910                 m_viz->recreate_win(m_screenh - 3);
 2911                 m_viz->render(true);
 2912                 m_viz->render(true);
 2913                 render();
 2914             }
 2915 
 2916             break;
 2917         case KEY_RESIZE:
 2918             getmaxyx(stdscr, m_screenh, m_screenw);
 2919             
 2920             render();
 2921 
 2922             if(m_spy_box)
 2923             {
 2924                 m_spy_box->render();
 2925                 m_spy_box->render();
 2926             }
 2927 
 2928             if(m_viz != NULL)
 2929             {
 2930                 m_viz->recreate_win(m_screenh - 3);
 2931                 m_viz->render(true);
 2932                 m_viz->render(true);
 2933             }
 2934             else if(m_spectro)
 2935             {
 2936                 m_spectro->recreate_win(m_screenh - 3);
 2937                 m_spectro->render(true);
 2938                 m_spectro->render(true);                
 2939             }
 2940 
 2941             if(m_viewinfo_page)
 2942             {
 2943                 m_viewinfo_page->render();
 2944                 m_viewinfo_page->render();
 2945             }
 2946 
 2947             render();
 2948 
 2949             break;
 2950         case KEY_MOUSE:
 2951             {
 2952                 MEVENT* event = NULL;
 2953 
 2954                 if(m_view_sidemenu != NULL)
 2955                 {
 2956                     event = &m_view_sidemenu->m_last_mevent;
 2957                 }
 2958                 else if(m_view_sort_sidemenu != NULL)
 2959                 {
 2960                     event = &m_view_sort_sidemenu->m_last_mevent;
 2961                 }
 2962                 else if(m_action_sidemenu != NULL)
 2963                 {
 2964                     event = &m_action_sidemenu->m_last_mevent;
 2965                 }
 2966                 else if(m_spy_box != NULL)
 2967                 {
 2968                     event = &m_spy_box->m_last_mevent;
 2969                 }
 2970                 else if(m_viz != NULL)
 2971                 {
 2972                     event = &m_viz->m_last_mevent;
 2973                 }
 2974                 else if(m_spectro != NULL)
 2975                 {
 2976                     event = &m_spectro->m_last_mevent;
 2977                 }
 2978 
 2979                 if(event == NULL)
 2980                 {
 2981                     ASSERT(false);
 2982                     break;
 2983                 }
 2984 
 2985                 if(event->bstate & BUTTON1_CLICKED ||
 2986                     event->bstate & BUTTON1_DOUBLE_CLICKED)
 2987                 {
 2988                     if((uint32_t)event->y == m_screenh - 1)
 2989                     {
 2990                         int keyc = m_mouse_to_key_list.get_key_from_coordinates(event->x, event->y);
 2991                         if(keyc != -1)
 2992                         {
 2993                             return handle_input(keyc);
 2994                         }
 2995                     }
 2996                     else if((uint32_t)event->y == 1 &&
 2997                         (uint32_t)event->x >= m_filterstring_start_x &&
 2998                         (uint32_t)event->x <= m_filterstring_end_x)
 2999                     {
 3000                         m_search_caller_interface = NULL;
 3001                         m_is_filter_sysdig = true;
 3002                         m_output_filtering = true;
 3003                         m_manual_filter = m_complete_filter;
 3004                         m_cursor_pos = 0;
 3005                         curs_set(1);
 3006                         render();
 3007                     }
 3008                 }
 3009             }
 3010 
 3011             break;
 3012         default:
 3013             break;
 3014     }
 3015 
 3016     return STA_NONE;
 3017 }
 3018 
 3019 #endif // NOCURSESUI
 3020 
 3021 int32_t sinsp_cursesui::get_viewnum_by_name(string name)
 3022 {
 3023     for(uint32_t j = 0; j < m_views.size(); ++j)
 3024     {
 3025         if(m_views.at(j)->m_id == name)
 3026         {
 3027             return j;
 3028         }
 3029     }
 3030 
 3031     return -1;
 3032 }
 3033 
 3034 //
 3035 // Note:
 3036 //  - The return value determines if the application should quit.
 3037 //  - res is set to false in case of error
 3038 //
 3039 bool sinsp_cursesui::handle_stdin_input(bool* res)
 3040 {
 3041     string input;
 3042 
 3043     *res = true;
 3044 
 3045     //
 3046     // Get the user json input
 3047     //
 3048     while(true)
 3049     {
 3050         std::getline(std::cin, input);
 3051         if(input != "")
 3052         {
 3053             break;
 3054         }
 3055     }
 3056 
 3057     //
 3058     // Parse the input
 3059     //
 3060     Json::Value root;
 3061     Json::Reader reader;
 3062     bool pres = reader.parse(input,
 3063         root,
 3064         false);
 3065 
 3066     if(!pres)
 3067     {
 3068         fprintf(stderr, "unable to parse the json input: %s",
 3069             reader.getFormattedErrorMessages().c_str());
 3070         *res = false;
 3071         return false;
 3072     }
 3073 
 3074     string astr = root["action"].asString();
 3075     Json::Value args = root["args"];
 3076 
 3077     sysdig_table_action ta;
 3078     uint32_t rownum = 0;
 3079 
 3080     if(astr == "apply")
 3081     {
 3082         ta = STA_SWITCH_VIEW;
 3083 
 3084         string vname = args["view"].asString();
 3085 
 3086         m_selected_view = get_viewnum_by_name(vname);
 3087         if(m_selected_view == -1)
 3088         {
 3089             fprintf(stderr, "unknown view: %s", vname.c_str());
 3090             *res = false;
 3091             return false;
 3092         }
 3093     }
 3094     else if(astr == "drilldown")
 3095     {
 3096         ta = STA_DRILLDOWN;
 3097 
 3098         rownum = args["rownum"].asInt();
 3099     }
 3100     else if(astr == "drillup")
 3101     {
 3102         ta = STA_DRILLUP;
 3103     }
 3104     else if(astr == "quit")
 3105     {
 3106         return true;
 3107     }
 3108     else
 3109     {
 3110         fprintf(stderr, "invalid action: %s", astr.c_str());
 3111         *res = false;
 3112         return false;
 3113     }
 3114 
 3115     bool tres;
 3116     execute_table_action(ta, rownum, &tres);
 3117     return false;
 3118 }
 3119 
 3120 uint64_t sinsp_cursesui::get_time_delta()
 3121 {
 3122     if(m_inspector->is_live())
 3123     {
 3124         return m_refresh_interval_ns;
 3125     }
 3126     else
 3127     {
 3128         return m_last_evt_ts - m_1st_evt_ts;
 3129     }
 3130 }
 3131 
 3132 void sinsp_cursesui::run_action(sinsp_view_action_info* action)
 3133 {
 3134     string resolved_command;
 3135     bool replacing = false;
 3136     string fld_to_replace;
 3137 
 3138 #ifndef NOCURSESUI
 3139     ASSERT(m_viz != NULL);
 3140     ASSERT(m_spectro == NULL);
 3141 
 3142     if(m_viz->get_data_size() == 0)
 3143     {
 3144         //
 3145         // No elements in the table means no selection
 3146         //
 3147         return;
 3148     }
 3149 #endif // NOCURSESUI
 3150 
 3151     //
 3152     // Scan the command string and replace the field names with the values from the selection
 3153     //
 3154     for(uint32_t j = 0; j < action->m_command.size(); j++)
 3155     {
 3156         char sc = action->m_command[j];
 3157 
 3158         if(sc == '%')
 3159         {
 3160             fld_to_replace = "";
 3161 
 3162             if(replacing)
 3163             {
 3164                 throw sinsp_exception("the following command has the wrong syntax: " + action->m_command);
 3165             }
 3166 
 3167             replacing = true;
 3168         }
 3169         else
 3170         {
 3171             if(replacing)
 3172             {
 3173                 if(sc == ' ' || sc == '\t' || sc == '0')
 3174                 {
 3175                     replacing = false;
 3176 #ifndef NOCURSESUI
 3177                     string val = m_viz->get_field_val(fld_to_replace);
 3178                     resolved_command += val;
 3179 #endif // NOCURSESUI
 3180                     resolved_command += sc;
 3181                 }
 3182                 else
 3183                 {
 3184                     fld_to_replace += sc;
 3185                 }
 3186             }
 3187             else
 3188             {
 3189                 resolved_command += sc;
 3190             }
 3191         }
 3192     }
 3193 
 3194     if(replacing)
 3195     {
 3196 #ifndef NOCURSESUI
 3197         string  val = m_viz->get_field_val(fld_to_replace);
 3198         resolved_command += val;
 3199 #endif // NOCURSESUI
 3200     }
 3201 
 3202     g_logger.format("original command: %s", action->m_command.c_str());
 3203     g_logger.format("running command: %s", resolved_command.c_str());
 3204 
 3205 #ifndef NOCURSESUI
 3206     //
 3207     // Exit curses mode
 3208     //
 3209     endwin();
 3210 #endif // NOCURSESUI
 3211 
 3212     //
 3213     // If needed, ask for confirmation
 3214     //
 3215     if(action->m_ask_confirmation)
 3216     {
 3217         printf("Confirm command '%s'? [y/N] ", resolved_command.c_str());
 3218         fflush(stdout);
 3219 
 3220         //
 3221         // Wait for the enter key
 3222         // 
 3223         while(int c = getch())
 3224         {
 3225             if(c == -1)
 3226             {
 3227                 do_sleep(10000);
 3228                 continue;
 3229             }
 3230             else if(c == 'y' || c == 'Y')
 3231             {
 3232                 break;
 3233             }
 3234             else
 3235             {
 3236                 goto action_end;
 3237             }
 3238         }
 3239     }
 3240 
 3241     //
 3242     // Run the command
 3243     //
 3244     {
 3245         int sret = system(resolved_command.c_str());
 3246         if(sret == -1)
 3247         {
 3248             g_logger.format("command failed");
 3249         }
 3250     }
 3251 
 3252     //
 3253     // If needed, wait for the command to complete
 3254     //
 3255     if(action->m_waitfinish)
 3256     {
 3257         printf("Command finished. Press ENTER to return to csysdig.");
 3258         fflush(stdout);
 3259 
 3260         //
 3261         // Wait for the enter key
 3262         // 
 3263         while(getch() == -1)
 3264         {
 3265             do_sleep(10000);
 3266         }
 3267     }
 3268 
 3269 action_end:
 3270     //
 3271     // Empty the keyboard buffer
 3272     //
 3273     while(getch() != -1);
 3274 
 3275 #ifndef NOCURSESUI
 3276     //
 3277     // Reenter curses mode
 3278     //
 3279     reset_prog_mode();
 3280 
 3281     //
 3282     // Refresh the screen
 3283     //
 3284     render();
 3285 #endif //  NOCURSESUI
 3286 }
 3287 
 3288 #ifndef NOCURSESUI
 3289 bool sinsp_cursesui::is_spectro_paused(int input)
 3290 {
 3291     if(m_spectro == NULL)
 3292     {
 3293         return false;
 3294     }
 3295 
 3296     if(input == ' ')
 3297     {
 3298         m_spectro->m_scroll_paused = false;
 3299     }
 3300 
 3301     return m_spectro->m_scroll_paused;
 3302 }
 3303 #endif //  NOCURSESUI
 3304 
 3305 //
 3306 // Returns true if the caller should return immediatly after calling us. 
 3307 // In that case, res is filled with the result.
 3308 //
 3309 bool sinsp_cursesui::execute_table_action(sysdig_table_action ta, uint32_t rownumber, bool* res)
 3310 {
 3311     //
 3312     // Some events require that we perform additional actions
 3313     //
 3314     switch(ta)
 3315     {
 3316     case STA_QUIT:
 3317         *res = true;
 3318         return true;
 3319     case STA_SWITCH_VIEW:
 3320         switch_view(false);
 3321         *res = false;
 3322         return true;
 3323     case STA_SWITCH_SPY:
 3324         switch_view(true);
 3325         *res = false;
 3326         return true;
 3327     case STA_DRILLDOWN:
 3328         {
 3329 #ifndef NOCURSESUI
 3330             if(m_viz != NULL)
 3331             {
 3332                 sinsp_view_column_info* kinfo = get_selected_view()->get_key();
 3333 
 3334                 //
 3335                 // Note: kinfo is null for list views, which currently don't support
 3336                 //       drill down
 3337                 //
 3338                 if(kinfo != NULL)
 3339                 {
 3340                     auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false);
 3341                     if(res.first != NULL)
 3342                     {
 3343                         drilldown(kinfo->get_filter_field(m_view_depth),
 3344                             res.second.c_str(), 
 3345                             kinfo,
 3346                             res.first,
 3347                             false);
 3348                     }
 3349                 }
 3350             }
 3351             else
 3352 #endif
 3353             {
 3354                 if(m_output_type == sinsp_table::OT_CURSES)
 3355                 {
 3356                     drilldown("", "", NULL, NULL, false);
 3357                 }
 3358                 else
 3359                 {
 3360                     sinsp_view_column_info* kinfo = get_selected_view()->get_key();
 3361                     auto res = m_datatable->get_row_key_name_and_val(rownumber, false);
 3362                     if(res.first != NULL)
 3363                     {
 3364                         drilldown(kinfo->get_filter_field(m_view_depth),
 3365                             res.second.c_str(), 
 3366                             kinfo,
 3367                             res.first, 
 3368                             false);
 3369                     }
 3370                 }
 3371             }
 3372         }
 3373 
 3374         *res = false;
 3375         return true;
 3376     case STA_DRILLDOWN_TEMPLATE:
 3377         {
 3378             sinsp_view_column_info* kinfo = get_selected_view()->get_key();
 3379             auto res = m_datatable->get_row_key_name_and_val(0, true);
 3380             if(res.first != NULL)
 3381             {
 3382                 drilldown(kinfo->get_filter_field(m_view_depth),
 3383                     res.second.c_str(), 
 3384                     kinfo,
 3385                     res.first,
 3386                     true);
 3387             }
 3388         }
 3389 
 3390         *res = false;
 3391         return true;
 3392     case STA_DRILLUP:
 3393         drillup();
 3394         
 3395         *res = false;
 3396         return true;
 3397 #ifndef NOCURSESUI
 3398     case STA_SPECTRO:
 3399     case STA_SPECTRO_FILE:
 3400         {
 3401             sinsp_view_column_info* kinfo = get_selected_view()->get_key();
 3402 
 3403             //
 3404             // Note: kinfo is null for list views, that currently don't support
 3405             //       drill down
 3406             //
 3407             if(kinfo != NULL)
 3408             {
 3409                 auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false);
 3410                 if(res.first != NULL)
 3411                 {
 3412                     spectro_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), 
 3413                         res.second.c_str(),
 3414                         get_selected_view()->get_key(),
 3415                         res.first, ta);
 3416                 }
 3417             }
 3418         }
 3419         
 3420         *res = false;
 3421         return true;
 3422 #endif
 3423     case STA_SPY:
 3424         {
 3425             pair<filtercheck_field_info*, string> res;
 3426 #ifndef NOCURSESUI
 3427             if(m_output_type == sinsp_table::OT_CURSES)
 3428             {
 3429                 res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false);
 3430             }
 3431             else
 3432 #endif
 3433             {
 3434                 res = m_datatable->get_row_key_name_and_val(rownumber, false);
 3435             }
 3436 
 3437             if(res.first != NULL)
 3438             {
 3439                 spy_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), 
 3440                     res.second.c_str(),
 3441                     get_selected_view()->get_key(),
 3442                     false);
 3443             }
 3444         }
 3445         
 3446         *res = false;
 3447         return true;
 3448     case STA_DIG:
 3449         {
 3450 #ifndef NOCURSESUI
 3451             if(m_viz)
 3452             {
 3453                 auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false);
 3454                 if(res.first != NULL)
 3455                 {
 3456                     spy_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), 
 3457                         res.second.c_str(),
 3458                         get_selected_view()->get_key(),
 3459                         true);
 3460                 }
 3461             }
 3462             else
 3463 #endif
 3464             {
 3465                 if(m_output_type == sinsp_table::OT_CURSES)
 3466                 {
 3467                     spy_selection("", "", NULL, true);
 3468                 }
 3469                 else
 3470                 {
 3471                     auto res = m_datatable->get_row_key_name_and_val(rownumber, false);
 3472                     if(res.first != NULL)
 3473                     {
 3474                         spy_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), 
 3475                             res.second.c_str(),
 3476                             get_selected_view()->get_key(),
 3477                             true);
 3478                     }
 3479                 }
 3480             }
 3481         }
 3482         
 3483         *res = false;
 3484         return true;
 3485     case STA_NONE:
 3486         break;
 3487     default:
 3488         ASSERT(false);
 3489         break;
 3490     }
 3491 
 3492     return false;
 3493 }
 3494 
 3495 #endif // CSYSDIG