degesch: stubplement plugins
This commit is contained in:
parent
5d55d7f6de
commit
5ee210a5b7
2
common.c
2
common.c
|
@ -38,7 +38,7 @@
|
|||
#define FAIL(...) \
|
||||
BLOCK_START \
|
||||
error_set (e, __VA_ARGS__); \
|
||||
return false; \
|
||||
return 0; \
|
||||
BLOCK_END
|
||||
|
||||
// --- To be moved to liberty --------------------------------------------------
|
||||
|
|
192
degesch.c
192
degesch.c
|
@ -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
|
||||
{
|
||||
bool no_colors; ///< Disable attribute printing
|
||||
|
@ -1411,6 +1435,8 @@ struct app_context
|
|||
|
||||
bool running_backlog_helper; ///< Running a backlog helper
|
||||
int terminal_suspended; ///< Terminal suspension level
|
||||
|
||||
struct plugin *plugins; ///< Loaded plugins
|
||||
}
|
||||
*g_ctx;
|
||||
|
||||
|
@ -1482,6 +1508,10 @@ app_context_init (struct app_context *self)
|
|||
static void
|
||||
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);
|
||||
for (size_t i = 0; i < ATTR_COUNT; i++)
|
||||
{
|
||||
|
@ -1749,6 +1779,11 @@ static struct config_schema g_config_behaviour[] =
|
|||
.type = CONFIG_ITEM_INTEGER,
|
||||
.validate = config_validate_nonnegative,
|
||||
.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 -----------------------------------------------------
|
||||
|
||||
// HANDLER_NEEDS_REG is primarily for message sending commands,
|
||||
|
@ -7502,6 +7656,40 @@ handle_command_save (struct handler_args *a)
|
|||
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
|
||||
show_aliases_list (struct app_context *ctx)
|
||||
{
|
||||
|
@ -8219,6 +8407,9 @@ g_command_handlers[] =
|
|||
{ "save", "Save configuration",
|
||||
NULL,
|
||||
handle_command_save, 0 },
|
||||
{ "plugin", "Manage plugins",
|
||||
"list | load <name> | unload <name>",
|
||||
handle_command_plugin, 0 },
|
||||
|
||||
{ "alias", "List or set aliases",
|
||||
"[<name> <definition>]",
|
||||
|
@ -10189,6 +10380,7 @@ main (int argc, char *argv[])
|
|||
toggle_bracketed_paste (true);
|
||||
|
||||
// Finally, we juice the configuration for some servers to create
|
||||
load_plugins (&ctx);
|
||||
load_servers (&ctx);
|
||||
|
||||
ctx.polling = true;
|
||||
|
|
2
liberty
2
liberty
|
@ -1 +1 @@
|
|||
Subproject commit 649c351560bf9fea9e7b889c117afa94a44150d6
|
||||
Subproject commit 0adcaf67c23fdc2a5082aa11aefd4fdc0aafd70a
|
Loading…
Reference in New Issue