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