degesch: stubplement plugins
This commit is contained in:
parent
5d55d7f6de
commit
5ee210a5b7
2
common.c
2
common.c
|
@ -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
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
|
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;
|
||||||
|
|
2
liberty
2
liberty
|
@ -1 +1 @@
|
||||||
Subproject commit 649c351560bf9fea9e7b889c117afa94a44150d6
|
Subproject commit 0adcaf67c23fdc2a5082aa11aefd4fdc0aafd70a
|
Loading…
Reference in New Issue