gplugin/gplugin
Clone
Summary
Browse
Changes
Graph
Make GPluginFileSource public
3 months ago, Gary Kramlich
7d9e362672b7
Make GPluginFileSource public
This also adds api for managing the paths it looks at. Currently GPluginManager
forwards the existing API to these new methods.
Testing Done:
Ran with the turtles.
Reviewed at https://reviews.imfreedom.org/r/2982/
/*
* 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 */
}