core: "clock-jitter" is in milliseconds
[vlc.git] / src / modules / cache.c
blobccd9ffccae6fb7012c040c300f0f8fc171ec0a64
1 /*****************************************************************************
2 * cache.c: Plugins cache
3 *****************************************************************************
4 * Copyright (C) 2001-2007 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Sam Hocevar <sam@zoy.org>
8 * Ethan C. Baldridge <BaldridgeE@cadmus.com>
9 * Hans-Peter Jansen <hpj@urpla.net>
10 * Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <stdalign.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <assert.h>
40 #include <vlc_common.h>
41 #include <vlc_block.h>
42 #include "libvlc.h"
44 #include <vlc_plugin.h>
45 #include <errno.h>
47 #include "config/configuration.h"
49 #include <vlc_fs.h>
51 #include "modules/modules.h"
54 /*****************************************************************************
55 * Local prototypes
56 *****************************************************************************/
57 #ifdef HAVE_DYNAMIC_PLUGINS
58 /* Sub-version number
59 * (only used to avoid breakage in dev version when cache structure changes) */
60 #define CACHE_SUBVERSION_NUM 35
62 /* Cache filename */
63 #define CACHE_NAME "plugins.dat"
64 /* Magic for the cache filename */
65 #define CACHE_STRING "cache "PACKAGE_NAME" "PACKAGE_VERSION
68 static int vlc_cache_load_immediate(void *out, block_t *in, size_t size)
70 if (in->i_buffer < size)
71 return -1;
73 memcpy(out, in->p_buffer, size);
74 in->p_buffer += size;
75 in->i_buffer -= size;
76 return 0;
79 static int vlc_cache_load_bool(bool *out, block_t *in)
81 unsigned char b;
83 if (vlc_cache_load_immediate(&b, in, 1) || b > 1)
84 return -1;
86 *out = b;
87 return 0;
90 static int vlc_cache_load_array(const void **p, size_t size, size_t n,
91 block_t *file)
93 if (n == 0)
95 *p = NULL;
96 return 0;
99 if (unlikely(size * n < size))
100 return -1;
102 size *= n;
104 if (file->i_buffer < size)
105 return -1;
107 *p = file->p_buffer;
108 file->p_buffer += size;
109 file->i_buffer -= size;
110 return 0;
113 static int vlc_cache_load_string(const char **restrict p, block_t *file)
115 uint16_t size;
117 if (vlc_cache_load_immediate(&size, file, sizeof (size)) || size > 16384)
118 return -1;
120 if (size == 0)
122 *p = NULL;
123 return 0;
126 const char *str = (char *)file->p_buffer;
128 if (file->i_buffer < size || str[size - 1] != '\0')
129 return -1;
131 file->p_buffer += size;
132 file->i_buffer -= size;
133 *p = str;
134 return 0;
137 static int vlc_cache_load_align(size_t align, block_t *file)
139 assert(align > 0);
141 size_t skip = (-(uintptr_t)file->p_buffer) % align;
142 if (skip == 0)
143 return 0;
145 assert(skip < align);
147 if (file->i_buffer < skip)
148 return -1;
150 file->p_buffer += skip;
151 file->i_buffer -= skip;
152 assert((((uintptr_t)file->p_buffer) % align) == 0);
153 return 0;
156 #define LOAD_IMMEDIATE(a) \
157 if (vlc_cache_load_immediate(&(a), file, sizeof (a))) \
158 goto error
159 #define LOAD_FLAG(a) \
160 do \
162 bool b; \
163 if (vlc_cache_load_bool(&b, file)) \
164 goto error; \
165 (a) = b; \
166 } while (0)
167 #define LOAD_ARRAY(a,n) \
168 do \
170 const void *base; \
171 if (vlc_cache_load_array(&base, sizeof (*(a)), (n), file)) \
172 goto error; \
173 (a) = base; \
174 } while (0)
175 #define LOAD_STRING(a) \
176 if (vlc_cache_load_string(&(a), file)) \
177 goto error
178 #define LOAD_ALIGNOF(t) \
179 if (vlc_cache_load_align(alignof(t), file)) \
180 goto error
182 static int vlc_cache_load_config(module_config_t *cfg, block_t *file)
184 LOAD_IMMEDIATE (cfg->i_type);
185 LOAD_IMMEDIATE (cfg->i_short);
186 LOAD_FLAG (cfg->b_internal);
187 LOAD_FLAG (cfg->b_unsaveable);
188 LOAD_FLAG (cfg->b_safe);
189 LOAD_FLAG (cfg->b_removed);
190 LOAD_STRING (cfg->psz_type);
191 LOAD_STRING (cfg->psz_name);
192 LOAD_STRING (cfg->psz_text);
193 LOAD_STRING (cfg->psz_longtext);
194 LOAD_IMMEDIATE (cfg->list_count);
196 if (IsConfigStringType (cfg->i_type))
198 const char *psz;
199 LOAD_STRING(psz);
200 cfg->orig.psz = (char *)psz;
201 cfg->value.psz = (psz != NULL) ? strdup (cfg->orig.psz) : NULL;
203 if (cfg->list_count)
204 cfg->list.psz = xmalloc (cfg->list_count * sizeof (char *));
205 else
206 LOAD_STRING(cfg->list_cb_name);
207 for (unsigned i = 0; i < cfg->list_count; i++)
209 LOAD_STRING (cfg->list.psz[i]);
210 if (cfg->list.psz[i] == NULL /* NULL -> empty string */
211 && (cfg->list.psz[i] = calloc (1, 1)) == NULL)
212 goto error;
215 else
217 LOAD_IMMEDIATE (cfg->orig);
218 LOAD_IMMEDIATE (cfg->min);
219 LOAD_IMMEDIATE (cfg->max);
220 cfg->value = cfg->orig;
222 if (cfg->list_count)
224 LOAD_ALIGNOF(*cfg->list.i);
226 else
227 LOAD_STRING(cfg->list_cb_name);
229 LOAD_ARRAY(cfg->list.i, cfg->list_count);
232 cfg->list_text = xmalloc (cfg->list_count * sizeof (char *));
233 for (unsigned i = 0; i < cfg->list_count; i++)
235 LOAD_STRING (cfg->list_text[i]);
236 if (cfg->list_text[i] == NULL /* NULL -> empty string */
237 && (cfg->list_text[i] = calloc (1, 1)) == NULL)
238 goto error;
241 return 0;
242 error:
243 return -1; /* FIXME: leaks */
246 static int vlc_cache_load_plugin_config(vlc_plugin_t *plugin, block_t *file)
248 uint16_t lines;
250 /* Calculate the structure length */
251 LOAD_IMMEDIATE (lines);
253 /* Allocate memory */
254 if (lines)
256 plugin->conf.items = calloc(sizeof (module_config_t), lines);
257 if (unlikely(plugin->conf.items == NULL))
259 plugin->conf.size = 0;
260 return -1;
263 else
264 plugin->conf.items = NULL;
266 plugin->conf.size = lines;
268 /* Do the duplication job */
269 for (size_t i = 0; i < lines; i++)
271 module_config_t *item = plugin->conf.items + i;
273 if (vlc_cache_load_config(item, file))
274 return -1;
276 if (CONFIG_ITEM(item->i_type))
278 plugin->conf.count++;
279 if (item->i_type == CONFIG_ITEM_BOOL)
280 plugin->conf.booleans++;
282 item->owner = plugin;
285 return 0;
286 error:
287 return -1; /* FIXME: leaks */
290 static int vlc_cache_load_module(vlc_plugin_t *plugin, block_t *file)
292 module_t *module = vlc_module_create(plugin);
293 if (unlikely(module == NULL))
294 return -1;
296 LOAD_STRING(module->psz_shortname);
297 LOAD_STRING(module->psz_longname);
298 LOAD_STRING(module->psz_help);
300 LOAD_IMMEDIATE(module->i_shortcuts);
301 if (module->i_shortcuts > MODULE_SHORTCUT_MAX)
302 goto error;
303 else
305 module->pp_shortcuts =
306 xmalloc (sizeof (*module->pp_shortcuts) * module->i_shortcuts);
307 for (unsigned j = 0; j < module->i_shortcuts; j++)
308 LOAD_STRING(module->pp_shortcuts[j]);
311 LOAD_STRING(module->activate_name);
312 LOAD_STRING(module->deactivate_name);
313 LOAD_STRING(module->psz_capability);
314 LOAD_IMMEDIATE(module->i_score);
315 return 0;
316 error:
317 return -1;
320 static vlc_plugin_t *vlc_cache_load_plugin(block_t *file)
322 vlc_plugin_t *plugin = vlc_plugin_create();
323 if (unlikely(plugin == NULL))
324 return NULL;
326 uint32_t modules;
327 LOAD_IMMEDIATE(modules);
329 for (size_t i = 0; i < modules; i++)
330 if (vlc_cache_load_module(plugin, file))
331 goto error;
333 if (vlc_cache_load_plugin_config(plugin, file))
334 goto error;
336 LOAD_STRING(plugin->textdomain);
338 const char *path;
339 LOAD_STRING(path);
340 if (path == NULL)
341 goto error;
343 plugin->path = strdup(path);
344 if (unlikely(plugin->path == NULL))
345 goto error;
347 LOAD_FLAG(plugin->unloadable);
348 LOAD_IMMEDIATE(plugin->mtime);
349 LOAD_IMMEDIATE(plugin->size);
351 if (plugin->textdomain != NULL)
352 vlc_bindtextdomain(plugin->textdomain);
354 return plugin;
356 error:
357 vlc_plugin_destroy(plugin);
358 return NULL;
362 * Loads a plugins cache file.
364 * This function will load the plugin cache if present and valid. This cache
365 * will in turn be queried by AllocateAllPlugins() to see if it needs to
366 * actually load the dynamically loadable module.
367 * This allows us to only fully load plugins when they are actually used.
369 vlc_plugin_t *vlc_cache_load(vlc_object_t *p_this, const char *dir,
370 block_t **backingp)
372 char *psz_filename;
374 assert( dir != NULL );
376 if( asprintf( &psz_filename, "%s"DIR_SEP CACHE_NAME, dir ) == -1 )
377 return NULL;
379 msg_Dbg( p_this, "loading plugins cache file %s", psz_filename );
381 block_t *file = block_FilePath(psz_filename, false);
382 if (file == NULL)
383 msg_Warn(p_this, "cannot read %s: %s", psz_filename,
384 vlc_strerror_c(errno));
385 free(psz_filename);
386 if (file == NULL)
387 return NULL;
389 /* Check the file is a plugins cache */
390 char cachestr[sizeof (CACHE_STRING) - 1];
392 if (vlc_cache_load_immediate(cachestr, file, sizeof (cachestr))
393 || memcmp(cachestr, CACHE_STRING, sizeof (cachestr)))
395 msg_Warn( p_this, "This doesn't look like a valid plugins cache" );
396 block_Release(file);
397 return NULL;
400 #ifdef DISTRO_VERSION
401 /* Check for distribution specific version */
402 char distrostr[sizeof (DISTRO_VERSION) - 1];
404 if (vlc_cache_load_immediate(distrostr, file, sizeof (distrostr))
405 || memcmp(distrostr, DISTRO_VERSION, sizeof (distrostr)))
407 msg_Warn( p_this, "This doesn't look like a valid plugins cache" );
408 block_Release(file);
409 return NULL;
411 #endif
413 /* Check sub-version number */
414 uint32_t marker;
416 if (vlc_cache_load_immediate(&marker, file, sizeof (marker))
417 || marker != CACHE_SUBVERSION_NUM)
419 msg_Warn( p_this, "This doesn't look like a valid plugins cache "
420 "(corrupted header)" );
421 block_Release(file);
422 return NULL;
425 /* Check header marker */
426 if (vlc_cache_load_immediate(&marker, file, sizeof (marker))
427 #ifdef DISTRO_VERSION
428 || marker != (sizeof (cachestr) + sizeof (distrostr) + sizeof (marker))
429 #else
430 || marker != (sizeof (cachestr) + sizeof (marker))
431 #endif
434 msg_Warn( p_this, "This doesn't look like a valid plugins cache "
435 "(corrupted header)" );
436 block_Release(file);
437 return NULL;
440 vlc_plugin_t *cache = NULL;
442 while (file->i_buffer > 0)
444 vlc_plugin_t *plugin = vlc_cache_load_plugin(file);
445 if (plugin == NULL)
446 goto error;
448 if (unlikely(asprintf(&plugin->abspath, "%s" DIR_SEP "%s", dir,
449 plugin->path) == -1))
451 plugin->abspath = NULL;
452 vlc_plugin_destroy(plugin);
453 goto error;
456 plugin->next = cache;
457 cache = plugin;
460 file->p_next = *backingp;
461 *backingp = file;
462 return cache;
464 error:
465 msg_Warn( p_this, "plugins cache not loaded (corrupted)" );
467 /* TODO: cleanup */
468 block_Release(file);
469 return NULL;
472 #define SAVE_IMMEDIATE( a ) \
473 if (fwrite (&(a), sizeof(a), 1, file) != 1) \
474 goto error
475 #define SAVE_FLAG(a) \
476 do { \
477 char b = (a); \
478 SAVE_IMMEDIATE(b); \
479 } while (0)
481 static int CacheSaveString (FILE *file, const char *str)
483 uint16_t size = (str != NULL) ? (strlen (str) + 1) : 0;
485 SAVE_IMMEDIATE (size);
486 if (size != 0 && fwrite(str, 1, size, file) != size)
488 error:
489 return -1;
491 return 0;
494 #define SAVE_STRING( a ) \
495 if (CacheSaveString (file, (a))) \
496 goto error
498 static int CacheSaveAlign(FILE *file, size_t align)
500 assert(align > 0);
502 size_t skip = (-ftell(file)) % align;
503 if (skip == 0)
504 return 0;
506 assert(((ftell(file) + skip) % align) == 0);
507 return fseek(file, skip, SEEK_CUR);
510 #define SAVE_ALIGNOF(t) \
511 if (CacheSaveAlign(file, alignof (t))) \
512 goto error
514 static int CacheSaveConfig (FILE *file, const module_config_t *cfg)
516 SAVE_IMMEDIATE (cfg->i_type);
517 SAVE_IMMEDIATE (cfg->i_short);
518 SAVE_FLAG (cfg->b_internal);
519 SAVE_FLAG (cfg->b_unsaveable);
520 SAVE_FLAG (cfg->b_safe);
521 SAVE_FLAG (cfg->b_removed);
522 SAVE_STRING (cfg->psz_type);
523 SAVE_STRING (cfg->psz_name);
524 SAVE_STRING (cfg->psz_text);
525 SAVE_STRING (cfg->psz_longtext);
526 SAVE_IMMEDIATE (cfg->list_count);
528 if (IsConfigStringType (cfg->i_type))
530 SAVE_STRING (cfg->orig.psz);
531 if (cfg->list_count == 0)
532 SAVE_STRING(cfg->list_cb_name);
534 for (unsigned i = 0; i < cfg->list_count; i++)
535 SAVE_STRING (cfg->list.psz[i]);
537 else
539 SAVE_IMMEDIATE (cfg->orig);
540 SAVE_IMMEDIATE (cfg->min);
541 SAVE_IMMEDIATE (cfg->max);
543 if (cfg->list_count > 0)
545 SAVE_ALIGNOF(*cfg->list.i);
547 else
548 SAVE_STRING(cfg->list_cb_name);
550 for (unsigned i = 0; i < cfg->list_count; i++)
551 SAVE_IMMEDIATE (cfg->list.i[i]);
553 for (unsigned i = 0; i < cfg->list_count; i++)
554 SAVE_STRING (cfg->list_text[i]);
556 return 0;
557 error:
558 return -1;
561 static int CacheSaveModuleConfig(FILE *file, const vlc_plugin_t *plugin)
563 uint16_t lines = plugin->conf.size;
565 SAVE_IMMEDIATE (lines);
567 for (size_t i = 0; i < lines; i++)
568 if (CacheSaveConfig(file, plugin->conf.items + i))
569 goto error;
571 return 0;
572 error:
573 return -1;
576 static int CacheSaveModule(FILE *file, const module_t *module)
578 SAVE_STRING(module->psz_shortname);
579 SAVE_STRING(module->psz_longname);
580 SAVE_STRING(module->psz_help);
581 SAVE_IMMEDIATE(module->i_shortcuts);
583 for (size_t j = 0; j < module->i_shortcuts; j++)
584 SAVE_STRING(module->pp_shortcuts[j]);
586 SAVE_STRING(module->activate_name);
587 SAVE_STRING(module->deactivate_name);
588 SAVE_STRING(module->psz_capability);
589 SAVE_IMMEDIATE(module->i_score);
590 return 0;
591 error:
592 return -1;
595 static int CacheSaveBank(FILE *file, vlc_plugin_t *const *cache, size_t n)
597 uint32_t i_file_size = 0;
599 /* Contains version number */
600 if (fputs (CACHE_STRING, file) == EOF)
601 goto error;
602 #ifdef DISTRO_VERSION
603 /* Allow binary maintaner to pass a string to detect new binary version*/
604 if (fputs( DISTRO_VERSION, file ) == EOF)
605 goto error;
606 #endif
607 /* Sub-version number (to avoid breakage in the dev version when cache
608 * structure changes) */
609 i_file_size = CACHE_SUBVERSION_NUM;
610 if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1 )
611 goto error;
613 /* Header marker */
614 i_file_size = ftell( file );
615 if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1)
616 goto error;
618 for (size_t i = 0; i < n; i++)
620 const vlc_plugin_t *plugin = cache[i];
621 uint32_t count = plugin->modules_count;
623 SAVE_IMMEDIATE(count);
625 for (module_t *module = plugin->module;
626 module != NULL;
627 module = module->next)
628 if (CacheSaveModule(file, module))
629 goto error;
631 /* Config stuff */
632 if (CacheSaveModuleConfig(file, plugin))
633 goto error;
635 /* Save common info */
636 SAVE_STRING(plugin->textdomain);
637 SAVE_STRING(plugin->path);
638 SAVE_FLAG(plugin->unloadable);
639 SAVE_IMMEDIATE(plugin->mtime);
640 SAVE_IMMEDIATE(plugin->size);
643 if (fflush (file)) /* flush libc buffers */
644 goto error;
645 return 0; /* success! */
647 error:
648 return -1;
652 * Saves a module cache to disk, and release cache data from memory.
654 void CacheSave(vlc_object_t *p_this, const char *dir,
655 vlc_plugin_t *const *entries, size_t n)
657 char *filename = NULL, *tmpname = NULL;
659 if (asprintf (&filename, "%s"DIR_SEP CACHE_NAME, dir ) == -1)
660 goto out;
662 if (asprintf (&tmpname, "%s.%"PRIu32, filename, (uint32_t)getpid ()) == -1)
663 goto out;
664 msg_Dbg (p_this, "saving plugins cache %s", filename);
666 FILE *file = vlc_fopen (tmpname, "wb");
667 if (file == NULL)
669 if (errno != EACCES && errno != ENOENT)
670 msg_Warn (p_this, "cannot create %s: %s", tmpname,
671 vlc_strerror_c(errno));
672 goto out;
675 if (CacheSaveBank(file, entries, n))
677 msg_Warn (p_this, "cannot write %s: %s", tmpname,
678 vlc_strerror_c(errno));
679 clearerr (file);
680 fclose (file);
681 vlc_unlink (tmpname);
682 goto out;
685 #if !defined( _WIN32 ) && !defined( __OS2__ )
686 vlc_rename (tmpname, filename); /* atomically replace old cache */
687 fclose (file);
688 #else
689 vlc_unlink (filename);
690 fclose (file);
691 vlc_rename (tmpname, filename);
692 #endif
693 out:
694 free (filename);
695 free (tmpname);
699 * Looks up a plugin file in a table of cached plugins.
701 vlc_plugin_t *vlc_cache_lookup(vlc_plugin_t **cache, const char *path)
703 vlc_plugin_t **pp = cache, *plugin;
705 while ((plugin = *pp) != NULL)
707 if (plugin->path != NULL && !strcmp(plugin->path, path))
709 *pp = plugin->next;
710 plugin->next = NULL;
711 return plugin;
714 pp = &plugin->next;
717 return NULL;
719 #endif /* HAVE_DYNAMIC_PLUGINS */