00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "xmms_configuration.h"
00018 #include "xmmspriv/xmms_plugin.h"
00019 #include "xmms/xmms_config.h"
00020 #include "xmmspriv/xmms_config.h"
00021 #include "xmms/xmms_object.h"
00022 #include "xmms/xmms_log.h"
00023 #include "xmmspriv/xmms_playlist.h"
00024 #include "xmmspriv/xmms_outputplugin.h"
00025 #include "xmmspriv/xmms_xform.h"
00026
00027 #include <gmodule.h>
00028 #include <string.h>
00029 #include <stdarg.h>
00030
00031 #ifdef HAVE_VALGRIND
00032 # include <memcheck.h>
00033 #endif
00034
00035
00036 #ifdef USE_BUNDLES
00037 #define get_module_ext(dir) g_build_filename (dir, "*.bundle", NULL)
00038 #else
00039 #define get_module_ext(dir) g_module_build_path (dir, "*")
00040 #endif
00041
00042
00043
00044
00045
00046 static GList *xmms_plugin_list;
00047
00048
00049
00050
00051 static gboolean xmms_plugin_setup (xmms_plugin_t *plugin, const xmms_plugin_desc_t *desc);
00052 static gboolean xmms_plugin_load (const xmms_plugin_desc_t *desc, GModule *module);
00053 static gboolean xmms_plugin_scan_directory (const gchar *dir);
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 xmms_config_property_t *
00076 xmms_plugin_config_lookup (xmms_plugin_t *plugin,
00077 const gchar *key)
00078 {
00079 gchar path[XMMS_PLUGIN_SHORTNAME_MAX_LEN + 256];
00080 xmms_config_property_t *prop;
00081
00082 g_return_val_if_fail (plugin, NULL);
00083 g_return_val_if_fail (key, NULL);
00084
00085 g_snprintf (path, sizeof (path), "%s.%s",
00086 xmms_plugin_shortname_get (plugin), key);
00087 prop = xmms_config_lookup (path);
00088
00089 return prop;
00090 }
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 xmms_config_property_t *
00104 xmms_plugin_config_property_register (xmms_plugin_t *plugin,
00105 const gchar *name,
00106 const gchar *default_value,
00107 xmms_object_handler_t cb,
00108 gpointer userdata)
00109 {
00110 gchar fullpath[XMMS_PLUGIN_SHORTNAME_MAX_LEN + 256];
00111 xmms_config_property_t *prop;
00112
00113 g_return_val_if_fail (plugin, NULL);
00114 g_return_val_if_fail (name, NULL);
00115 g_return_val_if_fail (default_value, NULL);
00116
00117 g_snprintf (fullpath, sizeof (fullpath), "%s.%s",
00118 xmms_plugin_shortname_get (plugin), name);
00119
00120 prop = xmms_config_property_register (fullpath, default_value, cb,
00121 userdata);
00122
00123 return prop;
00124 }
00125
00126
00127
00128
00129
00130
00131 xmms_plugin_type_t
00132 xmms_plugin_type_get (const xmms_plugin_t *plugin)
00133 {
00134 g_return_val_if_fail (plugin, 0);
00135
00136 return plugin->type;
00137 }
00138
00139
00140
00141
00142
00143
00144 const char *
00145 xmms_plugin_name_get (const xmms_plugin_t *plugin)
00146 {
00147 g_return_val_if_fail (plugin, NULL);
00148
00149 return plugin->name;
00150 }
00151
00152
00153
00154
00155
00156
00157 const gchar *
00158 xmms_plugin_shortname_get (const xmms_plugin_t *plugin)
00159 {
00160 g_return_val_if_fail (plugin, NULL);
00161
00162 return plugin->shortname;
00163 }
00164
00165
00166
00167
00168
00169
00170 const gchar *
00171 xmms_plugin_version_get (const xmms_plugin_t *plugin)
00172 {
00173 g_return_val_if_fail (plugin, NULL);
00174
00175 return plugin->version;
00176 }
00177
00178
00179
00180
00181
00182
00183 const char *
00184 xmms_plugin_description_get (const xmms_plugin_t *plugin)
00185 {
00186 g_return_val_if_fail (plugin, NULL);
00187
00188 return plugin->description;
00189 }
00190
00191
00192
00193
00194
00195
00196 static void
00197 xmms_plugin_add_builtin_plugins (void)
00198 {
00199 extern const xmms_plugin_desc_t xmms_builtin_ringbuf;
00200 extern const xmms_plugin_desc_t xmms_builtin_magic;
00201 extern const xmms_plugin_desc_t xmms_builtin_converter;
00202 extern const xmms_plugin_desc_t xmms_builtin_segment;
00203 extern const xmms_plugin_desc_t xmms_builtin_visualization;
00204
00205 xmms_plugin_load (&xmms_builtin_ringbuf, NULL);
00206 xmms_plugin_load (&xmms_builtin_magic, NULL);
00207 xmms_plugin_load (&xmms_builtin_converter, NULL);
00208 xmms_plugin_load (&xmms_builtin_segment, NULL);
00209 xmms_plugin_load (&xmms_builtin_visualization, NULL);
00210 }
00211
00212
00213
00214
00215
00216
00217
00218 gboolean
00219 xmms_plugin_init (const gchar *path)
00220 {
00221 if (!path)
00222 path = PKGLIBDIR;
00223
00224 xmms_plugin_scan_directory (path);
00225
00226 xmms_plugin_add_builtin_plugins ();
00227 return TRUE;
00228 }
00229
00230
00231
00232
00233
00234 void
00235 xmms_plugin_shutdown ()
00236 {
00237 #ifdef HAVE_VALGRIND
00238
00239
00240
00241
00242
00243
00244
00245 VALGRIND_DO_LEAK_CHECK
00246 ;
00247 #endif
00248
00249 while (xmms_plugin_list) {
00250 xmms_plugin_t *p = xmms_plugin_list->data;
00251
00252
00253
00254
00255 if (p->object.ref > 1) {
00256 XMMS_DBG ("%s's refcount is %i",
00257 p->name, p->object.ref);
00258 }
00259
00260 xmms_object_unref (p);
00261
00262 xmms_plugin_list = g_list_delete_link (xmms_plugin_list,
00263 xmms_plugin_list);
00264 }
00265 }
00266
00267
00268 static gboolean
00269 xmms_plugin_load (const xmms_plugin_desc_t *desc, GModule *module)
00270 {
00271 xmms_plugin_t *plugin;
00272 xmms_plugin_t *(*allocer) (void);
00273 gboolean (*verifier) (xmms_plugin_t *);
00274 gint expected_ver;
00275
00276 XMMS_DBG ("Loading plugin '%s'", desc->name);
00277
00278 switch (desc->type) {
00279 case XMMS_PLUGIN_TYPE_OUTPUT:
00280 expected_ver = XMMS_OUTPUT_API_VERSION;
00281 allocer = xmms_output_plugin_new;
00282 verifier = xmms_output_plugin_verify;
00283 break;
00284 case XMMS_PLUGIN_TYPE_XFORM:
00285 expected_ver = XMMS_XFORM_API_VERSION;
00286 allocer = xmms_xform_plugin_new;
00287 verifier = xmms_xform_plugin_verify;
00288 break;
00289 default:
00290 XMMS_DBG ("Unknown plugin type!");
00291 return FALSE;
00292 }
00293
00294 if (desc->api_version != expected_ver) {
00295 XMMS_DBG ("Bad api version!");
00296 return FALSE;
00297 }
00298
00299 plugin = allocer ();
00300 if (!plugin) {
00301 XMMS_DBG ("Alloc failed!");
00302 return FALSE;
00303 }
00304
00305 if (!xmms_plugin_setup (plugin, desc)) {
00306 xmms_log_error ("Setup failed for plugin '%s'!", desc->name);
00307 xmms_object_unref (plugin);
00308 return FALSE;
00309 }
00310
00311 if (!desc->setup_func (plugin)) {
00312 xmms_log_error ("Setup function failed for plugin '%s'!",
00313 desc->name);
00314 xmms_object_unref (plugin);
00315 return FALSE;
00316 }
00317
00318 if (!verifier (plugin)) {
00319 xmms_log_error ("Verify failed for plugin '%s'!", desc->name);
00320 xmms_object_unref (plugin);
00321 return FALSE;
00322 }
00323
00324 plugin->module = module;
00325
00326 xmms_plugin_list = g_list_prepend (xmms_plugin_list, plugin);
00327 return TRUE;
00328 }
00329
00330
00331
00332
00333
00334
00335 static gboolean
00336 xmms_plugin_scan_directory (const gchar *dir)
00337 {
00338 GDir *d;
00339 const char *name;
00340 gchar *path;
00341 gchar *temp;
00342 gchar *pattern;
00343 GModule *module;
00344 gpointer sym;
00345
00346 temp = get_module_ext (dir);
00347
00348 XMMS_DBG ("Scanning directory for plugins (%s)", temp);
00349
00350 pattern = g_path_get_basename (temp);
00351
00352 g_free (temp);
00353
00354 d = g_dir_open (dir, 0, NULL);
00355 if (!d) {
00356 xmms_log_error ("Failed to open plugin directory (%s)", dir);
00357 return FALSE;
00358 }
00359
00360 while ((name = g_dir_read_name (d))) {
00361
00362 if (!g_pattern_match_simple (pattern, name))
00363 continue;
00364
00365 path = g_build_filename (dir, name, NULL);
00366 if (!g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
00367 g_free (path);
00368 continue;
00369 }
00370
00371 XMMS_DBG ("Trying to load file: %s", path);
00372 module = g_module_open (path, G_MODULE_BIND_LOCAL);
00373 if (!module) {
00374 xmms_log_error ("Failed to open plugin %s: %s",
00375 path, g_module_error ());
00376 g_free (path);
00377 continue;
00378 }
00379
00380 if (!g_module_symbol (module, "XMMS_PLUGIN_DESC", &sym)) {
00381 xmms_log_error ("Failed to find plugin header in %s", path);
00382 g_module_close (module);
00383 g_free (path);
00384 continue;
00385 }
00386 g_free (path);
00387
00388 if (!xmms_plugin_load ((const xmms_plugin_desc_t *) sym, module)) {
00389 g_module_close (module);
00390 }
00391 }
00392
00393 g_dir_close (d);
00394 g_free (pattern);
00395
00396 return TRUE;
00397 }
00398
00399
00400
00401
00402
00403
00404
00405 void
00406 xmms_plugin_foreach (xmms_plugin_type_t type, xmms_plugin_foreach_func_t func, gpointer user_data)
00407 {
00408 GList *node;
00409
00410 for (node = xmms_plugin_list; node; node = g_list_next (node)) {
00411 xmms_plugin_t *plugin = node->data;
00412
00413 if (plugin->type == type || type == XMMS_PLUGIN_TYPE_ALL) {
00414 if (!func (plugin, user_data))
00415 break;
00416 }
00417 }
00418 }
00419
00420 typedef struct {
00421 const gchar *name;
00422 xmms_plugin_t *plugin;
00423 } xmms_plugin_find_foreach_data_t;
00424
00425 static gboolean
00426 xmms_plugin_find_foreach (xmms_plugin_t *plugin, gpointer udata)
00427 {
00428 xmms_plugin_find_foreach_data_t *data = udata;
00429
00430 if (!g_ascii_strcasecmp (plugin->shortname, data->name)) {
00431 xmms_object_ref (plugin);
00432 data->plugin = plugin;
00433 return FALSE;
00434 }
00435 return TRUE;
00436 }
00437
00438
00439
00440
00441
00442
00443
00444 xmms_plugin_t *
00445 xmms_plugin_find (xmms_plugin_type_t type, const gchar *name)
00446 {
00447 xmms_plugin_find_foreach_data_t data = {name, NULL};
00448 xmms_plugin_foreach (type, xmms_plugin_find_foreach, &data);
00449 return data.plugin;
00450 }
00451
00452
00453 static gboolean
00454 xmms_plugin_setup (xmms_plugin_t *plugin, const xmms_plugin_desc_t *desc)
00455 {
00456 plugin->type = desc->type;
00457 plugin->shortname = desc->shortname;
00458 plugin->name = desc->name;
00459 plugin->version = desc->version;
00460 plugin->description = desc->description;
00461
00462 return TRUE;
00463 }
00464
00465 void
00466 xmms_plugin_destroy (xmms_plugin_t *plugin)
00467 {
00468 if (plugin->module)
00469 g_module_close (plugin->module);
00470 }