1 /*****************************************************************************
2  * modules.c : Builtin and plugin modules management functions
3  *****************************************************************************
4  * Copyright (C) 2001-2011 VLC authors and VideoLAN
5  * $Id: 3ad50fb39153f3257304f1bad35c172e4b6526a5 $
6  *
7  * Authors: Sam Hocevar <sam@zoy.org>
8  *          Ethan C. Baldridge <BaldridgeE@cadmus.com>
9  *          Hans-Peter Jansen <hpj@urpla.net>
10  *          Gildas Bazin <gbazin@videolan.org>
11  *          Rémi Denis-Courmont
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program 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
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * 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 <stdlib.h>
33 #include <string.h>
34 #ifdef ENABLE_NLS
35 # include <libintl.h>
36 #endif
37 #include <assert.h>
38 
39 #include <vlc_common.h>
40 #include <vlc_modules.h>
41 #include "libvlc.h"
42 #include "config/configuration.h"
43 #include "vlc_arrays.h"
44 #include "modules/modules.h"
45 
46 /**
47  * Checks whether a module implements a capability.
48  *
49  * \param m the module
50  * \param cap the capability to check
51  * \return true if the module has the capability
52  */
module_provides(const module_t * m,const char * cap)53 bool module_provides (const module_t *m, const char *cap)
54 {
55     return !strcmp (module_get_capability (m), cap);
56 }
57 
58 /**
59  * Get the internal name of a module
60  *
61  * \param m the module
62  * \return the module name
63  */
module_get_object(const module_t * m)64 const char *module_get_object( const module_t *m )
65 {
66     if (unlikely(m->i_shortcuts == 0))
67         return "unnamed";
68     return m->pp_shortcuts[0];
69 }
70 
71 /**
72  * Get the human-friendly name of a module.
73  *
74  * \param m the module
75  * \param long_name TRUE to have the long name of the module
76  * \return the short or long name of the module
77  */
module_get_name(const module_t * m,bool long_name)78 const char *module_get_name( const module_t *m, bool long_name )
79 {
80     if( long_name && ( m->psz_longname != NULL) )
81         return m->psz_longname;
82 
83     if (m->psz_shortname != NULL)
84         return m->psz_shortname;
85     return module_get_object (m);
86 }
87 
88 /**
89  * Get the help for a module
90  *
91  * \param m the module
92  * \return the help
93  */
module_get_help(const module_t * m)94 const char *module_get_help( const module_t *m )
95 {
96     return m->psz_help;
97 }
98 
99 /**
100  * Gets the capability of a module
101  *
102  * \param m the module
103  * \return the capability, or "none" if unspecified
104  */
module_get_capability(const module_t * m)105 const char *module_get_capability (const module_t *m)
106 {
107     return (m->psz_capability != NULL) ? m->psz_capability : "none";
108 }
109 
110 /**
111  * Get the score for a module
112  *
113  * \param m the module
114  * return the score for the capability
115  */
module_get_score(const module_t * m)116 int module_get_score( const module_t *m )
117 {
118     return m->i_score;
119 }
120 
121 /**
122  * Translate a string using the module's text domain
123  *
124  * \param m the module
125  * \param str the American English ASCII string to localize
126  * \return the gettext-translated string
127  */
module_gettext(const module_t * m,const char * str)128 const char *module_gettext (const module_t *m, const char *str)
129 {
130     if (unlikely(str == NULL || *str == '\0'))
131         return "";
132 #ifdef ENABLE_NLS
133     const char *domain = m->plugin->textdomain;
134     return dgettext ((domain != NULL) ? domain : PACKAGE_NAME, str);
135 #else
136     (void)m;
137     return str;
138 #endif
139 }
140 
141 #undef module_start
module_start(vlc_object_t * obj,const module_t * m)142 int module_start (vlc_object_t *obj, const module_t *m)
143 {
144    int (*activate) (vlc_object_t *) = m->pf_activate;
145 
146    return (activate != NULL) ? activate (obj) : VLC_SUCCESS;
147 }
148 
149 #undef module_stop
module_stop(vlc_object_t * obj,const module_t * m)150 void module_stop (vlc_object_t *obj, const module_t *m)
151 {
152    void (*deactivate) (vlc_object_t *) = m->pf_deactivate;
153 
154     if (deactivate != NULL)
155         deactivate (obj);
156 }
157 
module_match_name(const module_t * m,const char * name)158 static bool module_match_name (const module_t *m, const char *name)
159 {
160      /* Plugins with zero score must be matched explicitly. */
161      if (!strcasecmp ("any", name))
162          return m->i_score > 0;
163 
164      for (unsigned i = 0; i < m->i_shortcuts; i++)
165           if (!strcasecmp (m->pp_shortcuts[i], name))
166               return true;
167      return false;
168 }
169 
module_load(vlc_object_t * obj,module_t * m,vlc_activate_t init,va_list args)170 static int module_load (vlc_object_t *obj, module_t *m,
171                         vlc_activate_t init, va_list args)
172 {
173     int ret = VLC_SUCCESS;
174 
175     if (module_Map(obj, m->plugin))
176         return VLC_EGENERIC;
177 
178     if (m->pf_activate != NULL)
179     {
180         va_list ap;
181 
182         va_copy (ap, args);
183         ret = init (m->pf_activate, ap);
184         va_end (ap);
185     }
186 
187     if (ret != VLC_SUCCESS)
188         vlc_objres_clear(obj);
189 
190     return ret;
191 }
192 
193 #undef vlc_module_load
194 /**
195  * Finds and instantiates the best module of a certain type.
196  * All candidates modules having the specified capability and name will be
197  * sorted in decreasing order of priority. Then the probe callback will be
198  * invoked for each module, until it succeeds (returns 0), or all candidate
199  * module failed to initialize.
200  *
201  * The probe callback first parameter is the address of the module entry point.
202  * Further parameters are passed as an argument list; it corresponds to the
203  * variable arguments passed to this function. This scheme is meant to
204  * support arbitrary prototypes for the module entry point.
205  *
206  * \param obj VLC object
207  * \param capability capability, i.e. class of module
208  * \param name name of the module asked, if any
209  * \param strict if true, do not fallback to plugin with a different name
210  *                 but the same capability
211  * \param probe module probe callback
212  * \return the module or NULL in case of a failure
213  */
vlc_module_load(vlc_object_t * obj,const char * capability,const char * name,bool strict,vlc_activate_t probe,...)214 module_t *vlc_module_load(vlc_object_t *obj, const char *capability,
215                           const char *name, bool strict,
216                           vlc_activate_t probe, ...)
217 {
218     char *var = NULL;
219 
220     if (name == NULL || name[0] == '\0')
221         name = "any";
222 
223     /* Deal with variables */
224     if (name[0] == '$')
225     {
226         var = var_InheritString (obj, name + 1);
227         name = (var != NULL) ? var : "any";
228     }
229 
230     /* Find matching modules */
231     module_t **mods;
232     ssize_t total = module_list_cap (&mods, capability);
233 
234     msg_Dbg (obj, "looking for %s module matching \"%s\": %zd candidates",
235              capability, name, total);
236     if (total <= 0)
237     {
238         module_list_free (mods);
239         msg_Dbg (obj, "no %s modules", capability);
240         return NULL;
241     }
242 
243     module_t *module = NULL;
244     const bool b_force_backup = obj->obj.force; /* FIXME: remove this */
245     va_list args;
246 
247     va_start(args, probe);
248     while (*name)
249     {
250         char buf[32];
251         size_t slen = strcspn (name, ",");
252 
253         if (likely(slen < sizeof (buf)))
254         {
255             memcpy(buf, name, slen);
256             buf[slen] = '\0';
257         }
258         name += slen;
259         name += strspn (name, ",");
260         if (unlikely(slen >= sizeof (buf)))
261             continue;
262 
263         const char *shortcut = buf;
264         assert (shortcut != NULL);
265 
266         if (!strcasecmp ("none", shortcut))
267             goto done;
268 
269         obj->obj.force = strict && strcasecmp ("any", shortcut);
270         for (ssize_t i = 0; i < total; i++)
271         {
272             module_t *cand = mods[i];
273             if (cand == NULL)
274                 continue; // module failed in previous iteration
275             if (!module_match_name (cand, shortcut))
276                 continue;
277             mods[i] = NULL; // only try each module once at most...
278 
279             int ret = module_load (obj, cand, probe, args);
280             switch (ret)
281             {
282                 case VLC_SUCCESS:
283                     module = cand;
284                     /* fall through */
285                 case VLC_ETIMEOUT:
286                     goto done;
287             }
288         }
289     }
290 
291     /* None of the shortcuts matched, fall back to any module */
292     if (!strict)
293     {
294         obj->obj.force = false;
295         for (ssize_t i = 0; i < total; i++)
296         {
297             module_t *cand = mods[i];
298             if (cand == NULL || module_get_score (cand) <= 0)
299                 continue;
300 
301             int ret = module_load (obj, cand, probe, args);
302             switch (ret)
303             {
304                 case VLC_SUCCESS:
305                     module = cand;
306                     /* fall through */
307                 case VLC_ETIMEOUT:
308                     goto done;
309             }
310         }
311     }
312 done:
313     va_end (args);
314     obj->obj.force = b_force_backup;
315     module_list_free (mods);
316     free (var);
317 
318     if (module != NULL)
319     {
320         msg_Dbg (obj, "using %s module \"%s\"", capability,
321                  module_get_object (module));
322         vlc_object_set_name (obj, module_get_object (module));
323     }
324     else
325         msg_Dbg (obj, "no %s modules matched", capability);
326     return module;
327 }
328 
329 #undef vlc_module_unload
330 /**
331  * Deinstantiates a module.
332  * \param module the module pointer as returned by vlc_module_load()
333  * \param deinit deactivation callback
334  */
vlc_module_unload(vlc_object_t * obj,module_t * module,vlc_deactivate_t deinit,...)335 void vlc_module_unload(vlc_object_t *obj, module_t *module,
336                        vlc_deactivate_t deinit, ...)
337 {
338     if (module->pf_deactivate != NULL)
339     {
340         va_list ap;
341 
342         va_start(ap, deinit);
343         deinit(module->pf_deactivate, ap);
344         va_end(ap);
345     }
346 
347     vlc_objres_clear(obj);
348 }
349 
350 
generic_start(void * func,va_list ap)351 static int generic_start(void *func, va_list ap)
352 {
353     vlc_object_t *obj = va_arg(ap, vlc_object_t *);
354     int (*activate)(vlc_object_t *) = func;
355 
356     return activate(obj);
357 }
358 
generic_stop(void * func,va_list ap)359 static void generic_stop(void *func, va_list ap)
360 {
361     vlc_object_t *obj = va_arg(ap, vlc_object_t *);
362     void (*deactivate)(vlc_object_t *) = func;
363 
364     deactivate(obj);
365 }
366 
367 #undef module_need
module_need(vlc_object_t * obj,const char * cap,const char * name,bool strict)368 module_t *module_need(vlc_object_t *obj, const char *cap, const char *name,
369                       bool strict)
370 {
371     return vlc_module_load(obj, cap, name, strict, generic_start, obj);
372 }
373 
374 #undef module_unneed
module_unneed(vlc_object_t * obj,module_t * module)375 void module_unneed(vlc_object_t *obj, module_t *module)
376 {
377     msg_Dbg(obj, "removing module \"%s\"", module_get_object(module));
378     vlc_module_unload(obj, module, generic_stop, obj);
379 }
380 
381 /**
382  * Get a pointer to a module_t given it's name.
383  *
384  * \param name the name of the module
385  * \return a pointer to the module or NULL in case of a failure
386  */
module_find(const char * name)387 module_t *module_find (const char *name)
388 {
389     size_t count;
390     module_t **list = module_list_get (&count);
391 
392     assert (name != NULL);
393 
394     for (size_t i = 0; i < count; i++)
395     {
396         module_t *module = list[i];
397 
398         if (unlikely(module->i_shortcuts == 0))
399             continue;
400         if (!strcmp (module->pp_shortcuts[0], name))
401         {
402             module_list_free (list);
403             return module;
404         }
405     }
406     module_list_free (list);
407     return NULL;
408 }
409 
410 /**
411  * Tell if a module exists
412  *
413  * \param psz_name th name of the module
414  * \return TRUE if the module exists
415  */
module_exists(const char * psz_name)416 bool module_exists (const char * psz_name)
417 {
418     return module_find (psz_name) != NULL;
419 }
420 
421 /**
422  * Get the configuration of a module
423  *
424  * \param module the module
425  * \param psize the size of the configuration returned
426  * \return the configuration as an array
427  */
module_config_get(const module_t * module,unsigned * restrict psize)428 module_config_t *module_config_get( const module_t *module, unsigned *restrict psize )
429 {
430     const vlc_plugin_t *plugin = module->plugin;
431 
432     if (plugin->module != module)
433     {   /* For backward compatibility, pretend non-first modules have no
434          * configuration items. */
435         *psize = 0;
436         return NULL;
437     }
438 
439     unsigned i,j;
440     size_t size = plugin->conf.size;
441     module_config_t *config = vlc_alloc( size, sizeof( *config ) );
442 
443     assert( psize != NULL );
444     *psize = 0;
445 
446     if( !config )
447         return NULL;
448 
449     for( i = 0, j = 0; i < size; i++ )
450     {
451         const module_config_t *item = plugin->conf.items + i;
452         if( item->b_internal /* internal option */
453          || item->b_removed /* removed option */ )
454             continue;
455 
456         memcpy( config + j, item, sizeof( *config ) );
457         j++;
458     }
459     *psize = j;
460 
461     return config;
462 }
463 
464 /**
465  * Release the configuration
466  *
467  * \param the configuration
468  * \return nothing
469  */
module_config_free(module_config_t * config)470 void module_config_free( module_config_t *config )
471 {
472     free( config );
473 }
474