Lumiera  0.pre.03
»edit your freedom«
plugin.c
Go to the documentation of this file.
1 /*
2  Plugin - Lumiera Plugin loader implementation
3 
4  Copyright (C) Lumiera.org
5  2008, Christian Thaeter <ct@pipapo.org>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
23 
30 #include "include/logging.h"
31 #include "lib/safeclib.h"
32 #include "lib/tmpbuf.h"
33 #include "lib/psplay.h"
34 #include "lib/recmutex.h"
35 #include "lib/error.h"
36 
37 #include "include/config-facade.h"
39 #include "common/config.h"
40 #include "common/plugin.h"
41 
42 #include <glob.h>
43 
44 #include <nobug.h>
45 
46 
47 
48 extern PSplay lumiera_pluginregistry;
49 static char* init_exts_globs (void);
50 
51 
52 
53 /* errors */
54 LUMIERA_ERROR_DEFINE(PLUGIN_INIT, "Initialisation error");
55 LUMIERA_ERROR_DEFINE(PLUGIN_OPEN, "Could not open plugin");
56 LUMIERA_ERROR_DEFINE(PLUGIN_WTF, "Not a Lumiera plugin");
57 LUMIERA_ERROR_DEFINE(PLUGIN_REGISTER, "Could not register plugin");
58 LUMIERA_ERROR_DEFINE(PLUGIN_VERSION, "Plugin Version unsupported");
59 
60 #define LUMIERA_PLUGIN_TYPE_PLANNED(name, ext)
61 
68 #define LUMIERA_PLUGIN_TYPES \
69  LUMIERA_PLUGIN_TYPE(DYNLIB, ".so") \
70  LUMIERA_PLUGIN_TYPE(DYNLIB, ".lum") \
71  LUMIERA_PLUGIN_TYPE_PLANNED(LUA, ".lua") \
72  LUMIERA_PLUGIN_TYPE_PLANNED(CSOURCE, ".c")
73 
74 
79 {
80  LumieraPlugin (*lumiera_plugin_load_fn)(const char*);
81  void (*lumiera_plugin_unload_fn)(LumieraPlugin);
82  const char* ext;
83 };
84 typedef struct lumiera_plugintype_struct lumiera_plugintype;
85 typedef lumiera_plugintype* LumieraPlugintype;
86 
87 /* forward declare loader functions for all types */
88 #define LUMIERA_PLUGIN_TYPE(type, ext) \
89  LumieraPlugin lumiera_plugin_load_##type (const char*); \
90  void lumiera_plugin_unload_##type (LumieraPlugin);
92 #undef LUMIERA_PLUGIN_TYPE
93 
94 /* and now setup a table which will be used for dispatching loaders depending on the type of the plugin */
95 #define LUMIERA_PLUGIN_TYPE(type, ext) {lumiera_plugin_load_##type, lumiera_plugin_unload_##type, ext},
96 static lumiera_plugintype lumiera_plugin_types[] =
97  {
99  {NULL, NULL, NULL}
100  };
101 #undef LUMIERA_PLUGIN_TYPE
102 
103 
104 
105 
107 {
108  psplaynode node;
109 
110  /* long names as looked up ("/usr/local/lib/lumiera/plugins/effects/audio/normalize.so") */
111  const char* name;
112 
113  /* use count for all interfaces of this plugin */
114  unsigned refcnt;
115 
116  /* time when the refcounter dropped to 0 last time */
117  time_t last;
118 
121  lumiera_err error;
122 
123  /* the 'plugin' interface itself */
124  LumieraInterface plugin;
125 
126  /* generic handle for the plugin, dlopen handle, etc */
127  void* handle;
128 };
129 
130 
131 LumieraPlugin
133 {
134  LumieraPlugin self = lumiera_malloc (sizeof (*self));
135 
136  psplaynode_init (&self->node);
137  self->name = lumiera_strndup (name, SIZE_MAX);
138  self->refcnt = 0;
139  time (&self->last);
140  self->error = LUMIERA_ERROR_PLUGIN_INIT;
141  self->plugin = NULL;
142  self->handle = NULL;
143  return self;
144 }
145 
146 
147 LumieraPlugin
148 lumiera_plugin_init (LumieraPlugin self, void* handle, LumieraInterface plugin)
149 {
150  self->error = lumiera_error ();
151  self->plugin = plugin;
152  self->handle = handle;
153  return self;
154 }
155 
156 
157 lumiera_err
158 lumiera_plugin_error (LumieraPlugin self)
159 {
160  REQUIRE (self);
161  return self->error;
162 }
163 
164 
165 void*
166 lumiera_plugin_handle (LumieraPlugin self)
167 {
168  REQUIRE (self);
169  return self->handle;
170 }
171 
172 
173 const char*
174 lumiera_plugin_name (LumieraPlugin self)
175 {
176  return self?self->name:NULL;
177 }
178 
179 
180 void
181 lumiera_plugin_refinc (LumieraPlugin self)
182 {
183  ++self->refcnt;
184 }
185 
186 
187 void
188 lumiera_plugin_refdec (LumieraPlugin self)
189 {
190  if (!--self->refcnt)
191  time (&self->last);
192 }
193 
194 
195 int
196 lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin),
197  int (*callback_register) (LumieraPlugin))
198 {
199  TRACE (pluginloader_dbg);
200  REQUIRE (callback_load);
201  REQUIRE (callback_register);
202 
203  // Note: because the full-blown Config system isn't implemented yet,
204  // as a temporary solution we fetch this basic configuration
205  // from the setup.ini used to bootstrap the application
206  lumiera_config_setdefault (lumiera_get_plugin_path_default());
207 
208  /* construct glob trail {.so,.lum,.lua} ... */
209  static char* exts_globs = NULL;
210  if (!exts_globs)
211  exts_globs = init_exts_globs ();
212 
213  const char* path;
214  unsigned i = 0;
215  int flags = GLOB_PERIOD|GLOB_BRACE|GLOB_TILDE_CHECK;
216  glob_t globs;
217 
218  while ((path = lumiera_config_wordlist_get_nth ("plugin.path", i, ":")))
219  {
220  path = lumiera_tmpbuf_snprintf (SIZE_MAX,"%s/%s", path, exts_globs);
221  TRACE (pluginloader_dbg, "globbing path '%s'", path);
222  int ret = glob (path, flags, NULL, &globs);
223  if (ret == GLOB_NOSPACE)
224  LUMIERA_DIE (NO_MEMORY);
225 
226  flags |= GLOB_APPEND;
227  ++i;
228  }
229 
230  if (globs.gl_pathc)
231  LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
232  {
233  for (char** itr = globs.gl_pathv; *itr; ++itr)
234  {
235  if (!psplay_find (lumiera_pluginregistry, *itr, 100))
236  {
237  TRACE (pluginloader, "found new plugin '%s'", *itr);
238  callback_register (callback_load (*itr));
239  }
240  }
241  }
242 
243  globfree (&globs);
244 
245  return !lumiera_error_peek ();
246 }
247 
248 
249 LumieraPlugin
250 lumiera_plugin_load (const char* plugin)
251 {
252  TRACE (pluginloader_dbg, "plugin=%s", plugin);
253 
254  /* dispatch on extension, call the registered function */
255  const char* ext = strrchr (plugin, '.');
256 
257  LumieraPlugintype itr = lumiera_plugin_types;
258  while (itr->ext)
259  {
260  if (!strcmp (itr->ext, ext))
261  return itr->lumiera_plugin_load_fn (plugin);
262  ++itr;
263  }
264  return NULL;
265 }
266 
267 
268 int
269 lumiera_plugin_register (LumieraPlugin plugin)
270 {
271  TRACE (pluginloader_dbg);
272  if (!plugin)
273  return 1;
274 
275  LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
276  {
277  if (psplay_insert (lumiera_pluginregistry, &plugin->node, 100))
278  {
279  if (!plugin->error)
280  {
281  switch (lumiera_interface_version (plugin->plugin, "lumieraorg__plugin"))
282  {
283  case 0:
284  {
285  TRACE (pluginloader, "registering %s", plugin->name);
286  LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle =
287  LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) plugin->plugin;
288  lumiera_interfaceregistry_bulkregister_interfaces (handle->plugin_interfaces (), plugin);
289  }
290  break;
291  default:
292  LUMIERA_ERROR_SET (pluginloader, PLUGIN_VERSION, plugin->name);
293  }
294  }
295  }
296  else
297  {
298  LUMIERA_ERROR_SET_CRITICAL (pluginloader, PLUGIN_REGISTER, plugin->name);
299  }
300  }
301  return !!lumiera_error_peek();
302 }
303 
304 
305 unsigned
306 lumiera_plugin_unload (LumieraPlugin self)
307 {
308  TRACE (pluginloader_dbg);
309 
310  if (!self)
311  return 0;
312 
313  if (self->refcnt)
314  return self->refcnt;
315 
316  /* dispatch on extension, call the registered function */
317  const char* ext = strrchr (self->name, '.');
318 
319  LumieraPlugintype itr = lumiera_plugin_types;
320  while (itr->ext)
321  {
322  if (!strcmp (itr->ext, ext))
323  {
324  LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
325  {
326  if (psplay_remove (lumiera_pluginregistry, &self->node))
327  {
328  if (!self->error)
329  {
330  LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle =
331  LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) self->plugin;
332  lumiera_interfaceregistry_bulkremove_interfaces (handle->plugin_interfaces ());
333  }
334  }
335  }
336  itr->lumiera_plugin_unload_fn (self);
337  break;
338  }
339  ++itr;
340  }
341 
342  return 0;
343 }
344 
345 
346 LumieraPlugin
348 {
349  LumieraPlugin ret = NULL;
350 
351  if (name)
352  LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
353  ret = (LumieraPlugin) psplay_find (lumiera_pluginregistry, name, 100);
354 
355  return ret;
356 }
357 
358 
359 static char* init_exts_globs (void)
360 {
361  char* exts_globs;
362  size_t exts_sz = 3; /* * { } \0 less one comma */
363  LumieraPlugintype itr = lumiera_plugin_types;
364  while (itr->ext)
365  {
366  exts_sz += strlen (itr->ext) + 1;
367  ++itr;
368  }
369 
370  exts_globs = lumiera_malloc (exts_sz);
371  *exts_globs = '\0';
372 
373  itr = lumiera_plugin_types;
374  strcat (exts_globs, "*{");
375 
376  while (itr->ext)
377  {
378  strcat (exts_globs, itr->ext);
379  strcat (exts_globs, ",");
380  ++itr;
381  }
382  exts_globs[exts_sz-2] = '}';
383  TRACE (pluginloader_dbg, "initialised extension glob to '%s'", exts_globs);
384  return exts_globs;
385 }
386 
387 int
388 lumiera_plugin_cmp_fn (const void* keya, const void* keyb)
389 {
390  return strcmp ((const char*)keya, (const char*)keyb);
391 }
392 
393 
394 const void*
395 lumiera_plugin_key_fn (const PSplaynode node)
396 {
397  return ((LumieraPlugin)node)->name;
398 }
399 
400 
401 void
402 lumiera_plugin_delete_fn (PSplaynode node)
403 {
404  LumieraPlugin self = (LumieraPlugin) node;
405 
406  ENSURE (!self->refcnt, "plugin %s still in use at shutdown", self->name);
407 
408  const char* ext = strrchr (self->name, '.');
409 
410  LumieraPlugintype itr = lumiera_plugin_types;
411  while (itr->ext)
412  {
413  if (!strcmp (itr->ext, ext))
414  {
415  if (!self->error)
416  {
417  LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle =
418  LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) self->plugin;
419  lumiera_interfaceregistry_bulkremove_interfaces (handle->plugin_interfaces ());
420  }
421  TRACE (pluginloader_dbg, "unloading plugin/module %s", self->name);
422  itr->lumiera_plugin_unload_fn (self);
423  break;
424  }
425  ++itr;
426  }
427 }
LumieraPlugin lumiera_plugin_lookup(const char *name)
Lookup a plugin handle in the pluginregistry.
Definition: plugin.c:347
Portable and safe wrappers around some C-Lib functions.
#define LUMIERA_PLUGIN_TYPES
Supported (and planned) plugin types and their file extensions This maps filename extensions to imple...
Definition: plugin.c:68
#define LUMIERA_ERROR_SET(flag, err, extra)
Helper macro to raise an error for the current thread.
Definition: error.h:91
#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx)
Recursive Mutual exclusive section.
Definition: recmutex.h:41
lumiera_err lumiera_error_peek(void)
Check current error state without clearing it Please avoid this function and use lumiera_error() if p...
Definition: error-state.c:142
LumieraPlugin lumiera_plugin_new(const char *name)
Allocates an preinitialises a plugin structure.
Definition: plugin.c:132
const char * lumiera_plugin_name(LumieraPlugin self)
Query the plugin name The name is the path and filename under which it was loaded.
Definition: plugin.c:174
int lumiera_plugin_register(LumieraPlugin plugin)
Register a plugin and its interfaces.
Definition: plugin.c:269
LumieraPlugin lumiera_plugin_load(const char *plugin)
Tries to load a plugin Creates a new plugin structure and tries to load and initialise the plugin...
Definition: plugin.c:250
record the extension and a callback function for loading the associated plugin for each plugin type ...
Definition: plugin.c:78
Lumiera plugins define &#39;interfaces&#39; as shown in interface.h, the plugin system handles the loading of...
Interface for a lumiera configuration system (draft).
unsigned lumiera_interface_version(LumieraInterface self, const char *iname)
Runtime check for interface type and version.
Definition: interface.c:247
This header is for including and configuring NoBug.
Mutual exclusion locking, header.
Round robin temporary buffers.
Lumiera error handling (C interface).
PSplaynode psplay_find(PSplay self, const void *key, int splayfactor)
Find a element in a splay tree.
Definition: psplay.c:309
PSplaynode psplay_insert(PSplay self, PSplaynode node, int splayfactor)
Insert a element into a splay tree.
Definition: psplay.c:255
unsigned lumiera_plugin_unload(LumieraPlugin self)
Tries to unload a plugin.
Definition: plugin.c:306
void lumiera_plugin_refinc(LumieraPlugin self)
Definition: plugin.c:181
#define LUMIERA_INTERFACE_CAST(name, version)
Construct a cast to the target interface type Used to cast a generic LumieraInterface to the real typ...
Definition: interface.h:146
The lumiera::Config wrapper class addresses two issues.
#define LUMIERA_ERROR_SET_CRITICAL(flag, err, extra)
Helper macro to raise an error for the current thread.
Definition: error.h:121
#define LUMIERA_DIE(err)
Abort unconditionally with a &#39;Fatal Error!&#39; message.
Definition: error.h:63
LumieraConfigitem lumiera_config_setdefault(const char *line)
Installs a default value for a config key.
Definition: config.c:274
Probabilistic splay tree.
int lumiera_plugin_discover(LumieraPlugin(*callback_load)(const char *plugin), int(*callback_register)(LumieraPlugin))
discover new plugins traverses the configured plugin paths and calls the callback_load function for a...
Definition: plugin.c:196
lumiera_err lumiera_error(void)
Get and clear current error state.
Definition: error-state.c:124
void * lumiera_malloc(size_t size)
Allocate memory.
Definition: safeclib.c:122
const char * lumiera_config_wordlist_get_nth(const char *key, unsigned nth, const char *delims)
return nth word of a wordlist
lumiera_err lumiera_plugin_error(LumieraPlugin self)
Query the error state of a plugin.
Definition: plugin.c:158
Global registry for interfaces (extension points).
PSplaynode psplay_remove(PSplay self, PSplaynode node)
Remove a node from a splay tree.
Definition: psplay.c:349
LumieraPlugin lumiera_plugin_init(LumieraPlugin self, void *handle, LumieraInterface plugin)
Definition: plugin.c:148
#define LUMIERA_INTERFACE_HANDLE(interface, version)
create a handle for a interface (WIP)
Definition: interface.h:356
char * lumiera_strndup(const char *str, size_t len)
Duplicate a C string.
Definition: safeclib.c:175
void * lumiera_plugin_handle(LumieraPlugin self)
Query the plugin handle.
Definition: plugin.c:166
PSplaynode psplaynode_init(PSplaynode self)
Initialise a splay tree node The user has to place this nodes within his datastructure and must Initi...
Definition: psplay.c:131
ElementBoxWidget::Config::Qualifier name(string id)
define the name-ID displayed in the caption
lumiera_err error
bulk loading plugins must not fail entirely, just because one plugin doesn&#39;t comply.
Definition: plugin.c:121
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition: error.h:80
void lumiera_plugin_refdec(LumieraPlugin self)
Definition: plugin.c:188
char * lumiera_tmpbuf_snprintf(size_t size, const char *fmt,...)
Construct a string in a tmpbuf.
Definition: tmpbuf.c:125