#include "vlc_arrays.h"
#include "modules/modules.h"
-#include "modules/builtin.h"
-module_bank_t *p_module_bank;
+static module_bank_t *p_module_bank = NULL;
+static vlc_mutex_t module_lock = VLC_STATIC_MUTEX;
+
+int vlc_entry__main( module_t * );
/*****************************************************************************
* Local prototypes
*****************************************************************************/
#ifdef HAVE_DYNAMIC_PLUGINS
-static void AllocateAllPlugins ( vlc_object_t * );
-static void AllocatePluginDir ( vlc_object_t *, const char *, int );
-static int AllocatePluginFile ( vlc_object_t *, char *, int64_t, int64_t );
-static module_t * AllocatePlugin( vlc_object_t *, char * );
+static void AllocateAllPlugins( vlc_object_t *, module_bank_t * );
+static void AllocatePluginDir( vlc_object_t *, module_bank_t *, const char *,
+ unsigned );
+static int AllocatePluginFile( vlc_object_t *, module_bank_t *, const char *,
+ int64_t, int64_t );
+static module_t * AllocatePlugin( vlc_object_t *, const char * );
#endif
static int AllocateBuiltinModule( vlc_object_t *, int ( * ) ( module_t * ) );
-static void DeleteModule ( module_t * );
+static void DeleteModule ( module_bank_t *, module_t * );
#ifdef HAVE_DYNAMIC_PLUGINS
static void DupModule ( module_t * );
static void UndupModule ( module_t * );
{
module_bank_t *p_bank = NULL;
- vlc_mutex_t *lock = var_AcquireMutex( "libvlc" );
+ vlc_mutex_lock( &module_lock );
if( p_module_bank == NULL )
{
p_bank->i_usage = 1;
p_bank->i_cache = p_bank->i_loaded_cache = 0;
p_bank->pp_cache = p_bank->pp_loaded_cache = NULL;
- p_bank->b_cache = p_bank->b_cache_dirty =
- p_bank->b_cache_delete = false;
+ p_bank->b_cache = p_bank->b_cache_dirty = false;
p_bank->head = NULL;
/* Everything worked, attach the object */
else
p_module_bank->i_usage++;
- vlc_mutex_unlock( lock );
+ /* We do retain the module bank lock until the plugins are loaded as well.
+ * This is ugly, this staged loading approach is needed: LibVLC gets
+ * some configuration parameters relevant to loading the plugins from
+ * the main (builtin) module. The module bank becomes shared read-only data
+ * once it is ready, so we need to fully serialize initialization.
+ * DO NOT UNCOMMENT the following line unless you managed to squeeze
+ * module_LoadPlugins() before you unlock the mutex. */
+ /*vlc_mutex_unlock( &module_lock );*/
}
-
+#undef module_EndBank
/**
- * End bank
- *
* Unloads all unused plugin modules and empties the module
* bank in case of success.
* \param p_this vlc object structure
* \return nothing
*/
-void __module_EndBank( vlc_object_t *p_this )
+void module_EndBank( vlc_object_t *p_this, bool b_plugins )
{
- module_bank_t *p_bank;
+ module_bank_t *p_bank = p_module_bank;
- vlc_mutex_t *lock = var_AcquireMutex( "libvlc" );
- p_bank = p_module_bank;
assert (p_bank != NULL);
+
+ /* Save the configuration */
+ if( !config_GetInt( p_this, "ignore-config" ) )
+ config_AutoSaveConfigFile( p_this );
+
+ /* If plugins were _not_ loaded, then the caller still has the bank lock
+ * from module_InitBank(). */
+ if( b_plugins )
+ vlc_mutex_lock( &module_lock );
+ /*else
+ vlc_assert_locked( &module_lock ); not for static mutexes :( */
+
if( --p_bank->i_usage > 0 )
{
- vlc_mutex_unlock( lock );
+ vlc_mutex_unlock( &module_lock );
return;
}
- /*FIXME: For thread safety, we need to:
- p_module_bank = NULL; - immediately, but that will crash the cache */
- vlc_mutex_unlock( lock );
-
- /* Save the configuration */
- config_AutoSaveConfigFile( p_this );
+ p_module_bank = NULL;
+ vlc_mutex_unlock( &module_lock );
#ifdef HAVE_DYNAMIC_PLUGINS
- if( p_bank->b_cache ) CacheSave( p_this );
+ if( p_bank->b_cache )
+ CacheSave( p_this, p_bank );
while( p_bank->i_loaded_cache-- )
{
if( p_bank->pp_loaded_cache[p_bank->i_loaded_cache] )
{
- DeleteModule(
+ DeleteModule( p_bank,
p_bank->pp_loaded_cache[p_bank->i_loaded_cache]->p_module );
free( p_bank->pp_loaded_cache[p_bank->i_loaded_cache]->psz_file );
free( p_bank->pp_loaded_cache[p_bank->i_loaded_cache] );
#endif
while( p_bank->head != NULL )
- DeleteModule( p_bank->head );
+ DeleteModule( p_bank, p_bank->head );
- p_module_bank = NULL; /* FIXME: do this inside the lock */
free( p_bank );
}
+#undef module_LoadPlugins
/**
- * Load all modules which we built with.
+ * Loads module descriptions for all available plugins.
+ * Fills the module bank structure with the plugin modules.
*
- * Fills the module bank structure with the builtin modules.
* \param p_this vlc object structure
* \return nothing
*/
-void __module_LoadBuiltins( vlc_object_t * p_this )
+void module_LoadPlugins( vlc_object_t * p_this, bool b_cache_delete )
{
- vlc_mutex_t *lock = var_AcquireMutex( "libvlc" );
- if( p_module_bank->b_builtins )
- {
- vlc_mutex_unlock( lock );
- return;
- }
- p_module_bank->b_builtins = true;
- vlc_mutex_unlock( lock );
+ module_bank_t *p_bank = p_module_bank;
- msg_Dbg( p_this, "checking builtin modules" );
- ALLOCATE_ALL_BUILTINS();
-}
+ assert( p_bank );
+ /*vlc_assert_locked( &module_lock ); not for static mutexes :( */
-/**
- * Load all plugins
- *
- * Load all plugin modules we can find.
- * Fills the module bank structure with the plugin modules.
- * \param p_this vlc object structure
- * \return nothing
- */
-void __module_LoadPlugins( vlc_object_t * p_this )
-{
#ifdef HAVE_DYNAMIC_PLUGINS
- vlc_mutex_t *lock = var_AcquireMutex( "libvlc" );
- if( p_module_bank->b_plugins )
+ if( p_bank->i_usage == 1 )
{
- vlc_mutex_unlock( lock );
- return;
- }
- p_module_bank->b_plugins = true;
- vlc_mutex_unlock( lock );
-
- msg_Dbg( p_this, "checking plugin modules" );
-
- if( config_GetInt( p_this, "plugins-cache" ) )
- p_module_bank->b_cache = true;
+ msg_Dbg( p_this, "checking plugin modules" );
+ p_module_bank->b_cache = config_GetInt( p_this, "plugins-cache" ) > 0;
- if( p_module_bank->b_cache ||
- p_module_bank->b_cache_delete ) CacheLoad( p_this );
-
- AllocateAllPlugins( p_this );
+ if( p_module_bank->b_cache || b_cache_delete )
+ CacheLoad( p_this, p_module_bank, b_cache_delete );
+ AllocateAllPlugins( p_this, p_module_bank );
+ }
#endif
+ p_module_bank->b_plugins = true;
+ vlc_mutex_unlock( &module_lock );
}
/**
if( long_name && ( m->psz_longname != NULL) )
return m->psz_longname;
- return m->psz_shortname ?: m->psz_object_name;
+ return m->psz_shortname ? m->psz_shortname : m->psz_object_name;
}
/**
return m->psz_help;
}
+/**
+ * Get the capability for a module
+ *
+ * \param m the module
+ * return the capability
+ */
+const char *module_get_capability( const module_t *m )
+{
+ return m->psz_capability;
+}
+
+/**
+ * Get the score for a module
+ *
+ * \param m the module
+ * return the score for the capability
+ */
+int module_get_score( const module_t *m )
+{
+ return m->i_score;
+}
+
module_t *module_hold (module_t *m)
{
vlc_hold (&m->vlc_gc_data);
/**
* Frees the flat list of VLC modules.
- * @param list list obtained by module_list_get
+ * @param list list obtained by module_list_get()
* @param length number of items on the list
* @return nothing.
*/
module_t **tab = NULL;
size_t i = 0;
+ assert (p_module_bank);
for (module_t *mod = p_module_bank->head; mod; mod = mod->next)
{
module_t **nt;
typedef struct module_list_t
{
module_t *p_module;
- uint16_t i_score;
+ int16_t i_score;
bool b_force;
} module_list_t;
if( b_strict )
continue;
}
- /* If we didn't require a shortcut, trash <= 0 scored plugins */
- else if( p_module->i_score <= 0 )
- {
+
+ /* Trash <= 0 scored plugins (they can only be selected by shortcut) */
+ if( p_module->i_score <= 0 )
continue;
- }
found_shortcut:
/* Store this new module */
/* Sort candidates by descending score */
qsort (p_list, count, sizeof (p_list[0]), modulecmp);
- msg_Dbg( p_this, "looking for %s module: %i candidate%s", psz_capability,
+ msg_Dbg( p_this, "looking for %s module: %zu candidate%s", psz_capability,
count, count == 1 ? "" : "s" );
/* Parse the linked list and use the first successful module */
if( p_new_module )
{
CacheMerge( p_this, p_real, p_new_module );
- DeleteModule( p_new_module );
+ DeleteModule( p_module_bank, p_new_module );
}
}
#endif
else if( count == 0 )
{
if( !strcmp( psz_capability, "access_demux" )
+ || !strcmp( psz_capability, "stream_filter" )
|| !strcmp( psz_capability, "vout_window" ) )
{
msg_Dbg( p_this, "no %s module matched \"%s\"",
/**
* Get a pointer to a module_t given it's name.
*
- * \param p_this vlc object structure
* \param psz_name the name of the module
* \return a pointer to the module or NULL in case of a failure
*/
-module_t *__module_find( vlc_object_t *p_this, const char * psz_name )
+module_t *module_find( const char * psz_name )
{
module_t **list, *module;
/**
* Tell if a module exists and release it in thic case
*
- * \param p_this vlc object structure
* \param psz_name th name of the module
* \return TRUE if the module exists
*/
-bool __module_exists( vlc_object_t *p_this, const char * psz_name )
+bool module_exists (const char * psz_name)
{
- module_t *p_module = __module_find( p_this, psz_name );
+ module_t *p_module = module_find (psz_name);
if( p_module )
module_release (p_module);
return p_module != NULL;
}
+/**
+ * Get a pointer to a module_t that matches a shortcut.
+ * This is a temporary hack for SD. Do not re-use (generally multiple modules
+ * can have the same shortcut, so this is *broken* - use module_need()!).
+ *
+ * \param psz_shortcut shortcut of the module
+ * \param psz_cap capability of the module
+ * \return a pointer to the module or NULL in case of a failure
+ */
+module_t *module_find_by_shortcut (const char *psz_shortcut)
+{
+ module_t **list, *module;
+
+ list = module_list_get (NULL);
+ if (!list)
+ return NULL;
+
+ for (size_t i = 0; (module = list[i]) != NULL; i++)
+ {
+ for (size_t j = 0;
+ (module->pp_shortcuts[j] != NULL) && (j < MODULE_SHORTCUT_MAX);
+ j++)
+ {
+ if (!strcmp (module->pp_shortcuts[j], psz_shortcut))
+ {
+ module_hold (module);
+ goto out;
+ }
+ }
+ }
+out:
+ module_list_free (list);
+ return module;
+}
+
/**
* GetModuleNamesForCapability
*
* Return a NULL terminated array with the names of the modules
* that have a certain capability.
* Free after uses both the string and the table.
- * \param p_this vlc object structure
* \param psz_capability the capability asked
* \param pppsz_longname an pointer to an array of string to contain
the long names of the modules. If set to NULL the function don't use it.
* \return the NULL terminated array
*/
-char ** __module_GetModulesNamesForCapability( vlc_object_t *p_this,
- const char *psz_capability,
- char ***pppsz_longname )
+char ** module_GetModulesNamesForCapability( const char *psz_capability,
+ char ***pppsz_longname )
{
size_t count = 0;
char **psz_ret;
module_t **list = module_list_get (NULL);
- /* Do it in two passes : count the number of modules before */
+ /* Proceed in two passes: count the number of modules first */
for (size_t i = 0; list[i]; i++)
{
module_t *p_module = list[i];
const char *psz_module_capability = p_module->psz_capability;
- if( psz_module_capability && !strcmp( psz_module_capability, psz_capability ) )
+ if( psz_module_capability
+ && !strcmp( psz_module_capability, psz_capability ) )
count++;
}
+ /* Then get the names */
psz_ret = malloc( sizeof(char*) * (count+1) );
if( pppsz_longname )
*pppsz_longname = malloc( sizeof(char*) * (count+1) );
if( !psz_ret || ( pppsz_longname && *pppsz_longname == NULL ) )
{
free( psz_ret );
- free( *pppsz_longname );
- *pppsz_longname = NULL;
+ if( pppsz_longname )
+ {
+ free( *pppsz_longname );
+ *pppsz_longname = NULL;
+ }
module_list_free (list);
return NULL;
}
module_t *p_module = list[i];
const char *psz_module_capability = p_module->psz_capability;
- if( psz_module_capability && !strcmp( psz_module_capability, psz_capability ) )
+ if( psz_module_capability
+ && !strcmp( psz_module_capability, psz_capability ) )
{
- int k = -1; /* hack to handle submodules properly */
- if( p_module->b_submodule )
- {
- while( p_module->pp_shortcuts[++k] != NULL );
- k--;
- }
- psz_ret[j] = strdup( k>=0?p_module->pp_shortcuts[k]
- :p_module->psz_object_name );
+ /* Explicit hack: Use the last shortcut. It _should_ be
+ * different from the object name, at least if the object
+ * contains multiple submodules with the same capability. */
+ unsigned k = 0;
+ while( p_module->pp_shortcuts[k] != NULL )
+ k++;
+ assert( k > 0); /* pp_shortcuts[0] is always set */
+ psz_ret[j] = strdup( p_module->pp_shortcuts[k - 1] );
if( pppsz_longname )
(*pppsz_longname)[j] = strdup( module_get_name( p_module, true ) );
j++;
return path;
}
+char *psz_vlcpath = NULL;
+
/*****************************************************************************
* AllocateAllPlugins: load all plugin modules we can find.
*****************************************************************************/
#ifdef HAVE_DYNAMIC_PLUGINS
-static void AllocateAllPlugins( vlc_object_t *p_this )
+static void AllocateAllPlugins( vlc_object_t *p_this, module_bank_t *p_bank )
{
- const char *vlcpath = vlc_global()->psz_vlcpath;
+ const char *vlcpath = psz_vlcpath;
int count,i;
char * path;
vlc_array_t *arraypaths = vlc_array_new();
msg_Dbg( p_this, "recursively browsing `%s'", path );
/* Don't go deeper than 5 subdirectories */
- AllocatePluginDir( p_this, path, 5 );
+ AllocatePluginDir( p_this, p_bank, path, 5 );
free( path );
}
/*****************************************************************************
* AllocatePluginDir: recursively parse a directory to look for plugins
*****************************************************************************/
-static void AllocatePluginDir( vlc_object_t *p_this, const char *psz_dir,
- int i_maxdepth )
+static void AllocatePluginDir( vlc_object_t *p_this, module_bank_t *p_bank,
+ const char *psz_dir, unsigned i_maxdepth )
{
/* FIXME: Needs to be ported to wide char on ALL Windows builds */
#ifdef WIN32
#endif
char * psz_file;
- if( p_this->p_libvlc->b_die || i_maxdepth < 0 )
- {
+ if( i_maxdepth == 0 )
return;
- }
#if defined( UNDER_CE ) || defined( _MSC_VER )
#ifdef UNDER_CE
if( GetFileAttributes( psz_path ) & FILE_ATTRIBUTE_DIRECTORY )
#endif
{
- AllocatePluginDir( p_this, psz_path, i_maxdepth - 1 );
+ AllocatePluginDir( p_this, p_bank, psz_path, i_maxdepth - 1 );
}
else if( i_len > strlen( LIBEXT )
/* We only load files ending with LIBEXT */
}
psz_file = psz_path;
- AllocatePluginFile( p_this, psz_file, i_time, i_size );
+ AllocatePluginFile( p_this, p_bank, psz_file, i_time, i_size );
}
}
while( !p_this->p_libvlc->b_die && FindNextFile( handle, &finddata ) );
i_stat = stat( psz_file, &statbuf );
if( !i_stat && statbuf.st_mode & S_IFDIR )
{
- AllocatePluginDir( p_this, psz_file, i_maxdepth - 1 );
+ AllocatePluginDir( p_this, p_bank, psz_file, i_maxdepth - 1 );
}
else if( i_len > strlen( LIBEXT )
/* We only load files ending with LIBEXT */
i_size = statbuf.st_size;
}
- AllocatePluginFile( p_this, psz_file, i_time, i_size );
+ AllocatePluginFile( p_this, p_bank, psz_file, i_time, i_size );
}
free( psz_file );
* for its information data. The module can then be handled by module_need
* and module_unneed. It can be removed by DeleteModule.
*****************************************************************************/
-static int AllocatePluginFile( vlc_object_t * p_this, char * psz_file,
+static int AllocatePluginFile( vlc_object_t * p_this, module_bank_t *p_bank,
+ const char *psz_file,
int64_t i_file_time, int64_t i_file_size )
{
module_t * p_module = NULL;
/*
* Check our plugins cache first then load plugin if needed
*/
- p_cache_entry =
- CacheFind( psz_file, i_file_time, i_file_size );
-
+ p_cache_entry = CacheFind( p_bank, psz_file, i_file_time, i_file_size );
if( !p_cache_entry )
{
p_module = AllocatePlugin( p_this, psz_file );
}
else
+ /* If junk dll, don't try to load it */
+ if( p_cache_entry->b_junk )
+ return -1;
+ else
{
- /* If junk dll, don't try to load it */
- if( p_cache_entry->b_junk )
- {
- p_module = NULL;
- }
- else
- {
- module_config_t *p_item = NULL, *p_end = NULL;
+ module_config_t *p_item = NULL, *p_end = NULL;
- p_module = p_cache_entry->p_module;
- p_module->b_loaded = false;
+ p_module = p_cache_entry->p_module;
+ p_module->b_loaded = false;
- /* For now we force loading if the module's config contains
- * callbacks or actions.
- * Could be optimized by adding an API call.*/
- for( p_item = p_module->p_config, p_end = p_item + p_module->confsize;
- p_item < p_end; p_item++ )
+ /* For now we force loading if the module's config contains
+ * callbacks or actions.
+ * Could be optimized by adding an API call.*/
+ for( p_item = p_module->p_config, p_end = p_item + p_module->confsize;
+ p_item < p_end; p_item++ )
+ {
+ if( p_item->pf_callback || p_item->i_action )
{
- if( p_item->pf_callback || p_item->i_action )
- {
- p_module = AllocatePlugin( p_this, psz_file );
- break;
- }
+ p_module = AllocatePlugin( p_this, psz_file );
+ break;
}
- if( p_module == p_cache_entry->p_module )
- p_cache_entry->b_used = true;
}
+ if( p_module == p_cache_entry->p_module )
+ p_cache_entry->b_used = true;
}
- if( p_module )
- {
- /* Everything worked fine !
- * The module is ready to be added to the list. */
- p_module->b_builtin = false;
-
- /* msg_Dbg( p_this, "plugin \"%s\", %s",
- p_module->psz_object_name, p_module->psz_longname ); */
- p_module->next = p_module_bank->head;
- p_module_bank->head = p_module;
-
- if( !p_module_bank->b_cache )
- return 0;
-
-#define p_bank p_module_bank
- /* Add entry to cache */
- p_bank->pp_cache =
- realloc( p_bank->pp_cache, (p_bank->i_cache + 1) * sizeof(void *) );
- p_bank->pp_cache[p_bank->i_cache] = malloc( sizeof(module_cache_t) );
- if( !p_bank->pp_cache[p_bank->i_cache] )
- return -1;
- p_bank->pp_cache[p_bank->i_cache]->psz_file = strdup( psz_file );
- p_bank->pp_cache[p_bank->i_cache]->i_time = i_file_time;
- p_bank->pp_cache[p_bank->i_cache]->i_size = i_file_size;
- p_bank->pp_cache[p_bank->i_cache]->b_junk = p_module ? 0 : 1;
- p_bank->pp_cache[p_bank->i_cache]->b_used = true;
- p_bank->pp_cache[p_bank->i_cache]->p_module = p_module;
- p_bank->i_cache++;
-#undef p_bank
- }
+ if( p_module == NULL )
+ return -1;
+
+ /* Everything worked fine !
+ * The module is ready to be added to the list. */
+ p_module->b_builtin = false;
+
+ /* msg_Dbg( p_this, "plugin \"%s\", %s",
+ p_module->psz_object_name, p_module->psz_longname ); */
+ p_module->next = p_bank->head;
+ p_bank->head = p_module;
+
+ if( !p_module_bank->b_cache )
+ return 0;
+
+ /* Add entry to cache */
+ module_cache_t **pp_cache = p_bank->pp_cache;
- return p_module ? 0 : -1;
+ pp_cache = realloc( pp_cache, (p_bank->i_cache + 1) * sizeof(void *) );
+ if( pp_cache == NULL )
+ return -1;
+ pp_cache[p_bank->i_cache] = malloc( sizeof(module_cache_t) );
+ if( pp_cache[p_bank->i_cache] == NULL )
+ return -1;
+ pp_cache[p_bank->i_cache]->psz_file = strdup( psz_file );
+ pp_cache[p_bank->i_cache]->i_time = i_file_time;
+ pp_cache[p_bank->i_cache]->i_size = i_file_size;
+ pp_cache[p_bank->i_cache]->b_junk = p_module ? 0 : 1;
+ pp_cache[p_bank->i_cache]->b_used = true;
+ pp_cache[p_bank->i_cache]->p_module = p_module;
+ p_bank->pp_cache = pp_cache;
+ p_bank->i_cache++;
+ return 0;
}
/*****************************************************************************
* for its information data. The module can then be handled by module_need
* and module_unneed. It can be removed by DeleteModule.
*****************************************************************************/
-static module_t * AllocatePlugin( vlc_object_t * p_this, char * psz_file )
+static module_t * AllocatePlugin( vlc_object_t * p_this, const char *psz_file )
{
module_t * p_module = NULL;
module_handle_t handle;
return NULL;
}
- /* We need to fill these since they may be needed by module_Call() */
- p_module->psz_filename = psz_file;
+ p_module->psz_filename = strdup( psz_file );
p_module->handle = handle;
p_module->b_loaded = true;
if( module_Call( p_this, p_module ) != 0 )
{
/* We couldn't call module_init() */
+ free( p_module->psz_filename );
module_release( p_module );
module_Unload( handle );
return NULL;
}
DupModule( p_module );
- p_module->psz_filename = strdup( p_module->psz_filename );
/* Everything worked fine ! The module is ready to be added to the list. */
p_module->b_builtin = false;
*****************************************************************************
* This function can only be called if the module isn't being used.
*****************************************************************************/
-static void DeleteModule( module_t * p_module )
+static void DeleteModule( module_bank_t *p_bank, module_t * p_module )
{
assert( p_module );
/* Unlist the module (if it is in the list) */
- module_t **pp_self = &p_module_bank->head;
+ module_t **pp_self = &p_bank->head;
while (*pp_self != NULL && *pp_self != p_module)
pp_self = &((*pp_self)->next);
if (*pp_self)