1 /*****************************************************************************
2  * entry.c : Callbacks for module entry point
3  *****************************************************************************
4  * Copyright (C) 2007 VLC authors and VideoLAN
5  * Copyright © 2007-2008 Rémi Denis-Courmont
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20  *****************************************************************************/
21 
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_memory.h>
29 #include <assert.h>
30 #include <stdarg.h>
31 #include <limits.h>
32 #include <float.h>
33 #ifdef HAVE_SEARCH_H
34 # include <search.h>
35 #endif
36 
37 #include "modules/modules.h"
38 #include "config/configuration.h"
39 #include "libvlc.h"
40 
vlc_module_create(vlc_plugin_t * plugin)41 module_t *vlc_module_create(vlc_plugin_t *plugin)
42 {
43     module_t *module = malloc (sizeof (*module));
44     if (module == NULL)
45         return NULL;
46 
47     /* NOTE XXX: For backward compatibility with preferences UIs, the first
48      * module must stay first. That defines under which module, the
49      * configuration items of the plugin belong. The order of the following
50      * entries is irrelevant. */
51     module_t *parent = plugin->module;
52     if (parent == NULL)
53     {
54         module->next = NULL;
55         plugin->module = module;
56     }
57     else
58     {
59         module->next = parent->next;
60         parent->next = module;
61     }
62 
63     plugin->modules_count++;
64     module->plugin = plugin;
65 
66     module->psz_shortname = NULL;
67     module->psz_longname = NULL;
68     module->psz_help = NULL;
69     module->pp_shortcuts = NULL;
70     module->i_shortcuts = 0;
71     module->psz_capability = NULL;
72     module->i_score = (parent != NULL) ? parent->i_score : 1;
73     module->activate_name = NULL;
74     module->deactivate_name = NULL;
75     module->pf_activate = NULL;
76     module->pf_deactivate = NULL;
77     return module;
78 }
79 
80 /**
81  * Destroys a module.
82  */
vlc_module_destroy(module_t * module)83 void vlc_module_destroy (module_t *module)
84 {
85     while (module != NULL)
86     {
87         module_t *next = module->next;
88 
89         free(module->pp_shortcuts);
90         free(module);
91         module = next;
92     }
93 }
94 
vlc_plugin_create(void)95 vlc_plugin_t *vlc_plugin_create(void)
96 {
97     vlc_plugin_t *plugin = malloc(sizeof (*plugin));
98     if (unlikely(plugin == NULL))
99         return NULL;
100 
101     plugin->modules_count = 0;
102     plugin->textdomain = NULL;
103     plugin->conf.items = NULL;
104     plugin->conf.size = 0;
105     plugin->conf.count = 0;
106     plugin->conf.booleans = 0;
107 #ifdef HAVE_DYNAMIC_PLUGINS
108     plugin->abspath = NULL;
109     atomic_init(&plugin->loaded, false);
110     plugin->unloadable = true;
111     plugin->handle = NULL;
112     plugin->abspath = NULL;
113     plugin->path = NULL;
114 #endif
115     plugin->module = NULL;
116 
117     return plugin;
118 }
119 
120 /**
121  * Destroys a plug-in.
122  * @warning If the plug-in was dynamically loaded in memory, the library handle
123  * and associated memory mappings and linker resources will be leaked.
124  */
vlc_plugin_destroy(vlc_plugin_t * plugin)125 void vlc_plugin_destroy(vlc_plugin_t *plugin)
126 {
127     assert(plugin != NULL);
128 #ifdef HAVE_DYNAMIC_PLUGINS
129     assert(!plugin->unloadable || !atomic_load(&plugin->loaded));
130 #endif
131 
132     if (plugin->module != NULL)
133         vlc_module_destroy(plugin->module);
134 
135     config_Free(plugin->conf.items, plugin->conf.size);
136 #ifdef HAVE_DYNAMIC_PLUGINS
137     free(plugin->abspath);
138     free(plugin->path);
139 #endif
140     free(plugin);
141 }
142 
vlc_config_create(vlc_plugin_t * plugin,int type)143 static module_config_t *vlc_config_create(vlc_plugin_t *plugin, int type)
144 {
145     unsigned confsize = plugin->conf.size;
146     module_config_t *tab = plugin->conf.items;
147 
148     if ((confsize & 0xf) == 0)
149     {
150         tab = realloc_or_free (tab, (confsize + 17) * sizeof (*tab));
151         if (tab == NULL)
152             return NULL;
153 
154         plugin->conf.items = tab;
155     }
156 
157     memset (tab + confsize, 0, sizeof (tab[confsize]));
158     tab += confsize;
159     tab->owner = plugin;
160 
161     if (IsConfigIntegerType (type))
162     {
163         tab->max.i = INT64_MAX;
164         tab->min.i = INT64_MIN;
165     }
166     else if( IsConfigFloatType (type))
167     {
168         tab->max.f = FLT_MAX;
169         tab->min.f = -FLT_MAX;
170     }
171     tab->i_type = type;
172 
173     if (CONFIG_ITEM(type))
174     {
175         plugin->conf.count++;
176         if (type == CONFIG_ITEM_BOOL)
177             plugin->conf.booleans++;
178     }
179     plugin->conf.size++;
180 
181     return tab;
182 }
183 
184 /**
185  * Plug-in descriptor callback.
186  *
187  * This callback populates modules, configuration items and properties of a
188  * plug-in from the plug-in descriptor.
189  */
vlc_plugin_desc_cb(void * ctx,void * tgt,int propid,...)190 static int vlc_plugin_desc_cb(void *ctx, void *tgt, int propid, ...)
191 {
192     vlc_plugin_t *plugin = ctx;
193     module_t *module = tgt;
194     module_config_t *item = tgt;
195     va_list ap;
196     int ret = 0;
197 
198     va_start (ap, propid);
199     switch (propid)
200     {
201         case VLC_MODULE_CREATE:
202         {
203             module_t *super = plugin->module;
204             module_t *submodule = vlc_module_create(plugin);
205             if (unlikely(submodule == NULL))
206             {
207                 ret = -1;
208                 break;
209             }
210 
211             *(va_arg (ap, module_t **)) = submodule;
212             if (super == NULL)
213                 break;
214 
215             /* Inheritance. Ugly!! */
216             submodule->pp_shortcuts = xmalloc (sizeof ( *submodule->pp_shortcuts ));
217             submodule->pp_shortcuts[0] = super->pp_shortcuts[0];
218             submodule->i_shortcuts = 1; /* object name */
219 
220             submodule->psz_shortname = super->psz_shortname;
221             submodule->psz_longname = super->psz_longname;
222             submodule->psz_capability = super->psz_capability;
223             break;
224         }
225 
226         case VLC_CONFIG_CREATE:
227         {
228             int type = va_arg (ap, int);
229             module_config_t **pp = va_arg (ap, module_config_t **);
230 
231             item = vlc_config_create(plugin, type);
232             if (unlikely(item == NULL))
233             {
234                 ret = -1;
235                 break;
236             }
237             *pp = item;
238             break;
239         }
240 
241         case VLC_MODULE_SHORTCUT:
242         {
243             unsigned i_shortcuts = va_arg (ap, unsigned);
244             unsigned index = module->i_shortcuts;
245             /* The cache loader accept only a small number of shortcuts */
246             assert(i_shortcuts + index <= MODULE_SHORTCUT_MAX);
247 
248             const char *const *tab = va_arg (ap, const char *const *);
249             const char **pp = realloc (module->pp_shortcuts,
250                                        sizeof (pp[0]) * (index + i_shortcuts));
251             if (unlikely(pp == NULL))
252             {
253                 ret = -1;
254                 break;
255             }
256             module->pp_shortcuts = pp;
257             module->i_shortcuts = index + i_shortcuts;
258             pp += index;
259             for (unsigned i = 0; i < i_shortcuts; i++)
260                 pp[i] = tab[i];
261             break;
262         }
263 
264         case VLC_MODULE_CAPABILITY:
265             module->psz_capability = va_arg (ap, const char *);
266             break;
267 
268         case VLC_MODULE_SCORE:
269             module->i_score = va_arg (ap, int);
270             break;
271 
272         case VLC_MODULE_CB_OPEN:
273             module->activate_name = va_arg(ap, const char *);
274             module->pf_activate = va_arg (ap, void *);
275             break;
276 
277         case VLC_MODULE_CB_CLOSE:
278             module->deactivate_name = va_arg(ap, const char *);
279             module->pf_deactivate = va_arg (ap, void *);
280             break;
281 
282         case VLC_MODULE_NO_UNLOAD:
283 #ifdef HAVE_DYNAMIC_PLUGINS
284             plugin->unloadable = false;
285 #endif
286             break;
287 
288         case VLC_MODULE_NAME:
289         {
290             const char *value = va_arg (ap, const char *);
291 
292             assert (module->i_shortcuts == 0);
293             module->pp_shortcuts = malloc( sizeof( *module->pp_shortcuts ) );
294             module->pp_shortcuts[0] = value;
295             module->i_shortcuts = 1;
296 
297             assert (module->psz_longname == NULL);
298             module->psz_longname = value;
299             break;
300         }
301 
302         case VLC_MODULE_SHORTNAME:
303             module->psz_shortname = va_arg (ap, const char *);
304             break;
305 
306         case VLC_MODULE_DESCRIPTION:
307             // TODO: do not set this in VLC_MODULE_NAME
308             module->psz_longname = va_arg (ap, const char *);
309             break;
310 
311         case VLC_MODULE_HELP:
312             module->psz_help = va_arg (ap, const char *);
313             break;
314 
315         case VLC_MODULE_TEXTDOMAIN:
316             plugin->textdomain = va_arg(ap, const char *);
317             break;
318 
319         case VLC_CONFIG_NAME:
320         {
321             const char *name = va_arg (ap, const char *);
322 
323             assert (name != NULL);
324             item->psz_name = name;
325             break;
326         }
327 
328         case VLC_CONFIG_VALUE:
329         {
330             if (IsConfigIntegerType (item->i_type)
331              || !CONFIG_ITEM(item->i_type))
332             {
333                 item->orig.i =
334                 item->value.i = va_arg (ap, int64_t);
335             }
336             else
337             if (IsConfigFloatType (item->i_type))
338             {
339                 item->orig.f =
340                 item->value.f = va_arg (ap, double);
341             }
342             else
343             if (IsConfigStringType (item->i_type))
344             {
345                 const char *value = va_arg (ap, const char *);
346                 item->value.psz = value ? strdup (value) : NULL;
347                 item->orig.psz = (char *)value;
348             }
349             break;
350         }
351 
352         case VLC_CONFIG_RANGE:
353         {
354             if (IsConfigFloatType (item->i_type))
355             {
356                 item->min.f = va_arg (ap, double);
357                 item->max.f = va_arg (ap, double);
358             }
359             else
360             {
361                 item->min.i = va_arg (ap, int64_t);
362                 item->max.i = va_arg (ap, int64_t);
363             }
364             break;
365         }
366 
367         case VLC_CONFIG_ADVANCED:
368             item->b_advanced = true;
369             break;
370 
371         case VLC_CONFIG_VOLATILE:
372             item->b_unsaveable = true;
373             break;
374 
375         case VLC_CONFIG_PRIVATE:
376             item->b_internal = true;
377             break;
378 
379         case VLC_CONFIG_REMOVED:
380             item->b_removed = true;
381             break;
382 
383         case VLC_CONFIG_CAPABILITY:
384             item->psz_type = va_arg (ap, const char *);
385             break;
386 
387         case VLC_CONFIG_SHORTCUT:
388             item->i_short = va_arg (ap, int);
389             break;
390 
391         case VLC_CONFIG_SAFE:
392             item->b_safe = true;
393             break;
394 
395         case VLC_CONFIG_DESC:
396             item->psz_text = va_arg (ap, const char *);
397             item->psz_longtext = va_arg (ap, const char *);
398             break;
399 
400         case VLC_CONFIG_LIST:
401         {
402             size_t len = va_arg (ap, size_t);
403 
404             assert (item->list_count == 0); /* cannot replace choices */
405             assert (item->list.psz_cb == NULL);
406             if (len == 0)
407                 break; /* nothing to do */
408             /* Copy values */
409             if (IsConfigIntegerType (item->i_type))
410                 item->list.i = va_arg(ap, const int *);
411             else
412             if (IsConfigStringType (item->i_type))
413             {
414                 const char *const *src = va_arg (ap, const char *const *);
415                 const char **dst = xmalloc (sizeof (const char *) * len);
416 
417                 memcpy(dst, src, sizeof (const char *) * len);
418                 item->list.psz = dst;
419             }
420             else
421                 break;
422 
423             /* Copy textual descriptions */
424             /* XXX: item->list_text[len + 1] is probably useless. */
425             const char *const *text = va_arg (ap, const char *const *);
426             const char **dtext = xmalloc (sizeof (const char *) * (len + 1));
427 
428             memcpy(dtext, text, sizeof (const char *) * len);
429             item->list_text = dtext;
430             item->list_count = len;
431             break;
432         }
433 
434         case VLC_CONFIG_LIST_CB:
435         {
436             void *cb;
437 
438             item->list_cb_name = va_arg(ap, const char *);
439             cb = va_arg(ap, void *);
440 
441             if (IsConfigIntegerType (item->i_type))
442                item->list.i_cb = cb;
443             else
444             if (IsConfigStringType (item->i_type))
445                item->list.psz_cb = cb;
446             else
447                 break;
448             break;
449         }
450 
451         default:
452             fprintf (stderr, "LibVLC: unknown module property %d\n", propid);
453             fprintf (stderr, "LibVLC: too old to use this module?\n");
454             ret = -1;
455             break;
456     }
457 
458     va_end (ap);
459     return ret;
460 }
461 
462 /**
463  * Runs a plug-in descriptor.
464  *
465  * This loads the plug-in meta-data in memory.
466  */
vlc_plugin_describe(vlc_plugin_cb entry)467 vlc_plugin_t *vlc_plugin_describe(vlc_plugin_cb entry)
468 {
469     vlc_plugin_t *plugin = vlc_plugin_create();
470     if (unlikely(plugin == NULL))
471         return NULL;
472 
473     if (entry(vlc_plugin_desc_cb, plugin) != 0)
474     {
475         vlc_plugin_destroy(plugin); /* partially initialized plug-in... */
476         plugin = NULL;
477     }
478     return plugin;
479 }
480 
481 struct vlc_plugin_symbol
482 {
483     const char *name;
484     void *addr;
485 };
486 
vlc_plugin_symbol_compare(const void * a,const void * b)487 static int vlc_plugin_symbol_compare(const void *a, const void *b)
488 {
489     const struct vlc_plugin_symbol *sa = a , *sb = b;
490 
491     return strcmp(sa->name, sb->name);
492 }
493 
494 /**
495  * Plug-in symbols callback.
496  *
497  * This callback generates a mapping of plugin symbol names to symbol
498  * addresses.
499  */
vlc_plugin_gpa_cb(void * ctx,void * tgt,int propid,...)500 static int vlc_plugin_gpa_cb(void *ctx, void *tgt, int propid, ...)
501 {
502     void **rootp = ctx;
503     const char *name;
504     void *addr;
505 
506     (void) tgt;
507 
508     switch (propid)
509     {
510         case VLC_MODULE_CB_OPEN:
511         case VLC_MODULE_CB_CLOSE:
512         case VLC_CONFIG_LIST_CB:
513         {
514             va_list ap;
515 
516             va_start(ap, propid);
517             name = va_arg(ap, const char *);
518             addr = va_arg(ap, void *);
519             va_end (ap);
520             break;
521         }
522         default:
523             return 0;
524     }
525 
526     struct vlc_plugin_symbol *sym = malloc(sizeof (*sym));
527 
528     sym->name = name;
529     sym->addr = addr;
530 
531     struct vlc_plugin_symbol **symp = tsearch(sym, rootp,
532                                               vlc_plugin_symbol_compare);
533     if (unlikely(symp == NULL))
534     {   /* Memory error */
535         free(sym);
536         return -1;
537     }
538 
539     if (*symp != sym)
540     {   /* Duplicate symbol */
541         assert((*symp)->addr == sym->addr);
542         free(sym);
543     }
544     return 0;
545 }
546 
547 /**
548  * Gets the symbols of a plugin.
549  *
550  * This function generates a list of symbol names and addresses for a given
551  * plugin descriptor. The result can be used with vlc_plugin_get_symbol()
552  * to resolve a symbol name to an address.
553  *
554  * The result must be freed with vlc_plugin_free_symbols(). The result is only
555  * meaningful until the plugin is unloaded.
556  */
vlc_plugin_get_symbols(vlc_plugin_cb entry)557 static void *vlc_plugin_get_symbols(vlc_plugin_cb entry)
558 {
559     void *root = NULL;
560 
561     if (entry(vlc_plugin_gpa_cb, &root))
562     {
563         tdestroy(root, free);
564         return NULL;
565     }
566 
567     return root;
568 }
569 
vlc_plugin_free_symbols(void * root)570 static void vlc_plugin_free_symbols(void *root)
571 {
572     tdestroy(root, free);
573 }
574 
vlc_plugin_get_symbol(void * root,const char * name,void ** restrict addrp)575 static int vlc_plugin_get_symbol(void *root, const char *name,
576                                  void **restrict addrp)
577 {
578     if (name == NULL)
579     {   /* TODO: use this; do not define "NULL" as a name for NULL? */
580         *addrp = NULL;
581         return 0;
582     }
583 
584     const struct vlc_plugin_symbol **symp = tfind(&name, &root,
585                                                   vlc_plugin_symbol_compare);
586 
587     if (symp == NULL)
588         return -1;
589 
590     *addrp = (*symp)->addr;
591     return 0;
592 }
593 
vlc_plugin_resolve(vlc_plugin_t * plugin,vlc_plugin_cb entry)594 int vlc_plugin_resolve(vlc_plugin_t *plugin, vlc_plugin_cb entry)
595 {
596     void *syms = vlc_plugin_get_symbols(entry);
597     int ret = -1;
598 
599     /* Resolve modules activate/deactivate callbacks */
600     for (module_t *module = plugin->module;
601          module != NULL;
602          module = module->next)
603     {
604         if (vlc_plugin_get_symbol(syms, module->activate_name,
605                                   &module->pf_activate)
606          || vlc_plugin_get_symbol(syms, module->deactivate_name,
607                                   &module->pf_deactivate))
608             goto error;
609     }
610 
611     /* Resolve configuration callbacks */
612     for (size_t i = 0; i < plugin->conf.size; i++)
613     {
614         module_config_t *item = plugin->conf.items + i;
615         void *cb;
616 
617         if (item->list_cb_name == NULL)
618             continue;
619         if (vlc_plugin_get_symbol(syms, item->list_cb_name, &cb))
620             goto error;
621 
622         if (IsConfigIntegerType (item->i_type))
623             item->list.i_cb = cb;
624         else
625         if (IsConfigStringType (item->i_type))
626             item->list.psz_cb = cb;
627     }
628 
629     ret = 0;
630 error:
631     vlc_plugin_free_symbols(syms);
632     return ret;
633 }
634