gplugin/gplugin
Clone
Summary
Browse
Changes
Graph
Prepare for the 0.43.0 release
v0.43.0
3 months ago, Gary Kramlich
4fb61c981e56
Prepare for the 0.43.0 release
Testing Done:
Ran `meson dist`
Reviewed at https://reviews.imfreedom.org/r/3026/
/*
* 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 */
}