Windows Store App: fix plugins loading
[vlc.git] / src / modules / bank.c
blob7b05b8d1bacb38562960e335e122cbd94e462dad
1 /*****************************************************************************
2 * bank.c : Modules list
3 *****************************************************************************
4 * Copyright (C) 2001-2011 VLC authors and VideoLAN
6 * Authors: Sam Hocevar <sam@zoy.org>
7 * Ethan C. Baldridge <BaldridgeE@cadmus.com>
8 * Hans-Peter Jansen <hpj@urpla.net>
9 * Gildas Bazin <gbazin@videolan.org>
10 * RĂ©mi Denis-Courmont
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <assert.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_modules.h>
45 #include <vlc_fs.h>
46 #include "libvlc.h"
47 #include "config/configuration.h"
48 #include "modules/modules.h"
50 static struct
52 vlc_mutex_t lock;
53 module_t *head;
54 unsigned usage;
55 } modules = { VLC_STATIC_MUTEX, NULL, 0 };
57 /*****************************************************************************
58 * Local prototypes
59 *****************************************************************************/
60 #ifdef HAVE_DYNAMIC_PLUGINS
61 static void AllocateAllPlugins (vlc_object_t *);
62 #endif
63 static module_t *module_InitStatic (vlc_plugin_cb);
65 static void module_StoreBank (module_t *module)
67 /*vlc_assert_locked (&modules.lock);*/
68 module->next = modules.head;
69 modules.head = module;
72 #if defined(__ELF__) || !HAVE_DYNAMIC_PLUGINS
73 # ifdef __GNUC__
74 __attribute__((weak))
75 # else
76 # pragma weak vlc_static_modules
77 # endif
78 extern vlc_plugin_cb vlc_static_modules[];
80 static void module_InitStaticModules(void)
82 if (!vlc_static_modules)
83 return;
85 for (unsigned i = 0; vlc_static_modules[i]; i++) {
86 module_t *module = module_InitStatic (vlc_static_modules[i]);
87 if (likely(module != NULL))
88 module_StoreBank (module);
91 #else
92 static void module_InitStaticModules(void) { }
93 #endif
95 /**
96 * Init bank
98 * Creates a module bank structure which will be filled later
99 * on with all the modules found.
101 void module_InitBank (void)
103 vlc_mutex_lock (&modules.lock);
105 if (modules.usage == 0)
107 /* Fills the module bank structure with the main module infos.
108 * This is very useful as it will allow us to consider the main
109 * library just as another module, and for instance the configuration
110 * options of main will be available in the module bank structure just
111 * as for every other module. */
112 module_t *module = module_InitStatic (vlc_entry__main);
113 if (likely(module != NULL))
114 module_StoreBank (module);
115 config_SortConfig ();
117 modules.usage++;
119 /* We do retain the module bank lock until the plugins are loaded as well.
120 * This is ugly, this staged loading approach is needed: LibVLC gets
121 * some configuration parameters relevant to loading the plugins from
122 * the main (builtin) module. The module bank becomes shared read-only data
123 * once it is ready, so we need to fully serialize initialization.
124 * DO NOT UNCOMMENT the following line unless you managed to squeeze
125 * module_LoadPlugins() before you unlock the mutex. */
126 /*vlc_mutex_unlock (&modules.lock);*/
130 * Unloads all unused plugin modules and empties the module
131 * bank in case of success.
133 void module_EndBank (bool b_plugins)
135 module_t *head = NULL;
137 /* If plugins were _not_ loaded, then the caller still has the bank lock
138 * from module_InitBank(). */
139 if( b_plugins )
140 vlc_mutex_lock (&modules.lock);
141 /*else
142 vlc_assert_locked (&modules.lock); not for static mutexes :( */
144 assert (modules.usage > 0);
145 if (--modules.usage == 0)
147 config_UnsortConfig ();
148 head = modules.head;
149 modules.head = NULL;
151 vlc_mutex_unlock (&modules.lock);
153 while (head != NULL)
155 module_t *module = head;
157 head = module->next;
158 #ifdef HAVE_DYNAMIC_PLUGINS
159 if (module->b_loaded && module->b_unloadable)
161 module_Unload (module->handle);
162 module->b_loaded = false;
164 #endif
165 vlc_module_destroy (module);
169 #undef module_LoadPlugins
171 * Loads module descriptions for all available plugins.
172 * Fills the module bank structure with the plugin modules.
174 * \param p_this vlc object structure
175 * \return total number of modules in bank after loading all plug-ins
177 size_t module_LoadPlugins (vlc_object_t *obj)
179 /*vlc_assert_locked (&modules.lock); not for static mutexes :( */
181 if (modules.usage == 1)
183 module_InitStaticModules ();
184 #ifdef HAVE_DYNAMIC_PLUGINS
185 msg_Dbg (obj, "searching plug-in modules");
186 AllocateAllPlugins (obj);
187 #endif
188 config_UnsortConfig ();
189 config_SortConfig ();
191 vlc_mutex_unlock (&modules.lock);
193 size_t count;
194 module_t **list = module_list_get (&count);
195 module_list_free (list);
196 msg_Dbg (obj, "plug-ins loaded: %zu modules", count);
197 return count;
201 * Frees the flat list of VLC modules.
202 * @param list list obtained by module_list_get()
203 * @param length number of items on the list
204 * @return nothing.
206 void module_list_free (module_t **list)
208 free (list);
212 * Gets the flat list of VLC modules.
213 * @param n [OUT] pointer to the number of modules
214 * @return table of module pointers (release with module_list_free()),
215 * or NULL in case of error (in that case, *n is zeroed).
217 module_t **module_list_get (size_t *n)
219 module_t **tab = NULL;
220 size_t i = 0;
222 assert (n != NULL);
224 for (module_t *mod = modules.head; mod; mod = mod->next)
226 module_t **nt;
227 nt = realloc (tab, (i + 1 + mod->submodule_count) * sizeof (*tab));
228 if (unlikely(nt == NULL))
230 free (tab);
231 *n = 0;
232 return NULL;
235 tab = nt;
236 tab[i++] = mod;
237 for (module_t *subm = mod->submodule; subm; subm = subm->next)
238 tab[i++] = subm;
240 *n = i;
241 return tab;
244 static int modulecmp (const void *a, const void *b)
246 const module_t *const *ma = a, *const *mb = b;
247 /* Note that qsort() uses _ascending_ order,
248 * so the smallest module is the one with the biggest score. */
249 return (*mb)->i_score - (*ma)->i_score;
253 * Builds a sorted list of all VLC modules with a given capability.
254 * The list is sorted from the highest module score to the lowest.
255 * @param list pointer to the table of modules [OUT]
256 * @param cap capability of modules to look for
257 * @return the number of matching found, or -1 on error (*list is then NULL).
258 * @note *list must be freed with module_list_free().
260 ssize_t module_list_cap (module_t ***restrict list, const char *cap)
262 /* TODO: This is quite inefficient. List should be sorted by capability. */
263 ssize_t n = 0;
265 assert (list != NULL);
267 for (module_t *mod = modules.head; mod != NULL; mod = mod->next)
269 if (module_provides (mod, cap))
270 n++;
271 for (module_t *subm = mod->submodule; subm != NULL; subm = subm->next)
272 if (module_provides (subm, cap))
273 n++;
276 module_t **tab = malloc (sizeof (*tab) * n);
277 *list = tab;
278 if (unlikely(tab == NULL))
279 return -1;
281 for (module_t *mod = modules.head; mod != NULL; mod = mod->next)
283 if (module_provides (mod, cap))
284 *(tab++)= mod;
285 for (module_t *subm = mod->submodule; subm != NULL; subm = subm->next)
286 if (module_provides (subm, cap))
287 *(tab++) = subm;
290 assert (tab == *list + n);
291 qsort (*list, n, sizeof (*tab), modulecmp);
292 return n;
295 #ifdef HAVE_DYNAMIC_PLUGINS
296 typedef enum { CACHE_USE, CACHE_RESET, CACHE_IGNORE } cache_mode_t;
298 static void AllocatePluginPath (vlc_object_t *, const char *, cache_mode_t);
301 * Enumerates all dynamic plug-ins that can be found.
303 * This function will recursively browse the default plug-ins directory and any
304 * directory listed in the VLC_PLUGIN_PATH environment variable.
305 * For performance reasons, a cache is normally used so that plug-in shared
306 * objects do not need to loaded and linked into the process.
308 static void AllocateAllPlugins (vlc_object_t *p_this)
310 char *paths;
311 cache_mode_t mode;
313 if( !var_InheritBool( p_this, "plugins-cache" ) )
314 mode = CACHE_IGNORE;
315 else if( var_InheritBool( p_this, "reset-plugins-cache" ) )
316 mode = CACHE_RESET;
317 else
318 mode = CACHE_USE;
320 #ifdef WINAPI_FAMILY_APP
321 /* Windows Store Apps can not load external plugins with absolute paths. */
322 AllocatePluginPath (p_this, "plugins", mode);
323 #else
324 /* Contruct the special search path for system that have a relocatable
325 * executable. Set it to <vlc path>/plugins. */
326 char *vlcpath = config_GetLibDir ();
327 if (likely(vlcpath != NULL)
328 && likely(asprintf (&paths, "%s" DIR_SEP "plugins", vlcpath) != -1))
330 AllocatePluginPath (p_this, paths, mode);
331 free( paths );
333 free (vlcpath);
334 #endif /* WINAPI_FAMILY_APP */
336 /* If the user provided a plugin path, we add it to the list */
337 paths = getenv( "VLC_PLUGIN_PATH" );
338 if( paths == NULL )
339 return;
341 paths = strdup( paths ); /* don't harm the environment ! :) */
342 if( unlikely(paths == NULL) )
343 return;
345 for( char *buf, *path = strtok_r( paths, PATH_SEP, &buf );
346 path != NULL;
347 path = strtok_r( NULL, PATH_SEP, &buf ) )
348 AllocatePluginPath (p_this, path, mode);
350 free( paths );
353 typedef struct module_bank
355 vlc_object_t *obj;
356 const char *base;
357 cache_mode_t mode;
359 size_t i_cache;
360 module_cache_t *cache;
362 int i_loaded_cache;
363 module_cache_t *loaded_cache;
364 } module_bank_t;
366 static void AllocatePluginDir (module_bank_t *, unsigned,
367 const char *, const char *);
370 * Scans for plug-ins within a file system hierarchy.
371 * \param path base directory to browse
373 static void AllocatePluginPath (vlc_object_t *p_this, const char *path,
374 cache_mode_t mode)
376 module_bank_t bank;
377 module_cache_t *cache = NULL;
378 size_t count = 0;
380 switch( mode )
382 case CACHE_USE:
383 count = CacheLoad( p_this, path, &cache );
384 break;
385 case CACHE_RESET:
386 CacheDelete( p_this, path );
387 break;
388 case CACHE_IGNORE:
389 msg_Dbg( p_this, "ignoring plugins cache file" );
392 msg_Dbg( p_this, "recursively browsing `%s'", path );
394 bank.obj = p_this;
395 bank.base = path;
396 bank.mode = mode;
397 bank.cache = NULL;
398 bank.i_cache = 0;
399 bank.loaded_cache = cache;
400 bank.i_loaded_cache = count;
402 /* Don't go deeper than 5 subdirectories */
403 AllocatePluginDir (&bank, 5, path, NULL);
405 switch( mode )
407 case CACHE_USE:
408 /* Discard unmatched cache entries */
409 for( size_t i = 0; i < count; i++ )
411 if (cache[i].p_module != NULL)
412 vlc_module_destroy (cache[i].p_module);
413 free (cache[i].path);
415 free( cache );
416 case CACHE_RESET:
417 CacheSave (p_this, path, bank.cache, bank.i_cache);
418 case CACHE_IGNORE:
419 break;
423 static int AllocatePluginFile (module_bank_t *, const char *,
424 const char *, const struct stat *);
427 * Recursively browses a directory to look for plug-ins.
429 static void AllocatePluginDir (module_bank_t *bank, unsigned maxdepth,
430 const char *absdir, const char *reldir)
432 if (maxdepth == 0)
433 return;
434 maxdepth--;
436 DIR *dh = vlc_opendir (absdir);
437 if (dh == NULL)
438 return;
440 /* Parse the directory and try to load all files it contains. */
441 for (;;)
443 char *file = vlc_readdir (dh), *relpath = NULL, *abspath = NULL;
444 if (file == NULL)
445 break;
447 /* Skip ".", ".." */
448 if (!strcmp (file, ".") || !strcmp (file, ".."))
449 goto skip;
451 /* Compute path relative to plug-in base directory */
452 if (reldir != NULL)
454 if (asprintf (&relpath, "%s"DIR_SEP"%s", reldir, file) == -1)
455 relpath = NULL;
457 else
458 relpath = strdup (file);
459 if (unlikely(relpath == NULL))
460 goto skip;
462 /* Compute absolute path */
463 if (asprintf (&abspath, "%s"DIR_SEP"%s", bank->base, relpath) == -1)
465 abspath = NULL;
466 goto skip;
469 struct stat st;
470 if (vlc_stat (abspath, &st) == -1)
471 goto skip;
473 if (S_ISREG (st.st_mode))
475 static const char prefix[] = "lib";
476 static const char suffix[] = "_plugin"LIBEXT;
477 size_t len = strlen (file);
479 #ifndef __OS2__
480 /* Check that file matches the "lib*_plugin"LIBEXT pattern */
481 if (len > strlen (suffix)
482 && !strncmp (file, prefix, strlen (prefix))
483 && !strcmp (file + len - strlen (suffix), suffix))
484 #else
485 /* We load all the files ending with LIBEXT on OS/2,
486 * because OS/2 has a 8.3 length limitation for DLL name */
487 if (len > strlen (LIBEXT)
488 && !strcasecmp (file + len - strlen (LIBEXT), LIBEXT))
489 #endif
490 AllocatePluginFile (bank, abspath, relpath, &st);
492 else if (S_ISDIR (st.st_mode))
493 /* Recurse into another directory */
494 AllocatePluginDir (bank, maxdepth, abspath, relpath);
495 skip:
496 free (relpath);
497 free (abspath);
498 free (file);
500 closedir (dh);
503 static module_t *module_InitDynamic (vlc_object_t *, const char *, bool);
506 * Scans a plug-in from a file.
508 static int AllocatePluginFile (module_bank_t *bank, const char *abspath,
509 const char *relpath, const struct stat *st)
511 module_t *module = NULL;
513 /* Check our plugins cache first then load plugin if needed */
514 if (bank->mode == CACHE_USE)
516 module = CacheFind (bank->loaded_cache, bank->i_loaded_cache,
517 relpath, st);
518 if (module != NULL)
520 module->psz_filename = strdup (abspath);
521 if (unlikely(module->psz_filename == NULL))
523 vlc_module_destroy (module);
524 module = NULL;
528 if (module == NULL)
529 module = module_InitDynamic (bank->obj, abspath, true);
530 if (module == NULL)
531 return -1;
533 /* We have not already scanned and inserted this module */
534 assert (module->next == NULL);
536 /* Unload plugin until we really need it */
537 if (module->b_loaded && module->b_unloadable)
539 module_Unload (module->handle);
540 module->b_loaded = false;
543 /* For now we force loading if the module's config contains callbacks.
544 * Could be optimized by adding an API call.*/
545 for (size_t n = module->confsize, i = 0; i < n; i++)
546 if (module->p_config[i].list_count == 0
547 && (module->p_config[i].list.psz_cb != NULL || module->p_config[i].list.i_cb != NULL))
549 /* !unloadable not allowed for plugins with callbacks */
550 vlc_module_destroy (module);
551 module = module_InitDynamic (bank->obj, abspath, false);
552 if (unlikely(module == NULL))
553 return -1;
554 break;
557 module_StoreBank (module);
559 if (bank->mode != CACHE_IGNORE) /* Add entry to cache */
560 CacheAdd (&bank->cache, &bank->i_cache, relpath, st, module);
561 /* TODO: deal with errors */
562 return 0;
565 #ifdef __OS2__
566 # define EXTERN_PREFIX "_"
567 #else
568 # define EXTERN_PREFIX
569 #endif
573 * Loads a dynamically-linked plug-in into memory and initialize it.
575 * The module can then be handled by module_need() and module_unneed().
577 * \param path file path of the shared object
578 * \param fast whether to optimize loading for speed or safety
579 * (fast is used when the plug-in is registered but not used)
581 static module_t *module_InitDynamic (vlc_object_t *obj, const char *path,
582 bool fast)
584 module_handle_t handle;
586 if (module_Load (obj, path, &handle, fast))
587 return NULL;
589 /* Try to resolve the symbol */
590 static const char entry_name[] = EXTERN_PREFIX "vlc_entry" MODULE_SUFFIX;
591 vlc_plugin_cb entry =
592 (vlc_plugin_cb) module_Lookup (handle, entry_name);
593 if (entry == NULL)
595 msg_Warn (obj, "cannot find plug-in entry point in %s", path);
596 goto error;
599 /* We can now try to call the symbol */
600 module_t *module = vlc_plugin_describe (entry);
601 if (unlikely(module == NULL))
603 /* With a well-written module we shouldn't have to print an
604 * additional error message here, but just make sure. */
605 msg_Err (obj, "cannot initialize plug-in %s", path);
606 goto error;
609 module->psz_filename = strdup (path);
610 if (unlikely(module->psz_filename == NULL))
612 vlc_module_destroy (module);
613 goto error;
615 module->handle = handle;
616 module->b_loaded = true;
617 return module;
618 error:
619 module_Unload( handle );
620 return NULL;
622 #endif /* HAVE_DYNAMIC_PLUGINS */
625 * Registers a statically-linked plug-in.
627 static module_t *module_InitStatic (vlc_plugin_cb entry)
629 /* Initializes the module */
630 module_t *module = vlc_plugin_describe (entry);
631 if (unlikely(module == NULL))
632 return NULL;
634 module->b_loaded = true;
635 module->b_unloadable = false;
636 return module;
640 * Makes sure the module is loaded in memory.
641 * \return 0 on success, -1 on failure
643 int module_Map (vlc_object_t *obj, module_t *module)
645 if (module->parent != NULL)
646 module = module->parent;
648 #warning FIXME: race condition!
649 if (module->b_loaded)
650 return 0;
651 assert (module->psz_filename != NULL);
653 #ifdef HAVE_DYNAMIC_PLUGINS
654 module_t *uncache = module_InitDynamic (obj, module->psz_filename, false);
655 if (uncache != NULL)
657 CacheMerge (obj, module, uncache);
658 vlc_module_destroy (uncache);
659 return 0;
661 #endif
662 msg_Err (obj, "corrupt module: %s", module->psz_filename);
663 return -1;