diff --git a/userspace/libsinsp/dumper.cpp b/userspace/libsinsp/dumper.cpp index 8645d8cc4af..c641365ad21 100644 --- a/userspace/libsinsp/dumper.cpp +++ b/userspace/libsinsp/dumper.cpp @@ -71,7 +71,7 @@ void sinsp_dumper::open(sinsp* inspector, const std::string& filename, bool comp // notify registered plugins of capture open for(auto& p : inspector->m_plugin_manager->plugins()) { - if(p->caps() & CAP_DUMPING) { + if(p->caps() & CAP_ASYNC) { if(!p->dump(*this)) { throw sinsp_exception("dump error for plugin '" + p->name() + "' : " + p->get_last_error()); diff --git a/userspace/libsinsp/plugin.cpp b/userspace/libsinsp/plugin.cpp index 7cc218dd0e6..855102223e2 100644 --- a/userspace/libsinsp/plugin.cpp +++ b/userspace/libsinsp/plugin.cpp @@ -856,36 +856,6 @@ bool sinsp_plugin::capture_close() { return m_handle->api.capture_close(m_state, &in) == SS_PLUGIN_SUCCESS; } -bool sinsp_plugin::dump(sinsp_dumper& dumper) { - if(!m_inited) { - throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name); - } - - if(!m_handle->api.dump) { - return false; - } - - uint32_t num_evts; - ss_plugin_event** evts; - - if(m_handle->api.dump(m_state, &num_evts, &evts) != SS_PLUGIN_SUCCESS) { - return false; - } - for(int i = 0; i < num_evts; i++) { - auto e = evts[i]; - auto evt = sinsp_evt(); - ASSERT(evt.get_scap_evt_storage() == nullptr); - evt.set_scap_evt_storage(new char[e->len]); - memcpy(evt.get_scap_evt_storage(), e, e->len); - evt.set_cpuid(0); - evt.set_num(0); - evt.set_scap_evt((scap_evt*)evt.get_scap_evt_storage()); - evt.init(); - dumper.dump(&evt); - } - return true; -} - /** Event Source CAP **/ scap_source_plugin& sinsp_plugin::as_scap_source() { @@ -1205,3 +1175,52 @@ bool sinsp_plugin::set_async_event_handler(async_event_handler_t handler) { return rc == SS_PLUGIN_SUCCESS; } + +bool sinsp_plugin::dump(sinsp_dumper& dumper) { + if(!m_inited) { + throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name); + } + + if(!(m_caps & CAP_ASYNC)) { + throw sinsp_exception("plugin " + m_name + "without async events cap used as dumper"); + } + + if(!m_handle->api.dump) { + // Not required. + return true; + } + + uint32_t num_evts; + ss_plugin_event** evts; + + if(m_handle->api.dump(m_state, &num_evts, &evts) != SS_PLUGIN_SUCCESS) { + return false; + } + + for(uint32_t i = 0; i < num_evts; i++) { + auto e = evts[i]; + // We only support dumping of PPME_ASYNCEVENT_E events + if(e->type != PPME_ASYNCEVENT_E || e->nparams != 3) { + throw sinsp_exception("malformed async event dumped by plugin: " + m_name); + } + + // Event name must be one of the async event names + auto name = (const char*)((uint8_t*)e + sizeof(ss_plugin_event) + 4 + 4 + 4 + 4); + if(async_event_names().find(name) == async_event_names().end()) { + throw sinsp_exception("incompatible async event '" + std::string(name) + + "' produced by plugin: " + m_name); + } + + // Build the sinsp_evt + auto evt = sinsp_evt(); + ASSERT(evt.get_scap_evt_storage() == nullptr); + evt.set_scap_evt_storage(new char[e->len]); + memcpy(evt.get_scap_evt_storage(), e, e->len); + evt.set_cpuid(0); + evt.set_num(0); + evt.set_scap_evt((scap_evt*)evt.get_scap_evt_storage()); + evt.init(); + dumper.dump(&evt); + } + return true; +} diff --git a/userspace/libsinsp/plugin.h b/userspace/libsinsp/plugin.h index 655b2d1933c..ad818f81c4a 100644 --- a/userspace/libsinsp/plugin.h +++ b/userspace/libsinsp/plugin.h @@ -25,6 +25,7 @@ limitations under the License. #include #include #include +#include #include #include #include diff --git a/userspace/libsinsp/sinsp_cycledumper.cpp b/userspace/libsinsp/sinsp_cycledumper.cpp index 158817e60c8..566b359ab25 100644 --- a/userspace/libsinsp/sinsp_cycledumper.cpp +++ b/userspace/libsinsp/sinsp_cycledumper.cpp @@ -110,7 +110,7 @@ void sinsp_cycledumper::autodump_start(const std::string& dump_filename) { std::for_each(m_open_file_callbacks.begin(), m_open_file_callbacks.end(), std::ref(*this)); m_dumper->open(m_inspector, - dump_filename.c_str(), + dump_filename, m_compress ? SCAP_COMPRESSION_GZIP : SCAP_COMPRESSION_NONE); m_inspector->set_dumping(true); diff --git a/userspace/libsinsp/test/plugins.ut.cpp b/userspace/libsinsp/test/plugins.ut.cpp index ed8173a4abf..84736f82725 100644 --- a/userspace/libsinsp/test/plugins.ut.cpp +++ b/userspace/libsinsp/test/plugins.ut.cpp @@ -378,6 +378,51 @@ TEST_F(sinsp_with_test_input, plugin_custom_source) { ASSERT_EQ(next_event(), nullptr); // EOF is expected } +class plugin_test_event_processor : public libsinsp::event_processor { +public: + plugin_test_event_processor() { num_async_evts = 0; } + + void on_capture_start() {} + + virtual void process_event(sinsp_evt* evt, libsinsp::event_return rc) { + if(evt->get_type() == PPME_ASYNCEVENT_E) { + num_async_evts++; + } + } + + int num_async_evts; +}; + +// scenario: a plugin with dump capability is requested a dump and then the capture file is read. +TEST_F(sinsp_with_test_input, plugin_dump) { + uint64_t max_count = 1; + uint64_t period_ns = 1000000; // 1ms + std::string async_pl_cfg = std::to_string(max_count) + ":" + std::to_string(period_ns); + register_plugin(&m_inspector, get_plugin_api_sample_syscall_async); + + // we will not use the test scap engine here, but open the src plugin instead + // note: we configure the plugin to just emit 1 event through its open params + m_inspector.open_nodriver(); + + auto evt = next_event(); + ASSERT_NE(evt, nullptr); + ASSERT_EQ(evt->get_type(), PPME_ASYNCEVENT_E); + + auto sinspdumper = sinsp_dumper(); + sinspdumper.open(&m_inspector, "test.txt", false); + sinspdumper.close(); + + // Here we open a replay inspector just to trigger the initstate events parsing + auto replay_inspector = sinsp(); + auto processor = plugin_test_event_processor(); + replay_inspector.register_external_event_processor(processor); + ASSERT_NO_THROW(replay_inspector.open_savefile("test.txt")); + + ASSERT_EQ(processor.num_async_evts, 10); + + remove("test.txt"); +} + TEST(sinsp_plugin, plugin_extract_compatibility) { std::string tmp; sinsp i; diff --git a/userspace/libsinsp/test/plugins/syscall_async.cpp b/userspace/libsinsp/test/plugins/syscall_async.cpp index b247b12ac7a..0fce1e708f8 100644 --- a/userspace/libsinsp/test/plugins/syscall_async.cpp +++ b/userspace/libsinsp/test/plugins/syscall_async.cpp @@ -188,6 +188,35 @@ ss_plugin_rc plugin_set_async_event_handler(ss_plugin_t* s, return SS_PLUGIN_SUCCESS; } +ss_plugin_rc plugin_dump(ss_plugin_t* s, uint32_t* nevts, ss_plugin_event*** evts) { + auto ps = reinterpret_cast(s); + + static uint8_t evt_buf[10][64]; + static ss_plugin_event* evt[10]; + const char* name = "sampleticker"; + const char* data = "hello world"; + for(int i = 0; i < 10; i++) { + evt[i] = (ss_plugin_event*)evt_buf[i]; + char error[SCAP_LASTERR_SIZE]; + int32_t encode_res = + scap_event_encode_params(scap_sized_buffer{evt[i], sizeof(evt_buf[i])}, + nullptr, + error, + PPME_ASYNCEVENT_E, + 3, + 0, + name, + scap_sized_buffer{(void*)data, strlen(data) + 1}); + + if(encode_res == SCAP_FAILURE) { + return SS_PLUGIN_FAILURE; + } + } + *evts = evt; + *nevts = 10; + return SS_PLUGIN_SUCCESS; +} + } // anonymous namespace void get_plugin_api_sample_syscall_async(plugin_api& out) { @@ -203,4 +232,5 @@ void get_plugin_api_sample_syscall_async(plugin_api& out) { out.get_async_event_sources = plugin_get_async_event_sources; out.get_async_events = plugin_get_async_events; out.set_async_event_handler = plugin_set_async_event_handler; + out.dump = plugin_dump; } diff --git a/userspace/plugin/plugin_api.h b/userspace/plugin/plugin_api.h index adcad46beed..fb458a79aee 100644 --- a/userspace/plugin/plugin_api.h +++ b/userspace/plugin/plugin_api.h @@ -1053,6 +1053,18 @@ typedef struct { ss_plugin_rc (*set_async_event_handler)(ss_plugin_t* s, ss_plugin_owner_t* owner, const ss_plugin_async_event_handler_t handler); + + // + // Called by the framework when a capture file dump is requested. + // + // Required: no + // Arguments: + // - s: the plugin state, returned by init(). Can be NULL. + // - nevts: number of events retrieved. + // - evts: events retrieved. Events MUST be of type PPME_ASYNCEVENT_E. + // + // Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE. + ss_plugin_rc (*dump)(ss_plugin_t* s, uint32_t* nevts, ss_plugin_event*** evts); }; // Sets a new plugin configuration when provided by the framework. @@ -1104,21 +1116,6 @@ typedef struct { // Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE. ss_plugin_rc (*capture_close)(ss_plugin_t* s, const ss_plugin_capture_listen_input* i); }; - - // Events dumping capability API - struct { - // - // Called by the framework when a capture file dump is requested. - // - // Required: yes - // Arguments: - // - s: the plugin state, returned by init(). Can be NULL. - // - evts: input containing vtables for performing table operations and - // subscribe/unsubscribe async routines - // - // Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE. - ss_plugin_rc (*dump)(ss_plugin_t* s, uint32_t* nevts, ss_plugin_event*** evts); - }; } plugin_api; #ifdef __cplusplus diff --git a/userspace/plugin/plugin_loader.c b/userspace/plugin/plugin_loader.c index afb292be57c..5b312e29035 100644 --- a/userspace/plugin/plugin_loader.c +++ b/userspace/plugin/plugin_loader.c @@ -122,11 +122,11 @@ plugin_handle_t* plugin_load(const char* path, char* err) { SYM_RESOLVE(ret, get_async_event_sources); SYM_RESOLVE(ret, get_async_events); SYM_RESOLVE(ret, set_async_event_handler); + SYM_RESOLVE(ret, dump); SYM_RESOLVE(ret, set_config); SYM_RESOLVE(ret, get_metrics); SYM_RESOLVE(ret, capture_open); SYM_RESOLVE(ret, capture_close); - SYM_RESOLVE(ret, dump); return ret; } @@ -282,10 +282,6 @@ plugin_caps_t plugin_get_capabilities(const plugin_handle_t* h, char* err) { ", "); } - if(h->api.dump != NULL) { - caps = (plugin_caps_t)((uint32_t)caps | (uint32_t)CAP_DUMPING); - } - return caps; } diff --git a/userspace/plugin/plugin_loader.h b/userspace/plugin/plugin_loader.h index a703fc0384b..e6e833333be 100644 --- a/userspace/plugin/plugin_loader.h +++ b/userspace/plugin/plugin_loader.h @@ -44,7 +44,6 @@ typedef enum { CAP_PARSING = 1 << 2, CAP_ASYNC = 1 << 3, CAP_CAPTURE_LISTENING = 1 << 4, - CAP_DUMPING = 1 << 5, CAP_BROKEN = 1 << 31, // used to report inconsistencies } plugin_caps_t;