/src/vlc/src/modules/bank.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * bank.c : Modules list |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001-2011 VLC authors and VideoLAN |
5 | | * |
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 |
11 | | * |
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. |
16 | | * |
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. |
21 | | * |
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 | | *****************************************************************************/ |
26 | | |
27 | | #ifdef HAVE_CONFIG_H |
28 | | # include "config.h" |
29 | | #endif |
30 | | |
31 | | #include <stdlib.h> |
32 | | #include <stdio.h> |
33 | | #include <string.h> |
34 | | #include <assert.h> |
35 | | |
36 | | #include <sys/types.h> |
37 | | #include <sys/stat.h> |
38 | | #include <unistd.h> |
39 | | #ifdef HAVE_SEARCH_H |
40 | | # include <search.h> |
41 | | #endif |
42 | | |
43 | | #include <vlc_common.h> |
44 | | #include <vlc_plugin.h> |
45 | | #include <vlc_modules.h> |
46 | | #include <vlc_fs.h> |
47 | | #include <vlc_block.h> |
48 | | #include "../libvlc.h" |
49 | | #include "config/configuration.h" |
50 | | #include "modules/modules.h" |
51 | | |
52 | | /** Core module */ |
53 | | VLC_DECL_MODULE_ENTRY(core); |
54 | | |
55 | | typedef struct vlc_modcap |
56 | | { |
57 | | char *name; |
58 | | module_t **modv; |
59 | | size_t modc; |
60 | | } vlc_modcap_t; |
61 | | |
62 | | static int vlc_modcap_cmp(const void *a, const void *b) |
63 | 13.8M | { |
64 | 13.8M | const vlc_modcap_t *capa = a, *capb = b; |
65 | 13.8M | return strcmp(capa->name, capb->name); |
66 | 13.8M | } |
67 | | |
68 | | static void vlc_modcap_free(void *data) |
69 | 5.32k | { |
70 | 5.32k | vlc_modcap_t *cap = data; |
71 | | |
72 | 5.32k | free(cap->modv); |
73 | 5.32k | free(cap->name); |
74 | 5.32k | free(cap); |
75 | 5.32k | } |
76 | | |
77 | | static int vlc_module_cmp (const void *a, const void *b) |
78 | 16.8k | { |
79 | 16.8k | module_t *const *ma = a, *const *mb = b; |
80 | | /* Note that qsort() uses _ascending_ order, |
81 | | * so the smallest module is the one with the biggest score. */ |
82 | 16.8k | return (*mb)->i_score - (*ma)->i_score; |
83 | 16.8k | } |
84 | | |
85 | | static void vlc_modcap_sort(const void *node, const VISIT which, |
86 | | const int depth) |
87 | 1.87k | { |
88 | 1.87k | vlc_modcap_t *const *cp = node, *cap = *cp; |
89 | | |
90 | 1.87k | if (which != postorder && which != leaf) |
91 | 960 | return; |
92 | | |
93 | 912 | qsort(cap->modv, cap->modc, sizeof (*cap->modv), vlc_module_cmp); |
94 | 912 | (void) depth; |
95 | 912 | } |
96 | | |
97 | | static struct |
98 | | { |
99 | | vlc_mutex_t lock; |
100 | | block_t *caches; |
101 | | void *caps_tree; |
102 | | size_t count; |
103 | | unsigned usage; |
104 | | } modules = { VLC_STATIC_MUTEX, NULL, NULL, 0, 0 }; |
105 | | |
106 | | vlc_plugin_t *vlc_plugins = NULL; |
107 | | |
108 | | /** |
109 | | * Adds a module to the bank |
110 | | */ |
111 | | static int vlc_module_store(module_t *mod) |
112 | 6.24k | { |
113 | 6.24k | const char *name = module_get_capability(mod); |
114 | 6.24k | vlc_modcap_t *cap = malloc(sizeof (*cap)); |
115 | 6.24k | if (unlikely(cap == NULL)) |
116 | 0 | return -1; |
117 | | |
118 | 6.24k | cap->name = strdup(name); |
119 | 6.24k | cap->modv = NULL; |
120 | 6.24k | cap->modc = 0; |
121 | | |
122 | 6.24k | if (unlikely(cap->name == NULL)) |
123 | 0 | goto error; |
124 | | |
125 | 6.24k | void **cp = tsearch(cap, &modules.caps_tree, vlc_modcap_cmp); |
126 | 6.24k | if (unlikely(cp == NULL)) |
127 | 0 | goto error; |
128 | | |
129 | 6.24k | if (*cp != cap) |
130 | 5.32k | { |
131 | 5.32k | vlc_modcap_free(cap); |
132 | 5.32k | cap = *cp; |
133 | 5.32k | } |
134 | | |
135 | 6.24k | module_t **modv = realloc(cap->modv, sizeof (*modv) * (cap->modc + 1)); |
136 | 6.24k | if (unlikely(modv == NULL)) |
137 | 0 | return -1; |
138 | | |
139 | 6.24k | cap->modv = modv; |
140 | 6.24k | cap->modv[cap->modc] = mod; |
141 | 6.24k | cap->modc++; |
142 | 6.24k | return 0; |
143 | 0 | error: |
144 | 0 | vlc_modcap_free(cap); |
145 | 0 | return -1; |
146 | 6.24k | } |
147 | | |
148 | | /** |
149 | | * Adds a plugin (and all its modules) to the bank |
150 | | */ |
151 | | static void vlc_plugin_store(vlc_plugin_t *lib) |
152 | 4.12k | { |
153 | 4.12k | vlc_mutex_assert(&modules.lock); |
154 | | |
155 | 4.12k | lib->next = vlc_plugins; |
156 | 4.12k | vlc_plugins = lib; |
157 | 4.12k | modules.count += lib->modules_count; |
158 | | |
159 | 10.3k | for (module_t *m = lib->module; m != NULL; m = m->next) |
160 | 6.24k | vlc_module_store(m); |
161 | 4.12k | } |
162 | | |
163 | | /** |
164 | | * Registers a statically-linked plug-in. |
165 | | */ |
166 | | static vlc_plugin_t *module_InitStatic(vlc_plugin_cb entry) |
167 | 4.12k | { |
168 | | /* Initializes the statically-linked library */ |
169 | 4.12k | vlc_plugin_t *lib = vlc_plugin_describe (entry); |
170 | 4.12k | if (unlikely(lib == NULL)) |
171 | 0 | return NULL; |
172 | | |
173 | | #ifdef HAVE_DYNAMIC_PLUGINS |
174 | | atomic_init(&lib->handle, 0); |
175 | | lib->unloadable = false; |
176 | | #endif |
177 | 4.12k | return lib; |
178 | 4.12k | } |
179 | | |
180 | | /* With weak linking in __ELF__, there is no need to provide a definition |
181 | | * of vlc_static_modules at build time and it will be evaluated to NULL if |
182 | | * not provided at runtime. However, although __MACH__ implies the same runtime |
183 | | * consequences for weak linking, it will still require the definition to exist |
184 | | * at build time. To workaround this, we add -Wl,-U,vlc_static_modules. */ |
185 | | #if defined(__ELF__) \ |
186 | | || (defined(__MACH__) && defined(HAVE_DYLIB_DYNAMIC_LOOKUP)) \ |
187 | | || !defined(HAVE_DYNAMIC_PLUGINS) |
188 | | VLC_WEAK |
189 | | extern const vlc_plugin_cb vlc_static_modules[]; |
190 | | |
191 | | static void module_InitStaticModules(void) |
192 | 48 | { |
193 | 48 | if (!vlc_static_modules) |
194 | 0 | return; |
195 | | |
196 | 4.12k | for (unsigned i = 0; vlc_static_modules[i]; i++) |
197 | 4.08k | { |
198 | 4.08k | vlc_plugin_t *lib = module_InitStatic(vlc_static_modules[i]); |
199 | 4.08k | if (likely(lib != NULL)) |
200 | 4.08k | vlc_plugin_store(lib); |
201 | 4.08k | } |
202 | 48 | } |
203 | | #else |
204 | | static void module_InitStaticModules(void) { } |
205 | | #endif |
206 | | |
207 | | #ifdef HAVE_DYNAMIC_PLUGINS |
208 | | static const char *module_GetVersion(void *handle) |
209 | | { |
210 | | const char *(*get_api_version)(void); |
211 | | |
212 | | get_api_version = vlc_dlsym(handle, "vlc_entry_api_version"); |
213 | | if (get_api_version == NULL) |
214 | | return NULL; |
215 | | |
216 | | return get_api_version(); |
217 | | } |
218 | | |
219 | | static void *module_Open(struct vlc_logger *log, |
220 | | const char *path, bool fast) |
221 | | { |
222 | | void *handle = vlc_dlopen(path, fast); |
223 | | if (handle == NULL) |
224 | | { |
225 | | char *errmsg = vlc_dlerror(); |
226 | | |
227 | | vlc_error(log, "cannot load plug-in %s: %s", path, |
228 | | errmsg ? errmsg : "unknown error"); |
229 | | free(errmsg); |
230 | | return NULL; |
231 | | } |
232 | | |
233 | | const char *str = module_GetVersion(handle); |
234 | | if (str == NULL) { |
235 | | vlc_error(log, "cannot load plug-in %s: %s", path, |
236 | | "unknown version or not a plug-in"); |
237 | | error: |
238 | | vlc_dlclose(handle); |
239 | | return NULL; |
240 | | } |
241 | | |
242 | | if (strcmp(str, VLC_API_VERSION_STRING)) { |
243 | | vlc_error(log, "cannot load plug-in %s: unsupported version %s", path, |
244 | | str); |
245 | | goto error; |
246 | | } |
247 | | |
248 | | return handle; |
249 | | } |
250 | | |
251 | | /** |
252 | | * Loads a dynamically-linked plug-in into memory and initialize it. |
253 | | * |
254 | | * The module can then be handled by module_need() and module_unneed(). |
255 | | * |
256 | | * \param path file path of the shared object |
257 | | * \param fast whether to optimize loading for speed or safety |
258 | | * (fast is used when the plug-in is registered but not used) |
259 | | */ |
260 | | static vlc_plugin_t *module_InitDynamic(libvlc_int_t *obj, const char *path, |
261 | | bool fast) |
262 | | { |
263 | | void *handle = module_Open(vlc_object_logger(obj), path, fast); |
264 | | if (handle == NULL) |
265 | | return NULL; |
266 | | |
267 | | /* Try to resolve the symbol */ |
268 | | vlc_plugin_cb entry = vlc_dlsym(handle, "vlc_entry"); |
269 | | if (entry == NULL) |
270 | | { |
271 | | msg_Warn (obj, "cannot find plug-in entry point in %s", path); |
272 | | goto error; |
273 | | } |
274 | | |
275 | | /* We can now try to call the symbol */ |
276 | | vlc_plugin_t *plugin = vlc_plugin_describe(entry); |
277 | | if (unlikely(plugin == NULL)) |
278 | | { |
279 | | /* With a well-written module we shouldn't have to print an |
280 | | * additional error message here, but just make sure. */ |
281 | | msg_Err (obj, "cannot initialize plug-in %s", path); |
282 | | goto error; |
283 | | } |
284 | | |
285 | | atomic_init(&plugin->handle, (uintptr_t)handle); |
286 | | return plugin; |
287 | | error: |
288 | | vlc_dlclose(handle); |
289 | | return NULL; |
290 | | } |
291 | | |
292 | | typedef enum |
293 | | { |
294 | | CACHE_READ_FILE = 0x1, |
295 | | CACHE_SCAN_DIR = 0x2, |
296 | | CACHE_WRITE_FILE = 0x4, |
297 | | } cache_mode_t; |
298 | | |
299 | | typedef struct module_bank |
300 | | { |
301 | | libvlc_int_t *obj; |
302 | | const char *base; |
303 | | cache_mode_t mode; |
304 | | |
305 | | size_t size; |
306 | | vlc_plugin_t **plugins; |
307 | | vlc_plugin_t *cache; |
308 | | } module_bank_t; |
309 | | |
310 | | /** |
311 | | * Scans a plug-in from a file. |
312 | | */ |
313 | | static int AllocatePluginFile (module_bank_t *bank, const char *abspath, |
314 | | const char *relpath, const struct stat *st) |
315 | | { |
316 | | vlc_plugin_t *plugin = NULL; |
317 | | |
318 | | /* Check our plugins cache first then load plugin if needed */ |
319 | | if (bank->mode & CACHE_READ_FILE) |
320 | | { |
321 | | plugin = vlc_cache_lookup(&bank->cache, relpath); |
322 | | |
323 | | if (plugin != NULL |
324 | | && (plugin->mtime != (int64_t)st->st_mtime |
325 | | || plugin->size != (uint64_t)st->st_size)) |
326 | | { |
327 | | msg_Err(bank->obj, "stale plugins cache: modified %s", |
328 | | plugin->abspath); |
329 | | vlc_plugin_destroy(plugin); |
330 | | plugin = NULL; |
331 | | } |
332 | | } |
333 | | |
334 | | if (plugin == NULL) |
335 | | { |
336 | | char *path = strdup(relpath); |
337 | | if (path == NULL) |
338 | | return -1; |
339 | | |
340 | | plugin = module_InitDynamic(bank->obj, abspath, true); |
341 | | |
342 | | if (plugin != NULL) |
343 | | { |
344 | | plugin->path = path; |
345 | | plugin->mtime = st->st_mtime; |
346 | | plugin->size = st->st_size; |
347 | | } |
348 | | else free(path); |
349 | | } |
350 | | |
351 | | if (plugin == NULL) |
352 | | return -1; |
353 | | |
354 | | vlc_plugin_store(plugin); |
355 | | |
356 | | if (bank->mode & CACHE_WRITE_FILE) /* Add entry to to-be-saved cache */ |
357 | | { |
358 | | bank->plugins = xrealloc(bank->plugins, |
359 | | (bank->size + 1) * sizeof (vlc_plugin_t *)); |
360 | | bank->plugins[bank->size] = plugin; |
361 | | bank->size++; |
362 | | } |
363 | | |
364 | | /* TODO: deal with errors */ |
365 | | return 0; |
366 | | } |
367 | | |
368 | | #ifdef __APPLE__ |
369 | | /* Apple specific framework library browsing */ |
370 | | |
371 | | static int AllocatePluginFramework (module_bank_t *bank, const char *file, |
372 | | const char *relpath, const char *abspath) |
373 | | { |
374 | | int i_ret = VLC_EGENERIC; |
375 | | size_t len_name = strlen (file); |
376 | | |
377 | | /* Skip frameworks not matching plugins naming conventions. */ |
378 | | if (len_name < strlen("_plugin.framework") |
379 | | || strncmp(file + len_name - strlen("_plugin.framework"), |
380 | | "_plugin", strlen("_plugin")) != 0) |
381 | | { |
382 | | /* The framework doesn't contain plugins, there's no need to |
383 | | * browse the rest of the framework folder. */ |
384 | | return VLC_EGENERIC; |
385 | | } |
386 | | |
387 | | /* The framework is a plugin, extract the dylib from it. */ |
388 | | int filename_len = len_name - strlen(".framework"); |
389 | | |
390 | | char *framework_relpath = NULL, *framework_abspath = NULL; |
391 | | /* Compute absolute path */ |
392 | | if (asprintf (&framework_abspath, "%s"DIR_SEP"%.*s", |
393 | | abspath, filename_len, file) == -1) |
394 | | { |
395 | | framework_abspath = NULL; |
396 | | goto end; |
397 | | } |
398 | | |
399 | | struct stat framework_st; |
400 | | if (vlc_stat (framework_abspath, &framework_st) == -1 |
401 | | || !S_ISREG (framework_st.st_mode)) |
402 | | goto end; |
403 | | |
404 | | if (asprintf (&framework_relpath, "%s"DIR_SEP"%.*s", |
405 | | relpath, filename_len, file) == -1) |
406 | | framework_relpath = NULL; |
407 | | |
408 | | i_ret = AllocatePluginFile (bank, framework_abspath, framework_relpath, &framework_st); |
409 | | |
410 | | end: |
411 | | free(framework_relpath); |
412 | | free(framework_abspath); |
413 | | return i_ret; |
414 | | } |
415 | | #endif |
416 | | |
417 | | |
418 | | /** |
419 | | * Recursively browses a directory to look for plug-ins. |
420 | | */ |
421 | | static void AllocatePluginDir (module_bank_t *bank, unsigned maxdepth, |
422 | | const char *absdir, const char *reldir) |
423 | | { |
424 | | if (maxdepth == 0) |
425 | | return; |
426 | | maxdepth--; |
427 | | |
428 | | vlc_DIR *dh = vlc_opendir (absdir); |
429 | | if (dh == NULL) |
430 | | return; |
431 | | |
432 | | /* Parse the directory and try to load all files it contains. */ |
433 | | for (;;) |
434 | | { |
435 | | char *relpath = NULL, *abspath = NULL; |
436 | | const char *file = vlc_readdir (dh); |
437 | | if (file == NULL) |
438 | | break; |
439 | | |
440 | | /* Skip ".", ".." */ |
441 | | if (!strcmp (file, ".") || !strcmp (file, "..")) |
442 | | continue; |
443 | | |
444 | | /* Compute path relative to plug-in base directory */ |
445 | | if (reldir != NULL) |
446 | | { |
447 | | if (asprintf (&relpath, "%s"DIR_SEP"%s", reldir, file) == -1) |
448 | | relpath = NULL; |
449 | | } |
450 | | else |
451 | | relpath = strdup (file); |
452 | | if (unlikely(relpath == NULL)) |
453 | | continue; |
454 | | |
455 | | /* Compute absolute path */ |
456 | | if (asprintf (&abspath, "%s"DIR_SEP"%s", bank->base, relpath) == -1) |
457 | | { |
458 | | abspath = NULL; |
459 | | goto skip; |
460 | | } |
461 | | |
462 | | struct stat st; |
463 | | if (vlc_stat (abspath, &st) == -1) |
464 | | goto skip; |
465 | | |
466 | | if (S_ISREG (st.st_mode)) |
467 | | { |
468 | | static const char prefix[] = "lib"; |
469 | | static const char suffix[] = "_plugin"LIBEXT; |
470 | | size_t len = strlen (file); |
471 | | |
472 | | #ifndef __OS2__ |
473 | | /* Check that file matches the "lib*_plugin"LIBEXT pattern */ |
474 | | if (len > strlen (suffix) |
475 | | && !strncmp (file, prefix, strlen (prefix)) |
476 | | && !strcmp (file + len - strlen (suffix), suffix)) |
477 | | #else |
478 | | /* We load all the files ending with LIBEXT on OS/2, |
479 | | * because OS/2 has a 8.3 length limitation for DLL name */ |
480 | | if (len > strlen (LIBEXT) |
481 | | && !strcasecmp (file + len - strlen (LIBEXT), LIBEXT)) |
482 | | #endif |
483 | | AllocatePluginFile (bank, abspath, relpath, &st); |
484 | | } |
485 | | else if (S_ISDIR (st.st_mode)) |
486 | | { |
487 | | #ifdef __APPLE__ |
488 | | size_t len_name = strlen (file); |
489 | | const char *framework_extension = |
490 | | file + len_name - strlen(".framework"); |
491 | | |
492 | | if (len_name > strlen(".framework") |
493 | | && strcmp(framework_extension, ".framework") == 0) |
494 | | { |
495 | | AllocatePluginFramework (bank, file, relpath, abspath); |
496 | | /* Don't browse framework directories. */ |
497 | | goto skip; |
498 | | } |
499 | | #endif |
500 | | |
501 | | /* Recurse into another directory */ |
502 | | AllocatePluginDir (bank, maxdepth, abspath, relpath); |
503 | | } |
504 | | skip: |
505 | | free (relpath); |
506 | | free (abspath); |
507 | | } |
508 | | vlc_closedir (dh); |
509 | | } |
510 | | |
511 | | /** |
512 | | * Scans for plug-ins within a file system hierarchy. |
513 | | * \param path base directory to browse |
514 | | */ |
515 | | static void AllocatePluginPath(libvlc_int_t *obj, const char *path, |
516 | | cache_mode_t mode) |
517 | | { |
518 | | module_bank_t bank = |
519 | | { |
520 | | .obj = obj, |
521 | | .base = path, |
522 | | .mode = mode, |
523 | | }; |
524 | | |
525 | | if (mode & CACHE_READ_FILE) |
526 | | bank.cache = vlc_cache_load(obj, path, &modules.caches); |
527 | | else |
528 | | msg_Dbg(bank.obj, "ignoring plugins cache file"); |
529 | | |
530 | | if (mode & CACHE_SCAN_DIR) |
531 | | { |
532 | | msg_Dbg(obj, "recursively browsing `%s'", bank.base); |
533 | | |
534 | | /* Don't go deeper than 5 subdirectories */ |
535 | | AllocatePluginDir(&bank, 5, path, NULL); |
536 | | } |
537 | | |
538 | | /* Deal with unmatched cache entries from cache file */ |
539 | | while (bank.cache != NULL) |
540 | | { |
541 | | vlc_plugin_t *plugin = bank.cache; |
542 | | |
543 | | bank.cache = plugin->next; |
544 | | if (mode & CACHE_SCAN_DIR) |
545 | | vlc_plugin_destroy(plugin); |
546 | | else |
547 | | vlc_plugin_store(plugin); |
548 | | } |
549 | | |
550 | | if (mode & CACHE_WRITE_FILE) |
551 | | CacheSave(obj, path, bank.plugins, bank.size); |
552 | | |
553 | | free(bank.plugins); |
554 | | } |
555 | | |
556 | | #if defined(_WIN32) |
557 | | # if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) |
558 | | # define HAVE_FORCED_PLUGINS |
559 | | # endif |
560 | | #endif |
561 | | |
562 | | /** |
563 | | * Enumerates all dynamic plug-ins that can be found. |
564 | | * |
565 | | * This function will recursively browse the default plug-ins directory and any |
566 | | * directory listed in the VLC_PLUGIN_PATH environment variable. |
567 | | * For performance reasons, a cache is normally used so that plug-in shared |
568 | | * objects do not need to loaded and linked into the process. |
569 | | */ |
570 | | static void AllocateAllPlugins (libvlc_int_t *p_this) |
571 | | { |
572 | | char *paths; |
573 | | cache_mode_t mode = 0; |
574 | | |
575 | | if (var_InheritBool(p_this, "plugins-cache")) |
576 | | mode |= CACHE_READ_FILE; |
577 | | if (var_InheritBool(p_this, "plugins-scan")) |
578 | | mode |= CACHE_SCAN_DIR; |
579 | | if (var_InheritBool(p_this, "reset-plugins-cache")) |
580 | | mode = (mode | CACHE_WRITE_FILE) & ~CACHE_READ_FILE; |
581 | | |
582 | | #ifdef HAVE_FORCED_PLUGINS |
583 | | /* Windows Store Apps can not load external plugins with absolute paths. */ |
584 | | AllocatePluginPath (p_this, "plugins", mode); |
585 | | #else |
586 | | /* Construct the special search path for system that have a relocatable |
587 | | * executable. Set it to <vlc path>/plugins. */ |
588 | | char *vlcpath = config_GetSysPath(VLC_PKG_LIB_DIR, "plugins"); |
589 | | if (likely(vlcpath != NULL)) |
590 | | { |
591 | | AllocatePluginPath(p_this, vlcpath, mode); |
592 | | free(vlcpath); |
593 | | } |
594 | | #endif |
595 | | |
596 | | /* If the user provided a plugin path, we add it to the list */ |
597 | | paths = getenv( "VLC_PLUGIN_PATH" ); |
598 | | if( paths == NULL ) |
599 | | return; |
600 | | |
601 | | #ifdef _WIN32 |
602 | | paths = realpath( paths, NULL ); |
603 | | #else |
604 | | paths = strdup( paths ); /* don't harm the environment ! :) */ |
605 | | #endif |
606 | | if( unlikely(paths == NULL) ) |
607 | | return; |
608 | | |
609 | | for( char *buf, *path = strtok_r( paths, PATH_SEP, &buf ); |
610 | | path != NULL; |
611 | | path = strtok_r( NULL, PATH_SEP, &buf ) ) |
612 | | AllocatePluginPath (p_this, path, mode); |
613 | | |
614 | | free( paths ); |
615 | | } |
616 | | |
617 | | /** |
618 | | * Ensures that a plug-in is loaded. |
619 | | * |
620 | | * \note This function is thread-safe but not re-entrant. |
621 | | * |
622 | | * \return 0 on success, -1 on failure |
623 | | */ |
624 | | int vlc_plugin_Map(struct vlc_logger *log, vlc_plugin_t *plugin) |
625 | | { |
626 | | static vlc_mutex_t lock = VLC_STATIC_MUTEX; |
627 | | |
628 | | if (plugin->abspath == NULL) |
629 | | return 0; /* static module needs not be mapped */ |
630 | | if (atomic_load_explicit(&plugin->handle, memory_order_acquire)) |
631 | | return 0; /* fast path: already loaded */ |
632 | | |
633 | | /* Try to load the plug-in (without locks, so read-only) */ |
634 | | assert(plugin->abspath != NULL); |
635 | | |
636 | | void *handle = module_Open(log, plugin->abspath, false); |
637 | | if (handle == NULL) |
638 | | return -1; |
639 | | |
640 | | vlc_plugin_cb entry = vlc_dlsym(handle, "vlc_entry"); |
641 | | if (entry == NULL) |
642 | | { |
643 | | vlc_error(log, "cannot find plug-in entry point in %s", |
644 | | plugin->abspath); |
645 | | goto error; |
646 | | } |
647 | | |
648 | | vlc_mutex_lock(&lock); |
649 | | if (atomic_load_explicit(&plugin->handle, memory_order_relaxed) == 0) |
650 | | { /* Lock is held, update the plug-in structure */ |
651 | | if (vlc_plugin_resolve(plugin, entry)) |
652 | | { |
653 | | vlc_mutex_unlock(&lock); |
654 | | goto error; |
655 | | } |
656 | | |
657 | | atomic_store_explicit(&plugin->handle, (uintptr_t)handle, |
658 | | memory_order_release); |
659 | | } |
660 | | else /* Another thread won the race to load the plugin */ |
661 | | vlc_dlclose(handle); |
662 | | vlc_mutex_unlock(&lock); |
663 | | |
664 | | return 0; |
665 | | error: |
666 | | vlc_dlclose(handle); |
667 | | return -1; |
668 | | } |
669 | | |
670 | | /** |
671 | | * Ensures that a module is not loaded. |
672 | | * |
673 | | * \note This function is not thread-safe. The caller must ensure that the |
674 | | * plug-in is no longer used before calling this function. |
675 | | */ |
676 | | static void vlc_plugin_Unmap(vlc_plugin_t *plugin) |
677 | | { |
678 | | if (!plugin->unloadable) |
679 | | return; |
680 | | |
681 | | void *handle = (void *)atomic_exchange_explicit(&plugin->handle, 0, |
682 | | memory_order_acquire); |
683 | | if (handle != NULL) |
684 | | vlc_dlclose(handle); |
685 | | } |
686 | | |
687 | | void *vlc_plugin_Symbol(struct vlc_logger *log, |
688 | | vlc_plugin_t *plugin, const char *name) |
689 | | { |
690 | | if (plugin->abspath == NULL || vlc_plugin_Map(log, plugin)) |
691 | | return NULL; |
692 | | |
693 | | void *handle = (void *)atomic_load_explicit(&plugin->handle, |
694 | | memory_order_relaxed); |
695 | | assert(handle != NULL); |
696 | | return vlc_dlsym(handle, name); |
697 | | } |
698 | | #else |
699 | | int vlc_plugin_Map(struct vlc_logger *log, vlc_plugin_t *plugin) |
700 | 39.4M | { |
701 | 39.4M | (void) log; (void) plugin; |
702 | 39.4M | return 0; |
703 | 39.4M | } |
704 | | |
705 | | static void vlc_plugin_Unmap(vlc_plugin_t *plugin) |
706 | 0 | { |
707 | 0 | (void) plugin; |
708 | 0 | } |
709 | | |
710 | | void *vlc_plugin_Symbol(struct vlc_logger *log, |
711 | | vlc_plugin_t *plugin, const char *name) |
712 | 0 | { |
713 | 0 | (void) log; (void) plugin; (void) name; |
714 | 0 | return NULL; |
715 | 0 | } |
716 | | #endif /* HAVE_DYNAMIC_PLUGINS */ |
717 | | |
718 | | /** |
719 | | * Init bank |
720 | | * |
721 | | * Creates a module bank structure which will be filled later |
722 | | * on with all the modules found. |
723 | | */ |
724 | | void module_InitBank (void) |
725 | 48 | { |
726 | 48 | vlc_mutex_lock (&modules.lock); |
727 | | |
728 | 48 | if (modules.usage == 0) |
729 | 48 | { |
730 | | /* Fills the module bank structure with the core module infos. |
731 | | * This is very useful as it will allow us to consider the core |
732 | | * library just as another module, and for instance the configuration |
733 | | * options of core will be available in the module bank structure just |
734 | | * as for every other module. */ |
735 | 48 | vlc_plugin_t *plugin = module_InitStatic(VLC_MODULE_ENTRY(core)); |
736 | 48 | if (likely(plugin != NULL)) |
737 | 48 | vlc_plugin_store(plugin); |
738 | 48 | config_SortConfig (); |
739 | 48 | } |
740 | 48 | modules.usage++; |
741 | | |
742 | | /* We do retain the module bank lock until the plugins are loaded as well. |
743 | | * This is ugly, this staged loading approach is needed: LibVLC gets |
744 | | * some configuration parameters relevant to loading the plugins from |
745 | | * the core (builtin) module. The module bank becomes shared read-only data |
746 | | * once it is ready, so we need to fully serialize initialization. |
747 | | * DO NOT UNCOMMENT the following line unless you managed to squeeze |
748 | | * module_LoadPlugins() before you unlock the mutex. */ |
749 | | /*vlc_mutex_unlock (&modules.lock);*/ |
750 | 48 | } |
751 | | |
752 | | /** |
753 | | * Unloads all unused plugin modules and empties the module |
754 | | * bank in case of success. |
755 | | */ |
756 | | void module_EndBank (bool b_plugins) |
757 | 0 | { |
758 | 0 | vlc_plugin_t *libs = NULL; |
759 | 0 | block_t *caches = NULL; |
760 | 0 | void *caps_tree = NULL; |
761 | | |
762 | | /* If plugins were _not_ loaded, then the caller still has the bank lock |
763 | | * from module_InitBank(). */ |
764 | 0 | if( b_plugins ) |
765 | 0 | vlc_mutex_lock (&modules.lock); |
766 | 0 | else |
767 | 0 | vlc_mutex_assert(&modules.lock); |
768 | |
|
769 | 0 | assert (modules.usage > 0); |
770 | 0 | if (--modules.usage == 0) |
771 | 0 | { |
772 | 0 | config_UnsortConfig (); |
773 | 0 | libs = vlc_plugins; |
774 | 0 | caches = modules.caches; |
775 | 0 | caps_tree = modules.caps_tree; |
776 | 0 | vlc_plugins = NULL; |
777 | 0 | modules.caches = NULL; |
778 | 0 | modules.caps_tree = NULL; |
779 | 0 | modules.count = 0; |
780 | 0 | } |
781 | 0 | vlc_mutex_unlock (&modules.lock); |
782 | |
|
783 | 0 | tdestroy(caps_tree, vlc_modcap_free); |
784 | |
|
785 | 0 | while (libs != NULL) |
786 | 0 | { |
787 | 0 | vlc_plugin_t *lib = libs; |
788 | |
|
789 | 0 | libs = lib->next; |
790 | 0 | vlc_plugin_Unmap(lib); |
791 | 0 | vlc_plugin_destroy(lib); |
792 | 0 | } |
793 | |
|
794 | 0 | block_ChainRelease(caches); |
795 | 0 | } |
796 | | |
797 | | /** |
798 | | * Loads module descriptions for all available plugins. |
799 | | * Fills the module bank structure with the plugin modules. |
800 | | * |
801 | | * \param obj vlc object structure |
802 | | */ |
803 | | void module_LoadPlugins(libvlc_int_t *obj) |
804 | 48 | { |
805 | | /*vlc_mutex_assert (&modules.lock); not for static mutexes :( */ |
806 | | |
807 | 48 | if (modules.usage == 1) |
808 | 48 | { |
809 | 48 | module_InitStaticModules (); |
810 | | #ifdef HAVE_DYNAMIC_PLUGINS |
811 | | msg_Dbg (obj, "searching plug-in modules"); |
812 | | AllocateAllPlugins (obj); |
813 | | #endif |
814 | 48 | config_UnsortConfig (); |
815 | 48 | config_SortConfig (); |
816 | | |
817 | 48 | twalk(modules.caps_tree, vlc_modcap_sort); |
818 | 48 | } |
819 | 48 | vlc_mutex_unlock (&modules.lock); |
820 | | |
821 | 48 | msg_Dbg (obj, "plug-ins loaded: %zu modules", modules.count); |
822 | 48 | } |
823 | | |
824 | | void module_list_free (module_t **list) |
825 | 0 | { |
826 | 0 | free (list); |
827 | 0 | } |
828 | | |
829 | | module_t **module_list_get (size_t *n) |
830 | 0 | { |
831 | 0 | assert (n != NULL); |
832 | 0 | *n = modules.count; |
833 | |
|
834 | 0 | if (unlikely(modules.count == 0)) |
835 | 0 | return NULL; |
836 | | |
837 | 0 | module_t **tab = malloc(modules.count * sizeof(*tab)); |
838 | 0 | if (unlikely(tab == NULL)) |
839 | 0 | return NULL; |
840 | | |
841 | 0 | size_t i = 0; |
842 | 0 | for (vlc_plugin_t *lib = vlc_plugins; lib != NULL; lib = lib->next) |
843 | 0 | { |
844 | 0 | for (module_t *m = lib->module; m != NULL; m = m->next) |
845 | 0 | { |
846 | 0 | assert(i < modules.count); |
847 | 0 | tab[i++] = m; |
848 | 0 | } |
849 | 0 | } |
850 | 0 | return tab; |
851 | 0 | } |
852 | | |
853 | | size_t module_list_cap(module_t *const **restrict list, const char *name) |
854 | 5.10M | { |
855 | 5.10M | vlc_modcap_t key; |
856 | | |
857 | 5.10M | assert(name != NULL); |
858 | 5.10M | key.name = (char *)name; |
859 | | |
860 | 5.10M | const void **cp = tfind(&key, &modules.caps_tree, vlc_modcap_cmp); |
861 | 5.10M | if (cp == NULL) |
862 | 96 | { |
863 | 96 | *list = NULL; |
864 | 96 | return 0; |
865 | 96 | } |
866 | | |
867 | 5.10M | const vlc_modcap_t *cap = *cp; |
868 | | |
869 | 5.10M | *list = cap->modv; |
870 | 5.10M | return cap->modc; |
871 | 5.10M | } |