XMMS2
plugin.c
Go to the documentation of this file.
1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2011 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 */
16
17#include "xmms_configuration.h"
19#include "xmms/xmms_config.h"
21#include "xmms/xmms_object.h"
22#include "xmms/xmms_log.h"
25#include "xmmspriv/xmms_xform.h"
26
27#include <gmodule.h>
28#include <string.h>
29#include <stdarg.h>
30
31#ifdef HAVE_VALGRIND
32# include <memcheck.h>
33#endif
34
35/* OSX uses the .bundle extension, but g_module_build_path returns .so. */
36#ifdef USE_BUNDLES
37#define get_module_ext(dir) g_build_filename (dir, "*.bundle", NULL)
38#else
39#define get_module_ext(dir) g_module_build_path (dir, "*")
40#endif
41
42
43/*
44 * Global variables
45 */
46static GList *xmms_plugin_list;
47
48/*
49 * Function prototypes
50 */
51static gboolean xmms_plugin_setup (xmms_plugin_t *plugin, const xmms_plugin_desc_t *desc);
52static gboolean xmms_plugin_load (const xmms_plugin_desc_t *desc, GModule *module);
53static gboolean xmms_plugin_scan_directory (const gchar *dir);
54
55/*
56 * Public functions
57 */
58
59
60/**
61 * @if internal
62 * -- internal documentation section --
63 * @addtogroup XMMSPlugin
64 * @{
65 */
66
67/**
68 * @internal
69 * Lookup the value of a plugin's config property, given the property key.
70 * @param[in] plugin The plugin
71 * @param[in] key The property key (config path)
72 * @return A config value
73 * @todo config value <-> property fixup
74 */
77 const gchar *key)
78{
79 gchar path[XMMS_PLUGIN_SHORTNAME_MAX_LEN + 256];
81
82 g_return_val_if_fail (plugin, NULL);
83 g_return_val_if_fail (key, NULL);
84
85 g_snprintf (path, sizeof (path), "%s.%s",
86 xmms_plugin_shortname_get (plugin), key);
87 prop = xmms_config_lookup (path);
88
89 return prop;
90}
91
92/**
93 * @internal
94 * Register a config property for a plugin.
95 * @param[in] plugin The plugin
96 * @param[in] name The property name
97 * @param[in] default_value The default value for the property
98 * @param[in] cb A callback function to be executed when the property value
99 * changes
100 * @param[in] userdata Pointer to data to be passed to the callback
101 * @todo config value <-> property fixup
102 */
105 const gchar *name,
106 const gchar *default_value,
108 gpointer userdata)
109{
110 gchar fullpath[XMMS_PLUGIN_SHORTNAME_MAX_LEN + 256];
112
113 g_return_val_if_fail (plugin, NULL);
114 g_return_val_if_fail (name, NULL);
115 g_return_val_if_fail (default_value, NULL);
116
117 g_snprintf (fullpath, sizeof (fullpath), "%s.%s",
118 xmms_plugin_shortname_get (plugin), name);
119
120 prop = xmms_config_property_register (fullpath, default_value, cb,
121 userdata);
122
123 return prop;
124}
125
126/**
127 * @internal Get the type of this plugin
128 * @param[in] plugin The plugin
129 * @return The plugin type (#xmms_plugin_type_t)
130 */
133{
134 g_return_val_if_fail (plugin, 0);
135
136 return plugin->type;
137}
138
139/**
140 * @internal Get the plugin's name. This is just an accessor method.
141 * @param[in] plugin The plugin
142 * @return A string containing the plugin's name
143 */
144const char *
146{
147 g_return_val_if_fail (plugin, NULL);
148
149 return plugin->name;
150}
151
152/**
153 * @internal Get the plugin's short name. This is just an accessor method.
154 * @param[in] plugin The plugin
155 * @return A string containing the plugin's short name
156 */
157const gchar *
159{
160 g_return_val_if_fail (plugin, NULL);
161
162 return plugin->shortname;
163}
164
165/**
166 * @internal Get the plugin's version. This is just an accessor method.
167 * @param[in] plugin The plugin
168 * @return A string containing the plugin's version
169 */
170const gchar *
172{
173 g_return_val_if_fail (plugin, NULL);
174
175 return plugin->version;
176}
177
178/**
179 * @internal Get the plugin's description. This is just an accessor method.
180 * @param[in] plugin The plugin
181 * @return A string containing the plugin's description
182 */
183const char *
185{
186 g_return_val_if_fail (plugin, NULL);
187
188 return plugin->description;
189}
190
191/*
192 * Private functions
193 */
194
195
196static void
197xmms_plugin_add_builtin_plugins (void)
198{
199 extern const xmms_plugin_desc_t xmms_builtin_ringbuf;
200 extern const xmms_plugin_desc_t xmms_builtin_magic;
201 extern const xmms_plugin_desc_t xmms_builtin_converter;
202 extern const xmms_plugin_desc_t xmms_builtin_segment;
203 extern const xmms_plugin_desc_t xmms_builtin_visualization;
204
205 xmms_plugin_load (&xmms_builtin_ringbuf, NULL);
206 xmms_plugin_load (&xmms_builtin_magic, NULL);
207 xmms_plugin_load (&xmms_builtin_converter, NULL);
208 xmms_plugin_load (&xmms_builtin_segment, NULL);
209 xmms_plugin_load (&xmms_builtin_visualization, NULL);
210}
211
212
213/**
214 * @internal Initialise the plugin system
215 * @param[in] path Absolute path to the plugins directory.
216 * @return Whether the initialisation was successful or not.
217 */
218gboolean
219xmms_plugin_init (const gchar *path)
220{
221 if (!path)
222 path = PKGLIBDIR;
223
224 xmms_plugin_scan_directory (path);
225
226 xmms_plugin_add_builtin_plugins ();
227 return TRUE;
228}
229
230/**
231 * @internal Shut down the plugin system. This function unrefs all the plugins
232 * loaded.
233 */
234void
236{
237#ifdef HAVE_VALGRIND
238 /* print out a leak summary at this point, because the final leak
239 * summary won't include proper backtraces of leaks found in
240 * plugins, since we close the so's here.
241 *
242 * note: the following call doesn't do anything if we're not run
243 * in valgrind
244 */
245 VALGRIND_DO_LEAK_CHECK
246 ;
247#endif
248
249 while (xmms_plugin_list) {
250 xmms_plugin_t *p = xmms_plugin_list->data;
251
252 /* if this plugin's refcount is > 1, then there's a bug
253 * in one of the other subsystems
254 */
255 if (p->object.ref > 1) {
256 XMMS_DBG ("%s's refcount is %i",
257 p->name, p->object.ref);
258 }
259
261
262 xmms_plugin_list = g_list_delete_link (xmms_plugin_list,
263 xmms_plugin_list);
264 }
265}
266
267
268static gboolean
269xmms_plugin_load (const xmms_plugin_desc_t *desc, GModule *module)
270{
271 xmms_plugin_t *plugin;
272 xmms_plugin_t *(*allocer) (void);
273 gboolean (*verifier) (xmms_plugin_t *);
274 gint expected_ver;
275
276 XMMS_DBG ("Loading plugin '%s'", desc->name);
277
278 switch (desc->type) {
280 expected_ver = XMMS_OUTPUT_API_VERSION;
281 allocer = xmms_output_plugin_new;
282 verifier = xmms_output_plugin_verify;
283 break;
285 expected_ver = XMMS_XFORM_API_VERSION;
286 allocer = xmms_xform_plugin_new;
287 verifier = xmms_xform_plugin_verify;
288 break;
289 default:
290 XMMS_DBG ("Unknown plugin type!");
291 return FALSE;
292 }
293
294 if (desc->api_version != expected_ver) {
295 XMMS_DBG ("Bad api version!");
296 return FALSE;
297 }
298
299 plugin = allocer ();
300 if (!plugin) {
301 XMMS_DBG ("Alloc failed!");
302 return FALSE;
303 }
304
305 if (!xmms_plugin_setup (plugin, desc)) {
306 xmms_log_error ("Setup failed for plugin '%s'!", desc->name);
307 xmms_object_unref (plugin);
308 return FALSE;
309 }
310
311 if (!desc->setup_func (plugin)) {
312 xmms_log_error ("Setup function failed for plugin '%s'!",
313 desc->name);
314 xmms_object_unref (plugin);
315 return FALSE;
316 }
317
318 if (!verifier (plugin)) {
319 xmms_log_error ("Verify failed for plugin '%s'!", desc->name);
320 xmms_object_unref (plugin);
321 return FALSE;
322 }
323
324 plugin->module = module;
325
326 xmms_plugin_list = g_list_prepend (xmms_plugin_list, plugin);
327 return TRUE;
328}
329
330/**
331 * @internal Scan a particular directory for plugins to load
332 * @param[in] dir Absolute path to plugins directory
333 * @return TRUE if directory successfully scanned for plugins
334 */
335static gboolean
336xmms_plugin_scan_directory (const gchar *dir)
337{
338 GDir *d;
339 const char *name;
340 gchar *path;
341 gchar *temp;
342 gchar *pattern;
343 GModule *module;
344 gpointer sym;
345
346 temp = get_module_ext (dir);
347
348 XMMS_DBG ("Scanning directory for plugins (%s)", temp);
349
350 pattern = g_path_get_basename (temp);
351
352 g_free (temp);
353
354 d = g_dir_open (dir, 0, NULL);
355 if (!d) {
356 xmms_log_error ("Failed to open plugin directory (%s)", dir);
357 return FALSE;
358 }
359
360 while ((name = g_dir_read_name (d))) {
361
362 if (!g_pattern_match_simple (pattern, name))
363 continue;
364
365 path = g_build_filename (dir, name, NULL);
366 if (!g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
367 g_free (path);
368 continue;
369 }
370
371 XMMS_DBG ("Trying to load file: %s", path);
372 module = g_module_open (path, G_MODULE_BIND_LOCAL);
373 if (!module) {
374 xmms_log_error ("Failed to open plugin %s: %s",
375 path, g_module_error ());
376 g_free (path);
377 continue;
378 }
379
380 if (!g_module_symbol (module, "XMMS_PLUGIN_DESC", &sym)) {
381 xmms_log_error ("Failed to find plugin header in %s", path);
382 g_module_close (module);
383 g_free (path);
384 continue;
385 }
386 g_free (path);
387
388 if (!xmms_plugin_load ((const xmms_plugin_desc_t *) sym, module)) {
389 g_module_close (module);
390 }
391 }
392
393 g_dir_close (d);
394 g_free (pattern);
395
396 return TRUE;
397}
398
399/**
400 * @internal Apply a function to all plugins of specified type.
401 * @param[in] type The type of plugin to look for.
402 * @param[in] func function to apply.
403 * @param[in] user_data Userspecified data passed to function.
404 */
405void
407{
408 GList *node;
409
410 for (node = xmms_plugin_list; node; node = g_list_next (node)) {
411 xmms_plugin_t *plugin = node->data;
412
413 if (plugin->type == type || type == XMMS_PLUGIN_TYPE_ALL) {
414 if (!func (plugin, user_data))
415 break;
416 }
417 }
418}
419
420typedef struct {
421 const gchar *name;
422 xmms_plugin_t *plugin;
423} xmms_plugin_find_foreach_data_t;
424
425static gboolean
426xmms_plugin_find_foreach (xmms_plugin_t *plugin, gpointer udata)
427{
428 xmms_plugin_find_foreach_data_t *data = udata;
429
430 if (!g_ascii_strcasecmp (plugin->shortname, data->name)) {
431 xmms_object_ref (plugin);
432 data->plugin = plugin;
433 return FALSE;
434 }
435 return TRUE;
436}
437
438/**
439 * @internal Find a plugin that's been loaded, by a particular type and name
440 * @param[in] type The type of plugin to look for
441 * @param[in] name The name of the plugin to look for
442 * @return The plugin instance, if found. NULL otherwise.
443 */
445xmms_plugin_find (xmms_plugin_type_t type, const gchar *name)
446{
447 xmms_plugin_find_foreach_data_t data = {name, NULL};
448 xmms_plugin_foreach (type, xmms_plugin_find_foreach, &data);
449 return data.plugin;
450}
451
452
453static gboolean
454xmms_plugin_setup (xmms_plugin_t *plugin, const xmms_plugin_desc_t *desc)
455{
456 plugin->type = desc->type;
457 plugin->shortname = desc->shortname;
458 plugin->name = desc->name;
459 plugin->version = desc->version;
460 plugin->description = desc->description;
461
462 return TRUE;
463}
464
465void
467{
468 if (plugin->module)
469 g_module_close (plugin->module);
470}
xmms_config_property_t * xmms_config_property_register(const gchar *path, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a new config property.
Definition config.c:334
xmms_config_property_t * xmms_config_lookup(const gchar *path)
Look up a config key from the global config.
Definition config.c:171
#define XMMS_OUTPUT_API_VERSION
The current API version.
#define XMMS_XFORM_API_VERSION
gboolean xmms_output_plugin_verify(xmms_plugin_t *_plugin)
xmms_plugin_t * xmms_output_plugin_new(void)
void xmms_plugin_destroy(xmms_plugin_t *plugin)
Definition plugin.c:466
#define get_module_ext(dir)
Definition plugin.c:39
const gchar * xmms_plugin_version_get(const xmms_plugin_t *plugin)
Definition plugin.c:171
gboolean xmms_plugin_init(const gchar *path)
Definition plugin.c:219
xmms_plugin_type_t xmms_plugin_type_get(const xmms_plugin_t *plugin)
Definition plugin.c:132
const gchar * xmms_plugin_shortname_get(const xmms_plugin_t *plugin)
Definition plugin.c:158
void xmms_plugin_foreach(xmms_plugin_type_t type, xmms_plugin_foreach_func_t func, gpointer user_data)
Definition plugin.c:406
const char * xmms_plugin_description_get(const xmms_plugin_t *plugin)
Definition plugin.c:184
xmms_config_property_t * xmms_plugin_config_lookup(xmms_plugin_t *plugin, const gchar *key)
Definition plugin.c:76
void xmms_plugin_shutdown()
Definition plugin.c:235
const char * xmms_plugin_name_get(const xmms_plugin_t *plugin)
Definition plugin.c:145
xmms_config_property_t * xmms_plugin_config_property_register(xmms_plugin_t *plugin, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Definition plugin.c:104
xmms_plugin_t * xmms_plugin_find(xmms_plugin_type_t type, const gchar *name)
Definition plugin.c:445
gboolean(* xmms_plugin_foreach_func_t)(xmms_plugin_t *, gpointer)
Definition xmms_plugin.h:48
GModule * module
Definition xmms_plugin.h:31
const gchar * description
Definition xmms_plugin.h:36
const gchar * shortname
Definition xmms_plugin.h:35
const gchar * name
Definition xmms_plugin.h:34
xmms_plugin_type_t type
Definition xmms_plugin.h:33
xmms_object_t object
Definition xmms_plugin.h:30
const gchar * version
Definition xmms_plugin.h:37
xmms_plugin_t * xmms_xform_plugin_new(void)
gboolean xmms_xform_plugin_verify(xmms_plugin_t *_plugin)
struct xmms_config_property_St xmms_config_property_t
Definition xmms_config.h:26
#define xmms_log_error(fmt,...)
Definition xmms_log.h:35
#define XMMS_DBG(fmt,...)
Definition xmms_log.h:32
G_BEGIN_DECLS struct xmms_plugin_desc_St xmms_plugin_desc_t
#define XMMS_PLUGIN_SHORTNAME_MAX_LEN
Definition xmms_plugin.h:27
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition xmms_object.h:66
#define xmms_object_ref(obj)
#define xmms_object_unref(obj)
xmms_plugin_type_t
@ XMMS_PLUGIN_TYPE_ALL
@ XMMS_PLUGIN_TYPE_XFORM
@ XMMS_PLUGIN_TYPE_OUTPUT