1 /*
2  * fs-plugin.c - Source for farstream plugin infrastructure
3  *
4  * Farstream Voice+Video library
5  * Copyright (c) 2005 INdT.
6  *   @author Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
7  * Copyright 2005-2007 Collabora Ltd.
8  * Copyright 2005-2007 Nokia Corp.
9  *   @author Rob Taylor <rob.taylor@collabora.co.uk>
10  *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
11  *
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "fs-plugin.h"
33 
34 #include <string.h>
35 
36 #include "fs-conference.h"
37 #include "fs-private.h"
38 
39 #define GST_CAT_DEFAULT _fs_conference_debug
40 
41 /**
42  * SECTION:fs-plugin
43  * @short_description: A class for defining Farstream plugins
44  *
45  * This class is a generic class to load GType plugins based on their name.
46  * With this simple class, you can only have one type per plugin.
47  */
48 
49 #define FS_PLUGIN_GET_PRIVATE(o)  \
50    (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_PLUGIN, FsPluginPrivate))
51 
52 static gboolean fs_plugin_load (GTypeModule *module);
53 
54 
55 static GMutex mutex;
56 static gchar **search_paths = NULL;
57 static GList *plugins = NULL;
58 
59 struct _FsPluginPrivate
60 {
61   GModule *handle;
62 };
63 
64 G_DEFINE_TYPE(FsPlugin, fs_plugin, G_TYPE_TYPE_MODULE);
65 
66 static void
fs_plugin_search_path_init(void)67 fs_plugin_search_path_init (void)
68 {
69   const gchar *env;
70 
71   if (search_paths)
72     return;
73 
74   env = g_getenv ("FS_PLUGIN_PATH");
75 
76   if (env == NULL)
77     {
78       search_paths = g_new (gchar *, 2);
79       search_paths[0] = g_strdup (FS_PLUGIN_PATH);
80       search_paths[1] = NULL;
81       return;
82     }
83   else
84     {
85       gchar *path;
86 
87       path = g_strjoin (":", env, FS_PLUGIN_PATH, NULL);
88       search_paths = g_strsplit (path, ":", -1);
89       g_free (path);
90     }
91 }
92 
93 static void
fs_plugin_class_init(FsPluginClass * klass)94 fs_plugin_class_init (FsPluginClass * klass)
95 {
96   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass);
97 
98   module_class->load = fs_plugin_load;
99 
100   g_type_class_add_private (klass, sizeof (FsPluginPrivate));
101 
102   /* Calling from class initializer so it only gets init'ed once */
103   fs_plugin_search_path_init ();
104 }
105 
106 
107 
108 static void
fs_plugin_init(FsPlugin * plugin)109 fs_plugin_init (FsPlugin * plugin)
110 {
111   /* member init */
112   plugin->priv = FS_PLUGIN_GET_PRIVATE (plugin);
113   plugin->priv->handle = NULL;
114 }
115 
fs_plugin_load(GTypeModule * module)116 static gboolean fs_plugin_load (GTypeModule *module)
117 {
118   FsPlugin *plugin = FS_PLUGIN(module);
119   gchar **search_path = NULL;
120   gchar *path=NULL;
121 
122   gboolean (*fs_init_plugin) (FsPlugin *);
123 
124   g_return_val_if_fail (plugin != NULL, FALSE);
125   g_return_val_if_fail (plugin->name != NULL && plugin->name[0] != '\0', FALSE);
126 
127   for (search_path = search_paths; *search_path; search_path++) {
128     GST_DEBUG("looking for plugins in %s", *search_path);
129 
130     path = g_module_build_path (*search_path, plugin->name);
131 
132     plugin->priv->handle = g_module_open (path, G_MODULE_BIND_LOCAL);
133     GST_INFO ("opening module %s: %s\n", path,
134       (plugin->priv->handle != NULL) ? "succeeded" : g_module_error ());
135     g_free (path);
136 
137     if (!plugin->priv->handle) {
138       continue;
139     }
140 
141     else if (!g_module_symbol (plugin->priv->handle,
142                           "fs_init_plugin",
143                           (gpointer) & fs_init_plugin)) {
144       g_module_close (plugin->priv->handle);
145       plugin->priv->handle = NULL;
146       GST_WARNING ("could not find init function in plugin\n");
147       continue;
148     }
149 
150     else
151       break;
152   }
153 
154   if (!plugin->priv->handle) {
155     return FALSE;
156   }
157 
158   fs_init_plugin (plugin);
159   if (!plugin->type) {
160     /* TODO error handling (init error or no info defined) */
161     GST_WARNING ("init error or no info defined");
162     goto err_close_module;
163   }
164 
165   return TRUE;
166 
167  err_close_module:
168   g_module_close (plugin->priv->handle);
169   return FALSE;
170 
171 }
172 
173 static FsPlugin *
fs_plugin_get_by_name_locked(const gchar * name,const gchar * type_suffix)174 fs_plugin_get_by_name_locked (const gchar * name, const gchar * type_suffix)
175 {
176   gchar *fullname;
177   FsPlugin *plugin = NULL;
178   GList *plugin_item;
179 
180   g_return_val_if_fail (name != NULL, NULL);
181   g_return_val_if_fail (type_suffix != NULL, NULL);
182 
183   fullname = g_strdup_printf ("%s-%s",name,type_suffix);
184 
185   for (plugin_item = plugins;
186        plugin_item;
187        plugin_item = g_list_next (plugin_item))  {
188     plugin = plugin_item->data;
189     if (plugin->name == NULL || plugin->name[0] == 0)
190       continue;
191     if (!strcmp (plugin->name, fullname)) {
192       break;
193     }
194 
195   }
196   g_free (fullname);
197 
198   if (plugin_item)
199     return plugin;
200 
201   return NULL;
202 }
203 
204 
205 /**
206  * fs_plugin_create_valist:
207  * @name: The name of the plugin to load
208  * @type_suffix: The type of plugin to load (normally "transmitter")
209  * @error: location of a #GError, or NULL if no error occured
210  * @first_property_name: The name of the first property to be set on the
211  *   object
212  * @var_args: The rest of the arguments
213  *
214  * Loads the appropriate plugin if necessary and creates a GObject of
215  * the requested type
216  *
217  * Returns: (transfer full): The object created (or NULL if there is an error)
218  **/
219 
220 GObject *
fs_plugin_create_valist(const gchar * name,const gchar * type_suffix,GError ** error,const gchar * first_property_name,va_list var_args)221 fs_plugin_create_valist (const gchar *name, const gchar *type_suffix,
222   GError **error, const gchar *first_property_name, va_list var_args)
223 {
224   GObject *object;
225   FsPlugin *plugin;
226 
227   g_return_val_if_fail (name, NULL);
228   g_return_val_if_fail (type_suffix, NULL);
229 
230   _fs_conference_init_debug ();
231 
232   g_mutex_lock (&mutex);
233 
234   plugin = fs_plugin_get_by_name_locked (name, type_suffix);
235 
236   if (!plugin) {
237     plugin = g_object_new (FS_TYPE_PLUGIN, NULL);
238     if (!plugin) {
239       g_mutex_unlock (&mutex);
240       g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
241         "Could not create a fsplugin object");
242       return NULL;
243     }
244     plugin->name = g_strdup_printf ("%s-%s",name,type_suffix);
245     g_type_module_set_name (G_TYPE_MODULE (plugin), plugin->name);
246     plugins = g_list_append (plugins, plugin);
247 
248     /* We do the use once and then we keep it loaded forever because
249      * the gstreamer libraries can't be unloaded
250      */
251     if (!g_type_module_use (G_TYPE_MODULE (plugin))) {
252       g_mutex_unlock (&mutex);
253       g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
254           "Could not load the %s-%s transmitter plugin", name, type_suffix);
255       return NULL;
256     }
257   }
258 
259   g_mutex_unlock (&mutex);
260 
261   object = g_object_new_valist (plugin->type, first_property_name, var_args);
262 
263   return object;
264 }
265 
266 
267 /**
268  * fs_plugin_create:
269  * @name: The name of the plugin to load
270  * @type_suffix: The type of plugin to load (normally "transmitter")
271  * @error: location of a #GError, or NULL if no error occured
272  * @first_property_name: The name of the first property to be set on the
273  *   object
274  * @...: The NULL-terminated list of properties to set on the transmitter
275  *
276  * Loads the appropriate plugin if necessary and creates a GObject of
277  * the requested type
278  *
279  * Returns: (transfer full): The object created (or NULL if there is an error)
280  **/
281 
282 GObject *
fs_plugin_create(const gchar * name,const gchar * type_suffix,GError ** error,const gchar * first_property_name,...)283 fs_plugin_create (const gchar *name, const gchar *type_suffix,
284   GError **error, const gchar *first_property_name, ...)
285 {
286   va_list var_args;
287   GObject *obj;
288 
289   va_start (var_args, first_property_name);
290   obj = fs_plugin_create_valist (name, type_suffix, error, first_property_name,
291     var_args);
292   va_end (var_args);
293 
294   return obj;
295 }
296 
297 /**
298  * fs_plugin_list_available:
299  * @type_suffix: Get list of plugins with this type suffix
300  *
301  * Gets the list of all available plugins of a certain type
302  *
303  * Returns: (transfer full): a newly allocated NULL terminated array of
304  * strings or %NULL if no strings were found.
305  * It should be freed with g_strfreev().
306  */
307 
308 gchar **
fs_plugin_list_available(const gchar * type_suffix)309 fs_plugin_list_available (const gchar *type_suffix)
310 {
311   GPtrArray *list = g_ptr_array_new ();
312   gchar **retval = NULL;
313   gchar **search_path = NULL;
314   GRegex *matcher;
315   GError *error = NULL;
316   gchar *tmp1, *tmp2, *tmp3;
317 
318   _fs_conference_init_debug ();
319 
320   g_mutex_lock (&mutex);
321 
322   fs_plugin_search_path_init ();
323 
324   tmp1 = g_strdup_printf ("(.+)-%s", type_suffix);
325   tmp2 = g_module_build_path ("", tmp1);
326   tmp3 = g_strconcat ("^", tmp2, NULL);
327   matcher = g_regex_new (tmp3, 0, 0, NULL);
328   g_free (tmp1);
329   g_free (tmp2);
330   g_free (tmp3);
331 
332 
333   for (search_path = search_paths; *search_path; search_path++)
334   {
335     GDir *dir = NULL;
336     const gchar *entry;
337 
338     dir = g_dir_open (*search_path, 0, &error);
339     if (!dir)
340     {
341       GST_WARNING ("Could not open path %s to look for plugins: %s",
342           *search_path, error ? error->message : "Unknown error");
343       g_clear_error (&error);
344       continue;
345     }
346 
347     while ((entry = g_dir_read_name (dir)))
348     {
349       gchar **matches = NULL;
350 
351       matches = g_regex_split (matcher, entry, 0);
352 
353       if (matches && g_strv_length (matches) == 3)
354       {
355         gint i;
356         gboolean found = FALSE;
357 
358         for (i = 0; i < list->len; i++)
359         {
360           if (!strcmp (matches[1], g_ptr_array_index (list, i)))
361           {
362             found = TRUE;
363             break;
364           }
365         }
366         if (!found)
367           g_ptr_array_add (list, g_strdup (matches[1]));
368       }
369 
370       g_strfreev (matches);
371     }
372 
373     g_dir_close (dir);
374   }
375 
376   g_regex_unref (matcher);
377 
378   if (list->len)
379   {
380     g_ptr_array_add (list, NULL);
381     retval = (gchar**) list->pdata;
382     g_ptr_array_free (list, FALSE);
383   }
384   else
385   {
386     g_ptr_array_free (list, TRUE);
387   }
388 
389   g_mutex_unlock (&mutex);
390 
391   return retval;
392 }
393 
394 /**
395  * fs_plugin_register_static:
396  * @name: The name of the plugin to register
397  * @type_suffix: The type of plugin to register (normally "transmitter")
398  *
399  * Register a staticly linked transmitter. This function should strictly be
400  * used by plugins own register function. To register a static plugin:
401  *   extern fs_plugin_<name>_<type>_register_pluing (void);
402  *   fs_plugin_<name>_<type>_register_pluing ();
403  **/
404 
405 void
fs_plugin_register_static(const gchar * name,const gchar * type_suffix,GType type)406 fs_plugin_register_static (const gchar *name, const gchar *type_suffix, GType type)
407 {
408   FsPlugin *plugin;
409 
410   plugin = g_object_new (FS_TYPE_PLUGIN, NULL);
411   plugin->name = g_strdup_printf ("%s-%s", name, type_suffix);
412   g_type_module_set_name (G_TYPE_MODULE (plugin), plugin->name);
413   plugin->type = type;
414   plugins = g_list_append (plugins, plugin);
415 }
416