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) ();
00273 gboolean (*verifier) (xmms_plugin_t *);
00274 gint expected_ver;
00275
00276 switch (desc->type) {
00277 case XMMS_PLUGIN_TYPE_OUTPUT:
00278 expected_ver = XMMS_OUTPUT_API_VERSION;
00279 allocer = xmms_output_plugin_new;
00280 verifier = xmms_output_plugin_verify;
00281 break;
00282 case XMMS_PLUGIN_TYPE_XFORM:
00283 expected_ver = XMMS_XFORM_API_VERSION;
00284 allocer = xmms_xform_plugin_new;
00285 verifier = xmms_xform_plugin_verify;
00286 break;
00287 default:
00288 XMMS_DBG ("Unknown plugin type!");
00289 return FALSE;
00290 }
00291
00292 if (desc->api_version != expected_ver) {
00293 XMMS_DBG ("Bad api version!");
00294 return FALSE;
00295 }
00296
00297 plugin = allocer ();
00298 if (!plugin) {
00299 XMMS_DBG ("Alloc failed!");
00300 return FALSE;
00301 }
00302
00303 if (!xmms_plugin_setup (plugin, desc)) {
00304 xmms_log_error ("Setup failed!");
00305 xmms_object_unref (plugin);
00306 return FALSE;
00307 }
00308
00309 if (!desc->setup_func (plugin)) {
00310 xmms_log_error ("Setup function returned error!");
00311 xmms_object_unref (plugin);
00312 return FALSE;
00313 }
00314
00315 if (!verifier (plugin)) {
00316 xmms_log_error ("Verify failed for plugin!");
00317 xmms_object_unref (plugin);
00318 return FALSE;
00319 }
00320
00321 plugin->module = module;
00322
00323 xmms_plugin_list = g_list_prepend (xmms_plugin_list, plugin);
00324 return TRUE;
00325 }
00326
00327
00328
00329
00330
00331
00332 static gboolean
00333 xmms_plugin_scan_directory (const gchar *dir)
00334 {
00335 GDir *d;
00336 const char *name;
00337 gchar *path;
00338 gchar *temp;
00339 gchar *pattern;
00340 GModule *module;
00341 gpointer sym;
00342
00343 temp = get_module_ext (dir);
00344
00345 XMMS_DBG ("Scanning directory for plugins (%s)", temp);
00346
00347 pattern = g_path_get_basename (temp);
00348
00349 g_free (temp);
00350
00351 d = g_dir_open (dir, 0, NULL);
00352 if (!d) {
00353 xmms_log_error ("Failed to open plugin directory (%s)", dir);
00354 return FALSE;
00355 }
00356
00357 while ((name = g_dir_read_name (d))) {
00358
00359 if (!g_pattern_match_simple (pattern, name))
00360 continue;
00361
00362 path = g_build_filename (dir, name, NULL);
00363 if (!g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
00364 g_free (path);
00365 continue;
00366 }
00367
00368 XMMS_DBG ("Trying to load file: %s", path);
00369 module = g_module_open (path, 0);
00370 if (!module) {
00371 xmms_log_error ("Failed to open plugin %s: %s",
00372 path, g_module_error ());
00373 g_free (path);
00374 continue;
00375 }
00376
00377 if (!g_module_symbol (module, "XMMS_PLUGIN_DESC", &sym)) {
00378 xmms_log_error ("Failed to find plugin header in %s", path);
00379 g_module_close (module);
00380 g_free (path);
00381 continue;
00382 }
00383 g_free (path);
00384
00385 if (!xmms_plugin_load ((const xmms_plugin_desc_t *) sym, module)) {
00386 g_module_close (module);
00387 }
00388 }
00389
00390 g_dir_close (d);
00391 g_free (pattern);
00392
00393 return TRUE;
00394 }
00395
00396 static gboolean
00397 xmms_plugin_client_list_foreach (xmms_plugin_t *plugin, gpointer data)
00398 {
00399 xmmsv_t *dict;
00400 GList **list = data;
00401
00402 dict = xmmsv_build_dict (
00403 XMMSV_DICT_ENTRY_STR ("name", xmms_plugin_name_get (plugin)),
00404 XMMSV_DICT_ENTRY_STR ("shortname", xmms_plugin_shortname_get (plugin)),
00405 XMMSV_DICT_ENTRY_STR ("version", xmms_plugin_version_get (plugin)),
00406 XMMSV_DICT_ENTRY_STR ("description", xmms_plugin_description_get (plugin)),
00407 XMMSV_DICT_ENTRY_INT ("type", xmms_plugin_type_get (plugin)),
00408 XMMSV_DICT_END);
00409
00410 *list = g_list_prepend (*list, dict);
00411
00412 return TRUE;
00413 }
00414
00415 GList *
00416 xmms_plugin_client_list (xmms_object_t *main, gint32 type, xmms_error_t *err)
00417 {
00418 GList *list = NULL;
00419 xmms_plugin_foreach (type, xmms_plugin_client_list_foreach, &list);
00420 return list;
00421 }
00422
00423
00424
00425
00426
00427
00428
00429 void
00430 xmms_plugin_foreach (xmms_plugin_type_t type, xmms_plugin_foreach_func_t func, gpointer user_data)
00431 {
00432 GList *node;
00433
00434 for (node = xmms_plugin_list; node; node = g_list_next (node)) {
00435 xmms_plugin_t *plugin = node->data;
00436
00437 if (plugin->type == type || type == XMMS_PLUGIN_TYPE_ALL) {
00438 if (!func (plugin, user_data))
00439 break;
00440 }
00441 }
00442 }
00443
00444 typedef struct {
00445 const gchar *name;
00446 xmms_plugin_t *plugin;
00447 } xmms_plugin_find_foreach_data_t;
00448
00449 static gboolean
00450 xmms_plugin_find_foreach (xmms_plugin_t *plugin, gpointer udata)
00451 {
00452 xmms_plugin_find_foreach_data_t *data = udata;
00453
00454 if (!g_ascii_strcasecmp (plugin->shortname, data->name)) {
00455 xmms_object_ref (plugin);
00456 data->plugin = plugin;
00457 return FALSE;
00458 }
00459 return TRUE;
00460 }
00461
00462
00463
00464
00465
00466
00467
00468 xmms_plugin_t *
00469 xmms_plugin_find (xmms_plugin_type_t type, const gchar *name)
00470 {
00471 xmms_plugin_find_foreach_data_t data = {name, NULL};
00472 xmms_plugin_foreach (type, xmms_plugin_find_foreach, &data);
00473 return data.plugin;
00474 }
00475
00476
00477 static gboolean
00478 xmms_plugin_setup (xmms_plugin_t *plugin, const xmms_plugin_desc_t *desc)
00479 {
00480 plugin->type = desc->type;
00481 plugin->shortname = desc->shortname;
00482 plugin->name = desc->name;
00483 plugin->version = desc->version;
00484 plugin->description = desc->description;
00485
00486 return TRUE;
00487 }
00488
00489 void
00490 xmms_plugin_destroy (xmms_plugin_t *plugin)
00491 {
00492 if (plugin->module)
00493 g_module_close (plugin->module);
00494 }