bank.c 19.1 KB
Newer Older
1
2
3
/*****************************************************************************
 * bank.c : Modules list
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2011 VLC authors and VideoLAN
5
6
7
8
9
10
11
 *
 * Authors: Sam Hocevar <sam@zoy.org>
 *          Ethan C. Baldridge <BaldridgeE@cadmus.com>
 *          Hans-Peter Jansen <hpj@urpla.net>
 *          Gildas Bazin <gbazin@videolan.org>
 *          Rémi Denis-Courmont
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
12
13
14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15
16
17
18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
19
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
22
23
24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
30
31
32
33
34
35
36
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
37
#include <sys/stat.h>
38
#include <unistd.h>
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_modules.h>
#include <vlc_fs.h>
#include "libvlc.h"
#include "config/configuration.h"
#include "modules/modules.h"

static struct
{
    vlc_mutex_t lock;
    module_t *head;
    unsigned usage;
} modules = { VLC_STATIC_MUTEX, NULL, 0 };

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
#ifdef HAVE_DYNAMIC_PLUGINS
static void AllocateAllPlugins (vlc_object_t *);
#endif
static module_t *module_InitStatic (vlc_plugin_cb);

static void module_StoreBank (module_t *module)
{
    /*vlc_assert_locked (&modules.lock);*/
    module->next = modules.head;
    modules.head = module;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
70
#if defined(__ELF__) || !HAVE_DYNAMIC_PLUGINS
71
# ifdef __GNUC__
72
__attribute__((weak))
73
74
75
# else
#  pragma weak vlc_static_modules
# endif
76
77
78
79
80
81
82
83
84
85
86
87
88
extern vlc_plugin_cb vlc_static_modules[];

static void module_InitStaticModules(void)
{
    if (!vlc_static_modules)
        return;

    for (unsigned i = 0; vlc_static_modules[i]; i++) {
        module_t *module = module_InitStatic (vlc_static_modules[i]);
        if (likely(module != NULL))
            module_StoreBank (module);
    }
}
89
90
91
#else
static void module_InitStaticModules(void) { }
#endif
92

93
94
95
96
97
98
99
100
101
102
103
104
/**
 * Init bank
 *
 * Creates a module bank structure which will be filled later
 * on with all the modules found.
 */
void module_InitBank (void)
{
    vlc_mutex_lock (&modules.lock);

    if (modules.usage == 0)
    {
Rafaël Carré's avatar
Rafaël Carré committed
105
106
        /* Fills the module bank structure with the core module infos.
         * This is very useful as it will allow us to consider the core
107
         * library just as another module, and for instance the configuration
Rafaël Carré's avatar
Rafaël Carré committed
108
         * options of core will be available in the module bank structure just
109
         * as for every other module. */
Rafaël Carré's avatar
Rafaël Carré committed
110
        module_t *module = module_InitStatic (vlc_entry__core);
111
112
113
114
115
116
117
118
119
        if (likely(module != NULL))
            module_StoreBank (module);
        config_SortConfig ();
    }
    modules.usage++;

    /* We do retain the module bank lock until the plugins are loaded as well.
     * This is ugly, this staged loading approach is needed: LibVLC gets
     * some configuration parameters relevant to loading the plugins from
Rafaël Carré's avatar
Rafaël Carré committed
120
     * the core (builtin) module. The module bank becomes shared read-only data
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
     * once it is ready, so we need to fully serialize initialization.
     * DO NOT UNCOMMENT the following line unless you managed to squeeze
     * module_LoadPlugins() before you unlock the mutex. */
    /*vlc_mutex_unlock (&modules.lock);*/
}

/**
 * Unloads all unused plugin modules and empties the module
 * bank in case of success.
 */
void module_EndBank (bool b_plugins)
{
    module_t *head = NULL;

    /* If plugins were _not_ loaded, then the caller still has the bank lock
     * from module_InitBank(). */
    if( b_plugins )
        vlc_mutex_lock (&modules.lock);
    /*else
        vlc_assert_locked (&modules.lock); not for static mutexes :( */

    assert (modules.usage > 0);
    if (--modules.usage == 0)
    {
        config_UnsortConfig ();
        head = modules.head;
        modules.head = NULL;
    }
    vlc_mutex_unlock (&modules.lock);

    while (head != NULL)
    {
        module_t *module = head;

        head = module->next;
#ifdef HAVE_DYNAMIC_PLUGINS
        if (module->b_loaded && module->b_unloadable)
        {
            module_Unload (module->handle);
            module->b_loaded = false;
        }
#endif
        vlc_module_destroy (module);
    }
}

#undef module_LoadPlugins
/**
 * Loads module descriptions for all available plugins.
 * Fills the module bank structure with the plugin modules.
 *
 * \param p_this vlc object structure
173
 * \return total number of modules in bank after loading all plug-ins
174
 */
175
size_t module_LoadPlugins (vlc_object_t *obj)
176
177
178
179
180
{
    /*vlc_assert_locked (&modules.lock); not for static mutexes :( */

    if (modules.usage == 1)
    {
181
182
        module_InitStaticModules ();
#ifdef HAVE_DYNAMIC_PLUGINS
183
184
        msg_Dbg (obj, "searching plug-in modules");
        AllocateAllPlugins (obj);
185
#endif
186
187
188
189
        config_UnsortConfig ();
        config_SortConfig ();
    }
    vlc_mutex_unlock (&modules.lock);
190
191
192
193
194
195

    size_t count;
    module_t **list = module_list_get (&count);
    module_list_free (list);
    msg_Dbg (obj, "plug-ins loaded: %zu modules", count);
    return count;
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
}

/**
 * Frees the flat list of VLC modules.
 * @param list list obtained by module_list_get()
 * @param length number of items on the list
 * @return nothing.
 */
void module_list_free (module_t **list)
{
    free (list);
}

/**
 * Gets the flat list of VLC modules.
211
212
213
 * @param n [OUT] pointer to the number of modules
 * @return table of module pointers (release with module_list_free()),
 *         or NULL in case of error (in that case, *n is zeroed).
214
215
216
217
218
219
 */
module_t **module_list_get (size_t *n)
{
    module_t **tab = NULL;
    size_t i = 0;

220
221
    assert (n != NULL);

222
223
224
    for (module_t *mod = modules.head; mod; mod = mod->next)
    {
         module_t **nt;
225
226
         nt  = realloc (tab, (i + 1 + mod->submodule_count) * sizeof (*tab));
         if (unlikely(nt == NULL))
227
         {
228
229
             free (tab);
             *n = 0;
230
231
232
233
234
235
236
237
             return NULL;
         }

         tab = nt;
         tab[i++] = mod;
         for (module_t *subm = mod->submodule; subm; subm = subm->next)
             tab[i++] = subm;
    }
238
    *n = i;
239
240
241
    return tab;
}

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
static int modulecmp (const void *a, const void *b)
{
    const module_t *const *ma = a, *const *mb = b;
    /* Note that qsort() uses _ascending_ order,
     * so the smallest module is the one with the biggest score. */
    return (*mb)->i_score - (*ma)->i_score;
}

/**
 * Builds a sorted list of all VLC modules with a given capability.
 * The list is sorted from the highest module score to the lowest.
 * @param list pointer to the table of modules [OUT]
 * @param cap capability of modules to look for
 * @return the number of matching found, or -1 on error (*list is then NULL).
 * @note *list must be freed with module_list_free().
 */
ssize_t module_list_cap (module_t ***restrict list, const char *cap)
{
    /* TODO: This is quite inefficient. List should be sorted by capability. */
    ssize_t n = 0;

    assert (list != NULL);

    for (module_t *mod = modules.head; mod != NULL; mod = mod->next)
    {
         if (module_provides (mod, cap))
             n++;
         for (module_t *subm = mod->submodule; subm != NULL; subm = subm->next)
             if (module_provides (subm, cap))
                 n++;
    }

    module_t **tab = malloc (sizeof (*tab) * n);
    *list = tab;
    if (unlikely(tab == NULL))
        return -1;

    for (module_t *mod = modules.head; mod != NULL; mod = mod->next)
    {
         if (module_provides (mod, cap))
             *(tab++)= mod;
         for (module_t *subm = mod->submodule; subm != NULL; subm = subm->next)
             if (module_provides (subm, cap))
                 *(tab++) = subm;
    }

    assert (tab == *list + n);
    qsort (*list, n, sizeof (*tab), modulecmp);
    return n;
}

293
#ifdef HAVE_DYNAMIC_PLUGINS
294
typedef enum { CACHE_USE, CACHE_RESET, CACHE_IGNORE } cache_mode_t;
295

296
297
298
299
300
301
302
303
304
305
static void AllocatePluginPath (vlc_object_t *, const char *, cache_mode_t);

/**
 * Enumerates all dynamic plug-ins that can be found.
 *
 * This function will recursively browse the default plug-ins directory and any
 * directory listed in the VLC_PLUGIN_PATH environment variable.
 * For performance reasons, a cache is normally used so that plug-in shared
 * objects do not need to loaded and linked into the process.
 */
306
307
308
309
310
311
312
313
314
315
316
317
static void AllocateAllPlugins (vlc_object_t *p_this)
{
    char *paths;
    cache_mode_t mode;

    if( !var_InheritBool( p_this, "plugins-cache" ) )
        mode = CACHE_IGNORE;
    else if( var_InheritBool( p_this, "reset-plugins-cache" ) )
        mode = CACHE_RESET;
    else
        mode = CACHE_USE;

318
#if VLC_WINSTORE_APP
319
320
321
    /* Windows Store Apps can not load external plugins with absolute paths. */
    AllocatePluginPath (p_this, "plugins", mode);
#else
322
323
    /* Contruct the special search path for system that have a relocatable
     * executable. Set it to <vlc path>/plugins. */
324
325
326
    char *vlcpath = config_GetLibDir ();
    if (likely(vlcpath != NULL)
     && likely(asprintf (&paths, "%s" DIR_SEP "plugins", vlcpath) != -1))
327
328
329
330
    {
        AllocatePluginPath (p_this, paths, mode);
        free( paths );
    }
331
    free (vlcpath);
332
#endif /* VLC_WINSTORE_APP */
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

    /* If the user provided a plugin path, we add it to the list */
    paths = getenv( "VLC_PLUGIN_PATH" );
    if( paths == NULL )
        return;

    paths = strdup( paths ); /* don't harm the environment ! :) */
    if( unlikely(paths == NULL) )
        return;

    for( char *buf, *path = strtok_r( paths, PATH_SEP, &buf );
         path != NULL;
         path = strtok_r( NULL, PATH_SEP, &buf ) )
        AllocatePluginPath (p_this, path, mode);

    free( paths );
}

351
typedef struct module_bank
352
{
353
354
355
356
    vlc_object_t *obj;
    const char   *base;
    cache_mode_t  mode;

357
358
359
360
361
    size_t         i_cache;
    module_cache_t *cache;

    int            i_loaded_cache;
    module_cache_t *loaded_cache;
362
363
364
365
} module_bank_t;

static void AllocatePluginDir (module_bank_t *, unsigned,
                               const char *, const char *);
366

367
368
369
370
/**
 * Scans for plug-ins within a file system hierarchy.
 * \param path base directory to browse
 */
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
static void AllocatePluginPath (vlc_object_t *p_this, const char *path,
                                cache_mode_t mode)
{
    module_bank_t bank;
    module_cache_t *cache = NULL;
    size_t count = 0;

    switch( mode )
    {
        case CACHE_USE:
            count = CacheLoad( p_this, path, &cache );
            break;
        case CACHE_RESET:
            CacheDelete( p_this, path );
            break;
        case CACHE_IGNORE:
            msg_Dbg( p_this, "ignoring plugins cache file" );
    }

    msg_Dbg( p_this, "recursively browsing `%s'", path );

392
393
394
    bank.obj = p_this;
    bank.base = path;
    bank.mode = mode;
395
396
397
398
399
400
    bank.cache = NULL;
    bank.i_cache = 0;
    bank.loaded_cache = cache;
    bank.i_loaded_cache = count;

    /* Don't go deeper than 5 subdirectories */
401
    AllocatePluginDir (&bank, 5, path, NULL);
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420

    switch( mode )
    {
        case CACHE_USE:
            /* Discard unmatched cache entries */
            for( size_t i = 0; i < count; i++ )
            {
                if (cache[i].p_module != NULL)
                   vlc_module_destroy (cache[i].p_module);
                free (cache[i].path);
            }
            free( cache );
        case CACHE_RESET:
            CacheSave (p_this, path, bank.cache, bank.i_cache);
        case CACHE_IGNORE:
            break;
    }
}

421
422
423
424
425
426
427
428
static int AllocatePluginFile (module_bank_t *, const char *,
                               const char *, const struct stat *);

/**
 * Recursively browses a directory to look for plug-ins.
 */
static void AllocatePluginDir (module_bank_t *bank, unsigned maxdepth,
                               const char *absdir, const char *reldir)
429
{
430
    if (maxdepth == 0)
431
        return;
432
    maxdepth--;
433

434
    DIR *dh = vlc_opendir (absdir);
435
436
437
438
439
440
    if (dh == NULL)
        return;

    /* Parse the directory and try to load all files it contains. */
    for (;;)
    {
441
        char *file = vlc_readdir (dh), *relpath = NULL, *abspath = NULL;
442
443
444
445
446
        if (file == NULL)
            break;

        /* Skip ".", ".." */
        if (!strcmp (file, ".") || !strcmp (file, ".."))
447
            continue;
448
449
450

        /* Compute path relative to plug-in base directory */
        if (reldir != NULL)
451
        {
452
453
            if (asprintf (&relpath, "%s"DIR_SEP"%s", reldir, file) == -1)
                relpath = NULL;
454
        }
455
456
457
        else
            relpath = strdup (file);
        if (unlikely(relpath == NULL))
458
            continue;
459

460
461
462
463
464
465
466
467
468
469
        /* Compute absolute path */
        if (asprintf (&abspath, "%s"DIR_SEP"%s", bank->base, relpath) == -1)
        {
            abspath = NULL;
            goto skip;
        }

        struct stat st;
        if (vlc_stat (abspath, &st) == -1)
            goto skip;
470

471
472
473
474
475
476
        if (S_ISREG (st.st_mode))
        {
            static const char prefix[] = "lib";
            static const char suffix[] = "_plugin"LIBEXT;
            size_t len = strlen (file);

477
#ifndef __OS2__
478
479
480
481
            /* Check that file matches the "lib*_plugin"LIBEXT pattern */
            if (len > strlen (suffix)
             && !strncmp (file, prefix, strlen (prefix))
             && !strcmp (file + len - strlen (suffix), suffix))
482
483
484
485
486
487
#else
            /* We load all the files ending with LIBEXT on OS/2,
             * because OS/2 has a 8.3 length limitation for DLL name */
            if (len > strlen (LIBEXT)
             && !strcasecmp (file + len - strlen (LIBEXT), LIBEXT))
#endif
488
489
490
                AllocatePluginFile (bank, abspath, relpath, &st);
        }
        else if (S_ISDIR (st.st_mode))
491
            /* Recurse into another directory */
492
493
494
495
            AllocatePluginDir (bank, maxdepth, abspath, relpath);
    skip:
        free (relpath);
        free (abspath);
496
497
498
499
    }
    closedir (dh);
}

500
501
502
503
504
505
506
static module_t *module_InitDynamic (vlc_object_t *, const char *, bool);

/**
 * Scans a plug-in from a file.
 */
static int AllocatePluginFile (module_bank_t *bank, const char *abspath,
                               const char *relpath, const struct stat *st)
507
{
508
    module_t *module = NULL;
509
510

    /* Check our plugins cache first then load plugin if needed */
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
    if (bank->mode == CACHE_USE)
    {
        module = CacheFind (bank->loaded_cache, bank->i_loaded_cache,
                            relpath, st);
        if (module != NULL)
        {
            module->psz_filename = strdup (abspath);
            if (unlikely(module->psz_filename == NULL))
            {
                vlc_module_destroy (module);
                module = NULL;
            }
        }
    }
    if (module == NULL)
        module = module_InitDynamic (bank->obj, abspath, true);
    if (module == NULL)
528
529
530
        return -1;

    /* We have not already scanned and inserted this module */
531
    assert (module->next == NULL);
532
533

    /* Unload plugin until we really need it */
534
    if (module->b_loaded && module->b_unloadable)
535
    {
536
537
        module_Unload (module->handle);
        module->b_loaded = false;
538
539
    }

540
    /* For now we force loading if the module's config contains callbacks.
541
     * Could be optimized by adding an API call.*/
542
    for (size_t n = module->confsize, i = 0; i < n; i++)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
543
         if (module->p_config[i].list_count == 0
544
          && (module->p_config[i].list.psz_cb != NULL || module->p_config[i].list.i_cb != NULL))
545
546
         {
             /* !unloadable not allowed for plugins with callbacks */
547
548
             vlc_module_destroy (module);
             module = module_InitDynamic (bank->obj, abspath, false);
549
550
             if (unlikely(module == NULL))
                 return -1;
551
552
553
             break;
         }

554
    module_StoreBank (module);
555

556
557
    if (bank->mode != CACHE_IGNORE) /* Add entry to cache */
        CacheAdd (&bank->cache, &bank->i_cache, relpath, st, module);
558
559
560
561
    /* TODO: deal with errors */
    return  0;
}

562
563
564
565
566
567
568
#ifdef __OS2__
#   define EXTERN_PREFIX "_"
#else
#   define EXTERN_PREFIX
#endif


569
570
571
572
573
574
575
576
577
/**
 * Loads a dynamically-linked plug-in into memory and initialize it.
 *
 * The module can then be handled by module_need() and module_unneed().
 *
 * \param path file path of the shared object
 * \param fast whether to optimize loading for speed or safety
 *             (fast is used when the plug-in is registered but not used)
 */
578
579
static module_t *module_InitDynamic (vlc_object_t *obj, const char *path,
                                     bool fast)
580
581
582
583
584
585
586
{
    module_handle_t handle;

    if (module_Load (obj, path, &handle, fast))
        return NULL;

    /* Try to resolve the symbol */
587
    static const char entry_name[] = EXTERN_PREFIX "vlc_entry" MODULE_SUFFIX;
588
589
590
591
592
593
594
595
596
    vlc_plugin_cb entry =
        (vlc_plugin_cb) module_Lookup (handle, entry_name);
    if (entry == NULL)
    {
        msg_Warn (obj, "cannot find plug-in entry point in %s", path);
        goto error;
    }

    /* We can now try to call the symbol */
597
    module_t *module = vlc_plugin_describe (entry);
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
    if (unlikely(module == NULL))
    {
        /* With a well-written module we shouldn't have to print an
         * additional error message here, but just make sure. */
        msg_Err (obj, "cannot initialize plug-in %s", path);
        goto error;
    }

    module->psz_filename = strdup (path);
    if (unlikely(module->psz_filename == NULL))
    {
        vlc_module_destroy (module);
        goto error;
    }
    module->handle = handle;
    module->b_loaded = true;
    return module;
error:
    module_Unload( handle );
    return NULL;
}
#endif /* HAVE_DYNAMIC_PLUGINS */

/**
 * Registers a statically-linked plug-in.
 */
static module_t *module_InitStatic (vlc_plugin_cb entry)
{
    /* Initializes the module */
627
628
    module_t *module = vlc_plugin_describe (entry);
    if (unlikely(module == NULL))
629
630
631
632
633
634
635
636
637
638
639
640
641
        return NULL;

    module->b_loaded = true;
    module->b_unloadable = false;
    return module;
}

/**
 * Makes sure the module is loaded in memory.
 * \return 0 on success, -1 on failure
 */
int module_Map (vlc_object_t *obj, module_t *module)
{
642
643
    static vlc_mutex_t lock = VLC_STATIC_MUTEX;

644
645
646
    if (module->parent != NULL)
        module = module->parent;

647
648
649
650
    vlc_mutex_lock(&lock);
    if (!module->b_loaded)
    {
        module_t *uncache;
651

652
        assert (module->psz_filename != NULL);
653
#ifdef HAVE_DYNAMIC_PLUGINS
654
655
656
657
658
659
660
        uncache = module_InitDynamic (obj, module->psz_filename, false);
        if (uncache != NULL)
        {
            CacheMerge (obj, module, uncache);
            vlc_module_destroy (uncache);
        }
        else
661
#endif
662
663
664
665
666
667
668
        {
            msg_Err (obj, "corrupt module: %s", module->psz_filename);
            module = NULL;
        }
    }
    vlc_mutex_unlock(&lock);
    return -(module == NULL);
669
}