Coverage Report

Created: 2025-07-11 07:16

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