X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fmodules.c;h=7ae18bc477f19e3935d363c63b93d5cc2b178de3;hb=a70f8bb371466209770c4c3bcdb7137b94acef66;hp=334e3a0cf9814349fa8381e92aa00ab6993e26b3;hpb=ffa693e8bdac429c229fa679e64a8bd6cd6af15f;p=vlc diff --git a/src/misc/modules.c b/src/misc/modules.c index 334e3a0cf9..7ae18bc477 100644 --- a/src/misc/modules.c +++ b/src/misc/modules.c @@ -1,7 +1,8 @@ /***************************************************************************** - * modules.c : Built-in and dynamic modules management functions + * modules.c : Built-in and plugin modules management functions ***************************************************************************** * Copyright (C) 2001 VideoLAN + * $Id: modules.c,v 1.28 2001/05/01 04:18:18 sam Exp $ * * Authors: Samuel Hocevar * Ethan C. Baldridge @@ -24,24 +25,33 @@ #include "config.h" +/* Some faulty libcs have a broken struct dirent when _FILE_OFFSET_BITS + * is set to 64. Don't try to be cleverer. */ +#ifdef _FILE_OFFSET_BITS +#undef _FILE_OFFSET_BITS +#endif + #include /* free(), strtol() */ #include /* sprintf() */ #include /* strdup() */ #include #if defined(HAVE_DLFCN_H) /* Linux, BSD, Hurd */ -#include /* dlopen(), dlsym(), dlclose() */ - +# include /* dlopen(), dlsym(), dlclose() */ +# define HAVE_DYNAMIC_PLUGINS #elif defined(HAVE_IMAGE_H) /* BeOS */ -#include - +# include +# define HAVE_DYNAMIC_PLUGINS #else -/* FIXME: this isn't supposed to be an error */ -#error no dynamic plugins available on your system ! +# undef HAVE_DYNAMIC_PLUGINS #endif #ifdef SYS_BEOS -#include "beos_specific.h" +# include "beos_specific.h" +#endif + +#ifdef SYS_DARWIN1_3 +# include "darwin_specific.h" #endif #include "common.h" @@ -49,59 +59,90 @@ #include "intf_msg.h" #include "modules.h" -#include "modules_core.h" +#ifdef HAVE_DYNAMIC_PLUGINS +# include "modules_core.h" +#endif +#include "modules_builtin.h" /* Local prototypes */ -static int AllocateDynModule( module_bank_t * p_bank, char * psz_filename ); -static int HideModule( module_t * p_module ); -static int FreeModule( module_bank_t * p_bank, module_t * p_module ); -static int CallSymbol( module_t * p_module, char * psz_name ); - -/***************************************************************************** - * module_CreateBank: create the module bank. - ***************************************************************************** - * This function creates a module bank structure. - *****************************************************************************/ -module_bank_t * module_CreateBank( void ) -{ - module_bank_t * p_bank; - - p_bank = malloc( sizeof( module_bank_t ) ); - - return( p_bank ); -} +#ifdef HAVE_DYNAMIC_PLUGINS +static int AllocatePluginModule ( char * ); +#endif +static int AllocateBuiltinModule( int ( * ) ( module_t * ), + int ( * ) ( module_t * ), + int ( * ) ( module_t * ) ); +static int DeleteModule ( module_t * ); +static int LockModule ( module_t * ); +static int UnlockModule ( module_t * ); +#ifdef HAVE_DYNAMIC_PLUGINS +static int HideModule ( module_t * ); +static int CallSymbol ( module_t *, char * ); +#endif /***************************************************************************** * module_InitBank: create the module bank. ***************************************************************************** * This function creates a module bank structure and fills it with the - * built-in modules, as well as all the dynamic modules it can find. + * built-in modules, as well as all the plugin modules it can find. *****************************************************************************/ -void module_InitBank( module_bank_t * p_bank ) +void module_InitBank( ) { - static char * path[] = { ".", "lib", PLUGIN_PATH, NULL } ; +#ifdef HAVE_DYNAMIC_PLUGINS + static char * path[] = { ".", "lib", PLUGIN_PATH, NULL, NULL }; char ** ppsz_path = path; + char * psz_fullpath; char * psz_file; -#ifdef SYS_BEOS - char * psz_program_path = beos_GetProgramPath(); - int i_programlen = strlen( psz_program_path ); +#if defined( SYS_BEOS ) || defined( SYS_DARWIN1_3 ) + char * psz_vlcpath = system_GetProgramPath(); + int i_vlclen = strlen( psz_vlcpath ); + boolean_t b_notinroot; #endif DIR * dir; struct dirent * file; +#endif /* HAVE_DYNAMIC_PLUGINS */ p_bank->first = NULL; vlc_mutex_init( &p_bank->lock ); - intf_Msg( "module: module bank initialized" ); + intf_WarnMsg( 2, "module: checking built-in modules" ); + + ALLOCATE_ALL_BUILTINS(); + +#ifdef HAVE_DYNAMIC_PLUGINS + intf_WarnMsg( 2, "module: checking plugin modules" ); for( ; *ppsz_path != NULL ; ppsz_path++ ) { - if( (dir = opendir( *ppsz_path )) ) + /* Store strlen(*ppsz_path) for later use. */ + int i_dirlen = strlen( *ppsz_path ); + +#if defined( SYS_BEOS ) || defined( SYS_DARWIN1_3 ) + b_notinroot = 0; + /* Under BeOS, we need to add beos_GetProgramPath() to access + * files under the current directory */ + if( ( i_dirlen > 1 ) && strncmp( *ppsz_path, "/", 1 ) ) + { + i_dirlen += i_vlclen + 2; + b_notinroot = 1; + + psz_fullpath = malloc( i_dirlen ); + if( psz_fullpath == NULL ) + { + continue; + } + sprintf( psz_fullpath, "%s/%s", psz_vlcpath, *ppsz_path ); + } + else +#endif { - /* Store strlen(*ppsz_path) for later use. */ - int i_dirlen = strlen( *ppsz_path ); + psz_fullpath = *ppsz_path; + } + intf_WarnMsgImm( 3, "module: browsing `%s'", psz_fullpath ); + + if( (dir = opendir( psz_fullpath )) ) + { /* Parse the directory and try to load all files it contains. */ while( (file = readdir( dir )) ) { @@ -109,59 +150,55 @@ void module_InitBank( module_bank_t * p_bank ) /* We only load files ending with ".so" */ if( i_filelen > 3 - && !strcmp( file->d_name + i_filelen - 3, ".so" ) ) + && !strncmp( file->d_name + i_filelen - 3, ".so", 3 ) ) { -#ifdef SYS_BEOS - /* Under BeOS, we need to add beos_GetProgramPath() to - * access files under the current directory */ - if( memcmp( file->d_name, "/", 1 ) ) + psz_file = malloc( i_dirlen + i_filelen + 2 ); + if( psz_file == NULL ) { - psz_file = malloc( i_programlen + i_dirlen - + i_filelen + 3 ); - if( psz_file == NULL ) - { - continue; - } - sprintf( psz_file, "%s/%s/%s", psz_programlen, - *ppsz_path, file->d_name ); - } - else -#endif - { - psz_file = malloc( i_dirlen + i_filelen + 2 ); - if( psz_file == NULL ) - { - continue; - } - sprintf( psz_file, "%s/%s", *ppsz_path, file->d_name ); + continue; } + sprintf( psz_file, "%s/%s", psz_fullpath, file->d_name ); + /* We created a nice filename -- now we just try to load - * it as a dynamic module. */ - AllocateDynModule( p_bank, psz_file ); + * it as a plugin module. */ + AllocatePluginModule( psz_file ); /* We don't care if the allocation succeeded */ free( psz_file ); } } + + /* Close the directory if successfully opened */ + closedir( dir ); + } + +#if defined( SYS_BEOS ) || defined( SYS_DARWIN1_3 ) + if( b_notinroot ) + { + free( psz_fullpath ); } +#endif } +#endif /* HAVE_DYNAMIC_PLUGINS */ + + intf_WarnMsg( 1, "module: module bank initialized" ); return; } /***************************************************************************** - * module_DestroyBank: destroy the module bank. + * module_EndBank: destroy the module bank. ***************************************************************************** - * This function unloads all unused dynamic modules and removes the module + * This function unloads all unused plugin modules and removes the module * bank in case of success. *****************************************************************************/ -void module_DestroyBank( module_bank_t * p_bank ) +void module_EndBank( ) { module_t * p_next; while( p_bank->first != NULL ) { - if( FreeModule( p_bank, p_bank->first ) ) + if( DeleteModule( p_bank->first ) ) { /* Module deletion failed */ intf_ErrMsg( "module error: `%s' can't be removed. trying harder.", @@ -174,8 +211,8 @@ void module_DestroyBank( module_bank_t * p_bank ) } } - /* We can free the module bank */ - free( p_bank ); + /* Destroy the lock */ + vlc_mutex_destroy( &p_bank->lock ); return; } @@ -183,10 +220,10 @@ void module_DestroyBank( module_bank_t * p_bank ) /***************************************************************************** * module_ResetBank: reset the module bank. ***************************************************************************** - * This function resets the module bank by unloading all unused dynamic + * This function resets the module bank by unloading all unused plugin * modules. *****************************************************************************/ -void module_ResetBank( module_bank_t * p_bank ) +void module_ResetBank( ) { intf_ErrMsg( "FIXME: module_ResetBank unimplemented" ); return; @@ -198,8 +235,9 @@ void module_ResetBank( module_bank_t * p_bank ) * This function parses the module bank and hides modules that have been * unused for a while. *****************************************************************************/ -void module_ManageBank( module_bank_t * p_bank ) +void module_ManageBank( ) { +#ifdef HAVE_DYNAMIC_PLUGINS module_t * p_module; /* We take the global lock */ @@ -210,7 +248,7 @@ void module_ManageBank( module_bank_t * p_bank ) p_module != NULL ; p_module = p_module->next ) { - /* If the module is unused and if it is a dynamic module... */ + /* If the module is unused and if it is a plugin module... */ if( p_module->i_usage == 0 && !p_module->b_builtin ) { if( p_module->i_unused_delay < MODULE_HIDE_DELAY ) @@ -219,125 +257,149 @@ void module_ManageBank( module_bank_t * p_bank ) } else { - intf_Msg( "module: hiding unused module `%s'", - p_module->psz_name ); + intf_WarnMsg( 3, "module: hiding unused plugin module `%s'", + p_module->psz_name ); HideModule( p_module ); + + /* Break here, so that we only hide one module at a time */ + break; } } } /* We release the global lock */ vlc_mutex_unlock( &p_bank->lock ); +#endif /* HAVE_DYNAMIC_PLUGINS */ return; } /***************************************************************************** - * module_Need: increase the usage count of a module and load it if needed. + * module_Need: return the best module function, given a capability list. ***************************************************************************** - * This function has to be called before a thread starts using a module. If - * the module is already loaded, we just increase its usage count. If it isn't - * loaded, we have to dynamically open it and initialize it. - * If you successfully call module_Need() at any moment, be careful to call - * module_Unneed() when you don't need it anymore. + * This function returns the module that best fits the asked capabilities. *****************************************************************************/ -int module_Need( module_t * p_module ) +module_t * module_Need( int i_capabilities, void *p_data ) { - if( p_module->i_usage >= 0 ) - { - /* This module is already loaded and activated, we can return */ - p_module->i_usage++; - return( 0 ); - } + module_t * p_module; + module_t * p_bestmodule = NULL; + int i_score, i_totalscore, i_bestscore = 0; + int i_index; - if( p_module->b_builtin ) - { - /* A built-in module should always have a refcount >= 0 ! */ - intf_ErrMsg( "module error: built-in module `%s' has refcount %i", - p_module->psz_name, p_module->i_usage ); - return( -1 ); - } + /* We take the global lock */ + vlc_mutex_lock( &p_bank->lock ); - if( p_module->i_usage != -1 ) + /* Parse the module list for capabilities and probe each of them */ + for( p_module = p_bank->first ; + p_module != NULL ; + p_module = p_module->next ) { - /* This shouldn't happen. Ever. We have serious problems here. */ - intf_ErrMsg( "module error: dynamic module `%s' has refcount %i", - p_module->psz_name, p_module->i_usage ); - return( -1 ); - } + /* Test that this module can do everything we need */ + if( ( p_module->i_capabilities & i_capabilities ) == i_capabilities ) + { + i_totalscore = 0; - /* i_usage == -1, which means that the module isn't in memory */ - if( ! module_load( p_module->psz_filename, &p_module->handle ) ) - { - /* The dynamic module couldn't be opened */ - intf_ErrMsg( "module error: cannot open %s (%s)", - p_module->psz_filename, module_error() ); - return( -1 ); + LockModule( p_module ); + + /* Parse all the requested capabilities and test them */ + for( i_index = 0 ; (1 << i_index) <= i_capabilities ; i_index++ ) + { + if( ( (1 << i_index) & i_capabilities ) ) + { + i_score = ( (function_list_t *)p_module->p_functions) + [i_index].pf_probe( p_data ); + + if( i_score ) + { + i_totalscore += i_score; + } + else + { + break; + } + } + } + + /* If the high score was broken, we have a new champion */ + if( i_totalscore > i_bestscore ) + { + /* Keep the current module locked, but release the previous */ + if( p_bestmodule != NULL ) + { + UnlockModule( p_bestmodule ); + } + + /* This is the new best module */ + i_bestscore = i_totalscore; + p_bestmodule = p_module; + } + else + { + /* This module wasn't interesting, unlock it and forget it */ + UnlockModule( p_module ); + } + } } - if( CallSymbol( p_module, "ActivateModule" ) != 0 ) + /* We can release the global lock, module refcount was incremented */ + vlc_mutex_unlock( &p_bank->lock ); + + if( p_bestmodule != NULL ) { - /* We couldn't call ActivateModule() -- looks nasty, but - * we can't do much about it. Just try to unload module. */ - module_unload( p_module->handle ); - p_module->i_usage = -1; - return( -1 ); + intf_WarnMsg( 3, "module: locking module `%s'", + p_bestmodule->psz_name ); } - /* Everything worked fine ! The module is ready to be used */ - p_module->i_usage = 1; - - return( 0 ); + /* Don't forget that the module is still locked if bestmodule != NULL */ + return( p_bestmodule ); } /***************************************************************************** * module_Unneed: decrease the usage count of a module. ***************************************************************************** - * This function has to be called before a thread starts using a module. If - * the module is already loaded, we just increase its usage count. If it isn't - * loaded, we have to dynamically open it and initialize it. - * If you successfully call module_Need() at any moment, be careful to call - * module_Unneed() when you don't need it anymore. + * This function must be called by the thread that called module_Need, to + * decrease the reference count and allow for hiding of modules. *****************************************************************************/ -int module_Unneed( module_t * p_module ) +void module_Unneed( module_t * p_module ) { - if( p_module->i_usage <= 0 ) - { - /* This shouldn't happen. Ever. We have serious problems here. */ - intf_ErrMsg( "module error: trying to call module_Unneed() on `%s'" - " which isn't even in use", p_module->psz_name ); - return( -1 ); - } + /* We take the global lock */ + vlc_mutex_lock( &p_bank->lock ); - /* This module is still in use, we can return */ - p_module->i_usage--; - p_module->i_unused_delay = 0; + /* Just unlock the module - we can't do anything if it fails, + * so there is no need to check the return value. */ + UnlockModule( p_module ); - return( 0 ); + intf_WarnMsg( 3, "module: unlocking module `%s'", p_module->psz_name ); + + /* We release the global lock */ + vlc_mutex_unlock( &p_bank->lock ); + + return; } /***************************************************************************** * Following functions are local. *****************************************************************************/ +#ifdef HAVE_DYNAMIC_PLUGINS /***************************************************************************** - * AllocateDynModule: load a module into memory and initialize it. + * AllocatePluginModule: load a module into memory and initialize it. ***************************************************************************** * This function loads a dynamically loadable module and allocates a structure * for its information data. The module can then be handled by module_Need, - * module_Unneed and HideModule. It can be removed by FreeModule. + * module_Unneed and HideModule. It can be removed by DeleteModule. *****************************************************************************/ -static int AllocateDynModule( module_bank_t * p_bank, char * psz_filename ) +static int AllocatePluginModule( char * psz_filename ) { - module_t * p_module; + module_t * p_module, * p_othermodule; module_handle_t handle; /* Try to dynamically load the module. */ - if( ! module_load( psz_filename, &handle ) ) + if( module_load( psz_filename, &handle ) ) { - /* The dynamic module couldn't be opened */ - intf_DbgMsg( "module error: cannot open %s (%s)", - psz_filename, module_error() ); + /* The plugin module couldn't be opened */ + intf_WarnMsgImm( 3, "module warning: cannot open %s (%s)", + psz_filename, module_error() ); return( -1 ); } @@ -352,9 +414,10 @@ static int AllocateDynModule( module_bank_t * p_bank, char * psz_filename ) } /* We need to fill these since they may be needed by CallSymbol() */ - p_module->psz_filename = psz_filename; - p_module->handle = handle; + p_module->is.plugin.psz_filename = psz_filename; + p_module->is.plugin.handle = handle; + /* Initialize the module : fill p_module->psz_name, etc. */ if( CallSymbol( p_module, "InitModule" ) != 0 ) { /* We couldn't call InitModule() */ @@ -363,6 +426,28 @@ static int AllocateDynModule( module_bank_t * p_bank, char * psz_filename ) return( -1 ); } + /* Check that version numbers match */ + if( strcmp( VERSION, p_module->psz_version ) ) + { + free( p_module ); + module_unload( handle ); + return( -1 ); + } + + /* Check that we don't already have a module with this name */ + for( p_othermodule = p_bank->first ; + p_othermodule != NULL ; + p_othermodule = p_othermodule->next ) + { + if( !strcmp( p_othermodule->psz_name, p_module->psz_name ) ) + { + free( p_module ); + module_unload( handle ); + return( -1 ); + } + } + + /* Activate the module : fill the capability structure, etc. */ if( CallSymbol( p_module, "ActivateModule" ) != 0 ) { /* We couldn't call ActivateModule() */ @@ -373,17 +458,18 @@ static int AllocateDynModule( module_bank_t * p_bank, char * psz_filename ) /* We strdup() these entries so that they are still valid when the * module is unloaded. */ - p_module->psz_filename = strdup( p_module->psz_filename ); + p_module->is.plugin.psz_filename = + strdup( p_module->is.plugin.psz_filename ); p_module->psz_name = strdup( p_module->psz_name ); p_module->psz_longname = strdup( p_module->psz_longname ); p_module->psz_version = strdup( p_module->psz_version ); - if( p_module->psz_filename == NULL + if( p_module->is.plugin.psz_filename == NULL || p_module->psz_name == NULL || p_module->psz_longname == NULL || p_module->psz_version == NULL ) { intf_ErrMsg( "module error: can't duplicate strings" ); - free( p_module->psz_filename ); + free( p_module->is.plugin.psz_filename ); free( p_module->psz_name ); free( p_module->psz_longname ); free( p_module->psz_version ); @@ -407,65 +493,121 @@ static int AllocateDynModule( module_bank_t * p_bank, char * psz_filename ) p_module->prev = NULL; p_bank->first = p_module; - intf_Msg( "module: dynamic module `%s', %s", - p_module->psz_name, p_module->psz_longname ); + /* Immediate message so that a slow module doesn't make the user wait */ + intf_WarnMsgImm( 2, "module: plugin module `%s', %s", + p_module->psz_name, p_module->psz_longname ); return( 0 ); } +#endif /* HAVE_DYNAMIC_PLUGINS */ /***************************************************************************** - * HideModule: remove a module from memory but keep its structure. + * AllocateBuiltinModule: initialize a built-in module. ***************************************************************************** - * This function can only be called if i_usage == 0. It will make a call - * to the module's inner DeactivateModule() symbol, and then unload it - * from memory. A call to module_Need() will automagically load it again. + * This function registers a built-in module and allocates a structure + * for its information data. The module can then be handled by module_Need, + * module_Unneed and HideModule. It can be removed by DeleteModule. *****************************************************************************/ -static int HideModule( module_t * p_module ) +static int AllocateBuiltinModule( int ( *pf_init ) ( module_t * ), + int ( *pf_activate ) ( module_t * ), + int ( *pf_deactivate ) ( module_t * ) ) { - if( p_module->b_builtin ) + module_t * p_module, * p_othermodule; + + /* Now that we have successfully loaded the module, we can + * allocate a structure for it */ + p_module = malloc( sizeof( module_t ) ); + if( p_module == NULL ) { - /* A built-in module should never be hidden. */ - intf_ErrMsg( "module error: trying to hide built-in module `%s'", - p_module->psz_name ); + intf_ErrMsg( "module error: can't allocate p_module" ); return( -1 ); } - if( p_module->i_usage >= 1 ) + /* Initialize the module : fill p_module->psz_name, etc. */ + if( pf_init( p_module ) != 0 ) { - intf_ErrMsg( "module error: trying to hide module `%s' which is still" - " in use", p_module->psz_name ); + /* With a well-written module we shouldn't have to print an + * additional error message here, but just make sure. */ + intf_ErrMsg( "module error: failed calling init in builtin module" ); + free( p_module ); return( -1 ); } - if( p_module->i_usage <= -1 ) + /* Check that version numbers match */ + if( strcmp( VERSION, p_module->psz_version ) ) { - intf_ErrMsg( "module error: trying to hide module `%s' which is already" - " hidden", p_module->psz_name ); + free( p_module ); return( -1 ); } - if( CallSymbol( p_module, "DeactivateModule" ) != 0 ) + /* Check that we don't already have a module with this name */ + for( p_othermodule = p_bank->first ; + p_othermodule != NULL ; + p_othermodule = p_othermodule->next ) { - /* We couldn't call DeactivateModule() -- looks nasty, but - * we can't do much about it. Just try to unload module anyway. */ - module_unload( p_module->handle ); - p_module->i_usage = -1; + if( !strcmp( p_othermodule->psz_name, p_module->psz_name ) ) + { + free( p_module ); + return( -1 ); + } + } + + if( pf_activate( p_module ) != 0 ) + { + /* With a well-written module we shouldn't have to print an + * additional error message here, but just make sure. */ + intf_ErrMsg( "module error: failed calling activate " + "in builtin module" ); + free( p_module ); return( -1 ); } - /* Everything worked fine, we can safely unload the module. */ - module_unload( p_module->handle ); - p_module->i_usage = -1; + /* We strdup() these entries so that they are still valid when the + * module is unloaded. */ + p_module->psz_name = strdup( p_module->psz_name ); + p_module->psz_longname = strdup( p_module->psz_longname ); + p_module->psz_version = strdup( p_module->psz_version ); + if( p_module->psz_name == NULL + || p_module->psz_longname == NULL + || p_module->psz_version == NULL ) + { + intf_ErrMsg( "module error: can't duplicate strings" ); + free( p_module->psz_name ); + free( p_module->psz_longname ); + free( p_module->psz_version ); + free( p_module ); + return( -1 ); + } + + /* Everything worked fine ! The module is ready to be added to the list. */ + p_module->i_usage = 0; + p_module->i_unused_delay = 0; + + p_module->b_builtin = 1; + p_module->is.builtin.pf_deactivate = pf_deactivate; + + /* Link module into the linked list */ + if( p_bank->first != NULL ) + { + p_bank->first->prev = p_module; + } + p_module->next = p_bank->first; + p_module->prev = NULL; + p_bank->first = p_module; + + /* Immediate message so that a slow module doesn't make the user wait */ + intf_WarnMsgImm( 2, "module: builtin module `%s', %s", + p_module->psz_name, p_module->psz_longname ); return( 0 ); } /***************************************************************************** - * FreeModule: delete a module and its structure. + * DeleteModule: delete a module and its structure. ***************************************************************************** * This function can only be called if i_usage <= 0. *****************************************************************************/ -static int FreeModule( module_bank_t * p_bank, module_t * p_module ) +static int DeleteModule( module_t * p_module ) { /* If the module is not in use but is still in memory, we first have * to hide it and remove it from memory before we can free the @@ -478,7 +620,13 @@ static int FreeModule( module_bank_t * p_bank, module_t * p_module ) " usage %i", p_module->psz_name, p_module->i_usage ); return( -1 ); } + else + { + /* We deactivate the module now. */ + p_module->is.builtin.pf_deactivate( p_module ); + } } +#ifdef HAVE_DYNAMIC_PLUGINS else { if( p_module->i_usage >= 1 ) @@ -499,6 +647,7 @@ static int FreeModule( module_bank_t * p_bank, module_t * p_module ) } } } +#endif /* Unlink the module from the linked list. */ if( p_module == p_bank->first ) @@ -517,7 +666,12 @@ static int FreeModule( module_bank_t * p_bank, module_t * p_module ) } /* We free the structures that we strdup()ed in Allocate*Module(). */ - free( p_module->psz_filename ); +#ifdef HAVE_DYNAMIC_PLUGINS + if( !p_module->b_builtin ) + { + free( p_module->is.plugin.psz_filename ); + } +#endif free( p_module->psz_name ); free( p_module->psz_longname ); free( p_module->psz_version ); @@ -527,6 +681,144 @@ static int FreeModule( module_bank_t * p_bank, module_t * p_module ) return( 0 ); } +/***************************************************************************** + * LockModule: increase the usage count of a module and load it if needed. + ***************************************************************************** + * This function has to be called before a thread starts using a module. If + * the module is already loaded, we just increase its usage count. If it isn't + * loaded, we have to dynamically open it and initialize it. + * If you successfully call LockModule() at any moment, be careful to call + * UnlockModule() when you don't need it anymore. + *****************************************************************************/ +static int LockModule( module_t * p_module ) +{ + if( p_module->i_usage >= 0 ) + { + /* This module is already loaded and activated, we can return */ + p_module->i_usage++; + return( 0 ); + } + + if( p_module->b_builtin ) + { + /* A built-in module should always have a refcount >= 0 ! */ + intf_ErrMsg( "module error: built-in module `%s' has refcount %i", + p_module->psz_name, p_module->i_usage ); + return( -1 ); + } + +#ifdef HAVE_DYNAMIC_PLUGINS + if( p_module->i_usage != -1 ) + { + /* This shouldn't happen. Ever. We have serious problems here. */ + intf_ErrMsg( "module error: plugin module `%s' has refcount %i", + p_module->psz_name, p_module->i_usage ); + return( -1 ); + } + + /* i_usage == -1, which means that the module isn't in memory */ + if( module_load( p_module->is.plugin.psz_filename, + &p_module->is.plugin.handle ) ) + { + /* The plugin module couldn't be opened */ + intf_ErrMsg( "module error: cannot open %s (%s)", + p_module->is.plugin.psz_filename, module_error() ); + return( -1 ); + } + + /* FIXME: what to do if the guy modified the plugin while it was + * unloaded ? It makes XMMS crash nastily, perhaps we should try + * to be a bit more clever here. */ + + /* Activate the module : fill the capability structure, etc. */ + if( CallSymbol( p_module, "ActivateModule" ) != 0 ) + { + /* We couldn't call ActivateModule() -- looks nasty, but + * we can't do much about it. Just try to unload module. */ + module_unload( p_module->is.plugin.handle ); + p_module->i_usage = -1; + return( -1 ); + } + + /* Everything worked fine ! The module is ready to be used */ + p_module->i_usage = 1; +#endif /* HAVE_DYNAMIC_PLUGINS */ + + return( 0 ); +} + +/***************************************************************************** + * UnlockModule: decrease the usage count of a module. + ***************************************************************************** + * We decrease the usage count of a module so that we know when a module + * becomes unused and can be hidden. + *****************************************************************************/ +static int UnlockModule( module_t * p_module ) +{ + if( p_module->i_usage <= 0 ) + { + /* This shouldn't happen. Ever. We have serious problems here. */ + intf_ErrMsg( "module error: trying to call module_Unneed() on `%s'" + " which isn't even in use", p_module->psz_name ); + return( -1 ); + } + + /* This module is still in use, we can return */ + p_module->i_usage--; + p_module->i_unused_delay = 0; + + return( 0 ); +} + +#ifdef HAVE_DYNAMIC_PLUGINS +/***************************************************************************** + * HideModule: remove a module from memory but keep its structure. + ***************************************************************************** + * This function can only be called if i_usage == 0. It will make a call + * to the module's inner DeactivateModule() symbol, and then unload it + * from memory. A call to module_Need() will automagically load it again. + *****************************************************************************/ +static int HideModule( module_t * p_module ) +{ + if( p_module->b_builtin ) + { + /* A built-in module should never be hidden. */ + intf_ErrMsg( "module error: trying to hide built-in module `%s'", + p_module->psz_name ); + return( -1 ); + } + + if( p_module->i_usage >= 1 ) + { + intf_ErrMsg( "module error: trying to hide module `%s' which is still" + " in use", p_module->psz_name ); + return( -1 ); + } + + if( p_module->i_usage <= -1 ) + { + intf_ErrMsg( "module error: trying to hide module `%s' which is already" + " hidden", p_module->psz_name ); + return( -1 ); + } + + /* Deactivate the module : free the capability structure, etc. */ + if( CallSymbol( p_module, "DeactivateModule" ) != 0 ) + { + /* We couldn't call DeactivateModule() -- looks nasty, but + * we can't do much about it. Just try to unload module anyway. */ + module_unload( p_module->is.plugin.handle ); + p_module->i_usage = -1; + return( -1 ); + } + + /* Everything worked fine, we can safely unload the module. */ + module_unload( p_module->is.plugin.handle ); + p_module->i_usage = -1; + + return( 0 ); +} + /***************************************************************************** * CallSymbol: calls a module symbol. ***************************************************************************** @@ -540,13 +832,15 @@ static int CallSymbol( module_t * p_module, char * psz_name ) symbol_t * p_symbol; /* Try to resolve the symbol */ - p_symbol = module_getsymbol( p_module->handle, psz_name ); + p_symbol = module_getsymbol( p_module->is.plugin.handle, psz_name ); if( !p_symbol ) { /* We couldn't load the symbol */ - intf_DbgMsg( "module warning: cannot find symbol %s in module %s (%s)", - psz_name, p_module->psz_filename, module_error() ); + intf_WarnMsg( 3, "module warning: " + "cannot find symbol %s in module %s (%s)", + psz_name, p_module->is.plugin.psz_filename, + module_error() ); return( -1 ); } @@ -556,11 +850,12 @@ static int CallSymbol( module_t * p_module, char * psz_name ) /* With a well-written module we shouldn't have to print an * additional error message here, but just make sure. */ intf_ErrMsg( "module error: failed calling symbol %s in module %s", - psz_name, p_module->psz_filename ); + psz_name, p_module->is.plugin.psz_filename ); return( -1 ); } /* Everything worked fine, we can return */ return( 0 ); } +#endif /* HAVE_DYNAMIC_PLUGINS */