/* * SwamiPlugin.c - Swami plugin system * * Swami * Copyright (C) 1999-2003 Josh Green * * Code borrowed and modified from: * GStreamer (gst/gstplugin.c) * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net */ #include #include #include #include #include #include #include "SwamiPlugin.h" #include "SwamiLog.h" #include "SwamiObject.h" /* FIXME: SWAMI_OK */ #include "version.h" #include "config.h" #include "i18n.h" static GModule *main_module; GList *_swami_plugin_static = NULL; /* global list of plugins and its sequence number */ GList *_swami_plugins = NULL; int _swami_plugins_seqno = 0; /* list of paths to check for plugins */ GList *_swami_plugin_paths = NULL; GList *_swami_libraries = NULL; gint _swami_libraries_seqno = 0; /* whether or not to spew library load issues */ gboolean _swami_plugin_spew = FALSE; static void swami_plugin_register_statics (GModule *module); static SwamiPlugin *swami_plugin_register_func (SwamiPluginDesc *desc, SwamiPlugin *plugin, GModule *module); void _swami_plugin_initialize (void) { GList *paths = NULL; main_module = g_module_open (NULL, G_MODULE_BIND_LAZY); swami_plugin_register_statics (main_module); #ifdef PLUGINS_USE_BUILDDIR paths = g_list_prepend (paths, PLUGINS_BUILDDIR "/src/plugins"); #else /* add the main (installed) library path */ paths = g_list_prepend (paths, PLUGINS_DIR); #endif _swami_plugin_paths = g_list_concat (_swami_plugin_paths, paths); } void _swami_plugin_register_static (SwamiPluginDesc *desc) { _swami_plugin_static = g_list_prepend (_swami_plugin_static, desc); } static void swami_plugin_register_statics (GModule *module) { GList *walk = _swami_plugin_static; while (walk) { SwamiPluginDesc *desc = (SwamiPluginDesc *) walk->data; SwamiPlugin *plugin; plugin = g_new0 (SwamiPlugin, 1); plugin->filename = NULL; plugin->module = NULL; plugin = swami_plugin_register_func (desc, plugin, module); if (plugin) { _swami_plugins = g_list_prepend (_swami_plugins, plugin); _swami_plugins_seqno++; plugin->module = module; } walk = g_list_next (walk); } } /** * swami_plugin_add_path: * @path: the directory to add to the search path * * Add a directory to the path searched for plugins. */ void swami_plugin_add_path (const gchar *path) { _swami_plugin_paths = g_list_prepend (_swami_plugin_paths, g_strdup (path)); } static gboolean swami_plugin_load_recurse (gchar *directory, gchar *name) { DIR *dir; struct dirent *dirent; gboolean loaded = FALSE; gchar *dirname; dir = opendir(directory); if (dir) { while ((dirent = readdir(dir))) { /* don't want to recurse in place or backwards */ if (strcmp(dirent->d_name,".") && strcmp(dirent->d_name,"..")) { dirname = g_strjoin("/",directory,dirent->d_name,NULL); loaded = swami_plugin_load_recurse(dirname,name); g_free(dirname); if (loaded && name) { closedir(dir); return TRUE; } } } closedir(dir); } else { if (strstr(directory,".so")) { gchar *temp; if (name) { if ((temp = strstr(directory,name)) && (!strcmp(temp,name))) loaded = swami_plugin_load_absolute(directory); } else if ((temp = strstr(directory,".so")) && (!strcmp(temp,".so"))) loaded = swami_plugin_load_absolute(directory); } } return loaded; } /** * swami_plugin_load_all: * * Load all plugins in the path (in the global GList* _swami_plugin_paths). */ void swami_plugin_load_all (void) { GList *path; path = _swami_plugin_paths; if (path == NULL) g_warning ("swami_plugin_load_all: path is NULL !"); while (path != NULL) { g_message ("Loading plugins from %s", (gchar *)path->data); swami_plugin_load_recurse(path->data,NULL); path = g_list_next(path); } g_message (_("Loaded %d plugins"), _swami_plugins_seqno); } /** * swami_plugin_unload_all: * * Unload all plugins in memory. */ void swami_plugin_unload_all (void) { GList *walk = _swami_plugins; while (walk) { SwamiPlugin *plugin = (SwamiPlugin *) walk->data; SWAMI_INFO ("unloading plugin %s", plugin->name); if (plugin->module) { /* FIXME - Plugin cleanup? */ if (g_module_close (plugin->module)) plugin->module = NULL; else g_warning ("error closing module"); } walk = g_list_next (walk); } } /** * swami_plugin_load: * @name: name of plugin to load * * Load the named plugin. Name should be the file name of the plugin without * a path component. * * Returns: whether the plugin was loaded or not */ gboolean swami_plugin_load (const gchar *name) { GList *path; gchar *libspath; gchar *pluginname; SwamiPlugin *plugin; g_return_val_if_fail (name != NULL, FALSE); plugin = swami_plugin_find (name); if (plugin && plugin->module) return TRUE; path = _swami_plugin_paths; while (path != NULL) { pluginname = g_module_build_path(path->data,name); if (swami_plugin_load_absolute(pluginname)) { g_free(pluginname); return TRUE; } g_free(pluginname); libspath = g_strconcat(path->data,"/.libs",NULL); pluginname = g_module_build_path(libspath,name); g_free(libspath); if (swami_plugin_load_absolute(pluginname)) { g_free(pluginname); return TRUE; } g_free(pluginname); pluginname = g_module_build_path("",name); if (swami_plugin_load_recurse(path->data,pluginname)) { g_free(pluginname); return TRUE; } g_free(pluginname); path = g_list_next(path); } return FALSE; } /** * swami_plugin_load_absolute: * @name: name of plugin to load * * Load the named plugin. Name should be given as * "/path/to/plugin/plugin.so". * * Returns: whether the plugin was loaded or not */ gboolean swami_plugin_load_absolute (const gchar *filename) { SwamiPlugin *plugin = NULL; GList *plugins = _swami_plugins; g_return_val_if_fail (filename != NULL, FALSE); while (plugins) { SwamiPlugin *testplugin = (SwamiPlugin *)plugins->data; if (testplugin->filename) { if (!strcmp (testplugin->filename, filename)) { plugin = testplugin; break; } } plugins = g_list_next (plugins); } if (!plugin) { plugin = g_new0 (SwamiPlugin, 1); plugin->filename = g_strdup (filename); _swami_plugins = g_list_prepend (_swami_plugins, plugin); _swami_plugins_seqno++; } return swami_plugin_load_plugin (plugin); } static gboolean swami_plugin_check_version (gint major, gint minor) { /* return NULL if the major and minor version numbers are not compatible */ /* with ours. */ if (major != SWAMI_VERSION_MAJOR || minor != SWAMI_VERSION_MINOR) return FALSE; return TRUE; } static SwamiPlugin* swami_plugin_register_func (SwamiPluginDesc *desc, SwamiPlugin *plugin, GModule *module) { if (!swami_plugin_check_version (desc->major_version, desc->minor_version)) { g_warning (_("Plugin \"%s\" has incompatible version, not loading"), plugin->filename); return NULL; } plugin->name = g_strdup(desc->name); plugin->descr = g_strdup (desc->descr); plugin->author = g_strdup (desc->author); plugin->copyright = g_strdup (desc->copyright); if ((desc->plugin_init) (module, plugin) != SWAMI_OK) { g_warning (_("Plugin \"%s\" failed to initialize"), plugin->filename); return NULL; } return plugin; } /** * swami_plugin_load_plugin: * @plugin: The plugin to load * * Load the given plugin. * * Returns: whether or not the plugin loaded */ gboolean swami_plugin_load_plugin (SwamiPlugin *plugin) { GModule *module; SwamiPluginDesc *desc; struct stat file_status; gchar *filename; g_return_val_if_fail (plugin != NULL, FALSE); if (plugin->module) return TRUE; filename = plugin->filename; if (g_module_supported () == FALSE) { g_critical ("SwamiPlugin: wow, you built this on a platform without dynamic loading???"); return FALSE; } if (stat (filename, &file_status)) return FALSE; module = g_module_open (filename, G_MODULE_BIND_LAZY); if (module != NULL) { if (g_module_symbol (module, "swami_plugin_desc", (gpointer *)&desc)) { g_message (_("Loading plugin \"%s\"..."), filename); plugin->filename = g_strdup (filename); plugin = swami_plugin_register_func (desc, plugin, module); if (plugin != NULL) { g_message (_("Plugin \"%s\" loaded"), plugin->filename); plugin->module = module; return TRUE; } } } else g_warning (_("Error loading plugin %s, reason: %s\n"), filename, g_module_error()); return FALSE; } /** * swami_plugin_is_loaded: * @plugin: plugin to query * * queries if the plugin is loaded into memory * * Returns: TRUE is loaded, FALSE otherwise */ gboolean swami_plugin_is_loaded (SwamiPlugin *plugin) { g_return_val_if_fail (plugin != NULL, FALSE); return (plugin->module != NULL); } /** * swami_plugin_find: * @name: name of plugin to find * * Search the list of registered plugins for one of the given name * * Returns: pointer to the #SwamiPlugin if found, NULL otherwise */ SwamiPlugin* swami_plugin_find (const gchar *name) { GList *plugins = _swami_plugins; g_return_val_if_fail (name != NULL, NULL); while (plugins) { SwamiPlugin *plugin = (SwamiPlugin *)plugins->data; if (plugin->name && !strcmp (plugin->name, name)) return plugin; plugins = g_list_next (plugins); } return NULL; } /** * swami_plugin_get_list: * * get the currently loaded plugins * * Returns: a GList of SwamiPlugin elements which should be freed with * g_list_free when finished with. */ GList* swami_plugin_get_list (void) { return g_list_copy (_swami_plugins); }