LUMIERA.clone/src/common/plugin.c

419 lines
10 KiB
C
Raw Normal View History

2007-07-15 17:08:45 +02:00
/*
Plugin - Lumiera Plugin loader implementation
2007-07-15 17:08:45 +02:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2008, Christian Thaeter <ct@pipapo.org>
2007-07-15 17:08:45 +02:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
2007-07-15 17:08:45 +02:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
* *****************************************************************/
2025-06-07 23:59:57 +02:00
/** @file plugin.c
** Plugin loader implementation.
*/
#include "include/logging.h"
#include "lib/safeclib.h"
#include "lib/tmpbuf.h"
#include "lib/psplay.h"
#include "lib/recmutex.h"
#include "lib/error.h"
#include "include/config-facade.h"
#include "common/interfaceregistry.h"
#include "common/config.h"
#include "common/plugin.h"
2007-07-15 02:23:37 +02:00
#include <glob.h>
#include <nobug.h>
extern PSplay lumiera_pluginregistry;
static char* init_exts_globs (void);
2007-07-15 02:23:37 +02:00
/* errors */
LUMIERA_ERROR_DEFINE(PLUGIN_INIT, "Initialisation error");
LUMIERA_ERROR_DEFINE(PLUGIN_OPEN, "Could not open plugin");
LUMIERA_ERROR_DEFINE(PLUGIN_WTF, "Not a Lumiera plugin");
LUMIERA_ERROR_DEFINE(PLUGIN_REGISTER, "Could not register plugin");
LUMIERA_ERROR_DEFINE(PLUGIN_VERSION, "Plugin Version unsupported");
2007-07-15 02:23:37 +02:00
#define LUMIERA_PLUGIN_TYPE_PLANNED(name, ext)
2007-07-17 04:40:07 +02:00
/**
* Supported (and planned) plugin types and their file extensions
* This maps filename extensions to implementations (of the respective _load_NAME and _unload_NAME functions)
* So far we only support platform dynamic libraries, later we may add plugins implemented in Lua
* and c source modules which get compiled on the fly.
*/
#define LUMIERA_PLUGIN_TYPES \
LUMIERA_PLUGIN_TYPE(DYNLIB, ".so") \
LUMIERA_PLUGIN_TYPE(DYNLIB, ".lum") \
LUMIERA_PLUGIN_TYPE_PLANNED(LUA, ".lua") \
LUMIERA_PLUGIN_TYPE_PLANNED(CSOURCE, ".c")
/**
* record the extension and a callback function for loading the associated plugin for each plugin type
*/
struct lumiera_plugintype_struct
{
LumieraPlugin (*lumiera_plugin_load_fn)(const char*);
void (*lumiera_plugin_unload_fn)(LumieraPlugin);
const char* ext;
};
typedef struct lumiera_plugintype_struct lumiera_plugintype;
typedef lumiera_plugintype* LumieraPlugintype;
/* forward declare loader functions for all types */
#define LUMIERA_PLUGIN_TYPE(type, ext) \
LumieraPlugin lumiera_plugin_load_##type (const char*); \
void lumiera_plugin_unload_##type (LumieraPlugin);
LUMIERA_PLUGIN_TYPES
#undef LUMIERA_PLUGIN_TYPE
2007-07-17 04:40:07 +02:00
/* and now setup a table which will be used for dispatching loaders depending on the type of the plugin */
#define LUMIERA_PLUGIN_TYPE(type, ext) {lumiera_plugin_load_##type, lumiera_plugin_unload_##type, ext},
static lumiera_plugintype lumiera_plugin_types[] =
2007-07-17 04:40:07 +02:00
{
LUMIERA_PLUGIN_TYPES
{NULL, NULL, NULL}
2007-07-17 04:40:07 +02:00
};
#undef LUMIERA_PLUGIN_TYPE
2007-07-17 04:40:07 +02:00
struct lumiera_plugin_struct
2007-07-15 02:23:37 +02:00
{
psplaynode node;
2008-03-10 06:09:44 +01:00
/* long names as looked up ("/usr/local/lib/lumiera/plugins/effects/audio/normalize.so") */
const char* name;
2007-07-15 02:23:37 +02:00
/* use count for all interfaces of this plugin */
unsigned refcnt;
2007-07-15 02:23:37 +02:00
/* time when the refcounter dropped to 0 last time */
2007-07-15 02:23:37 +02:00
time_t last;
/** bulk loading plugins must not fail entirely, just because one
* plugin doesn't comply. Thus we're recording local errors here */
lumiera_err error;
/* the 'plugin' interface itself */
LumieraInterface plugin;
2007-07-17 04:40:07 +02:00
/* generic handle for the plugin, dlopen handle, etc */
2007-07-15 02:23:37 +02:00
void* handle;
};
2007-07-17 04:40:07 +02:00
LumieraPlugin
lumiera_plugin_new (const char* name)
{
LumieraPlugin self = lumiera_malloc (sizeof (*self));
psplaynode_init (&self->node);
self->name = lumiera_strndup (name, SIZE_MAX);
self->refcnt = 0;
time (&self->last);
self->error = LUMIERA_ERROR_PLUGIN_INIT;
self->plugin = NULL;
self->handle = NULL;
return self;
}
LumieraPlugin
lumiera_plugin_init (LumieraPlugin self, void* handle, LumieraInterface plugin)
{
self->error = lumiera_error ();
self->plugin = plugin;
self->handle = handle;
return self;
}
lumiera_err
lumiera_plugin_error (LumieraPlugin self)
{
REQUIRE (self);
return self->error;
}
void*
lumiera_plugin_handle (LumieraPlugin self)
{
REQUIRE (self);
return self->handle;
}
2007-07-15 02:23:37 +02:00
const char*
lumiera_plugin_name (LumieraPlugin self)
{
return self?self->name:NULL;
}
void
lumiera_plugin_refinc (LumieraPlugin self)
{
++self->refcnt;
}
void
lumiera_plugin_refdec (LumieraPlugin self)
{
if (!--self->refcnt)
time (&self->last);
}
2007-07-17 04:40:07 +02:00
int
lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin),
int (*callback_register) (LumieraPlugin))
{
TRACE (pluginloader_dbg);
REQUIRE (callback_load);
REQUIRE (callback_register);
// Note: because the full-blown Config system isn't implemented yet,
// as a temporary solution we fetch this basic configuration
// from the setup.ini used to bootstrap the application
lumiera_config_setdefault (lumiera_get_plugin_path_default());
/* construct glob trail {.so,.lum,.lua} ... */
static char* exts_globs = NULL;
if (!exts_globs)
exts_globs = init_exts_globs ();
2007-07-15 02:23:37 +02:00
const char* path;
unsigned i = 0;
int flags = GLOB_PERIOD|GLOB_BRACE|GLOB_TILDE_CHECK;
glob_t globs;
2007-07-15 02:23:37 +02:00
while ((path = lumiera_config_wordlist_get_nth ("plugin.path", i, ":")))
2007-07-15 02:23:37 +02:00
{
path = lumiera_tmpbuf_snprintf (SIZE_MAX,"%s/%s", path, exts_globs);
TRACE (pluginloader_dbg, "globbing path '%s'", path);
int ret = glob (path, flags, NULL, &globs);
if (ret == GLOB_NOSPACE)
LUMIERA_DIE (NO_MEMORY);
flags |= GLOB_APPEND;
++i;
}
2007-07-15 02:23:37 +02:00
if (globs.gl_pathc)
LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
{
for (char** itr = globs.gl_pathv; *itr; ++itr)
{
if (!psplay_find (lumiera_pluginregistry, *itr, 100))
{
TRACE (pluginloader, "found new plugin '%s'", *itr);
callback_register (callback_load (*itr));
}
}
}
2007-07-15 02:23:37 +02:00
globfree (&globs);
2007-07-15 02:23:37 +02:00
return !lumiera_error_peek ();
}
2007-07-17 04:40:07 +02:00
2007-08-18 05:05:38 +02:00
LumieraPlugin
lumiera_plugin_load (const char* plugin)
{
2011-12-02 21:34:29 +01:00
TRACE (pluginloader_dbg, "plugin=%s", plugin);
2007-07-15 02:23:37 +02:00
/* dispatch on extension, call the registered function */
const char* ext = strrchr (plugin, '.');
LumieraPlugintype itr = lumiera_plugin_types;
while (itr->ext)
{
if (!strcmp (itr->ext, ext))
return itr->lumiera_plugin_load_fn (plugin);
++itr;
}
return NULL;
}
2007-07-15 02:23:37 +02:00
int
lumiera_plugin_register (LumieraPlugin plugin)
{
TRACE (pluginloader_dbg);
if (!plugin)
return 1;
LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
2007-07-15 02:23:37 +02:00
{
if (psplay_insert (lumiera_pluginregistry, &plugin->node, 100))
{
if (!plugin->error)
{
switch (lumiera_interface_version (plugin->plugin, "lumieraorg__plugin"))
{
case 0:
{
TRACE (pluginloader, "registering %s", plugin->name);
LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle =
LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) plugin->plugin;
lumiera_interfaceregistry_bulkregister_interfaces (handle->plugin_interfaces (), plugin);
}
break;
default:
LUMIERA_ERROR_SET (pluginloader, PLUGIN_VERSION, plugin->name);
}
}
}
else
{
LUMIERA_ERROR_SET_CRITICAL (pluginloader, PLUGIN_REGISTER, plugin->name);
}
}
return !!lumiera_error_peek();
2007-07-15 02:23:37 +02:00
}
unsigned
lumiera_plugin_unload (LumieraPlugin self)
{
TRACE (pluginloader_dbg);
if (!self)
return 0;
if (self->refcnt)
return self->refcnt;
/* dispatch on extension, call the registered function */
const char* ext = strrchr (self->name, '.');
LumieraPlugintype itr = lumiera_plugin_types;
while (itr->ext)
{
if (!strcmp (itr->ext, ext))
{
LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
{
if (psplay_remove (lumiera_pluginregistry, &self->node))
{
if (!self->error)
{
LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle =
LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) self->plugin;
lumiera_interfaceregistry_bulkremove_interfaces (handle->plugin_interfaces ());
}
}
}
itr->lumiera_plugin_unload_fn (self);
break;
}
++itr;
}
return 0;
}
LumieraPlugin
lumiera_plugin_lookup (const char* name)
{
LumieraPlugin ret = NULL;
if (name)
LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
ret = (LumieraPlugin) psplay_find (lumiera_pluginregistry, name, 100);
return ret;
}
static char* init_exts_globs (void)
{
char* exts_globs;
size_t exts_sz = 3; /* * { } \0 less one comma */
LumieraPlugintype itr = lumiera_plugin_types;
while (itr->ext)
{
exts_sz += strlen (itr->ext) + 1;
++itr;
}
exts_globs = lumiera_malloc (exts_sz);
*exts_globs = '\0';
itr = lumiera_plugin_types;
strcat (exts_globs, "*{");
while (itr->ext)
{
strcat (exts_globs, itr->ext);
strcat (exts_globs, ",");
++itr;
}
exts_globs[exts_sz-2] = '}';
TRACE (pluginloader_dbg, "initialised extension glob to '%s'", exts_globs);
return exts_globs;
}
2007-07-15 02:23:37 +02:00
int
lumiera_plugin_cmp_fn (const void* keya, const void* keyb)
{
return strcmp ((const char*)keya, (const char*)keyb);
}
2007-07-15 02:23:37 +02:00
const void*
lumiera_plugin_key_fn (const PSplaynode node)
{
return ((LumieraPlugin)node)->name;
2007-07-15 02:23:37 +02:00
}
void
lumiera_plugin_delete_fn (PSplaynode node)
{
LumieraPlugin self = (LumieraPlugin) node;
ENSURE (!self->refcnt, "plugin %s still in use at shutdown", self->name);
const char* ext = strrchr (self->name, '.');
LumieraPlugintype itr = lumiera_plugin_types;
while (itr->ext)
{
if (!strcmp (itr->ext, ext))
{
if (!self->error)
{
LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle =
LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) self->plugin;
lumiera_interfaceregistry_bulkremove_interfaces (handle->plugin_interfaces ());
}
2011-12-02 21:34:29 +01:00
TRACE (pluginloader_dbg, "unloading plugin/module %s", self->name);
itr->lumiera_plugin_unload_fn (self);
break;
}
++itr;
}
}