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 34
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_advanced
);
187 LOAD_FLAG (cfg
->b_internal
);
188 LOAD_FLAG (cfg
->b_unsaveable
);
189 LOAD_FLAG (cfg
->b_safe
);
190 LOAD_FLAG (cfg
->b_removed
);
191 LOAD_STRING (cfg
->psz_type
);
192 LOAD_STRING (cfg
->psz_name
);
193 LOAD_STRING (cfg
->psz_text
);
194 LOAD_STRING (cfg
->psz_longtext
);
195 LOAD_IMMEDIATE (cfg
->list_count
);
197 if (IsConfigStringType (cfg
->i_type
))
201 cfg
->orig
.psz
= (char *)psz
;
202 cfg
->value
.psz
= (psz
!= NULL
) ? strdup (cfg
->orig
.psz
) : NULL
;
205 cfg
->list
.psz
= xmalloc (cfg
->list_count
* sizeof (char *));
207 LOAD_STRING(cfg
->list_cb_name
);
208 for (unsigned i
= 0; i
< cfg
->list_count
; i
++)
210 LOAD_STRING (cfg
->list
.psz
[i
]);
211 if (cfg
->list
.psz
[i
] == NULL
/* NULL -> empty string */
212 && (cfg
->list
.psz
[i
] = calloc (1, 1)) == NULL
)
218 LOAD_IMMEDIATE (cfg
->orig
);
219 LOAD_IMMEDIATE (cfg
->min
);
220 LOAD_IMMEDIATE (cfg
->max
);
221 cfg
->value
= cfg
->orig
;
225 LOAD_ALIGNOF(*cfg
->list
.i
);
228 LOAD_STRING(cfg
->list_cb_name
);
230 LOAD_ARRAY(cfg
->list
.i
, cfg
->list_count
);
233 cfg
->list_text
= xmalloc (cfg
->list_count
* sizeof (char *));
234 for (unsigned i
= 0; i
< cfg
->list_count
; i
++)
236 LOAD_STRING (cfg
->list_text
[i
]);
237 if (cfg
->list_text
[i
] == NULL
/* NULL -> empty string */
238 && (cfg
->list_text
[i
] = calloc (1, 1)) == NULL
)
244 return -1; /* FIXME: leaks */
247 static int vlc_cache_load_plugin_config(vlc_plugin_t
*plugin
, block_t
*file
)
251 /* Calculate the structure length */
252 LOAD_IMMEDIATE (lines
);
254 /* Allocate memory */
257 plugin
->conf
.items
= calloc(sizeof (module_config_t
), lines
);
258 if (unlikely(plugin
->conf
.items
== NULL
))
260 plugin
->conf
.size
= 0;
265 plugin
->conf
.items
= NULL
;
267 plugin
->conf
.size
= lines
;
269 /* Do the duplication job */
270 for (size_t i
= 0; i
< lines
; i
++)
272 module_config_t
*item
= plugin
->conf
.items
+ i
;
274 if (vlc_cache_load_config(item
, file
))
277 if (CONFIG_ITEM(item
->i_type
))
279 plugin
->conf
.count
++;
280 if (item
->i_type
== CONFIG_ITEM_BOOL
)
281 plugin
->conf
.booleans
++;
283 item
->owner
= plugin
;
288 return -1; /* FIXME: leaks */
291 static int vlc_cache_load_module(vlc_plugin_t
*plugin
, block_t
*file
)
293 module_t
*module
= vlc_module_create(plugin
);
294 if (unlikely(module
== NULL
))
297 LOAD_STRING(module
->psz_shortname
);
298 LOAD_STRING(module
->psz_longname
);
299 LOAD_STRING(module
->psz_help
);
301 LOAD_IMMEDIATE(module
->i_shortcuts
);
302 if (module
->i_shortcuts
> MODULE_SHORTCUT_MAX
)
306 module
->pp_shortcuts
=
307 xmalloc (sizeof (*module
->pp_shortcuts
) * module
->i_shortcuts
);
308 for (unsigned j
= 0; j
< module
->i_shortcuts
; j
++)
309 LOAD_STRING(module
->pp_shortcuts
[j
]);
312 LOAD_STRING(module
->activate_name
);
313 LOAD_STRING(module
->deactivate_name
);
314 LOAD_STRING(module
->psz_capability
);
315 LOAD_IMMEDIATE(module
->i_score
);
321 static vlc_plugin_t
*vlc_cache_load_plugin(block_t
*file
)
323 vlc_plugin_t
*plugin
= vlc_plugin_create();
324 if (unlikely(plugin
== NULL
))
328 LOAD_IMMEDIATE(modules
);
330 for (size_t i
= 0; i
< modules
; i
++)
331 if (vlc_cache_load_module(plugin
, file
))
334 if (vlc_cache_load_plugin_config(plugin
, file
))
337 LOAD_STRING(plugin
->textdomain
);
344 plugin
->path
= strdup(path
);
345 if (unlikely(plugin
->path
== NULL
))
348 LOAD_FLAG(plugin
->unloadable
);
349 LOAD_IMMEDIATE(plugin
->mtime
);
350 LOAD_IMMEDIATE(plugin
->size
);
352 if (plugin
->textdomain
!= NULL
)
353 vlc_bindtextdomain(plugin
->textdomain
);
358 vlc_plugin_destroy(plugin
);
363 * Loads a plugins cache file.
365 * This function will load the plugin cache if present and valid. This cache
366 * will in turn be queried by AllocateAllPlugins() to see if it needs to
367 * actually load the dynamically loadable module.
368 * This allows us to only fully load plugins when they are actually used.
370 vlc_plugin_t
*vlc_cache_load(vlc_object_t
*p_this
, const char *dir
,
375 assert( dir
!= NULL
);
377 if( asprintf( &psz_filename
, "%s"DIR_SEP CACHE_NAME
, dir
) == -1 )
380 msg_Dbg( p_this
, "loading plugins cache file %s", psz_filename
);
382 block_t
*file
= block_FilePath(psz_filename
, false);
384 msg_Warn(p_this
, "cannot read %s: %s", psz_filename
,
385 vlc_strerror_c(errno
));
390 /* Check the file is a plugins cache */
391 char cachestr
[sizeof (CACHE_STRING
) - 1];
393 if (vlc_cache_load_immediate(cachestr
, file
, sizeof (cachestr
))
394 || memcmp(cachestr
, CACHE_STRING
, sizeof (cachestr
)))
396 msg_Warn( p_this
, "This doesn't look like a valid plugins cache" );
401 #ifdef DISTRO_VERSION
402 /* Check for distribution specific version */
403 char distrostr
[sizeof (DISTRO_VERSION
) - 1];
405 if (vlc_cache_load_immediate(distrostr
, file
, sizeof (distrostr
))
406 || memcmp(distrostr
, DISTRO_VERSION
, sizeof (distrostr
)))
408 msg_Warn( p_this
, "This doesn't look like a valid plugins cache" );
414 /* Check sub-version number */
417 if (vlc_cache_load_immediate(&marker
, file
, sizeof (marker
))
418 || marker
!= CACHE_SUBVERSION_NUM
)
420 msg_Warn( p_this
, "This doesn't look like a valid plugins cache "
421 "(corrupted header)" );
426 /* Check header marker */
427 if (vlc_cache_load_immediate(&marker
, file
, sizeof (marker
))
428 #ifdef DISTRO_VERSION
429 || marker
!= (sizeof (cachestr
) + sizeof (distrostr
) + sizeof (marker
))
431 || marker
!= (sizeof (cachestr
) + sizeof (marker
))
435 msg_Warn( p_this
, "This doesn't look like a valid plugins cache "
436 "(corrupted header)" );
441 vlc_plugin_t
*cache
= NULL
;
443 while (file
->i_buffer
> 0)
445 vlc_plugin_t
*plugin
= vlc_cache_load_plugin(file
);
449 if (unlikely(asprintf(&plugin
->abspath
, "%s" DIR_SEP
"%s", dir
,
450 plugin
->path
) == -1))
452 plugin
->abspath
= NULL
;
453 vlc_plugin_destroy(plugin
);
457 plugin
->next
= cache
;
461 file
->p_next
= *backingp
;
466 msg_Warn( p_this
, "plugins cache not loaded (corrupted)" );
473 #define SAVE_IMMEDIATE( a ) \
474 if (fwrite (&(a), sizeof(a), 1, file) != 1) \
476 #define SAVE_FLAG(a) \
482 static int CacheSaveString (FILE *file
, const char *str
)
484 uint16_t size
= (str
!= NULL
) ? (strlen (str
) + 1) : 0;
486 SAVE_IMMEDIATE (size
);
487 if (size
!= 0 && fwrite(str
, 1, size
, file
) != size
)
495 #define SAVE_STRING( a ) \
496 if (CacheSaveString (file, (a))) \
499 static int CacheSaveAlign(FILE *file
, size_t align
)
503 size_t skip
= (-ftell(file
)) % align
;
507 assert(((ftell(file
) + skip
) % align
) == 0);
508 return fseek(file
, skip
, SEEK_CUR
);
511 #define SAVE_ALIGNOF(t) \
512 if (CacheSaveAlign(file, alignof (t))) \
515 static int CacheSaveConfig (FILE *file
, const module_config_t
*cfg
)
517 SAVE_IMMEDIATE (cfg
->i_type
);
518 SAVE_IMMEDIATE (cfg
->i_short
);
519 SAVE_FLAG (cfg
->b_advanced
);
520 SAVE_FLAG (cfg
->b_internal
);
521 SAVE_FLAG (cfg
->b_unsaveable
);
522 SAVE_FLAG (cfg
->b_safe
);
523 SAVE_FLAG (cfg
->b_removed
);
524 SAVE_STRING (cfg
->psz_type
);
525 SAVE_STRING (cfg
->psz_name
);
526 SAVE_STRING (cfg
->psz_text
);
527 SAVE_STRING (cfg
->psz_longtext
);
528 SAVE_IMMEDIATE (cfg
->list_count
);
530 if (IsConfigStringType (cfg
->i_type
))
532 SAVE_STRING (cfg
->orig
.psz
);
533 if (cfg
->list_count
== 0)
534 SAVE_STRING(cfg
->list_cb_name
);
536 for (unsigned i
= 0; i
< cfg
->list_count
; i
++)
537 SAVE_STRING (cfg
->list
.psz
[i
]);
541 SAVE_IMMEDIATE (cfg
->orig
);
542 SAVE_IMMEDIATE (cfg
->min
);
543 SAVE_IMMEDIATE (cfg
->max
);
545 if (cfg
->list_count
> 0)
547 SAVE_ALIGNOF(*cfg
->list
.i
);
550 SAVE_STRING(cfg
->list_cb_name
);
552 for (unsigned i
= 0; i
< cfg
->list_count
; i
++)
553 SAVE_IMMEDIATE (cfg
->list
.i
[i
]);
555 for (unsigned i
= 0; i
< cfg
->list_count
; i
++)
556 SAVE_STRING (cfg
->list_text
[i
]);
563 static int CacheSaveModuleConfig(FILE *file
, const vlc_plugin_t
*plugin
)
565 uint16_t lines
= plugin
->conf
.size
;
567 SAVE_IMMEDIATE (lines
);
569 for (size_t i
= 0; i
< lines
; i
++)
570 if (CacheSaveConfig(file
, plugin
->conf
.items
+ i
))
578 static int CacheSaveModule(FILE *file
, const module_t
*module
)
580 SAVE_STRING(module
->psz_shortname
);
581 SAVE_STRING(module
->psz_longname
);
582 SAVE_STRING(module
->psz_help
);
583 SAVE_IMMEDIATE(module
->i_shortcuts
);
585 for (size_t j
= 0; j
< module
->i_shortcuts
; j
++)
586 SAVE_STRING(module
->pp_shortcuts
[j
]);
588 SAVE_STRING(module
->activate_name
);
589 SAVE_STRING(module
->deactivate_name
);
590 SAVE_STRING(module
->psz_capability
);
591 SAVE_IMMEDIATE(module
->i_score
);
597 static int CacheSaveBank(FILE *file
, vlc_plugin_t
*const *cache
, size_t n
)
599 uint32_t i_file_size
= 0;
601 /* Contains version number */
602 if (fputs (CACHE_STRING
, file
) == EOF
)
604 #ifdef DISTRO_VERSION
605 /* Allow binary maintaner to pass a string to detect new binary version*/
606 if (fputs( DISTRO_VERSION
, file
) == EOF
)
609 /* Sub-version number (to avoid breakage in the dev version when cache
610 * structure changes) */
611 i_file_size
= CACHE_SUBVERSION_NUM
;
612 if (fwrite (&i_file_size
, sizeof (i_file_size
), 1, file
) != 1 )
616 i_file_size
= ftell( file
);
617 if (fwrite (&i_file_size
, sizeof (i_file_size
), 1, file
) != 1)
620 for (size_t i
= 0; i
< n
; i
++)
622 const vlc_plugin_t
*plugin
= cache
[i
];
623 uint32_t count
= plugin
->modules_count
;
625 SAVE_IMMEDIATE(count
);
627 for (module_t
*module
= plugin
->module
;
629 module
= module
->next
)
630 if (CacheSaveModule(file
, module
))
634 if (CacheSaveModuleConfig(file
, plugin
))
637 /* Save common info */
638 SAVE_STRING(plugin
->textdomain
);
639 SAVE_STRING(plugin
->path
);
640 SAVE_FLAG(plugin
->unloadable
);
641 SAVE_IMMEDIATE(plugin
->mtime
);
642 SAVE_IMMEDIATE(plugin
->size
);
645 if (fflush (file
)) /* flush libc buffers */
647 return 0; /* success! */
654 * Saves a module cache to disk, and release cache data from memory.
656 void CacheSave(vlc_object_t
*p_this
, const char *dir
,
657 vlc_plugin_t
*const *entries
, size_t n
)
659 char *filename
= NULL
, *tmpname
= NULL
;
661 if (asprintf (&filename
, "%s"DIR_SEP CACHE_NAME
, dir
) == -1)
664 if (asprintf (&tmpname
, "%s.%"PRIu32
, filename
, (uint32_t)getpid ()) == -1)
666 msg_Dbg (p_this
, "saving plugins cache %s", filename
);
668 FILE *file
= vlc_fopen (tmpname
, "wb");
671 if (errno
!= EACCES
&& errno
!= ENOENT
)
672 msg_Warn (p_this
, "cannot create %s: %s", tmpname
,
673 vlc_strerror_c(errno
));
677 if (CacheSaveBank(file
, entries
, n
))
679 msg_Warn (p_this
, "cannot write %s: %s", tmpname
,
680 vlc_strerror_c(errno
));
683 vlc_unlink (tmpname
);
687 #if !defined( _WIN32 ) && !defined( __OS2__ )
688 vlc_rename (tmpname
, filename
); /* atomically replace old cache */
691 vlc_unlink (filename
);
693 vlc_rename (tmpname
, filename
);
701 * Looks up a plugin file in a table of cached plugins.
703 vlc_plugin_t
*vlc_cache_lookup(vlc_plugin_t
**cache
, const char *path
)
705 vlc_plugin_t
**pp
= cache
, *plugin
;
707 while ((plugin
= *pp
) != NULL
)
709 if (plugin
->path
!= NULL
&& !strcmp(plugin
->path
, path
))
721 #endif /* HAVE_DYNAMIC_PLUGINS */