degesch: stubplement plugins
This commit is contained in:
		
							
								
								
									
										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
									
									
									
									
									
								
							 Submodule liberty updated: 649c351560...0adcaf67c2
									
								
							
		Reference in New Issue
	
	Block a user