/* * fs-plugin.c - Source for farstream plugin infrastructure * * Farstream Voice+Video library * Copyright (c) 2005 INdT. * @author Andre Moreira Magalhaes * Copyright 2005-2007 Collabora Ltd. * Copyright 2005-2007 Nokia Corp. * @author Rob Taylor * @author: Olivier Crete * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "fs-plugin.h" #include #include "fs-conference.h" #include "fs-private.h" #define GST_CAT_DEFAULT _fs_conference_debug /** * SECTION:fs-plugin * @short_description: A class for defining Farstream plugins * * This class is a generic class to load GType plugins based on their name. * With this simple class, you can only have one type per plugin. */ #define FS_PLUGIN_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_PLUGIN, FsPluginPrivate)) static gboolean fs_plugin_load (GTypeModule *module); static GMutex mutex; static gchar **search_paths = NULL; static GList *plugins = NULL; struct _FsPluginPrivate { GModule *handle; }; G_DEFINE_TYPE(FsPlugin, fs_plugin, G_TYPE_TYPE_MODULE); static void fs_plugin_search_path_init (void) { const gchar *env; if (search_paths) return; env = g_getenv ("FS_PLUGIN_PATH"); if (env == NULL) { search_paths = g_new (gchar *, 2); search_paths[0] = g_strdup (FS_PLUGIN_PATH); search_paths[1] = NULL; return; } else { gchar *path; path = g_strjoin (":", env, FS_PLUGIN_PATH, NULL); search_paths = g_strsplit (path, ":", -1); g_free (path); } } static void fs_plugin_class_init (FsPluginClass * klass) { GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass); module_class->load = fs_plugin_load; g_type_class_add_private (klass, sizeof (FsPluginPrivate)); /* Calling from class initializer so it only gets init'ed once */ fs_plugin_search_path_init (); } static void fs_plugin_init (FsPlugin * plugin) { /* member init */ plugin->priv = FS_PLUGIN_GET_PRIVATE (plugin); plugin->priv->handle = NULL; } static gboolean fs_plugin_load (GTypeModule *module) { FsPlugin *plugin = FS_PLUGIN(module); gchar **search_path = NULL; gchar *path=NULL; gboolean (*fs_init_plugin) (FsPlugin *); g_return_val_if_fail (plugin != NULL, FALSE); g_return_val_if_fail (plugin->name != NULL && plugin->name[0] != '\0', FALSE); for (search_path = search_paths; *search_path; search_path++) { GST_DEBUG("looking for plugins in %s", *search_path); path = g_module_build_path (*search_path, plugin->name); plugin->priv->handle = g_module_open (path, G_MODULE_BIND_LOCAL); GST_INFO ("opening module %s: %s\n", path, (plugin->priv->handle != NULL) ? "succeeded" : g_module_error ()); g_free (path); if (!plugin->priv->handle) { continue; } else if (!g_module_symbol (plugin->priv->handle, "fs_init_plugin", (gpointer) & fs_init_plugin)) { g_module_close (plugin->priv->handle); plugin->priv->handle = NULL; GST_WARNING ("could not find init function in plugin\n"); continue; } else break; } if (!plugin->priv->handle) { return FALSE; } fs_init_plugin (plugin); if (!plugin->type) { /* TODO error handling (init error or no info defined) */ GST_WARNING ("init error or no info defined"); goto err_close_module; } return TRUE; err_close_module: g_module_close (plugin->priv->handle); return FALSE; } static FsPlugin * fs_plugin_get_by_name_locked (const gchar * name, const gchar * type_suffix) { gchar *fullname; FsPlugin *plugin = NULL; GList *plugin_item; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (type_suffix != NULL, NULL); fullname = g_strdup_printf ("%s-%s",name,type_suffix); for (plugin_item = plugins; plugin_item; plugin_item = g_list_next (plugin_item)) { plugin = plugin_item->data; if (plugin->name == NULL || plugin->name[0] == 0) continue; if (!strcmp (plugin->name, fullname)) { break; } } g_free (fullname); if (plugin_item) return plugin; return NULL; } /** * fs_plugin_create_valist: * @name: The name of the plugin to load * @type_suffix: The type of plugin to load (normally "transmitter") * @error: location of a #GError, or NULL if no error occured * @first_property_name: The name of the first property to be set on the * object * @var_args: The rest of the arguments * * Loads the appropriate plugin if necessary and creates a GObject of * the requested type * * Returns: (transfer full): The object created (or NULL if there is an error) **/ GObject * fs_plugin_create_valist (const gchar *name, const gchar *type_suffix, GError **error, const gchar *first_property_name, va_list var_args) { GObject *object; FsPlugin *plugin; g_return_val_if_fail (name, NULL); g_return_val_if_fail (type_suffix, NULL); _fs_conference_init_debug (); g_mutex_lock (&mutex); plugin = fs_plugin_get_by_name_locked (name, type_suffix); if (!plugin) { plugin = g_object_new (FS_TYPE_PLUGIN, NULL); if (!plugin) { g_mutex_unlock (&mutex); g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not create a fsplugin object"); return NULL; } plugin->name = g_strdup_printf ("%s-%s",name,type_suffix); g_type_module_set_name (G_TYPE_MODULE (plugin), plugin->name); plugins = g_list_append (plugins, plugin); /* We do the use once and then we keep it loaded forever because * the gstreamer libraries can't be unloaded */ if (!g_type_module_use (G_TYPE_MODULE (plugin))) { g_mutex_unlock (&mutex); g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not load the %s-%s transmitter plugin", name, type_suffix); return NULL; } } g_mutex_unlock (&mutex); object = g_object_new_valist (plugin->type, first_property_name, var_args); return object; } /** * fs_plugin_create: * @name: The name of the plugin to load * @type_suffix: The type of plugin to load (normally "transmitter") * @error: location of a #GError, or NULL if no error occured * @first_property_name: The name of the first property to be set on the * object * @...: The NULL-terminated list of properties to set on the transmitter * * Loads the appropriate plugin if necessary and creates a GObject of * the requested type * * Returns: (transfer full): The object created (or NULL if there is an error) **/ GObject * fs_plugin_create (const gchar *name, const gchar *type_suffix, GError **error, const gchar *first_property_name, ...) { va_list var_args; GObject *obj; va_start (var_args, first_property_name); obj = fs_plugin_create_valist (name, type_suffix, error, first_property_name, var_args); va_end (var_args); return obj; } /** * fs_plugin_list_available: * @type_suffix: Get list of plugins with this type suffix * * Gets the list of all available plugins of a certain type * * Returns: (transfer full): a newly allocated NULL terminated array of * strings or %NULL if no strings were found. * It should be freed with g_strfreev(). */ gchar ** fs_plugin_list_available (const gchar *type_suffix) { GPtrArray *list = g_ptr_array_new (); gchar **retval = NULL; gchar **search_path = NULL; GRegex *matcher; GError *error = NULL; gchar *tmp1, *tmp2, *tmp3; _fs_conference_init_debug (); g_mutex_lock (&mutex); fs_plugin_search_path_init (); tmp1 = g_strdup_printf ("(.+)-%s", type_suffix); tmp2 = g_module_build_path ("", tmp1); tmp3 = g_strconcat ("^", tmp2, NULL); matcher = g_regex_new (tmp3, 0, 0, NULL); g_free (tmp1); g_free (tmp2); g_free (tmp3); for (search_path = search_paths; *search_path; search_path++) { GDir *dir = NULL; const gchar *entry; dir = g_dir_open (*search_path, 0, &error); if (!dir) { GST_WARNING ("Could not open path %s to look for plugins: %s", *search_path, error ? error->message : "Unknown error"); g_clear_error (&error); continue; } while ((entry = g_dir_read_name (dir))) { gchar **matches = NULL; matches = g_regex_split (matcher, entry, 0); if (matches && g_strv_length (matches) == 3) { gint i; gboolean found = FALSE; for (i = 0; i < list->len; i++) { if (!strcmp (matches[1], g_ptr_array_index (list, i))) { found = TRUE; break; } } if (!found) g_ptr_array_add (list, g_strdup (matches[1])); } g_strfreev (matches); } g_dir_close (dir); } g_regex_unref (matcher); if (list->len) { g_ptr_array_add (list, NULL); retval = (gchar**) list->pdata; g_ptr_array_free (list, FALSE); } else { g_ptr_array_free (list, TRUE); } g_mutex_unlock (&mutex); return retval; } /** * fs_plugin_register_static: * @name: The name of the plugin to register * @type_suffix: The type of plugin to register (normally "transmitter") * * Register a staticly linked transmitter. This function should strictly be * used by plugins own register function. To register a static plugin: * extern fs_plugin___register_pluing (void); * fs_plugin___register_pluing (); **/ void fs_plugin_register_static (const gchar *name, const gchar *type_suffix, GType type) { FsPlugin *plugin; plugin = g_object_new (FS_TYPE_PLUGIN, NULL); plugin->name = g_strdup_printf ("%s-%s", name, type_suffix); g_type_module_set_name (G_TYPE_MODULE (plugin), plugin->name); plugin->type = type; plugins = g_list_append (plugins, plugin); }