dlvhex  2.5.0
src/PluginContainer.cpp
Go to the documentation of this file.
00001 /* dlvhex -- Answer-Set Programming with external interfaces.
00002  * Copyright (C) 2005-2007 Roman Schindlauer
00003  * Copyright (C) 2006-2015 Thomas Krennwallner
00004  * Copyright (C) 2009-2016 Peter Schüller
00005  * Copyright (C) 2011-2016 Christoph Redl
00006  * Copyright (C) 2015-2016 Tobias Kaminski
00007  * Copyright (C) 2015-2016 Antonius Weinzierl
00008  *
00009  * This file is part of dlvhex.
00010  *
00011  * dlvhex is free software; you can redistribute it and/or modify it
00012  * under the terms of the GNU Lesser General Public License as
00013  * published by the Free Software Foundation; either version 2.1 of
00014  * the License, or (at your option) any later version.
00015  *
00016  * dlvhex is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with dlvhex; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00024  * 02110-1301 USA.
00025  */
00026 
00039 #ifdef HAVE_CONFIG_H
00040 #include "config.h"
00041 #endif                           // HAVE_CONFIG_H
00042 
00043 #include "dlvhex2/PluginContainer.h"
00044 #include "dlvhex2/Configuration.h"
00045 #include "dlvhex2/Error.h"
00046 #include "dlvhex2/Logger.h"
00047 #include "dlvhex2/PluginInterface.h"
00048 #include "dlvhex2/Registry.h"
00049 
00050 #include <ltdl.h>
00051 
00052 #include <sys/types.h>
00053 //#include <dirent.h>
00054 //#include <pwd.h>
00055 #ifdef WIN32
00056 #include <windows.h>
00057 #endif
00058 
00059 #include <boost/foreach.hpp>
00060 
00061 #include <cstdlib>
00062 #include <iostream>
00063 #include <sstream>
00064 #include <set>
00065 
00066 DLVHEX_NAMESPACE_BEGIN
00067 
00068 #ifdef WIN32
00069 void setenv(const char* var, const char* val, int v)
00070 {
00071     SetEnvironmentVariable(var, val);
00072 }
00073 void unsetenv(const char* var)
00074 {
00075     SetEnvironmentVariable(var, NULL);
00076 }
00077 #else
00078 #define setenv(VAR, VAL, V) ::setenv(VAR, VAL, V)
00079 #define unsetenv(VAR) ::unsetenv(VAR)
00080 #endif
00081 
00082 struct PluginContainer::LoadedPlugin
00083 {
00084     // handle is NULL for statically linked plugins
00085     lt_dlhandle handle;
00086     PluginInterfacePtr plugin;
00087 
00088     LoadedPlugin(lt_dlhandle handle, PluginInterfacePtr plugin):
00089     handle(handle), plugin(plugin) {}
00090 };
00091 
00092 namespace
00093 {
00094 
00095     typedef PluginContainer::LoadedPlugin LoadedPlugin;
00096     typedef boost::shared_ptr<LoadedPlugin> LoadedPluginPtr;
00097     typedef PluginContainer::LoadedPluginVector LoadedPluginVector;
00098     typedef PluginInterface* (*t_import)();
00099     typedef int (*t_getversion)();
00100 
00101     int
00102     findplugins(const char* filename, lt_ptr data) {
00103         std::vector<std::string>* pluginlist = reinterpret_cast<std::vector<std::string>*>(data);
00104 
00105         std::string fn(filename);
00106         std::string::size_type base = fn.find_last_of("/");
00107 
00108         // if basename starts with 'libdlvhexplugin', then we should have a plugin here
00109         // we cannot simply try to open any lib, as opening could already overwrite loaded symbols
00110         // (if we load our own base library again, this re-constructs global data structures)
00111         if (fn.substr(base).find("/libdlvhexplugin") == 0) {
00112             pluginlist->push_back(fn);
00113         }
00114 
00115         return 0;
00116     }
00117 
00118     void findPluginLibraryCandidates(const std::string& searchpath, std::vector<std::string>& libcandidates) {
00119         std::string oldenv_ld;
00120         const char *envld = ::getenv("LD_LIBRARY_PATH");
00121         if( envld ) {
00122             oldenv_ld = envld;
00123             unsetenv("LD_LIBRARY_PATH");
00124         }
00125 
00126         try
00127         {
00128             if (lt_dlinit()) {
00129                 throw GeneralError("Could not initialize libltdl");
00130             }
00131 
00132             //
00133             // now look into the user's home, and into the global plugin directory
00134             //
00135 
00136             LOG(PLUGIN,"findPluginLibraryCandidates with searchpath='" << searchpath << "'");
00137             if (lt_dlsetsearchpath(searchpath.c_str())) {
00138                 throw GeneralError("Could not set libltdl search path: " + searchpath);
00139             }
00140 
00141             // search the directory search paths for plugins and setup pluginList
00142             LOG(PLUGIN,"looking for plugin libraries named 'libdlvhexplugin...'");
00143             lt_dlforeachfile(NULL, findplugins, reinterpret_cast<void*>(&libcandidates));
00144         }
00145         catch(...) {
00146             if( !oldenv_ld.empty() )
00147                 setenv("LD_LIBRARY_PATH", oldenv_ld.c_str(), 1);
00148             throw;
00149         }
00150         if( !oldenv_ld.empty() )
00151             setenv("LD_LIBRARY_PATH", oldenv_ld.c_str(), 1);
00152     }
00153 
00154     void loadCandidates(
00155         const std::vector<std::string>& libnames,
00156     LoadedPluginVector& plugins) {
00157         BOOST_FOREACH(const std::string& lib, libnames) {
00158             LOG(PLUGIN,"loading Plugin Library: '" << lib << "'");
00159             // reset lt_dlerror
00160             (void)lt_dlerror();
00161             lt_dlhandle dlHandle = lt_dlopenext(lib.c_str());
00162 
00163             // do while false for breaking out easily
00164             do {
00165                 if( dlHandle == NULL ) {
00166                     LOG(WARNING,"Selected library '" << lib << "' for opening but cannot open: lt_dlerror='" << lt_dlerror() << "' (skipping)");
00167                     LOG(WARNING,"(You might want to use 'LD_DEBUG={files,help,all} dlvhex2 <arguments>' to debug plugin loading.))");
00168                     break;
00169                 }
00170 
00171                 t_getversion getversion = reinterpret_cast<t_getversion>(lt_dlsym(dlHandle, PLUGINABIVERSIONFUNCTIONSTRING));
00172                 if( getversion == NULL ) {
00173                     LOG(INFO,"Library '" << lib << "' selected for opening, but found no "
00174                         "version function '" << PLUGINABIVERSIONFUNCTIONSTRING << "' (skipping)");
00175                     break;
00176                 }
00177 
00178                 t_import getplugin = reinterpret_cast<t_import>(lt_dlsym(dlHandle, PLUGINIMPORTFUNCTIONSTRING));
00179                 if( getplugin == NULL ) {
00180                     LOG(INFO,"Library '" << lib << "' selected for opening, but found no "
00181                         "import function '" << PLUGINIMPORTFUNCTIONSTRING << "' (skipping)");
00182                     break;
00183                 }
00184 
00185                 // verify ABI version
00186                 //
00187                 // we use the Apache APR approach to versioning
00188                 // see https://apr.apache.org/versioning.html
00189                 DBGLOG(DBG,"now checking plugin ABI version for " << lib);
00190                 int iversion = getversion();
00191                 {
00192                     int imajor = iversion / 10000;
00193                     int iminor = (iversion / 100) % 100;
00194                     int imicro = iversion % 100;
00195                     LOG(INFO,"got ABI version " << iversion << " from " << lib <<
00196                         ", interpreted this as (" << imajor << "," << iminor << "," << imicro << ")");
00197                     if( imajor != DLVHEX_ABI_VERSION_MAJOR ) {
00198                         LOG(INFO,"Library '" << lib << "' returned incompatible major ABI version: " <<
00199                             imajor << " (library) != " << DLVHEX_ABI_VERSION_MAJOR << " (dlvhex) (skipping)");
00200                         break;
00201                     }
00202                     if( iminor > DLVHEX_ABI_VERSION_MINOR ) {
00203                         LOG(INFO,"Library '" << lib << "' returned incompatible minor ABI version: " <<
00204                             iminor << " (library) > " << DLVHEX_ABI_VERSION_MINOR << " (dlvhex) (skipping)");
00205                         break;
00206                     }
00207                     // ignore micro abi version
00208                     // version check successful
00209                 }
00210 
00211                 // get it!
00212                 DBGLOG(DBG,"now calling plugin import function for " << lib);
00213                 PluginInterface* plugin(getplugin());
00214                 DBGLOG(DBG,"plugin import function returned " << printptr(plugin));
00215 
00216                 // now wrap in non-deleting shared_ptr
00217                 PluginInterfacePtr nopDeletingPluginPtr(plugin, PluginPtrNOPDeleter<PluginInterface>());
00218 
00219                 plugins.push_back(LoadedPluginPtr(new LoadedPlugin(dlHandle, nopDeletingPluginPtr)));
00220             }
00221             while(false);
00222         }
00223     }
00224 
00225     void selectLoadedPlugins(
00226         LoadedPluginVector& plugins,
00227     LoadedPluginVector& candidates) {
00228         // remove those with duplicate names
00229         std::set<std::string> names;
00230         BOOST_FOREACH(LoadedPluginPtr lplugin, plugins) {
00231             names.insert(lplugin->plugin->getPluginName());
00232         }
00233         DBGLOG(DBG,"selectLoadedPlugins: already loaded: " << printset(names));
00234 
00235         LoadedPluginVector::iterator it = candidates.begin();
00236         while(it != candidates.end()) {
00237             const std::string& pname = (*it)->plugin->getPluginName();
00238             if( names.find(pname) != names.end() ) {
00239                 // warn, unload, remove, restart loop
00240 
00241                 // warn
00242                 LOG(WARNING,"already loaded a plugin with name " << pname << " (skipping)");
00243 
00244                 WARNING("TODO check if any pointer is used?")
00245                     DBGLOG(DBG,"usage count on interface ptr is " << (*it)->plugin.use_count() << " (should be 1)");
00246 
00247                 // unload lib
00248                 if( 0 != lt_dlclose((*it)->handle) ) {
00249                     LOG(WARNING,"failed unloading plugin library " << pname << ":" << lt_dlerror());
00250                 }
00251                 // remove
00252                 candidates.erase(it);
00253                 // restart
00254                 it = candidates.begin();
00255             }
00256             else {
00257                 // check next
00258                 ++it;
00259             }
00260         }
00261     }
00262 
00263 }                                // anonymous namespace
00264 
00265 
00266 #if 0
00267 PluginContainer::PluginContainer(const PluginContainer& pc):
00268 registry(pc.registry),
00269 searchPath(pc.searchPath),
00270 plugins(pc.plugins),
00271 pluginAtoms(pc.pluginAtoms)
00272 {
00273 }
00274 #endif
00275 
00276 PluginContainer::PluginContainer()
00277 {
00278 }
00279 
00280 
00281 PluginContainer::~PluginContainer()
00282 {
00283     // these are pointers also existing in vector plugins -> destroy them first
00284     pluginInterfaces.clear();
00285 
00286     DBGLOG(DBG,"unloading plugins");
00287     // unload all plugins in reverse order
00288     for(LoadedPluginVector::reverse_iterator it = plugins.rbegin();
00289     it != plugins.rend(); ++it) {
00290         LoadedPluginPtr lp = *it;
00291         const std::string& pname = lp->plugin->getPluginName();
00292         if( lp->handle == 0 ) {
00293             DBGLOG(DBG,"unloading plugin '" << pname << "' not necessary (NULL handle)");
00294             continue;
00295         }
00296 
00297         LOG(DBG,"about to unload loaded plugin '" << pname << "'");
00298 
00299         WARNING("TODO check if any pointer is used?")
00300             unsigned use = lp->plugin.use_count();
00301         if( use != 1 )
00302             LOG(WARNING,"usage count on PluginInterfacePtr is " << use << " (should be 1)");
00303 
00304         // unload lib
00305         if( 0 != lt_dlclose(lp->handle) ) {
00306             LOG(WARNING,"failed unloading plugin library '" << pname << "':" << lt_dlerror());
00307         }
00308     }
00309     DBGLOG(DBG,"done unloading");
00310 }
00311 
00312 
00313 // search for plugins in searchpath and open those that are plugins
00314 // may be called multiple times with different paths
00315 // paths may be separated by ":" just like LD_LIBRARY_PATH
00316 void PluginContainer::loadPlugins(const std::string& search)
00317 {
00318     LOG_SCOPE(PLUGIN,"loadPlugins",false);
00319 
00320     // find candidates
00321     std::vector<std::string> libcandidates;
00322     findPluginLibraryCandidates(search, libcandidates);
00323 
00324     // TODO probably preselect using library names and already loaded plugins
00325 
00326     // load candidates
00327     LoadedPluginVector plugincandidates;
00328     loadCandidates(libcandidates, plugincandidates);
00329 
00330     // TODO probably select/unload using PluginInterface and already loaded plugins
00331     selectLoadedPlugins(plugins, plugincandidates);
00332 
00333     // add new plugins to list of loaded plugins
00334     BOOST_FOREACH(LoadedPluginPtr cand, plugincandidates) {
00335         // (automatically adds atoms)
00336         addInternalPlugin(cand);
00337     }
00338 
00339     // add to existing search path
00340     if( !searchPath.empty() )
00341         searchPath += ":";
00342     searchPath += search;
00343 }
00344 
00345 
00346 // add dlhandle and plugin interface to the container
00347 void PluginContainer::addInternalPlugin(LoadedPluginPtr lplugin)
00348 {
00349     LOG(PLUGIN,"adding PluginInterface '" << lplugin->plugin->getPluginName() << "' with dlhandle " << lplugin->handle);
00350 
00351     plugins.push_back(lplugin);
00352     pluginInterfaces.push_back(lplugin->plugin);
00353 }
00354 
00355 
00356 // add a PluginInterface to the container
00357 void PluginContainer::addInternalPlugin(PluginInterfacePtr plugin)
00358 {
00359     addInternalPlugin(LoadedPluginPtr(new LoadedPlugin(0, plugin)));
00360 }
00361 
00362 
00363 // call printUsage for each loaded plugin
00364 void PluginContainer::printUsage(
00365 std::ostream& o)
00366 {
00367     BOOST_FOREACH(PluginInterfacePtr plugin, pluginInterfaces) {
00368         o << std::endl <<
00369             "Plugin help for " << plugin->getPluginName() << ":" << std::endl;
00370         plugin->printUsage(o);
00371     }
00372 }
00373 
00374 
00375 DLVHEX_NAMESPACE_END
00376 
00377 
00378 // vim:expandtab:ts=4:sw=4:
00379 // mode: C++
00380 // End: