diff --git a/src/plug-in/Makefile.am b/src/plug-in/Makefile.am index b69f39ca9cdca75d415b6470d84f7a371e8157c1..c97ccdc21ad9d23126553e6efeda496aba88d944 100644 --- a/src/plug-in/Makefile.am +++ b/src/plug-in/Makefile.am @@ -8,5 +8,5 @@ SUBDIRS=test libplugin_la_SOURCES = \ pluginmanager.cpp \ - plugin.cpp + librarymanager.cpp diff --git a/src/plug-in/librarymanager.cpp b/src/plug-in/librarymanager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..138b59f5f49b2c2ed19b170deb36dfb373b00846 --- /dev/null +++ b/src/plug-in/librarymanager.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#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..e244d448a1152a46950d4b0ec95e06847eac4ff5 --- /dev/null +++ b/src/plug-in/librarymanager.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#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.cpp b/src/plug-in/plugin.cpp deleted file mode 100644 index deaf1a4818fbe8a01a720b0b856c36f1ac33a277..0000000000000000000000000000000000000000 --- a/src/plug-in/plugin.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "plugin.h" - -::sflphone::Plugin::Plugin (void *handle, PluginInterface *interface) - :_handlePtr(handle), _interface(interface) -{ -} - -::sflphone::Plugin::Plugin (PluginInterface *interface) - :_interface(interface) -{ -} - -::sflphone::Plugin::~Plugin () -{ -} diff --git a/src/plug-in/plugin.h b/src/plug-in/plugin.h index fe548361eb1098e566ffacf00cb58d61ba31a236..2769c56c372e22be24334107ef6efdf5bf4e8b31 100644 --- a/src/plug-in/plugin.h +++ b/src/plug-in/plugin.h @@ -1,32 +1,61 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + #ifndef PLUGIN_H #define PLUGIN_H -#include "plugininterface.h" +#include "global.h" + +#include "pluginmanager.h" + +/* + * @file plugin.h + * @brief Define a plugin object + */ -namespace sflphone { +class Plugin { - class PluginInterface; + public: + Plugin( const std::string &name ){ + _name = name; + } - class Plugin { + virtual ~Plugin() {} - public: + inline std::string getPluginName (void) { return _name; } - Plugin (void*, PluginInterface *interface); - Plugin (PluginInterface *interface); + /** + * Return the minimal core version required so that the plugin could work + * @return int The version required + */ + virtual int initFunc (PluginInfo **info) = 0; - ~Plugin (); + private: + Plugin &operator =(const Plugin &plugin); - void setName (std::string name); - private: - std::string _name; - int _version_major; - int _version_minor; - int _required; - void *_handlePtr; - PluginInterface *_interface; + std::string _name; +}; + +typedef Plugin* createFunc (void); + +typedef void destroyFunc (Plugin*); - friend class PluginTest; - friend class PluginManager; - }; -} #endif //PLUGIN_H + diff --git a/src/plug-in/pluginmanager.cpp b/src/plug-in/pluginmanager.cpp index f8135f57806590ea3c32f5c304f3de3be8814896..8d391f6ae6383c18220484b0c98d00991184ac9b 100644 --- a/src/plug-in/pluginmanager.cpp +++ b/src/plug-in/pluginmanager.cpp @@ -1,12 +1,31 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + #include <dirent.h> #include <dlfcn.h> #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 +33,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::PluginInterface *interface; DIR *dir; dirent *dirStruct; - int result=0; - void *handle; - createFunc* createPlugin; - + LibraryManager *library; + Plugin *plugin; + const std::string pDir = ".."; const std::string cDir = "."; @@ -52,19 +69,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, &interface) != 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; @@ -75,89 +98,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::PluginInterface **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, PluginInterface *interface) +PluginManager::deletePlugin (PluginInfo *plugin) { - Plugin *myplugin; - std::string name; + destroyFunc *destroyPlugin; + LibraryManager::SymbolHandle symbol; - if( !( handlePtr && interface!=0 ) ) + if (plugin->_libraryPtr->resolveSymbol ("destroyPlugin", &symbol) != 0) return 1; - - /* Fetch information from the loaded plugin interface */ - if(interface->registerFunc (&myplugin) != 0) + destroyPlugin = (destroyFunc*)symbol; + /* Call it */ + destroyPlugin (plugin->_plugin); + return 0; +} + + int +PluginManager::registerPlugin (Plugin *plugin, LibraryManager *library) +{ + std::string key; + PluginInfo *p_info; + + if( plugin==0 ) return 1; - /* Creation of the plugin wrapper */ - myplugin = new Plugin (handlePtr, interface); + + 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[ myplugin->_name ] = myplugin; + _loadedPlugins[ key ] = p_info; return 0; } - void -::sflphone::PluginManager::unloadDynamicLibrary (void * pluginHandlePtr) +int +PluginManager::unregisterPlugin (PluginInfo *plugin) { - dlclose( pluginHandlePtr ); - dlerror(); -} + 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 671834369da67dc151cd24e8ab9ca62a0261d6c1..4b3a1869200ce7deaac770d661d360223adbc7b0 100644 --- a/src/plug-in/pluginmanager.h +++ b/src/plug-in/pluginmanager.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + #ifndef PLUGIN_MANAGER_H #define PLUGIN_MANAGER_H @@ -6,18 +25,27 @@ * @brief Base class of the plugin manager */ -#include "plugininterface.h" +#include "librarymanager.h" #include "global.h" #include <map> #include <string> +#include <vector> -namespace sflphone { +class Plugin; - class PluginManager { +typedef struct PluginInfo { + std::string _name; + LibraryManager *_libraryPtr; + Plugin *_plugin; + int _major_version; + int _minor_version; +} PluginInfo; +#include "plugin.h" - public: +class PluginManager { + public: /** * Destructor */ @@ -33,45 +61,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 unloadPlugins (void); - int instanciatePlugin( void *handlePtr, PluginInterface** plugin ); + 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, PluginInterface *interface); + int registerPlugin (Plugin *plugin, LibraryManager *library); + + int unregisterPlugin (PluginInfo *plugin); - private: - /** - * Default constructor - */ - PluginManager(); + 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 */ - void unloadDynamicLibrary( void * pluginHandlePtr ); + int unloadDynamicLibrary (LibraryManager* libraryPtr); + + 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/test/pluginmanagerTest.h b/test/pluginmanagerTest.h index 0cd92da42ce87ff50b2eb399975a68da9abd2f4f..2e421958460445c3339ea1c2a97cef767258fabc 100644 --- a/test/pluginmanagerTest.h +++ b/test/pluginmanagerTest.h @@ -27,6 +27,8 @@ // Application import #include "plug-in/pluginmanager.h" +#include "plug-in/librarymanager.h" +#include "plug-in/plugin.h" /* * @file pluginManagerTest.cpp @@ -42,9 +44,13 @@ class PluginManagerTest : public CppUnit::TestCase { * Use cppunit library macros to add unit test the factory */ CPPUNIT_TEST_SUITE( PluginManagerTest ); - CPPUNIT_TEST( testLoadPluginDirectory ); - CPPUNIT_TEST( testLoadPlugin ); + CPPUNIT_TEST( testLoadDynamicLibrary ); + CPPUNIT_TEST( testUnloadDynamicLibrary ); + CPPUNIT_TEST( testInstanciatePlugin ); + CPPUNIT_TEST( testInitPlugin ); CPPUNIT_TEST( testRegisterPlugin ); + CPPUNIT_TEST( testLoadPlugins ); + CPPUNIT_TEST( testUnloadPlugins ); CPPUNIT_TEST_SUITE_END(); public: @@ -60,16 +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 testLoadPluginDirectory(); + void testLoadDynamicLibrary (); - void testLoadPlugin(); + void testUnloadDynamicLibrary (); - void testRegisterPlugin(); + void testInstanciatePlugin (); + + void testInitPlugin (); + + void testRegisterPlugin (); + + void testLoadPlugins (); + + void testUnloadPlugins (); private: - ::sflphone::PluginManager *_pm; + PluginManager *_pm; + LibraryManager *library; + Plugin *plugin; }; /* Register our test module */