Browse Source

degesch: stubplement plugins

tags/v0.9.2
Přemysl Janouch 4 years ago
parent
commit
5ee210a5b7
3 changed files with 194 additions and 2 deletions
  1. +1
    -1
      common.c
  2. +192
    -0
      degesch.c
  3. +1
    -1
      liberty

+ 1
- 1
common.c View File

@@ -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
- 0
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
{
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;

+ 1
- 1
liberty

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

Loading…
Cancel
Save