gplugin/gplugin

Update the convey plans
default tip
24 hours ago, Gary Kramlich
9d8481feda6e
Update the convey plans

Testing Done:
Ran `convey run all` without issue.

Reviewed at https://reviews.imfreedom.org/r/3150/
/*
* Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <https://www.gnu.org/licenses/>.
*/
#include <gmodule.h>
#include <glib/gi18n-lib.h>
#include <gplugin/gplugin-core.h>
#include <gplugin/gplugin-native-loader.h>
#include <gplugin/gplugin-native-plugin.h>
#include <gplugin/gplugin-private.h>
#define GPLUGIN_QUERY_SYMBOL "gplugin_query"
#define GPLUGIN_LOAD_SYMBOL "gplugin_load"
#define GPLUGIN_UNLOAD_SYMBOL "gplugin_unload"
/**
* GPluginNativeLoader:
*
* A #GPluginLoader subclass that is able to load native plugins.
*/
/**
* GPLUGIN_NATIVE_PLUGIN_ABI_VERSION:
*
* The ABI version of the #GPluginNativeLoader. Your plugin should use this
* as the value for #abi_version when call #gplugin_plugin_info_new.
*/
struct _GPluginNativeLoader {
GPluginLoader parent;
};
G_DEFINE_FINAL_TYPE(
GPluginNativeLoader,
gplugin_native_loader,
GPLUGIN_TYPE_LOADER)
/******************************************************************************
* Helpers
*****************************************************************************/
static gpointer
gplugin_native_loader_lookup_symbol(
GModule *module,
const gchar *name,
GError **error)
{
gpointer symbol = NULL;
g_return_val_if_fail(module != NULL, NULL);
if(!g_module_symbol(module, name, &symbol)) {
g_set_error(
error,
GPLUGIN_DOMAIN,
0,
_("symbol %s was not found"),
name);
return NULL;
}
return symbol;
}
/******************************************************************************
* GPluginLoaderInterface API
*****************************************************************************/
static GSList *
gplugin_native_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
{
GSList *exts = NULL;
#if defined(_WIN32)
exts = g_slist_append(exts, "dll");
#elif defined(__APPLE__) || defined(__MACH__)
/* Both .so and .dylib are used on macOS. */
exts = g_slist_append(exts, "so");
exts = g_slist_append(exts, "dylib");
#else
exts = g_slist_append(exts, "so");
#endif
return exts;
}
static GPluginPluginInfo *
gplugin_native_loader_open_and_query(
const gchar *filename,
GModule **module,
GModuleFlags flags,
GPluginNativePluginQueryFunc *query,
GError **error)
{
GPluginPluginInfo *info = NULL;
GError *local_error = NULL;
*module = g_module_open_full(filename, flags, &local_error);
if(local_error != NULL) {
if(*module != NULL) {
g_module_close(*module);
*module = NULL;
}
g_propagate_error(error, local_error);
return NULL;
}
*query = gplugin_native_loader_lookup_symbol(
*module,
GPLUGIN_QUERY_SYMBOL,
&local_error);
if(local_error != NULL) {
g_module_close(*module);
*module = NULL;
g_propagate_error(error, local_error);
return NULL;
}
info = ((GPluginNativePluginQueryFunc)(*query))(&local_error);
if(local_error != NULL) {
/* We can't use g_propagate_error here, because local_error is pointing
* to memory in the address space of the module we're about to close.
* So instead, create a copy of it and free it before closing the
* module.
*/
if(error != NULL && *error == NULL) {
*error = g_error_copy(local_error);
}
g_clear_error(&local_error);
g_module_close(*module);
*module = NULL;
return NULL;
}
return info;
}
static GPluginPlugin *
gplugin_native_loader_query(
GPluginLoader *loader,
const gchar *filename,
GError **error)
{
GPluginPlugin *plugin = NULL;
GPluginPluginInfo *info = NULL;
GPluginNativePluginLoadFunc load = NULL;
GPluginNativePluginQueryFunc query = NULL;
GPluginNativePluginUnloadFunc unload = NULL;
GError *local_error = NULL;
GModule *module = NULL;
info = gplugin_native_loader_open_and_query(
filename,
&module,
G_MODULE_BIND_LOCAL,
&query,
&local_error);
/* If the query returned an error, clear any info it may have set and return
* NULL.
*/
if(local_error != NULL) {
g_propagate_error(error, local_error);
g_clear_object(&info);
return NULL;
}
/* If we didn't get an info back, create a generic error. */
if(!GPLUGIN_IS_PLUGIN_INFO(info)) {
/* Error was already checked earlier if it was non-null. */
g_set_error_literal(
error,
GPLUGIN_DOMAIN,
0,
_("the query function did not return a "
"GPluginPluginInfo instance"));
g_clear_object(&info);
return NULL;
}
if(gplugin_plugin_info_get_bind_global(info)) {
/* Clear the info first, as closing the module will invalidate the
* memory that info is pointing to.
*/
g_clear_object(&info);
g_module_close(module);
info = gplugin_native_loader_open_and_query(
filename,
&module,
0,
&query,
&local_error);
if(!GPLUGIN_IS_PLUGIN_INFO(info)) {
if(local_error != NULL) {
g_propagate_error(error, local_error);
} else {
g_set_error_literal(
error,
GPLUGIN_DOMAIN,
0,
_("the query function did not return a "
"GPluginPluginInfo instance"));
}
return NULL;
}
}
/* now look for the load symbol */
load = gplugin_native_loader_lookup_symbol(
module,
GPLUGIN_LOAD_SYMBOL,
&local_error);
if(local_error != NULL) {
g_clear_object(&info);
g_module_close(module);
g_propagate_error(error, local_error);
return NULL;
}
/* now look for the unload symbol */
unload = gplugin_native_loader_lookup_symbol(
module,
GPLUGIN_UNLOAD_SYMBOL,
&local_error);
if(local_error != NULL) {
g_clear_object(&info);
g_module_close(module);
g_propagate_error(error, local_error);
return NULL;
}
/* now create the actual plugin instance */
/* clang-format off */
plugin = g_object_new(
GPLUGIN_TYPE_NATIVE_PLUGIN,
"module", module,
"info", info,
"load-func", load,
"unload-func", unload,
"loader", loader,
"filename", filename,
NULL);
/* clang-format on */
/* now that the plugin instance owns the info, remove our ref */
g_clear_object(&info);
if(!GPLUGIN_IS_NATIVE_PLUGIN(plugin)) {
g_module_close(module);
g_set_error_literal(
error,
GPLUGIN_DOMAIN,
0,
_("failed to create plugin instance"));
return NULL;
}
return plugin;
}
static gboolean
gplugin_native_loader_load(
G_GNUC_UNUSED GPluginLoader *loader,
GPluginPlugin *plugin,
GError **error)
{
GPluginNativePluginLoadFunc func;
GError *local_error = NULL;
g_return_val_if_fail(plugin != NULL, FALSE);
g_return_val_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin), FALSE);
/* get and call the function */
g_object_get(plugin, "load-func", &func, NULL);
if(!func(plugin, &local_error)) {
if(local_error != NULL) {
g_propagate_error(error, local_error);
} else {
g_set_error_literal(error, GPLUGIN_DOMAIN, 0, _("unknown failure"));
}
return FALSE;
}
return TRUE;
}
static gboolean
gplugin_native_loader_unload(
G_GNUC_UNUSED GPluginLoader *loader,
GPluginPlugin *plugin,
gboolean shutdown,
GError **error)
{
GPluginNativePluginUnloadFunc func;
GError *local_error = NULL;
g_return_val_if_fail(plugin != NULL, FALSE);
g_return_val_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin), FALSE);
/* get the function */
g_object_get(plugin, "unload-func", &func, NULL);
/* validate the function */
if(func == NULL) {
gchar *filename = gplugin_plugin_get_filename(plugin);
g_warning(_("unload function for %s is NULL"), filename);
g_free(filename);
return FALSE;
}
/* now call the function */
if(!func(plugin, shutdown, &local_error)) {
if(local_error != NULL) {
g_propagate_error(error, local_error);
} else {
g_set_error_literal(error, GPLUGIN_DOMAIN, 0, _("unknown failure"));
}
return FALSE;
}
return TRUE;
}
static void
gplugin_native_loader_init(G_GNUC_UNUSED GPluginNativeLoader *loader)
{
}
static void
gplugin_native_loader_class_init(GPluginNativeLoaderClass *klass)
{
GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
loader_class->supported_extensions =
gplugin_native_loader_supported_extensions;
loader_class->query = gplugin_native_loader_query;
loader_class->load = gplugin_native_loader_load;
loader_class->unload = gplugin_native_loader_unload;
}
/******************************************************************************
* Public API
*****************************************************************************/
/**
* gplugin_native_loader_new:
*
* Create a new instance of the native plugin loader.
*
* Typically you won't need to call this directly as GPlugin will create it
* unless #GPLUGIN_CORE_FLAGS_DISABLE_NATIVE_LOADER is passed to
* gplugin_init().
*
* Returns: (transfer full): The new instance.
*
* Since: 0.34
*/
GPluginLoader *
gplugin_native_loader_new(void)
{
/* clang-format off */
return g_object_new(
GPLUGIN_TYPE_NATIVE_LOADER,
"id", "gplugin-native",
NULL);
/* clang-format on */
}