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
00204 xmms_plugin_load (&xmms_builtin_ringbuf, NULL);
00205 xmms_plugin_load (&xmms_builtin_magic, NULL);
00206 xmms_plugin_load (&xmms_builtin_converter, NULL);
00207 xmms_plugin_load (&xmms_builtin_segment, NULL);
00208 }
00209
00210
00211
00212
00213
00214
00215
00216 gboolean
00217 xmms_plugin_init (const gchar *path)
00218 {
00219 if (!path)
00220 path = PKGLIBDIR;
00221
00222 xmms_plugin_scan_directory (path);
00223
00224 xmms_plugin_add_builtin_plugins ();
00225 return TRUE;
00226 }
00227
00228
00229
00230
00231
00232 void
00233 xmms_plugin_shutdown ()
00234 {
00235 #ifdef HAVE_VALGRIND
00236
00237
00238
00239
00240
00241
00242
00243 VALGRIND_DO_LEAK_CHECK
00244 ;
00245 #endif
00246
00247 while (xmms_plugin_list) {
00248 xmms_plugin_t *p = xmms_plugin_list->data;
00249
00250
00251
00252
00253 if (p->object.ref > 1) {
00254 XMMS_DBG ("%s's refcount is %i",
00255 p->name, p->object.ref);
00256 }
00257
00258 xmms_object_unref (p);
00259
00260 xmms_plugin_list = g_list_delete_link (xmms_plugin_list,
00261 xmms_plugin_list);
00262 }
00263 }
00264
00265
00266 static gboolean
00267 xmms_plugin_load (const xmms_plugin_desc_t *desc, GModule *module)
00268 {
00269 xmms_plugin_t *plugin;
00270 xmms_plugin_t *(*allocer) ();
00271 gboolean (*verifier) (xmms_plugin_t *);
00272 gint expected_ver;
00273
00274 switch (desc->type) {
00275 case XMMS_PLUGIN_TYPE_OUTPUT:
00276 expected_ver = XMMS_OUTPUT_API_VERSION;
00277 allocer = xmms_output_plugin_new;
00278 verifier = xmms_output_plugin_verify;
00279 break;
00280 case XMMS_PLUGIN_TYPE_XFORM:
00281 expected_ver = XMMS_XFORM_API_VERSION;
00282 allocer = xmms_xform_plugin_new;
00283 verifier = xmms_xform_plugin_verify;
00284 break;
00285 default:
00286 XMMS_DBG ("Unknown plugin type!");
00287 return FALSE;
00288 }
00289
00290 if (desc->api_version != expected_ver) {
00291 XMMS_DBG ("Bad api version!");
00292 return FALSE;
00293 }
00294
00295 plugin = allocer ();
00296 if (!plugin) {
00297 XMMS_DBG ("Alloc failed!");
00298 return FALSE;
00299 }
00300
00301 if (!xmms_plugin_setup (plugin, desc)) {
00302 xmms_log_error ("Setup failed!");
00303 xmms_object_unref (plugin);
00304 return FALSE;
00305 }
00306
00307 if (!desc->setup_func (plugin)) {
00308 xmms_log_error ("Setup function returned error!");
00309 xmms_object_unref (plugin);
00310 return FALSE;
00311 }
00312
00313 if (!verifier (plugin)) {
00314 xmms_log_error ("Verify failed for plugin!");
00315 xmms_object_unref (plugin);
00316 return FALSE;
00317 }
00318
00319 plugin->module = module;
00320
00321 xmms_plugin_list = g_list_prepend (xmms_plugin_list, plugin);
00322 return TRUE;
00323 }
00324
00325
00326
00327
00328
00329
00330 static gboolean
00331 xmms_plugin_scan_directory (const gchar *dir)
00332 {
00333 GDir *d;
00334 const char *name;
00335 gchar *path;
00336 gchar *temp;
00337 gchar *pattern;
00338 GModule *module;
00339 gpointer sym;
00340
00341 temp = get_module_ext (dir);
00342
00343 XMMS_DBG ("Scanning directory for plugins (%s)", temp);
00344
00345 pattern = g_path_get_basename (temp);
00346
00347 g_free (temp);
00348
00349 d = g_dir_open (dir, 0, NULL);
00350 if (!d) {
00351 xmms_log_error ("Failed to open plugin directory (%s)", dir);
00352 return FALSE;
00353 }
00354
00355 while ((name = g_dir_read_name (d))) {
00356
00357 if (!g_pattern_match_simple (pattern, name))
00358 continue;
00359
00360 path = g_build_filename (dir, name, NULL);
00361 if (!g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
00362 g_free (path);
00363 continue;
00364 }
00365
00366 XMMS_DBG ("Trying to load file: %s", path);
00367 module = g_module_open (path, 0);
00368 if (!module) {
00369 xmms_log_error ("Failed to open plugin %s: %s",
00370 path, g_module_error ());
00371 g_free (path);
00372 continue;
00373 }
00374
00375 if (!g_module_symbol (module, "XMMS_PLUGIN_DESC", &sym)) {
00376 xmms_log_error ("Failed to find plugin header in %s", path);
00377 g_module_close (module);
00378 g_free (path);
00379 continue;
00380 }
00381 g_free (path);
00382
00383 if (!xmms_plugin_load ((const xmms_plugin_desc_t *) sym, module)) {
00384 g_module_close (module);
00385 }
00386 }
00387
00388 g_dir_close (d);
00389 g_free (pattern);
00390
00391 return TRUE;
00392 }
00393
00394 static gboolean
00395 xmms_plugin_client_list_foreach (xmms_plugin_t *plugin, gpointer data)
00396 {
00397 GTree *dict;
00398 GList **list = data;
00399
00400 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
00401 NULL,
00402 (GDestroyNotify)xmms_object_cmd_value_unref);
00403
00404 g_tree_insert (dict, (gpointer) "name",
00405 xmms_object_cmd_value_str_new (xmms_plugin_name_get (plugin)));
00406 g_tree_insert (dict, (gpointer) "shortname",
00407 xmms_object_cmd_value_str_new (xmms_plugin_shortname_get (plugin)));
00408 g_tree_insert (dict, (gpointer) "version",
00409 xmms_object_cmd_value_str_new (xmms_plugin_version_get (plugin)));
00410 g_tree_insert (dict, (gpointer) "description",
00411 xmms_object_cmd_value_str_new (xmms_plugin_description_get (plugin)));
00412 g_tree_insert (dict, (gpointer) "type",
00413 xmms_object_cmd_value_uint_new (xmms_plugin_type_get (plugin)));
00414
00415 *list = g_list_prepend (*list, xmms_object_cmd_value_dict_new (dict));
00416
00417 return TRUE;
00418 }
00419
00420 GList *
00421 xmms_plugin_client_list (xmms_object_t *main, guint32 type, xmms_error_t *err)
00422 {
00423 GList *list = NULL;
00424 xmms_plugin_foreach (type, xmms_plugin_client_list_foreach, &list);
00425 return list;
00426 }
00427
00428
00429
00430
00431
00432
00433
00434 void
00435 xmms_plugin_foreach (xmms_plugin_type_t type, xmms_plugin_foreach_func_t func, gpointer user_data)
00436 {
00437 GList *node;
00438
00439 for (node = xmms_plugin_list; node; node = g_list_next (node)) {
00440 xmms_plugin_t *plugin = node->data;
00441
00442 if (plugin->type == type || type == XMMS_PLUGIN_TYPE_ALL) {
00443 if (!func (plugin, user_data))
00444 break;
00445 }
00446 }
00447 }
00448
00449 typedef struct {
00450 const gchar *name;
00451 xmms_plugin_t *plugin;
00452 } xmms_plugin_find_foreach_data_t;
00453
00454 static gboolean
00455 xmms_plugin_find_foreach (xmms_plugin_t *plugin, gpointer udata)
00456 {
00457 xmms_plugin_find_foreach_data_t *data = udata;
00458
00459 if (!g_ascii_strcasecmp (plugin->shortname, data->name)) {
00460 xmms_object_ref (plugin);
00461 data->plugin = plugin;
00462 return FALSE;
00463 }
00464 return TRUE;
00465 }
00466
00467
00468
00469
00470
00471
00472
00473 xmms_plugin_t *
00474 xmms_plugin_find (xmms_plugin_type_t type, const gchar *name)
00475 {
00476 xmms_plugin_find_foreach_data_t data = {name, NULL};
00477 xmms_plugin_foreach (type, xmms_plugin_find_foreach, &data);
00478 return data.plugin;
00479 }
00480
00481
00482 static gboolean
00483 xmms_plugin_setup (xmms_plugin_t *plugin, const xmms_plugin_desc_t *desc)
00484 {
00485 plugin->type = desc->type;
00486 plugin->shortname = desc->shortname;
00487 plugin->name = desc->name;
00488 plugin->version = desc->version;
00489 plugin->description = desc->description;
00490
00491 return TRUE;
00492 }
00493
00494 void
00495 xmms_plugin_destroy (xmms_plugin_t *plugin)
00496 {
00497 if (plugin->module)
00498 g_module_close (plugin->module);
00499 }