Coverage Report

Created: 2026-08-14 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/config/core.c
Line
Count
Source
1
/*****************************************************************************
2
 * core.c management of the modules configuration
3
 *****************************************************************************
4
 * Copyright (C) 2001-2007 VLC authors and VideoLAN
5
 *
6
 * Authors: Gildas Bazin <gbazin@videolan.org>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
#ifdef HAVE_CONFIG_H
24
# include "config.h"
25
#endif
26
27
#include <stdatomic.h>
28
#include <vlc_common.h>
29
#include <vlc_actions.h>
30
#include <vlc_modules.h>
31
#include <vlc_plugin.h>
32
#include <vlc_threads.h>
33
34
#include "vlc_configuration.h"
35
36
#include <errno.h>
37
#include <assert.h>
38
39
#include "configuration.h"
40
#include "modules/modules.h"
41
#include "misc/rcu.h"
42
43
static vlc_mutex_t config_lock = VLC_STATIC_MUTEX;
44
static atomic_bool config_dirty = false;
45
46
void config_Lock(void)
47
0
{
48
0
    vlc_mutex_lock(&config_lock);
49
0
}
50
51
void config_Unlock(void)
52
0
{
53
0
    vlc_mutex_unlock(&config_lock);
54
0
}
55
56
int config_GetType(const char *name)
57
0
{
58
0
    const struct vlc_param *param = vlc_param_Find(name);
59
60
0
    if (param == NULL)
61
0
        return 0;
62
63
0
    switch (CONFIG_CLASS(param->item.i_type))
64
0
    {
65
0
        case CONFIG_ITEM_FLOAT:
66
0
            return VLC_VAR_FLOAT;
67
0
        case CONFIG_ITEM_INTEGER:
68
0
            return VLC_VAR_INTEGER;
69
0
        case CONFIG_ITEM_BOOL:
70
0
            return VLC_VAR_BOOL;
71
0
        case CONFIG_ITEM_STRING:
72
0
            return VLC_VAR_STRING;
73
0
        default:
74
0
            return 0;
75
0
    }
76
0
}
77
78
bool config_IsSafe( const char *name )
79
0
{
80
0
    const struct vlc_param *param = vlc_param_Find(name);
81
82
0
    return (param != NULL) ? param->safe : false;
83
0
}
84
85
int64_t config_GetInt(const char *name)
86
3.37M
{
87
3.37M
    const struct vlc_param *param = vlc_param_Find(name);
88
89
    /* sanity checks */
90
3.37M
    assert(param != NULL);
91
3.37M
    assert(IsConfigIntegerType(param->item.i_type));
92
93
3.37M
    return atomic_load_explicit(&param->value.i, memory_order_relaxed);
94
3.37M
}
95
96
float config_GetFloat(const char *name)
97
17.5k
{
98
17.5k
    const struct vlc_param *param = vlc_param_Find(name);
99
100
    /* sanity checks */
101
17.5k
    assert(param != NULL);
102
17.5k
    assert(IsConfigFloatType(param->item.i_type));
103
104
17.5k
    return atomic_load_explicit(&param->value.f, memory_order_relaxed);
105
17.5k
}
106
107
char *config_GetPsz(const char *name)
108
1.30M
{
109
1.30M
    const struct vlc_param *param = vlc_param_Find(name);
110
1.30M
    char *str;
111
112
    /* sanity checks */
113
1.30M
    assert(param != NULL);
114
1.30M
    assert(IsConfigStringType(param->item.i_type));
115
116
    /* return a copy of the string */
117
1.30M
    vlc_rcu_read_lock();
118
1.30M
    str = atomic_load_explicit(&param->value.str, memory_order_acquire);
119
1.30M
    if (str != NULL)
120
29.5k
        str = strdup(str);
121
1.30M
    vlc_rcu_read_unlock();
122
1.30M
    return str;
123
1.30M
}
124
125
int vlc_param_SetString(struct vlc_param *param, const char *value)
126
28.5k
{
127
28.5k
    char *str = NULL, *oldstr;
128
129
28.5k
    assert(param != NULL);
130
28.5k
    assert(IsConfigStringType(param->item.i_type));
131
132
28.5k
    if (value != NULL && value[0] != '\0') {
133
11.8k
        str = strdup(value);
134
11.8k
        if (unlikely(str == NULL))
135
0
            return -1;
136
11.8k
    }
137
138
28.5k
    oldstr = atomic_load_explicit(&param->value.str, memory_order_relaxed);
139
28.5k
    atomic_store_explicit(&param->value.str, str, memory_order_release);
140
28.5k
    param->item.value.psz = str;
141
28.5k
    vlc_rcu_synchronize();
142
28.5k
    free(oldstr);
143
28.5k
    return 0;
144
28.5k
}
145
146
void config_PutPsz(const char *psz_name, const char *psz_value)
147
0
{
148
0
    vlc_mutex_lock(&config_lock);
149
0
    vlc_param_SetString(vlc_param_Find(psz_name), psz_value);
150
0
    vlc_mutex_unlock(&config_lock);
151
0
    atomic_store_explicit(&config_dirty, true, memory_order_release);
152
0
}
153
154
void config_PutInt(const char *name, int64_t i_value)
155
0
{
156
0
    struct vlc_param *param = vlc_param_Find(name);
157
0
    module_config_t *p_config = &param->item;
158
159
    /* sanity checks */
160
0
    assert(param != NULL);
161
0
    assert(IsConfigIntegerType(param->item.i_type));
162
163
0
    if (i_value < p_config->min.i)
164
0
        i_value = p_config->min.i;
165
0
    if (i_value > p_config->max.i)
166
0
        i_value = p_config->max.i;
167
168
0
    atomic_store_explicit(&param->value.i, i_value, memory_order_relaxed);
169
0
    vlc_mutex_lock(&config_lock);
170
0
    p_config->value.i = i_value;
171
0
    vlc_mutex_unlock(&config_lock);
172
0
    atomic_store_explicit(&config_dirty, true, memory_order_release);
173
0
}
174
175
void config_PutFloat(const char *name, float f_value)
176
0
{
177
0
    struct vlc_param *param = vlc_param_Find(name);
178
0
    module_config_t *p_config = &param->item;
179
180
    /* sanity checks */
181
0
    assert(param != NULL);
182
0
    assert(IsConfigFloatType(param->item.i_type));
183
184
    /* if f_min == f_max == 0, then do not use them */
185
0
    if ((p_config->min.f == 0.f) && (p_config->max.f == 0.f))
186
0
        ;
187
0
    else if (f_value < p_config->min.f)
188
0
        f_value = p_config->min.f;
189
0
    else if (f_value > p_config->max.f)
190
0
        f_value = p_config->max.f;
191
192
0
    atomic_store_explicit(&param->value.f, f_value, memory_order_relaxed);
193
0
    vlc_mutex_lock(&config_lock);
194
0
    p_config->value.f = f_value;
195
0
    vlc_mutex_unlock(&config_lock);
196
0
    atomic_store_explicit(&config_dirty, true, memory_order_release);
197
0
}
198
199
ssize_t config_GetIntChoices(const char *name,
200
                             int64_t **restrict values, char ***restrict texts)
201
0
{
202
0
    *values = NULL;
203
0
    *texts = NULL;
204
205
0
    struct vlc_param *param = vlc_param_Find(name);
206
0
    if (param == NULL)
207
0
    {
208
0
        errno = ENOENT;
209
0
        return -1;
210
0
    }
211
212
0
    module_config_t *cfg = &param->item;
213
0
    size_t count = cfg->list_count;
214
0
    if (count == 0)
215
0
    {
216
0
        int (*cb)(const char *, int64_t **, char ***);
217
218
0
        cb = vlc_plugin_Symbol(NULL, param->owner, "vlc_entry_cfg_int_enum");
219
0
        if (cb == NULL)
220
0
            return 0;
221
222
0
        return cb(name, values, texts);
223
0
    }
224
225
0
    int64_t *vals = vlc_alloc (count, sizeof (*vals));
226
0
    char **txts = vlc_alloc (count, sizeof (*txts));
227
0
    if (vals == NULL || txts == NULL)
228
0
    {
229
0
        errno = ENOMEM;
230
0
        goto error;
231
0
    }
232
233
0
    for (size_t i = 0; i < count; i++)
234
0
    {
235
0
        vals[i] = cfg->list.i[i];
236
        /* FIXME: use module_gettext() instead */
237
0
        txts[i] = strdup ((cfg->list_text[i] != NULL)
238
0
                                       ? vlc_gettext (cfg->list_text[i]) : "");
239
0
        if (unlikely(txts[i] == NULL))
240
0
        {
241
0
            for (int j = i - 1; j >= 0; --j)
242
0
                free(txts[j]);
243
0
            errno = ENOMEM;
244
0
            goto error;
245
0
        }
246
0
    }
247
248
0
    *values = vals;
249
0
    *texts = txts;
250
0
    return count;
251
0
error:
252
253
0
    free(vals);
254
0
    free(txts);
255
0
    return -1;
256
0
}
257
258
259
static ssize_t config_ListModules (const char *cap, char ***restrict values,
260
                                   char ***restrict texts)
261
0
{
262
0
    module_t *const *list;
263
0
    size_t n = module_list_cap(&list, cap);
264
0
    char **vals = malloc ((n + 2) * sizeof (*vals));
265
0
    char **txts = malloc ((n + 2) * sizeof (*txts));
266
0
    if (!vals || !txts)
267
0
    {
268
0
        free (vals);
269
0
        free (txts);
270
0
        *values = *texts = NULL;
271
0
        return -1;
272
0
    }
273
274
0
    size_t i = 0;
275
276
0
    vals[i] = strdup ("any");
277
0
    txts[i] = strdup (_("Automatic"));
278
0
    if (!vals[i] || !txts[i])
279
0
        goto error;
280
281
0
    ++i;
282
0
    for (; i <= n; i++)
283
0
    {
284
0
        vals[i] = strdup (module_get_object (list[i - 1]));
285
0
        txts[i] = strdup (module_gettext (list[i - 1],
286
0
                               module_GetLongName (list[i - 1])));
287
0
        if( !vals[i] || !txts[i])
288
0
            goto error;
289
0
    }
290
0
    vals[i] = strdup ("none");
291
0
    txts[i] = strdup (_("Disable"));
292
0
    if( !vals[i] || !txts[i])
293
0
        goto error;
294
295
0
    *values = vals;
296
0
    *texts = txts;
297
0
    return i + 1;
298
299
0
error:
300
0
    for (size_t j = 0; j <= i; ++j)
301
0
    {
302
0
        free (vals[j]);
303
0
        free (txts[j]);
304
0
    }
305
0
    free(vals);
306
0
    free(txts);
307
0
    *values = *texts = NULL;
308
0
    return -1;
309
0
}
310
311
ssize_t config_GetPszChoices(const char *name,
312
                             char ***restrict values, char ***restrict texts)
313
0
{
314
0
    *values = *texts = NULL;
315
316
0
    struct vlc_param *param = vlc_param_Find(name);
317
0
    if (param == NULL)
318
0
    {
319
0
        errno = ENOENT;
320
0
        return -1;
321
0
    }
322
323
0
    module_config_t *cfg = &param->item;
324
0
    switch (cfg->i_type)
325
0
    {
326
0
        case CONFIG_ITEM_MODULE:
327
0
            return config_ListModules (cfg->psz_type, values, texts);
328
0
        default:
329
0
            if (!IsConfigStringType (cfg->i_type))
330
0
            {
331
0
                errno = EINVAL;
332
0
                return -1;
333
0
            }
334
0
            break;
335
0
    }
336
337
0
    size_t count = cfg->list_count;
338
0
    if (count == 0)
339
0
    {
340
0
        int (*cb)(const char *, char ***, char ***);
341
342
0
        cb = vlc_plugin_Symbol(NULL, param->owner, "vlc_entry_cfg_str_enum");
343
0
        if (cb == NULL)
344
0
            return 0;
345
346
0
        return cb(name, values, texts);
347
0
    }
348
349
0
    char **vals = malloc (sizeof (*vals) * count);
350
0
    char **txts = malloc (sizeof (*txts) * count);
351
0
    if (!vals || !txts)
352
0
    {
353
0
        free (vals);
354
0
        free (txts);
355
0
        errno = ENOMEM;
356
0
        return -1;
357
0
    }
358
359
0
    size_t i;
360
0
    for (i = 0; i < count; i++)
361
0
    {
362
0
        vals[i] = strdup ((cfg->list.psz[i] != NULL) ? cfg->list.psz[i] : "");
363
        /* FIXME: use module_gettext() instead */
364
0
        txts[i] = strdup ((cfg->list_text[i] != NULL)
365
0
                                       ? vlc_gettext (cfg->list_text[i]) : "");
366
0
        if (!vals[i] || !txts[i])
367
0
            goto error;
368
0
    }
369
370
0
    *values = vals;
371
0
    *texts = txts;
372
0
    return count;
373
374
0
error:
375
0
    for (size_t j = 0; j <= i; ++j)
376
0
    {
377
0
        free (vals[j]);
378
0
        free (txts[j]);
379
0
    }
380
0
    free(vals);
381
0
    free(txts);
382
0
    errno = ENOMEM;
383
0
    return -1;
384
0
}
385
386
static int confcmp (const void *a, const void *b)
387
699k
{
388
699k
    const struct vlc_param *const *ca = a, *const *cb = b;
389
390
699k
    return strcmp ((*ca)->item.psz_name, (*cb)->item.psz_name);
391
699k
}
392
393
static int confnamecmp (const void *key, const void *elem)
394
38.1M
{
395
38.1M
    const struct vlc_param *const *conf = elem;
396
397
38.1M
    return strcmp (key, (*conf)->item.psz_name);
398
38.1M
}
399
400
static struct
401
{
402
    struct vlc_param **list;
403
    size_t count;
404
} config = { NULL, 0 };
405
406
/**
407
 * Index the configuration items by name for faster lookups.
408
 */
409
int config_SortConfig (void)
410
172
{
411
172
    vlc_plugin_t *p;
412
172
    size_t nconf = 0;
413
414
8.51k
    for (p = vlc_plugins; p != NULL; p = p->next)
415
8.34k
        nconf += p->conf.count;
416
417
172
    struct vlc_param **clist = vlc_alloc(nconf, sizeof (*clist));
418
172
    if (unlikely(clist == NULL))
419
0
        return VLC_ENOMEM;
420
421
172
    size_t index = 0;
422
8.51k
    for (p = vlc_plugins; p != NULL; p = p->next)
423
8.34k
    {
424
120k
        for (size_t i = 0; i < p->conf.size; i++)
425
111k
        {
426
111k
            struct vlc_param *param = p->conf.params + i;
427
111k
            module_config_t *item = &param->item;
428
429
111k
            if (!CONFIG_ITEM(item->i_type))
430
19.5k
                continue; /* ignore hints */
431
111k
            assert(index < nconf);
432
92.2k
            clist[index++] = param;
433
92.2k
        }
434
8.34k
    }
435
436
172
    qsort (clist, nconf, sizeof (*clist), confcmp);
437
438
172
    config.list = clist;
439
172
    config.count = nconf;
440
172
    return VLC_SUCCESS;
441
172
}
442
443
void config_UnsortConfig (void)
444
86
{
445
86
    struct vlc_param **clist;
446
447
86
    clist = config.list;
448
86
    config.list = NULL;
449
86
    config.count = 0;
450
451
86
    free (clist);
452
86
}
453
454
struct vlc_param *vlc_param_Find(const char *name)
455
4.69M
{
456
4.69M
    struct vlc_param *const *p;
457
458
4.69M
    assert(name != NULL);
459
4.69M
    p = bsearch (name, config.list, config.count, sizeof (*p), confnamecmp);
460
4.69M
    return (p != NULL) ? *p : NULL;
461
4.69M
}
462
463
module_config_t *config_FindConfig(const char *name)
464
86
{
465
86
    if (unlikely(name == NULL))
466
0
        return NULL;
467
468
86
    struct vlc_param *param = vlc_param_Find(name);
469
470
86
    return (param != NULL) ? &param->item : NULL;
471
86
}
472
473
/**
474
 * Destroys an array of configuration items.
475
 * \param tab start of array of items
476
 * \param confsize number of items in the array
477
 */
478
void config_Free(struct vlc_param *tab, size_t confsize)
479
0
{
480
0
    for (size_t j = 0; j < confsize; j++)
481
0
    {
482
0
        struct vlc_param *param = &tab[j];
483
0
        module_config_t *p_item = &param->item;
484
485
0
        if (IsConfigStringType (p_item->i_type))
486
0
        {
487
0
            free(atomic_load_explicit(&param->value.str,
488
0
                                      memory_order_relaxed));
489
0
            if (p_item->list_count)
490
0
                free (p_item->list.psz);
491
0
        }
492
493
0
        free (p_item->list_text);
494
0
    }
495
496
0
    free (tab);
497
0
}
498
499
void config_ResetAll(void)
500
0
{
501
0
    vlc_mutex_lock(&config_lock);
502
0
    for (vlc_plugin_t *p = vlc_plugins; p != NULL; p = p->next)
503
0
    {
504
0
        for (size_t i = 0; i < p->conf.size; i++ )
505
0
        {
506
0
            struct vlc_param *param = p->conf.params + i;
507
0
            module_config_t *p_config = &param->item;
508
509
0
            if (IsConfigIntegerType (p_config->i_type))
510
0
            {
511
0
                atomic_store_explicit(&param->value.i, p_config->orig.i,
512
0
                                      memory_order_relaxed);
513
0
                p_config->value.i = p_config->orig.i;
514
0
            }
515
0
            else
516
0
            if (IsConfigFloatType (p_config->i_type))
517
0
            {
518
0
                atomic_store_explicit(&param->value.f, p_config->orig.f,
519
0
                                      memory_order_relaxed);
520
0
                p_config->value.f = p_config->orig.f;
521
0
            }
522
0
            else
523
0
            if (IsConfigStringType (p_config->i_type))
524
0
                vlc_param_SetString(param, p_config->orig.psz);
525
0
        }
526
0
    }
527
0
    vlc_mutex_unlock(&config_lock);
528
0
    atomic_store_explicit(&config_dirty, true, memory_order_release);
529
0
}
530
531
532
int config_AutoSaveConfigFile( libvlc_int_t *p_this )
533
0
{
534
0
    assert( p_this );
535
536
0
    if (!atomic_exchange_explicit(&config_dirty, false, memory_order_acquire))
537
0
        return 0;
538
539
0
    int ret = config_SaveConfigFile (p_this);
540
541
0
    if (unlikely(ret != 0))
542
        /*
543
         * On write failure, set the dirty flag back again. While it looks
544
         * racy, it really means to retry later in hope that it does not
545
         * fail again. Concurrent write attempts would not succeed anyway.
546
         */
547
0
        atomic_store_explicit(&config_dirty, true, memory_order_relaxed);
548
549
0
    return ret;
550
0
}