diff --git a/src/plug-in/Makefile.am b/src/plug-in/Makefile.am index ae5675eef08ac940e627a72bc08ace76bd99c370..f62b7c3dedba166753218480c9c1bad87d89405b 100644 --- a/src/plug-in/Makefile.am +++ b/src/plug-in/Makefile.am @@ -5,5 +5,6 @@ noinst_LTLIBRARIES = libplugin.la SUBDIRS=test libplugin_la_SOURCES = \ - pluginmanager.cpp + pluginmanager.cpp \ + librarymanager.cpp diff --git a/src/plug-in/librarymanager.cpp b/src/plug-in/librarymanager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cc2dd54661cd94ea75375bb40006eebd2bd683f --- /dev/null +++ b/src/plug-in/librarymanager.cpp @@ -0,0 +1,83 @@ +#include "librarymanager.h" + + LibraryManager::LibraryManager (const std::string &filename) + : _filename(filename), _handlePtr(NULL) +{ + _handlePtr = loadLibrary (filename); +} + +LibraryManager::~LibraryManager (void) +{ + unloadLibrary (); +} + +LibraryManager::LibraryHandle LibraryManager::loadLibrary (const std::string &filename) +{ + LibraryHandle pluginHandlePtr = NULL; + const char *error; + + _debug("Loading dynamic library %s\n", filename.c_str()); + + /* Load the library */ + pluginHandlePtr = dlopen( filename.c_str(), RTLD_LAZY ); + if( !pluginHandlePtr ) { + error = dlerror(); + _debug("Error while opening plug-in: %s\n", error); + return NULL; + } + dlerror(); + return pluginHandlePtr; +} + +int LibraryManager::unloadLibrary () +{ + if (_handlePtr == NULL) + return 1; + + _debug("Unloading dynamic library ...\n"); + dlclose( _handlePtr ); + if (dlerror()) + { + _debug("Error unloading the library : %s\n...", dlerror()); + return 1; + } + return 0; +} + +int LibraryManager::resolveSymbol (const std::string &symbol, SymbolHandle *symbolPtr) +{ + SymbolHandle sy = 0; + + if (_handlePtr){ + try { + sy = dlsym(_handlePtr, symbol.c_str()); + if(sy != NULL) { + *symbolPtr = sy; + return 0; + } + } + catch (...) {} + + throw LibraryManagerException ( _filename, symbol, LibraryManagerException::symbolNotFound); + } + else + return 1; +} + + +/************************************************************************************************/ + +LibraryManagerException::LibraryManagerException (const std::string &libraryName, const std::string &details, Reason reason) : + _reason (reason), _details (""), std::runtime_error ("") + +{ + if (_reason == loadingFailed) + _details = "Error when loading " + libraryName + "\n" + details; + else + _details = "Error when resolving symbol " + details + " in " + libraryName; +} + +const char* LibraryManagerException::what () const throw() +{ + return _details.c_str(); +} diff --git a/src/plug-in/librarymanager.h b/src/plug-in/librarymanager.h new file mode 100644 index 0000000000000000000000000000000000000000..5360047e742fa5bb9313679600578b964056dd3a --- /dev/null +++ b/src/plug-in/librarymanager.h @@ -0,0 +1,51 @@ +#ifndef LIBRARY_MANAGER_H +#define LIBRARY_MANAGER_H + +#include "dlfcn.h" +#include <stdexcept> + +#include "global.h" + +class LibraryManager { + + public: + typedef void* LibraryHandle; + typedef void* SymbolHandle; + + LibraryManager (const std::string &filename); + ~LibraryManager (void); + + int resolveSymbol (const std::string &symbol, SymbolHandle *ptr); + + int unloadLibrary (void); + + protected: + LibraryHandle loadLibrary (const std::string &filename); + + private: + std::string _filename; + LibraryHandle _handlePtr; +}; + +class LibraryManagerException : public std::runtime_error { + + public: + + typedef enum Reason { + loadingFailed = 0, + symbolNotFound + }Reason; + + LibraryManagerException (const std::string &libraryName, const std::string &details, Reason reason); + ~LibraryManagerException (void) throw() {} + + inline Reason getReason (void) { return _reason; } + + const char* what () const throw(); + + private: + Reason _reason; + std::string _details; +}; + +#endif // LIBRARY_MANAGER_H diff --git a/src/plug-in/plugin.h b/src/plug-in/plugin.h index 6adac44f980256c13ab9f0c23d0a0e228ec37183..b020cf1474d9474068d7820538c3774d15a9bf98 100644 --- a/src/plug-in/plugin.h +++ b/src/plug-in/plugin.h @@ -4,40 +4,39 @@ #include <string> #include "global.h" +#include "pluginmanager.h" + /* * @file plugin.h * @brief Define a plugin object */ -namespace sflphone { +class Plugin { - class Plugin { + public: + Plugin( const std::string &name ){ + _name = name; + } - public: - Plugin( const std::string &name ){ - _name = name; - } + virtual ~Plugin() {} - virtual ~Plugin() {} + inline std::string getPluginName (void) { return _name; } - inline std::string getPluginName (void) { return _name; } + /** + * Return the minimal core version required so that the plugin could work + * @return int The version required + */ + virtual int initFunc (PluginInfo **info) = 0; - /** - * Return the minimal core version required so that the plugin could work - * @return int The version required - */ - virtual int initFunc (int i) = 0; - - private: - Plugin &operator =(const Plugin &plugin); + private: + Plugin &operator =(const Plugin &plugin); - std::string _name; - }; + std::string _name; +}; -} -typedef ::sflphone::Plugin* createFunc (void); +typedef Plugin* createFunc (void); -typedef void destroyFunc (::sflphone::Plugin*); +typedef void destroyFunc (Plugin*); #endif //PLUGIN_H diff --git a/src/plug-in/pluginmanager.cpp b/src/plug-in/pluginmanager.cpp index 7a259f7fc91f20de57f9fecc75be8d26cfbc6d95..5b5467569afcb20f89d8e8ed55458a4ebfaac7f0 100644 --- a/src/plug-in/pluginmanager.cpp +++ b/src/plug-in/pluginmanager.cpp @@ -3,10 +3,10 @@ #include "pluginmanager.h" -::sflphone::PluginManager* ::sflphone::PluginManager::_instance = 0; +PluginManager* PluginManager::_instance = 0; - ::sflphone::PluginManager* -::sflphone::PluginManager::instance() + PluginManager* +PluginManager::instance() { if(!_instance){ return new PluginManager(); @@ -14,28 +14,26 @@ return _instance; } -::sflphone::PluginManager::PluginManager() - :_loadedPlugins() + PluginManager::PluginManager() +:_loadedPlugins() { _instance = this; } -::sflphone::PluginManager::~PluginManager() +PluginManager::~PluginManager() { _instance = 0; } int -::sflphone::PluginManager::loadPlugins (const std::string &path) +PluginManager::loadPlugins (const std::string &path) { std::string pluginDir, current; - ::sflphone::Plugin *plugin; DIR *dir; dirent *dirStruct; - int result=0; - void *handle; - createFunc* createPlugin; - + LibraryManager *library; + Plugin *plugin; + const std::string pDir = ".."; const std::string cDir = "."; @@ -52,22 +50,25 @@ current = dirStruct->d_name; /* Test if the current item is not the parent or the current directory */ if( current != pDir && current != cDir ){ - handle = loadDynamicLibrary( pluginDir + current ); - - if(instanciatePlugin (handle, &plugin) != 0) + + /* Load the dynamic library */ + library = loadDynamicLibrary( pluginDir + current ); + + /* Instanciate the plugin object */ + if(instanciatePlugin (library, &plugin) != 0) { _debug("Error instanciating the plugin ...\n"); return 1; } - - /* - if(registerPlugin (handle, interface) != 0) + + /* Regitering the current plugin */ + if(registerPlugin (plugin, library) != 0) { _debug("Error registering the plugin ...\n"); return 1; - }*/ + } } - } + } } else return 1; @@ -78,84 +79,134 @@ return 0; } - ::sflphone::Plugin* -::sflphone::PluginManager::isPluginLoaded (const std::string &name) +int +PluginManager::unloadPlugins (void) { - if(_loadedPlugins.empty()) return NULL; + PluginInfo *info; + + if(_loadedPlugins.empty()) return 0; /* Use an iterator on the loaded plugins map */ pluginMap::iterator iter; iter = _loadedPlugins.begin(); while( iter != _loadedPlugins.end() ) { - if ( iter->first == name ) { - /* Return the associated plugin */ - return iter->second; + info = iter->second; + + if (deletePlugin (info) != 0) + { + _debug("Error deleting the plugin ... \n"); + return 1; } + + unloadDynamicLibrary (info->_libraryPtr); + + if (unregisterPlugin (info) != 0) + { + _debug("Error unregistering the plugin ... \n"); + return 1; + } + iter++; } - - /* If we are here, it means that the plugin we were looking for has not been loaded */ - return NULL; + return 0; } - - void* -::sflphone::PluginManager::loadDynamicLibrary (const std::string& filename) + bool +PluginManager::isPluginLoaded (const std::string &name) { + if(_loadedPlugins.empty()) return false; + + /* Use an iterator on the loaded plugins map */ + pluginMap::iterator iter; + iter = _loadedPlugins.find (name); - void *pluginHandlePtr = NULL; - const char *error; + /* Returns map::end if the specified key has not been found */ + if(iter==_loadedPlugins.end()) + return false; - _debug("Loading dynamic library %s\n", filename.c_str()); + /* Returns the plugin pointer */ + return true; +} - /* Load the library */ - pluginHandlePtr = dlopen( filename.c_str(), RTLD_LAZY ); - if( !pluginHandlePtr ) { - error = dlerror(); - _debug("Error while opening plug-in: %s\n", error); - return NULL; - } - dlerror(); - return pluginHandlePtr; + LibraryManager* +PluginManager::loadDynamicLibrary (const std::string& filename) +{ + /* Load the library through the library manager */ + return new LibraryManager (filename); +} + + int +PluginManager::unloadDynamicLibrary (LibraryManager *libraryPtr) +{ + _debug("Unloading dynamic library ...\n"); + /* Close it */ + return libraryPtr->unloadLibrary (); } int -::sflphone::PluginManager::instanciatePlugin (void *handlePtr, ::sflphone::Plugin **plugin) +PluginManager::instanciatePlugin (LibraryManager *libraryPtr, Plugin **plugin) { createFunc *createPlugin; + LibraryManager::SymbolHandle symbol; - createPlugin = (createFunc*)dlsym(handlePtr, "create"); - if( dlerror() ) - { - _debug("Error creating the plugin: %s\n", dlerror()); + if (libraryPtr->resolveSymbol ("createPlugin", &symbol) != 0) return 1; - } + createPlugin = (createFunc*)symbol; *plugin = createPlugin(); return 0; } int -::sflphone::PluginManager::registerPlugin (void *handlePtr, Plugin *plugin) +PluginManager::deletePlugin (PluginInfo *plugin) { - std::string name; + destroyFunc *destroyPlugin; + LibraryManager::SymbolHandle symbol; - if( !(handlePtr && plugin!=0) ) + if (plugin->_libraryPtr->resolveSymbol ("destroyPlugin", &symbol) != 0) return 1; - - name = plugin->getPluginName(); - /* Add the data in the loaded plugin map */ - _loadedPlugins[ name ] = plugin; + destroyPlugin = (destroyFunc*)symbol; + /* Call it */ + destroyPlugin (plugin->_plugin); return 0; } int -::sflphone::PluginManager::unloadDynamicLibrary (void * pluginHandlePtr) +PluginManager::registerPlugin (Plugin *plugin, LibraryManager *library) { - dlclose( pluginHandlePtr ); - dlerror(); + std::string key; + PluginInfo *p_info; + + if( plugin==0 ) + return 1; + + p_info = new PluginInfo(); + /* Retrieve information from the plugin */ + plugin->initFunc (&p_info); + key = p_info->_name; + + //p_info->_plugin = plugin; + p_info->_libraryPtr = library; + + /* Add the data in the loaded plugin map */ + _loadedPlugins[ key ] = p_info; return 0; } +int +PluginManager::unregisterPlugin (PluginInfo *plugin) +{ + pluginMap::iterator iter; + std::string key; + + key = plugin->_name; + if (!isPluginLoaded(key)) + return 1; + + iter = _loadedPlugins.find (key); + _loadedPlugins.erase (iter); + + return 0; +} diff --git a/src/plug-in/pluginmanager.h b/src/plug-in/pluginmanager.h index f24b1779f0266a7eeaaf29209a254b0b30993c65..13db428d6c53bc5f658aec5ea3f9fc3afba8e72c 100644 --- a/src/plug-in/pluginmanager.h +++ b/src/plug-in/pluginmanager.h @@ -6,19 +6,27 @@ * @brief Base class of the plugin manager */ -#include "plugin.h" +#include "librarymanager.h" #include "global.h" #include <map> #include <string> +#include <vector> -namespace sflphone { - - class PluginManager { +class Plugin; +typedef struct PluginInfo { + std::string _name; + LibraryManager *_libraryPtr; + Plugin *_plugin; + int _major_version; + int _minor_version; +} PluginInfo; - public: +#include "plugin.h" +class PluginManager { + public: /** * Destructor */ @@ -34,46 +42,51 @@ namespace sflphone { * @param path The absolute path to the directory * @return int The number of items loaded */ - int loadPlugins( const std::string &path = "" ); + int loadPlugins (const std::string &path = ""); - int instanciatePlugin( void *handlePtr, Plugin** plugin ); + int unloadPlugins (void); + + int instanciatePlugin (LibraryManager* libraryPtr, Plugin** plugin); /** * Check if a plugin has been already loaded * @param name The name of the plugin looked for - * @return Plugin* The pointer on the plugin or NULL if not found + * @return bool The pointer on the plugin or NULL if not found */ - Plugin* isPluginLoaded( const std::string &name ); + bool isPluginLoaded (const std::string &name); - int registerPlugin (void *handle, Plugin *plugin); + int registerPlugin (Plugin *plugin, LibraryManager *library); + int unregisterPlugin (PluginInfo *plugin); + + int deletePlugin (PluginInfo *plugin); + /** * Load a unix dynamic/shared library * @param filename The path to the dynamic/shared library - * @return void* A pointer on it + * @return LibraryManager* A pointer on the library */ - void * loadDynamicLibrary( const std::string &filename ); - + LibraryManager* loadDynamicLibrary (const std::string &filename); + /** * Unload a unix dynamic/shared library - * @param pluginHandleptr The pointer on the loaded plugin + * @param LibraryManager* The pointer on the loaded library */ - int unloadDynamicLibrary( void * pluginHandlePtr ); - + int unloadDynamicLibrary (LibraryManager* libraryPtr); - private: + private: /** * Default constructor */ PluginManager(); - + /* Map of plugins associated by their string name */ - typedef std::map<std::string, Plugin*> pluginMap; + typedef std::map<std::string, PluginInfo*> pluginMap; pluginMap _loadedPlugins; /* The unique static instance */ static PluginManager* _instance; - }; -} + +}; #endif //PLUGIN_MANAGER_H diff --git a/src/plug-in/test/pluginTest.cpp b/src/plug-in/test/pluginTest.cpp index 3aeb927077cc4ef6fffa1745ced804dab5e434b3..20038527fda163260c35c7e429b19823666d55f1 100644 --- a/src/plug-in/test/pluginTest.cpp +++ b/src/plug-in/test/pluginTest.cpp @@ -1,26 +1,30 @@ #include "../plugin.h" -namespace sflphone { - - class PluginTest : public Plugin { - - public: - PluginTest( const std::string &name ) - :Plugin( name ) { - } - - virtual int initFunc (int i) - { - return i; +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 + +class PluginTest : public Plugin { + + public: + PluginTest( const std::string &name ) + :Plugin( name ) { } - }; -} + virtual int initFunc (PluginInfo **info) { + + (*info)->_plugin = this; + (*info)->_major_version = MAJOR_VERSION; + (*info)->_minor_version = MINOR_VERSION; + (*info)->_name = getPluginName(); + + return 0; + } +}; -extern "C" ::sflphone::Plugin* create (void){ - return new ::sflphone::PluginTest("mytest"); +extern "C" Plugin* createPlugin (void){ + return new PluginTest("mytest"); } -extern "C" void destroy (::sflphone::Plugin *p){ +extern "C" void destroyPlugin (Plugin *p){ delete p; } diff --git a/test/pluginmanagerTest.cpp b/test/pluginmanagerTest.cpp index 5a45ffabf2a7667f9276601b1b9d57464077d14b..96994b6925219cfc565906873e8d6574fcc3c238 100644 --- a/test/pluginmanagerTest.cpp +++ b/test/pluginmanagerTest.cpp @@ -26,60 +26,70 @@ using std::cout; using std::endl; -#define PLUGIN_TEST_DIR "/usr/lib/sflphone/plugins/libplugintest.so" -#define PLUGIN_TEST_NAME "mytest" +#define PLUGIN_TEST_DIR "/usr/lib/sflphone/plugins/" +#define PLUGIN_TEST_DESC "mytest" +#define PLUGIN_TEST_NAME "/usr/lib/sflphone/plugins/libplugintest.so" void PluginManagerTest::setUp(){ // Instanciate the plugin manager singleton - _pm = ::sflphone::PluginManager::instance(); - handlePtr = NULL; + _pm = PluginManager::instance(); + library = 0; plugin = 0; } void PluginManagerTest::testLoadDynamicLibrary(){ - CPPUNIT_ASSERT(_pm->loadDynamicLibrary(PLUGIN_TEST_DIR) != NULL); + CPPUNIT_ASSERT(_pm->loadDynamicLibrary(PLUGIN_TEST_NAME) != NULL); } void PluginManagerTest::testUnloadDynamicLibrary(){ - - handlePtr = _pm->loadDynamicLibrary(PLUGIN_TEST_DIR); - CPPUNIT_ASSERT(handlePtr != 0); - CPPUNIT_ASSERT(_pm->unloadDynamicLibrary(handlePtr) == 0 ); + library = _pm->loadDynamicLibrary(PLUGIN_TEST_NAME); + CPPUNIT_ASSERT(library != NULL); + CPPUNIT_ASSERT(_pm->unloadDynamicLibrary(library) == 0 ); } void PluginManagerTest::testInstanciatePlugin(){ - - handlePtr = _pm->loadDynamicLibrary (PLUGIN_TEST_DIR); - CPPUNIT_ASSERT (handlePtr != 0); - CPPUNIT_ASSERT (_pm->instanciatePlugin (handlePtr, &plugin) == 0); + library = _pm->loadDynamicLibrary (PLUGIN_TEST_NAME); + CPPUNIT_ASSERT(library != NULL); + CPPUNIT_ASSERT (_pm->instanciatePlugin (library, &plugin) == 0); CPPUNIT_ASSERT (plugin!=NULL); } void PluginManagerTest::testInitPlugin(){ - handlePtr = _pm->loadDynamicLibrary (PLUGIN_TEST_DIR); - CPPUNIT_ASSERT (handlePtr != 0); - CPPUNIT_ASSERT (_pm->instanciatePlugin (handlePtr, &plugin) == 0); + library = _pm->loadDynamicLibrary (PLUGIN_TEST_NAME); + CPPUNIT_ASSERT(library != NULL); + CPPUNIT_ASSERT (_pm->instanciatePlugin (library, &plugin) == 0); CPPUNIT_ASSERT (plugin!=NULL); - CPPUNIT_ASSERT (plugin->initFunc(0) == 0); - CPPUNIT_ASSERT (plugin->getPluginName() == PLUGIN_TEST_NAME); + CPPUNIT_ASSERT (plugin->getPluginName() == PLUGIN_TEST_DESC); } void PluginManagerTest::testRegisterPlugin(){ + library = _pm->loadDynamicLibrary (PLUGIN_TEST_NAME); + CPPUNIT_ASSERT(library != NULL); + CPPUNIT_ASSERT (_pm->instanciatePlugin (library, &plugin) == 0); + CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_DESC) == false); + CPPUNIT_ASSERT (_pm->registerPlugin (plugin, library) == 0); + CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_DESC) == true); +} + +void PluginManagerTest::testLoadPlugins (){ + CPPUNIT_ASSERT (_pm->loadPlugins (PLUGIN_TEST_DIR) == 0); + CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_DESC) == true); +} - handlePtr = _pm->loadDynamicLibrary (PLUGIN_TEST_DIR); - CPPUNIT_ASSERT (handlePtr != 0); - CPPUNIT_ASSERT (_pm->instanciatePlugin (handlePtr, &plugin) == 0); - CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_NAME) == NULL); - CPPUNIT_ASSERT (_pm->registerPlugin (handlePtr, plugin) == 0); - CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_NAME) == plugin); +void PluginManagerTest::testUnloadPlugins (){ + CPPUNIT_ASSERT (_pm->loadPlugins (PLUGIN_TEST_DIR) == 0); + CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_DESC) == true); + CPPUNIT_ASSERT (_pm->unloadPlugins () == 0); + CPPUNIT_ASSERT (_pm->isPluginLoaded (PLUGIN_TEST_DESC) == false); } void PluginManagerTest::tearDown(){ // Delete the plugin manager object delete _pm; _pm=0; - handlePtr = NULL; if(plugin) delete plugin; plugin = 0; + if(library) + delete library; library = 0; } diff --git a/test/pluginmanagerTest.h b/test/pluginmanagerTest.h index 857b265dec58b8386be8a73f2f933e4329a522ed..2e421958460445c3339ea1c2a97cef767258fabc 100644 --- a/test/pluginmanagerTest.h +++ b/test/pluginmanagerTest.h @@ -27,6 +27,7 @@ // Application import #include "plug-in/pluginmanager.h" +#include "plug-in/librarymanager.h" #include "plug-in/plugin.h" /* @@ -39,8 +40,6 @@ class PluginManagerTest : public CppUnit::TestCase { - class Plugin; - /* * Use cppunit library macros to add unit test the factory */ @@ -50,6 +49,8 @@ class PluginManagerTest : public CppUnit::TestCase { CPPUNIT_TEST( testInstanciatePlugin ); CPPUNIT_TEST( testInitPlugin ); CPPUNIT_TEST( testRegisterPlugin ); + CPPUNIT_TEST( testLoadPlugins ); + CPPUNIT_TEST( testUnloadPlugins ); CPPUNIT_TEST_SUITE_END(); public: @@ -65,22 +66,26 @@ class PluginManagerTest : public CppUnit::TestCase { * Code factoring - Common resources can be released here. * This method is called by unitcpp after each test */ - inline void tearDown(); + inline void tearDown (); - void testLoadDynamicLibrary(); + void testLoadDynamicLibrary (); - void testUnloadDynamicLibrary(); + void testUnloadDynamicLibrary (); + + void testInstanciatePlugin (); + + void testInitPlugin (); - void testInstanciatePlugin(); + void testRegisterPlugin (); - void testInitPlugin(); + void testLoadPlugins (); - void testRegisterPlugin(); + void testUnloadPlugins (); private: - ::sflphone::PluginManager *_pm; - void *handlePtr; - ::sflphone::Plugin *plugin; + PluginManager *_pm; + LibraryManager *library; + Plugin *plugin; }; /* Register our test module */