degesch: stubplement plugins

This commit is contained in:
Přemysl Eric Janouch 2015-11-19 14:23:10 +01:00
parent 5d55d7f6de
commit 5ee210a5b7
3 changed files with 194 additions and 2 deletions

View File

@ -38,7 +38,7 @@
#define FAIL(...) \ #define FAIL(...) \
BLOCK_START \ BLOCK_START \
error_set (e, __VA_ARGS__); \ error_set (e, __VA_ARGS__); \
return false; \ return 0; \
BLOCK_END BLOCK_END
// --- To be moved to liberty -------------------------------------------------- // --- To be moved to liberty --------------------------------------------------

192
degesch.c
View File

@ -1352,6 +1352,30 @@ REF_COUNTABLE_METHODS (server)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct plugin
{
LIST_HEADER (struct plugin)
char *name; ///< Name of the plugin
struct plugin_vtable *vtable; ///< Methods
};
struct plugin_vtable
{
/// Unregister and free the plugin including all relevant resources
void (*free) (struct plugin *self);
};
static void
plugin_destroy (struct plugin *self)
{
self->vtable->free (self);
free (self->name);
free (self);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct app_context struct app_context
{ {
bool no_colors; ///< Disable attribute printing bool no_colors; ///< Disable attribute printing
@ -1411,6 +1435,8 @@ struct app_context
bool running_backlog_helper; ///< Running a backlog helper bool running_backlog_helper; ///< Running a backlog helper
int terminal_suspended; ///< Terminal suspension level int terminal_suspended; ///< Terminal suspension level
struct plugin *plugins; ///< Loaded plugins
} }
*g_ctx; *g_ctx;
@ -1482,6 +1508,10 @@ app_context_init (struct app_context *self)
static void static void
app_context_free (struct app_context *self) app_context_free (struct app_context *self)
{ {
// Plugins can try to use of the other fields when destroyed
LIST_FOR_EACH (struct plugin, iter, self->plugins)
plugin_destroy (iter);
config_free (&self->config); config_free (&self->config);
for (size_t i = 0; i < ATTR_COUNT; i++) for (size_t i = 0; i < ATTR_COUNT; i++)
{ {
@ -1749,6 +1779,11 @@ static struct config_schema g_config_behaviour[] =
.type = CONFIG_ITEM_INTEGER, .type = CONFIG_ITEM_INTEGER,
.validate = config_validate_nonnegative, .validate = config_validate_nonnegative,
.default_ = "600" }, .default_ = "600" },
{ .name = "plugin_autoload",
.comment = "Plugins to automatically load on start",
.type = CONFIG_ITEM_STRING_ARRAY,
.validate = config_validate_nonjunk_string },
{} {}
}; };
@ -7094,6 +7129,125 @@ server_rename (struct app_context *ctx, struct server *s, const char *new_name)
} }
} }
// --- Plugins -----------------------------------------------------------------
typedef struct plugin *(*plugin_load_fn)
(struct app_context *ctx, const char *filename, struct error **e);
// We can potentially add support for other scripting languages if so desired,
// however this possibility is just a byproduct of abstraction
static plugin_load_fn g_plugin_loaders[] =
{
};
static struct plugin *
plugin_load_from_filename (struct app_context *ctx, const char *filename,
struct error **e)
{
struct plugin *plugin = NULL;
struct error *error = NULL;
for (size_t i = 0; i < N_ELEMENTS (g_plugin_loaders); i++)
if ((plugin = g_plugin_loaders[i](ctx, filename, &error)) || error)
break;
if (error)
error_propagate (e, error);
else if (!plugin)
FAIL ("no plugin handler for \"%s\"", filename);
return plugin;
}
static struct plugin *
plugin_find (struct app_context *ctx, const char *name)
{
LIST_FOR_EACH (struct plugin, iter, ctx->plugins)
if (!strcmp (name, iter->name))
return iter;
return NULL;
}
static char *
plugin_resolve_relative_filename (const char *filename)
{
struct str_vector paths;
str_vector_init (&paths);
get_xdg_data_dirs (&paths);
char *result = resolve_relative_filename_generic
(&paths, PROGRAM_NAME "/plugins/", filename);
str_vector_free (&paths);
return result;
}
static struct plugin *
plugin_load_by_name (struct app_context *ctx, const char *name,
struct error **e)
{
struct plugin *plugin = plugin_find (ctx, name);
if (plugin)
FAIL ("plugin already loaded");
// As a side effect, a plugin can be loaded multiple times by giving
// various relative or non-relative paths to the function; this is not
// supposed to be fool-proof though, that requires other mechanisms
char *filename = resolve_filename (name, plugin_resolve_relative_filename);
if (!filename)
FAIL ("file not found");
plugin = plugin_load_from_filename (ctx, filename, e);
free (filename);
return plugin;
}
static void
plugin_load (struct app_context *ctx, const char *name)
{
struct error *e = NULL;
struct plugin *plugin = plugin_load_by_name (ctx, name, &e);
if (plugin)
{
log_global_status (ctx, "Plugin \"#s\" loaded", name);
LIST_PREPEND (ctx->plugins, plugin);
plugin->name = xstrdup (name);
}
else
{
log_global_error (ctx, "Can't load plugin \"#s\": #s",
name, e->message);
error_free (e);
}
}
static void
plugin_unload (struct app_context *ctx, const char *name)
{
struct plugin *plugin = plugin_find (ctx, name);
if (!plugin)
log_global_error (ctx, "Can't unload plugin \"#s\": #s",
name, "plugin not loaded");
else
{
log_global_status (ctx, "Plugin \"#s\" unloaded", name);
LIST_UNLINK (ctx->plugins, plugin);
plugin_destroy (plugin);
}
}
static void
load_plugins (struct app_context *ctx)
{
const char *plugins = get_config_string
(ctx->config.root, "behaviour.plugin_autoload");
if (plugins)
{
struct str_vector v;
str_vector_init (&v);
cstr_split_ignore_empty (plugins, ',', &v);
for (size_t i = 0; i < v.len; i++)
plugin_load (ctx, v.vector[i]);
str_vector_free (&v);
}
}
// --- User input handling ----------------------------------------------------- // --- User input handling -----------------------------------------------------
// HANDLER_NEEDS_REG is primarily for message sending commands, // HANDLER_NEEDS_REG is primarily for message sending commands,
@ -7502,6 +7656,40 @@ handle_command_save (struct handler_args *a)
return true; return true;
} }
static void
show_plugin_list (struct app_context *ctx)
{
log_global_indent (ctx, "");
log_global_indent (ctx, "Plugins:");
LIST_FOR_EACH (struct plugin, iter, ctx->plugins)
log_global_indent (ctx, " #s", iter->name);
}
static bool
handle_command_plugin (struct handler_args *a)
{
char *action = cut_word (&a->arguments);
if (!*action || !strcasecmp_ascii (action, "list"))
show_plugin_list (a->ctx);
else if (!strcasecmp_ascii (action, "load"))
{
if (!*a->arguments)
return false;
plugin_load (a->ctx, cut_word (&a->arguments));
}
else if (!strcasecmp_ascii (action, "unload"))
{
if (!*a->arguments)
return false;
plugin_unload (a->ctx, cut_word (&a->arguments));
}
else
return false;
return true;
}
static bool static bool
show_aliases_list (struct app_context *ctx) show_aliases_list (struct app_context *ctx)
{ {
@ -8219,6 +8407,9 @@ g_command_handlers[] =
{ "save", "Save configuration", { "save", "Save configuration",
NULL, NULL,
handle_command_save, 0 }, handle_command_save, 0 },
{ "plugin", "Manage plugins",
"list | load <name> | unload <name>",
handle_command_plugin, 0 },
{ "alias", "List or set aliases", { "alias", "List or set aliases",
"[<name> <definition>]", "[<name> <definition>]",
@ -10189,6 +10380,7 @@ main (int argc, char *argv[])
toggle_bracketed_paste (true); toggle_bracketed_paste (true);
// Finally, we juice the configuration for some servers to create // Finally, we juice the configuration for some servers to create
load_plugins (&ctx);
load_servers (&ctx); load_servers (&ctx);
ctx.polling = true; ctx.polling = true;

@ -1 +1 @@
Subproject commit 649c351560bf9fea9e7b889c117afa94a44150d6 Subproject commit 0adcaf67c23fdc2a5082aa11aefd4fdc0aafd70a