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