Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(libsinsp): plugin dynamic reconfiguration #1674

Merged
merged 7 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions userspace/libsinsp/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ bool sinsp_plugin::init(const std::string &config, std::string &errstr)
}

ss_plugin_rc rc;

std::string conf = config;
validate_init_config(conf);
validate_config(conf);

ss_plugin_init_input in = {};
in.owner = this;
Expand Down Expand Up @@ -635,7 +636,7 @@ const libsinsp::events::set<ppm_event_code>& sinsp_plugin::parse_event_codes() c
return m_parse_event_codes;
}

void sinsp_plugin::validate_init_config(std::string& config)
void sinsp_plugin::validate_config(std::string& config)
{
ss_plugin_schema_type schema_type;
std::string schema = get_init_schema(schema_type);
Expand All @@ -644,7 +645,7 @@ void sinsp_plugin::validate_init_config(std::string& config)
switch (schema_type)
{
case SS_PLUGIN_SCHEMA_JSON:
validate_init_config_json_schema(config, schema);
validate_config_json_schema(config, schema);
break;
default:
ASSERT(false);
Expand All @@ -657,7 +658,7 @@ void sinsp_plugin::validate_init_config(std::string& config)
}
}

void sinsp_plugin::validate_init_config_json_schema(std::string& config, std::string &schema)
void sinsp_plugin::validate_config_json_schema(std::string& config, std::string &schema)
{
Json::Value schemaJson;
if(!Json::Reader().parse(schema, schemaJson) || schemaJson.type() != Json::objectValue)
Expand Down Expand Up @@ -712,6 +713,27 @@ void sinsp_plugin::validate_init_config_json_schema(std::string& config, std::st
}
}

bool sinsp_plugin::set_config(const std::string& config)
{
if(!m_inited)
{
throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name);
}

std::string conf = config;
mrgian marked this conversation as resolved.
Show resolved Hide resolved
validate_config(conf);

ss_plugin_set_config_input input;
input.config = conf.c_str();

if(!m_handle->api.set_config)
{
return false;
}

return m_handle->api.set_config(m_state, &input) == SS_PLUGIN_SUCCESS;
}

/** Event Source CAP **/

scap_source_plugin& sinsp_plugin::as_scap_source()
Expand Down
5 changes: 3 additions & 2 deletions userspace/libsinsp/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ class sinsp_plugin
void destroy();
std::string get_last_error() const;
std::string get_init_schema(ss_plugin_schema_type& schema_type) const;
bool set_config(const std::string& config);

/** Event Sourcing **/
inline uint32_t id() const
Expand Down Expand Up @@ -263,7 +264,7 @@ class sinsp_plugin
std::atomic<async_event_handler_t*> m_async_evt_handler; // note: we don't have thread-safe smart pointers

/** Generic helpers **/
void validate_init_config(std::string& config);
void validate_config(std::string& config);
bool resolve_dylib_symbols(std::string& errstr);
void resolve_dylib_field_arg(Json::Value root, filtercheck_field_info& tf);
void resolve_dylib_compatible_sources(
Expand All @@ -274,7 +275,7 @@ class sinsp_plugin
uint16_t *(*get_codes)(uint32_t* numtypes,ss_plugin_t* s),
const std::unordered_set<std::string>& sources,
libsinsp::events::set<ppm_event_code>& codes);
void validate_init_config_json_schema(std::string& config, std::string& schema);
void validate_config_json_schema(std::string& config, std::string& schema);
static const char* get_owner_last_error(ss_plugin_owner_t* o);

/** Event parsing helpers **/
Expand Down
24 changes: 24 additions & 0 deletions userspace/libsinsp/test/plugins.ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,3 +673,27 @@ TEST(sinsp_plugin, plugin_logging)

libsinsp_logger()->remove_callback_log();
}

// Scenario: we provide the plugin with a new configuration,
// expecting it to log when it's notified.
TEST(sinsp_plugin, plugin_set_config)
{
std::string tmp;
sinsp i;
plugin_api api;
get_plugin_api_sample_plugin_extract(api);

api.get_name = [](){ return "plugin_name"; };

auto p = i.register_plugin(&api);
p->init("", tmp);

libsinsp_logger()->add_callback_log([](std::string&& str, sinsp_logger::severity sev) {
std::string expected = "plugin_name: new config!";
ASSERT_TRUE(std::equal(expected.rbegin(), expected.rend(), str.rbegin()));
});

ASSERT_TRUE(p->set_config("some config"));

libsinsp_logger()->remove_callback_log();
}
9 changes: 9 additions & 0 deletions userspace/libsinsp/test/plugins/plugin_extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ static ss_plugin_rc plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event_
return SS_PLUGIN_SUCCESS;
}

static ss_plugin_rc plugin_set_config(ss_plugin_t *s, const ss_plugin_set_config_input* i)
{
plugin_state *ps = (plugin_state *) s;
ps->log(ps->owner, NULL, "new config!", SS_PLUGIN_LOG_SEV_INFO);

return SS_PLUGIN_SUCCESS;
}

void get_plugin_api_sample_plugin_extract(plugin_api& out)
{
memset(&out, 0, sizeof(plugin_api));
Expand All @@ -177,4 +185,5 @@ void get_plugin_api_sample_plugin_extract(plugin_api& out)
out.get_extract_event_sources = plugin_get_extract_event_sources;
out.get_extract_event_types = plugin_get_extract_event_types;
out.extract_fields = plugin_extract_fields;
out.set_config = plugin_set_config;
}
21 changes: 20 additions & 1 deletion userspace/plugin/plugin_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extern "C" {
//
// todo(jasondellaluce): when/if major changes to v4, check and solve all todos
#define PLUGIN_API_VERSION_MAJOR 3
#define PLUGIN_API_VERSION_MINOR 3
#define PLUGIN_API_VERSION_MINOR 4
#define PLUGIN_API_VERSION_PATCH 0

//
Expand Down Expand Up @@ -371,6 +371,14 @@ typedef struct ss_plugin_event_parse_input
ss_plugin_table_writer_vtable_ext* table_writer_ext;
} ss_plugin_event_parse_input;

// Input passed to the plugin when setting a new configuration
typedef struct ss_plugin_set_config_input
{
//
// An opaque string representing the new configuration provided by the framework
const char* config;
} ss_plugin_set_config_input;

//
// Function handler used by plugin for sending asynchronous events to the
// Falcosecurity libs during a live event capture. The asynchronous events
Expand Down Expand Up @@ -939,6 +947,17 @@ 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);
};

// Sets a new plugin configuration when provided by the framework.
// Required: no
// Arguments:
// - s: the plugin state, returned by init(). Can be NULL.
// - i: configuration input provided by the framework.
//
// Return value: A ss_plugin_rc with value SS_PLUGIN_SUCCESS if the config is accepted
// or SS_PLUGIN_FAILURE if the config is rejected.
// If rejected the plugin should provide context in the string returned by get_last_error().
ss_plugin_rc (*set_config)(ss_plugin_t* s, const ss_plugin_set_config_input* i);
} plugin_api;

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions userspace/plugin/plugin_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ 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, set_config);
return ret;
}

Expand Down
Loading