From 3c19c0c3e1ac5c41511cc00c8f2a62629951a482 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Tue, 3 Dec 2024 09:50:29 +0100 Subject: [PATCH] new(userspace/libsinsp): support plugins in sinsp-example. Signed-off-by: Federico Di Pierro --- userspace/libsinsp/examples/test.cpp | 93 ++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 11 deletions(-) diff --git a/userspace/libsinsp/examples/test.cpp b/userspace/libsinsp/examples/test.cpp index d0334b4bd8..693b3b7033 100644 --- a/userspace/libsinsp/examples/test.cpp +++ b/userspace/libsinsp/examples/test.cpp @@ -55,13 +55,16 @@ static bool ppm_sc_modifies_state = false; static bool ppm_sc_repair_state = false; static bool ppm_sc_state_remove_io_sc = false; static bool enable_glogger = false; -static string engine_string = KMOD_ENGINE; /* Default for backward compatibility. */ +static string engine_string; static string filter_string = ""; static string file_path = ""; static string bpf_path = ""; static unsigned long buffer_bytes_dim = DEFAULT_DRIVER_BUFFER_BYTES_DIM; static uint64_t max_events = UINT64_MAX; -static sinsp_filter_check_list s_filterlist; +static std::shared_ptr plugin; +static std::string open_params; // for source plugins, its open params +static std::unique_ptr filter_list; +static std::shared_ptr filter_factory; sinsp_evt* get_event(sinsp& inspector, std::function handle_error); @@ -74,6 +77,8 @@ sinsp_evt* get_event(sinsp& inspector, std::function h #define PROCESS_DEFAULTS \ EVENT_HEADER "ppid=%proc.ppid exe=%proc.exe args=[%proc.cmdline] " EVENT_TRAILER +#define PLUGIN_DEFAULTS "%evt.num %evt.time [%evt.pluginname] %evt.plugininfo" + #define JSON_PROCESS_DEFAULTS \ "*%evt.num %evt.time %evt.category %container.id %proc.ppid %proc.pid %evt.type %proc.exe " \ "%proc.cmdline %evt.args" @@ -81,10 +86,12 @@ sinsp_evt* get_event(sinsp& inspector, std::function h std::string default_output = EVENT_DEFAULTS; std::string process_output = PROCESS_DEFAULTS; std::string net_output = PROCESS_DEFAULTS " %fd.name"; +std::string plugin_output = PLUGIN_DEFAULTS; static std::unique_ptr default_formatter = nullptr; static std::unique_ptr process_formatter = nullptr; static std::unique_ptr net_formatter = nullptr; +static std::unique_ptr plugin_evt_formatter = nullptr; static void sigint_handler(int signum) { g_interrupted = true; @@ -104,6 +111,7 @@ Overview: Goal of sinsp-example binary is to test and debug sinsp functionality -m, --modern_bpf modern BPF probe. -k, --kmod Kernel module -s , --scap_file Scap file + -p , --plugin Plugin. Path can follow the pattern "filepath.so|init_cfg|open_params". -d , --buffer_dim Dimension in bytes that every per-CPU buffer will have. -o , --output-fields Output fields string (see for supported display fields) that overwrites default output fields for all events. * at the beginning prints JSON keys with null values, else no null fields are printed. -E, --exclude-users Don't create the user/group tables @@ -117,6 +125,15 @@ Overview: Goal of sinsp-example binary is to test and debug sinsp functionality cout << usage << endl; } +static void select_engine(const char* select) { + if(!engine_string.empty()) { + std::cerr << "While selecting " << select + << ": another engine was previously selected: " << engine_string << endl; + exit(EXIT_FAILURE); + } + engine_string = select; +} + #ifndef _WIN32 // Parse CLI options. void parse_CLI_options(sinsp& inspector, int argc, char** argv) { @@ -128,6 +145,7 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv) { {"modern_bpf", no_argument, 0, 'm'}, {"kmod", no_argument, 0, 'k'}, {"scap_file", required_argument, 0, 's'}, + {"plugin", required_argument, 0, 'p'}, {"buffer_dim", required_argument, 0, 'd'}, {"output-fields", required_argument, 0, 'o'}, {"exclude-users", no_argument, 0, 'E'}, @@ -142,7 +160,7 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv) { bool format_set = false; int op; int long_index = 0; - while((op = getopt_long(argc, argv, "hf:jab:mks:d:o:En:zxqgr", long_options, &long_index)) != + while((op = getopt_long(argc, argv, "hf:jab:mks:p:d:o:En:zxqgr", long_options, &long_index)) != -1) { switch(op) { case 'h': @@ -157,6 +175,7 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv) { default_output = DEFAULT_OUTPUT_STR; process_output = JSON_PROCESS_DEFAULTS; net_output = JSON_PROCESS_DEFAULTS " %fd.name"; + plugin_output = PLUGIN_DEFAULTS; } inspector.set_buffer_format(sinsp_evt::PF_JSON); break; @@ -164,19 +183,47 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv) { g_all_threads = true; break; case 'b': - engine_string = BPF_ENGINE; + select_engine(BPF_ENGINE); bpf_path = optarg; break; case 'm': - engine_string = MODERN_BPF_ENGINE; + select_engine(MODERN_BPF_ENGINE); break; case 'k': - engine_string = KMOD_ENGINE; + select_engine(KMOD_ENGINE); break; case 's': - engine_string = SAVEFILE_ENGINE; + select_engine(SAVEFILE_ENGINE); file_path = optarg; break; + case 'p': { + std::string pluginpath = optarg; + size_t cpos = pluginpath.find('|'); + std::string init_config; + // Extract init config from string if present + if(cpos != std::string::npos) { + init_config = pluginpath.substr(cpos + 1); + pluginpath = pluginpath.substr(0, cpos); + } + cpos = init_config.find('|'); + if(cpos != std::string::npos) { + open_params = init_config.substr(cpos + 1); + init_config = init_config.substr(0, cpos); + } + plugin = inspector.register_plugin(pluginpath); + if(std::string err; !plugin->init(init_config, err)) { + std::cerr << "Error while initing plugin: " << err << std::endl; + exit(EXIT_FAILURE); + } + if(plugin->caps() & CAP_SOURCING) { + select_engine(SOURCE_PLUGIN_ENGINE); + filter_list->add_filter_check(inspector.new_generic_filtercheck()); + } + if(plugin->caps() & CAP_EXTRACTION) { + filter_list->add_filter_check(sinsp_plugin::new_filtercheck(plugin)); + } + break; + } case 'd': buffer_bytes_dim = strtoul(optarg, NULL, 10); break; @@ -184,6 +231,7 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv) { default_output = optarg; process_output = optarg; net_output = optarg; + plugin_output = optarg; format_set = true; break; case 'E': @@ -260,6 +308,11 @@ void open_engine(sinsp& inspector, libsinsp::events::set events_sc_ } } + if(engine_string.empty()) { + // Default for backward compat + engine_string = KMOD_ENGINE; + } + if(false) { } #ifdef HAS_ENGINE_KMOD @@ -293,6 +346,14 @@ void open_engine(sinsp& inspector, libsinsp::events::set events_sc_ else if(!engine_string.compare(MODERN_BPF_ENGINE)) { inspector.open_modern_bpf(buffer_bytes_dim, DEFAULT_CPU_FOR_EACH_BUFFER, true, ppm_sc); } +#endif +#ifdef HAS_ENGINE_SOURCE_PLUGIN + else if(!engine_string.compare(SOURCE_PLUGIN_ENGINE)) { + inspector.open_plugin(plugin->name(), + "", + plugin->id() == 0 ? sinsp_plugin_platform::SINSP_PLATFORM_FULL + : sinsp_plugin_platform::SINSP_PLATFORM_HOSTINFO); + } #endif else { std::cerr << "Unknown engine" << std::endl; @@ -357,6 +418,9 @@ static bool insert_module() { int main(int argc, char** argv) { sinsp inspector; + filter_list.reset(new sinsp_filter_check_list()); + filter_factory.reset(new sinsp_filter_factory(&inspector, *filter_list.get())); + #ifndef _WIN32 parse_CLI_options(inspector, argc, argv); @@ -382,7 +446,9 @@ int main(int argc, char** argv) { if(!filter_string.empty()) { try { - inspector.set_filter(filter_string); + sinsp_filter_compiler compiler(filter_factory, filter_string); + std::unique_ptr s = compiler.compile(); + inspector.set_filter(std::move(s), filter_string); } catch(const sinsp_exception& e) { cerr << "[ERROR] Unable to set filter: " << e.what() << endl; } @@ -403,10 +469,13 @@ int main(int argc, char** argv) { inspector.start_capture(); default_formatter = - std::make_unique(&inspector, default_output, s_filterlist); + std::make_unique(&inspector, default_output, *filter_list.get()); process_formatter = - std::make_unique(&inspector, process_output, s_filterlist); - net_formatter = std::make_unique(&inspector, net_output, s_filterlist); + std::make_unique(&inspector, process_output, *filter_list.get()); + net_formatter = + std::make_unique(&inspector, net_output, *filter_list.get()); + plugin_evt_formatter = + std::make_unique(&inspector, plugin_output, *filter_list.get()); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); uint64_t num_events = 0; @@ -467,6 +536,8 @@ void formatted_dump(sinsp&, sinsp_evt* ev) { } else if(ev->get_category() == EC_NET || ev->get_category() == EC_IO_READ || ev->get_category() == EC_IO_WRITE) { net_formatter->tostring(ev, output); + } else if(ev->get_info()->category & EC_PLUGIN) { + plugin_evt_formatter->tostring(ev, output); } else { default_formatter->tostring(ev, output); }