dlvhex
2.5.0
|
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: