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