diff --git a/globals.mak b/globals.mak index c03985035c4058b00b378f1b0809017241339313..7a0b085b9efbe0f7deefb226deed286cbba7982a 100644 --- a/globals.mak +++ b/globals.mak @@ -1,6 +1,7 @@ # Global variables src=$(top_srcdir) sflcodecdir=$(libdir)/sflphone/codecs +sflplugindir=$(libdir)/sflphone/plugins PJSIP_LIBS = -lpjnath-sfl -lpjsua-sfl -lpjsip-sfl -lpjmedia-sfl -lpjsip-simple-sfl -lpjsip-ua-sfl -lpjmedia-codec-sfl -lpjlib-util-sfl -lpj-sfl @@ -16,5 +17,6 @@ AM_CPPFLAGS = \ @SIP_CFLAGS@ \ @DBUSCPP_CFLAGS@ \ -DCODECS_DIR=\""$(sflcodecdir)"\" \ + -DPLUGINS_DIR=\""$(sflplugindir)"\" \ -DENABLE_TRACE diff --git a/src/Makefile.am b/src/Makefile.am index f9af16aafd648b9d39f9df13a17ca5e9ae66c343..90953f1607ffb826cc2fe4279696b3d02f400df9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -87,6 +87,7 @@ libsflphone_la_LIBADD = \ ./audio/libaudio.la \ ./dbus/libdbus.la \ ./config/libconfig.la \ + ./plug-in/libplugin.la \ $(IAX_LIBS) libsflphone_la_SOURCES = diff --git a/src/plug-in/Makefile.am b/src/plug-in/Makefile.am index de286d046c31970b74807d1acce3febd7c4b02b6..53bf6b050cba69da07a0f8fd60b2f4b52c8f41d6 100644 --- a/src/plug-in/Makefile.am +++ b/src/plug-in/Makefile.am @@ -1,5 +1,8 @@ +include ../../globals.mak + noinst_LTLIBRARIES = libplugin.la libplugin_la_SOURCES = \ - pluginmanager.cpp - plugin.cpp + pluginmanager.cpp \ + plugin.h + diff --git a/src/plug-in/plugin.cpp b/src/plug-in/plugin.cpp index bc7cf18c38c58f3a89521553fca90664d8180246..495437076549dee7c9bafbdc5ca6782076400d97 100644 --- a/src/plug-in/plugin.cpp +++ b/src/plug-in/plugin.cpp @@ -1,26 +1,27 @@ #include "plugin.h" -Plugin::Plugin( const std::string &filename ) +::sflphone::Plugin::Plugin( const std::string &filename UNUSED ) { //TODO IMPLEMENT } -Plugin::Plugin( const Plugin &plugin ) +::sflphone::Plugin::Plugin( const Plugin &plugin UNUSED ) { //TODO IMPLEMENT } -Plugin::~Plugin() +::sflphone::Plugin::~Plugin() { //TODO IMPLEMENT } -int Plugin::getCoreVersion( void ) +int ::sflphone::Plugin::getCoreVersion( void ) const { //TODO IMPLEMENT + return 1; } -void Plugin::registerPlugin( PluginManager & ) +void ::sflphone::Plugin::registerPlugin( PluginManager & ) { //TODO IMPLEMENT } diff --git a/src/plug-in/plugin.h b/src/plug-in/plugin.h index 5c825f872f4c03cfe43e0ab4b9fe6b65b1a1c29e..100d6ea5b40bc618e3e6cae9c4435b9b192af676 100644 --- a/src/plug-in/plugin.h +++ b/src/plug-in/plugin.h @@ -3,6 +3,8 @@ #include <string> +#include "global.h" + /* * @file plugin.h * @brief Define a plugin object @@ -15,21 +17,21 @@ class PluginManager; class Plugin { public: - Plugin( const std::string &filename ); - Plugin( const Plugin &plugin ); - ~Plugin(); + Plugin( const std::string &name ); + //Plugin( const Plugin &plugin ); + virtual ~Plugin() {} public: /** * Return the minimal core version required so that the plugin could work * @return int The version required */ - int getCoreVersion() const; + virtual int getCoreVersion() const = 0; /** * Register the plugin to the plugin manager */ - void registerPlugin( PluginManager & ); + virtual void registerPlugin( PluginManager & ) = 0; private: Plugin &operator =(const Plugin &plugin); diff --git a/src/plug-in/pluginmanager.cpp b/src/plug-in/pluginmanager.cpp index 9c792dc3ff6d1381d0f57147c7a5ae45b30964c9..6012a59bde292396b2f70fdce8d0a1d458ef8aa7 100644 --- a/src/plug-in/pluginmanager.cpp +++ b/src/plug-in/pluginmanager.cpp @@ -1,6 +1,98 @@ +#include <dirent.h> +#include <dlfcn.h> + #include "pluginmanager.h" -void ::sflphone::PluginManager::loadPlugins( const std::string &path ) +::sflphone::PluginManager::PluginManager():_loadedPlugins() +{ + _instance = this; +} + +::sflphone::PluginManager::~PluginManager() +{ + _instance = 0; +} + +int ::sflphone::PluginManager::loadPlugins( const std::string &path ) +{ + std::string pluginDir, current; + ::sflphone::Plugin *plugin; + DIR *dir; + dirent *dirStruct; + int result=0; + + const std::string pDir = ".."; + const std::string cDir = "."; + + /* The directory in which plugins are dropped. Default: /usr/lib/sflphone/plugins/ */ + ( path == "" )? pluginDir = std::string(PLUGINS_DIR).append("/"):pluginDir = path; + _debug("Loading plugins from %s...\n", pluginDir.c_str()); + + dir = opendir( pluginDir.c_str() ); + /* Test if the directory exists or is readable */ + if( dir ){ + /* Read the directory */ + while( (dirStruct=readdir(dir)) ){ + /* Get the name of the current item in the directory */ + current = dirStruct->d_name; + /* Test if the current item is not the parent or the current directory */ + if( current != pDir && current != cDir ){ + loadDynamicLibrary( current ); + result++; + } + } + } + /* Close the directory */ + closedir( dir ); + + return result; +} + +::sflphone::Plugin* ::sflphone::PluginManager::isPluginLoaded( const std::string &name ) { - //TODO IMPLEMENT + if(_loadedPlugins.empty()) return NULL; + + /* 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; + } + iter++; + } + + /* If we are here, it means that the plugin we were looking for has not been loaded */ + return NULL; } + +void* ::sflphone::PluginManager::loadDynamicLibrary( const std::string& filename ) { + + void *pluginHandlePtr = NULL; + const char *error; + + _debug("Loading dynamic library %s\n", filename.c_str()); + +#if defined(Q_OS_UNIX) + /* Load the library */ + pluginHandlePtr = dlopen( filename.c_str(), RTLD_LAZY ); + if( !pluginHandlePtr ) { + error = dlerror(); + _debug("Error while opening plug-in: %s\n", error); + } + dlerror(); +#endif + + return pluginHandlePtr; +} + +void ::sflphone::PluginManager::unloadDynamicLibrary( void * pluginHandlePtr ) { + + dlclose( pluginHandlePtr ); + dlerror(); +} + +::sflphone::PluginManager* ::sflphone::PluginManager::_instance = 0; + diff --git a/src/plug-in/pluginmanager.h b/src/plug-in/pluginmanager.h index d91d115d49138679733efe6f86f2cb041fcf5fc5..651a0902f8e0db872052a2b0c234c66866ee92f1 100644 --- a/src/plug-in/pluginmanager.h +++ b/src/plug-in/pluginmanager.h @@ -7,6 +7,7 @@ */ #include "plugin.h" +#include "global.h" #include <map> #include <string> @@ -16,17 +17,55 @@ namespace sflphone { class PluginManager { public: + /** + * Default constructor + */ + PluginManager(); + + /** + * Destructor + */ + ~PluginManager(); + + /** + * Returns the unique instance of the plugin manager + */ + static PluginManager* instance(); + /** * Load all the plugins found in a specific directory * @param path The absolute path to the directory + * @return int The number of items loaded + */ + int loadPlugins( const std::string &path = "" ); + + /** + * 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 */ - void loadPlugins( const std::string &path ); + Plugin* isPluginLoaded( const std::string &name ); private: - /* Map of plugins associated by their string name */ - typedef std::map<std::string, ::sflphone::Plugin> pluginMap; + /** + * Load a unix dynamic/shared library + * @param filename The path to the dynamic/shared library + * @return void* A pointer on it + */ + void * loadDynamicLibrary( const std::string &filename ); + /** + * Unload a unix dynamic/shared library + * @param pluginHandleptr The pointer on the loaded plugin + */ + void unloadDynamicLibrary( void * pluginHandlePtr ); + + /* Map of plugins associated by their string name */ + typedef std::map<std::string, ::sflphone::Plugin*> pluginMap; pluginMap _loadedPlugins; + + /* The unique static instance */ + static PluginManager* _instance; }; } diff --git a/test/Makefile.am b/test/Makefile.am index 14411aa140bbc3264308b27f0cd418287544821c..86e70dbc38e76a6fda92b43be969afd51e260781 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ include ../globals.mak -bin_PROGRAMS = configurationTester +bin_PROGRAMS = configurationTester pluginmanagerTester OBJECT_FILES= \ ../src/sflphoned-managerimpl.o \ @@ -16,6 +16,7 @@ OBJECT_FILES= \ ../src/sflphoned-iaxaccount.o \ ../src/sflphoned-eventthread.o \ ../src/sflphoned-useragent.o \ + ../src/plug-in/pluginmanager.o \ ../src/sflphoned-samplerateconverter.o configurationTester_SOURCES = \ @@ -23,6 +24,11 @@ configurationTester_SOURCES = \ configurationTest.h \ TestMain.cpp +pluginmanagerTester_SOURCES = \ + pluginmanagerTest.h \ + pluginmanagerTest.cpp \ + TestMain.cpp + configurationTester_LDADD = \ ../src/libsflphone.la \ $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \ @@ -36,4 +42,18 @@ configurationTester_LDADD = \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ $(OBJECT_FILES) - + +pluginmanagerTester_LDADD = \ + ../src/libsflphone.la \ + $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \ + @ALSA_LIBS@ \ + @PULSEAUDIO_LIBS@ \ + @CPPUNIT_LIBS@ \ + @CCEXT2_LIBS@ \ + @CCGNU2_LIBS@ \ + @CCRTP_LIBS@ \ + @DBUSCPP_LIBS@ \ + @SAMPLERATE_LIBS@ \ + $(PJSIP_LIBS) \ + $(OBJECT_FILES) + diff --git a/test/pluginmanagerTest.cpp b/test/pluginmanagerTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9a3d413a400d422a7510963d36124ba1d026e14 --- /dev/null +++ b/test/pluginmanagerTest.cpp @@ -0,0 +1,44 @@ +/* + * 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 <stdio.h> +#include <sstream> + +#include "pluginmanagerTest.h" + +using std::cout; +using std::endl; + +void PluginManagerTest::setUp(){ + // Instanciate the plugin manager object + _pm = new ::sflphone::PluginManager(); +} + +void PluginManagerTest::testLoadPluginDirectory(){ + _pm->loadPlugins(); +} + +void PluginManagerTest::testNonloadedPlugin(){ + CPPUNIT_ASSERT( _pm->isPluginLoaded("test") == NULL ); +} + +void PluginManagerTest::tearDown(){ + // Delete the plugin manager object + delete _pm; _pm=NULL; +} diff --git a/test/pluginmanagerTest.h b/test/pluginmanagerTest.h new file mode 100644 index 0000000000000000000000000000000000000000..e43ba8f7ab09262243fddd3edc21130ddca5dd80 --- /dev/null +++ b/test/pluginmanagerTest.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +// Cppunit import +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCaller.h> +#include <cppunit/TestCase.h> +#include <cppunit/TestSuite.h> + +#include <assert.h> + +// Application import +#include "plug-in/pluginmanager.h" + +/* + * @file pluginManagerTest.cpp + * @brief Regroups unitary tests related to the plugin manager. + */ + +#ifndef _PLUGINMANAGER_TEST_ +#define _PLUGINMANAGER_TEST_ + +class PluginManagerTest : public CppUnit::TestCase { + + /* + * Use cppunit library macros to add unit test the factory + */ + CPPUNIT_TEST_SUITE( PluginManagerTest ); + CPPUNIT_TEST( testLoadPluginDirectory ); + CPPUNIT_TEST( testNonloadedPlugin ); + CPPUNIT_TEST_SUITE_END(); + + public: + PluginManagerTest() : CppUnit::TestCase("Plugin Manager Tests") {} + + /* + * Code factoring - Common resources can be initialized here. + * This method is called by unitcpp before each test + */ + void setUp(); + + /* + * Code factoring - Common resources can be released here. + * This method is called by unitcpp after each test + */ + inline void tearDown(); + + void testLoadPluginDirectory(); + + void testNonloadedPlugin(); + + private: + ::sflphone::PluginManager *_pm; +}; + +/* Register our test module */ +CPPUNIT_TEST_SUITE_REGISTRATION( PluginManagerTest ); + +#endif