1 /*****************************************************************************
2 * cache.c: Plugins cache
3 *****************************************************************************
4 * Copyright (C) 2001-2007 VLC authors and VideoLAN
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 *****************************************************************************/
35 #include <sys/types.h>
40 #include <vlc_common.h>
41 #include <vlc_block.h>
44 #include <vlc_plugin.h>
47 #include "config/configuration.h"
51 #include "modules/modules.h"
54 /*****************************************************************************
56 *****************************************************************************/
57 #ifdef HAVE_DYNAMIC_PLUGINS
59 * (only used to avoid breakage in dev version when cache structure changes) */
60 #define CACHE_SUBVERSION_NUM 35
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
)
73 memcpy(out
, in
->p_buffer
, size
);
79 static int vlc_cache_load_bool(bool *out
, block_t
*in
)
83 if (vlc_cache_load_immediate(&b
, in
, 1) || b
> 1)
90 static int vlc_cache_load_array(const void **p
, size_t size
, size_t n
,
99 if (unlikely(size
* n
< size
))
104 if (file
->i_buffer
< size
)
108 file
->p_buffer
+= size
;
109 file
->i_buffer
-= size
;
113 static int vlc_cache_load_string(const char **restrict p
, block_t
*file
)
117 if (vlc_cache_load_immediate(&size
, file
, sizeof (size
)) || size
> 16384)
126 const char *str
= (char *)file
->p_buffer
;
128 if (file
->i_buffer
< size
|| str
[size
- 1] != '\0')
131 file
->p_buffer
+= size
;
132 file
->i_buffer
-= size
;
137 static int vlc_cache_load_align(size_t align
, block_t
*file
)
141 size_t skip
= (-(uintptr_t)file
->p_buffer
) % align
;
145 assert(skip
< align
);
147 if (file
->i_buffer
< skip
)
150 file
->p_buffer
+= skip
;
151 file
->i_buffer
-= skip
;
152 assert((((uintptr_t)file
->p_buffer
) % align
) == 0);
156 #define LOAD_IMMEDIATE(a) \
157 if (vlc_cache_load_immediate(&(a), file, sizeof (a))) \
159 #define LOAD_FLAG(a) \
163 if (vlc_cache_load_bool(&b, file)) \
167 #define LOAD_ARRAY(a,n) \
171 if (vlc_cache_load_array(&base, sizeof (*(a)), (n), file)) \
175 #define LOAD_STRING(a) \
176 if (vlc_cache_load_string(&(a), file)) \
178 #define LOAD_ALIGNOF(t) \
179 if (vlc_cache_load_align(alignof(t), file)) \
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
))
200 cfg
->orig
.psz
= (char *)psz
;
201 cfg
->value
.psz
= (psz
!= NULL
) ? strdup (cfg
->orig
.psz
) : NULL
;
204 cfg
->list
.psz
= xmalloc (cfg
->list_count
* sizeof (char *));
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
)
217 LOAD_IMMEDIATE (cfg
->orig
);
218 LOAD_IMMEDIATE (cfg
->min
);
219 LOAD_IMMEDIATE (cfg
->max
);
220 cfg
->value
= cfg
->orig
;
224 LOAD_ALIGNOF(*cfg
->list
.i
);
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
)
243 return -1; /* FIXME: leaks */
246 static int vlc_cache_load_plugin_config(vlc_plugin_t
*plugin
, block_t
*file
)
250 /* Calculate the structure length */
251 LOAD_IMMEDIATE (lines
);
253 /* Allocate memory */
256 plugin
->conf
.items
= calloc(sizeof (module_config_t
), lines
);
257 if (unlikely(plugin
->conf
.items
== NULL
))
259 plugin
->conf
.size
= 0;
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
))
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
;
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
))
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
)
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
);
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
))
327 LOAD_IMMEDIATE(modules
);
329 for (size_t i
= 0; i
< modules
; i
++)
330 if (vlc_cache_load_module(plugin
, file
))
333 if (vlc_cache_load_plugin_config(plugin
, file
))
336 LOAD_STRING(plugin
->textdomain
);
343 plugin
->path
= strdup(path
);
344 if (unlikely(plugin
->path
== NULL
))
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
);
357 vlc_plugin_destroy(plugin
);
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
,
374 assert( dir
!= NULL
);
376 if( asprintf( &psz_filename
, "%s"DIR_SEP CACHE_NAME
, dir
) == -1 )
379 msg_Dbg( p_this
, "loading plugins cache file %s", psz_filename
);
381 block_t
*file
= block_FilePath(psz_filename
, false);
383 msg_Warn(p_this
, "cannot read %s: %s", psz_filename
,
384 vlc_strerror_c(errno
));
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" );
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" );
413 /* Check sub-version number */
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)" );
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
))
430 || marker
!= (sizeof (cachestr
) + sizeof (marker
))
434 msg_Warn( p_this
, "This doesn't look like a valid plugins cache "
435 "(corrupted header)" );
440 vlc_plugin_t
*cache
= NULL
;
442 while (file
->i_buffer
> 0)
444 vlc_plugin_t
*plugin
= vlc_cache_load_plugin(file
);
448 if (unlikely(asprintf(&plugin
->abspath
, "%s" DIR_SEP
"%s", dir
,
449 plugin
->path
) == -1))
451 plugin
->abspath
= NULL
;
452 vlc_plugin_destroy(plugin
);
456 plugin
->next
= cache
;
460 file
->p_next
= *backingp
;
465 msg_Warn( p_this
, "plugins cache not loaded (corrupted)" );
472 #define SAVE_IMMEDIATE( a ) \
473 if (fwrite (&(a), sizeof(a), 1, file) != 1) \
475 #define SAVE_FLAG(a) \
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
)
494 #define SAVE_STRING( a ) \
495 if (CacheSaveString (file, (a))) \
498 static int CacheSaveAlign(FILE *file
, size_t align
)
502 size_t skip
= (-ftell(file
)) % align
;
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))) \
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
]);
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
);
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
]);
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
))
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
);
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
)
602 #ifdef DISTRO_VERSION
603 /* Allow binary maintaner to pass a string to detect new binary version*/
604 if (fputs( DISTRO_VERSION
, file
) == EOF
)
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 )
614 i_file_size
= ftell( file
);
615 if (fwrite (&i_file_size
, sizeof (i_file_size
), 1, file
) != 1)
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
;
627 module
= module
->next
)
628 if (CacheSaveModule(file
, module
))
632 if (CacheSaveModuleConfig(file
, plugin
))
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 */
645 return 0; /* success! */
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)
662 if (asprintf (&tmpname
, "%s.%"PRIu32
, filename
, (uint32_t)getpid ()) == -1)
664 msg_Dbg (p_this
, "saving plugins cache %s", filename
);
666 FILE *file
= vlc_fopen (tmpname
, "wb");
669 if (errno
!= EACCES
&& errno
!= ENOENT
)
670 msg_Warn (p_this
, "cannot create %s: %s", tmpname
,
671 vlc_strerror_c(errno
));
675 if (CacheSaveBank(file
, entries
, n
))
677 msg_Warn (p_this
, "cannot write %s: %s", tmpname
,
678 vlc_strerror_c(errno
));
681 vlc_unlink (tmpname
);
685 #if !defined( _WIN32 ) && !defined( __OS2__ )
686 vlc_rename (tmpname
, filename
); /* atomically replace old cache */
689 vlc_unlink (filename
);
691 vlc_rename (tmpname
, filename
);
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
))
719 #endif /* HAVE_DYNAMIC_PLUGINS */