//------------------------------------------------------------------------------ // Copyright (c) 2014 by European Organization for Nuclear Research (CERN) // Author: Lukasz Janyst //------------------------------------------------------------------------------ // This file is part of the XRootD software suite. // // XRootD is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // XRootD 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 Lesser General Public License // along with XRootD. If not, see . // // In applying this licence, CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. //------------------------------------------------------------------------------ #include "XrdCl/XrdClPlugInManager.hh" #include "XrdCl/XrdClDefaultEnv.hh" #include "XrdCl/XrdClLog.hh" #include "XrdCl/XrdClConstants.hh" #include "XrdCl/XrdClUtils.hh" #include "XrdCl/XrdClURL.hh" #include "XrdSys/XrdSysPwd.hh" #include "XrdVersion.hh" #include #include #include #include #include XrdVERSIONINFOREF( XrdCl ); namespace XrdCl { //---------------------------------------------------------------------------- // Constructor //---------------------------------------------------------------------------- PlugInManager::PlugInManager(): pDefaultFactory(0) { } //---------------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------------- PlugInManager:: ~PlugInManager() { std::map::iterator it; for( it = pFactoryMap.begin(); it != pFactoryMap.end(); ++it ) { it->second->counter--; if( it->second->counter == 0 ) delete it->second; } delete pDefaultFactory; } //---------------------------------------------------------------------------- // Register a plug-in favtory for the given url //---------------------------------------------------------------------------- bool PlugInManager::RegisterFactory( const std::string &url, PlugInFactory *factory ) { Log *log = DefaultEnv::GetLog(); XrdSysMutexHelper scopedLock( pMutex ); std::string normUrl = NormalizeURL( url ); if( normUrl == "" ) return false; std::map::iterator it; it = pFactoryMap.find( normUrl ); if( it != pFactoryMap.end() ) { if( it->second->isEnv ) return false; // we don't need to check the counter because it's valid only // for environment plugins which cannot be replaced via // this method delete it->second; } if( !factory ) { log->Debug( PlugInMgrMsg, "Removing the factory for %s", normUrl.c_str() ); pFactoryMap.erase( it ); return true; } log->Debug( PlugInMgrMsg, "Registering a factory for %s", normUrl.c_str() ); FactoryHelper *h = new FactoryHelper(); h->factory = factory; h->counter = 1; pFactoryMap[normUrl] = h; return true; } //------------------------------------------------------------------------ //! Register a plug-in factory applying to all URLs //------------------------------------------------------------------------ bool PlugInManager::RegisterDefaultFactory( PlugInFactory *factory ) { Log *log = DefaultEnv::GetLog(); XrdSysMutexHelper scopedLock( pMutex ); if( pDefaultFactory && pDefaultFactory->isEnv ) return false; delete pDefaultFactory; pDefaultFactory = 0; if( factory ) { log->Debug( PlugInMgrMsg, "Registering a default factory" ); pDefaultFactory = new FactoryHelper; pDefaultFactory->factory = factory; } else log->Debug( PlugInMgrMsg, "Removing the default factory" ); return true; } //---------------------------------------------------------------------------- // Retrieve the plug-in factory for the given URL //---------------------------------------------------------------------------- PlugInFactory *PlugInManager::GetFactory( const std::string url ) { XrdSysMutexHelper scopedLock( pMutex ); if( pDefaultFactory && pDefaultFactory->isEnv ) return pDefaultFactory->factory; std::string normUrl = NormalizeURL( url ); if( normUrl.empty() ) { if( pDefaultFactory ) return pDefaultFactory->factory; return 0; } std::map::iterator it; it = pFactoryMap.find( normUrl ); if( it != pFactoryMap.end() && it->second->isEnv ) return it->second->factory; std::string protocol = URL( url ).GetProtocol(); std::map::iterator itProt; itProt = pFactoryMap.find( protocol ); if( itProt != pFactoryMap.end() && itProt->second->isEnv ) return itProt->second->factory; if( pDefaultFactory ) return pDefaultFactory->factory; if( it != pFactoryMap.end() ) return it->second->factory; if( itProt != pFactoryMap.end() ) return itProt->second->factory; return 0; } //---------------------------------------------------------------------------- // Process user environment to load plug-in settings. //---------------------------------------------------------------------------- void PlugInManager::ProcessEnvironmentSettings() { XrdSysMutexHelper scopedLock( pMutex ); Log *log = DefaultEnv::GetLog(); Env *env = DefaultEnv::GetEnv(); log->Debug( PlugInMgrMsg, "Initializing plug-in manager..." ); //-------------------------------------------------------------------------- // Check if a default plug-in has been specified in the environment //-------------------------------------------------------------------------- bool loadConfigs = true; std::string defaultPlugIn = DefaultPlugIn; env->GetString( "PlugIn", defaultPlugIn ); if( !defaultPlugIn.empty() ) { loadConfigs = false; log->Debug( PlugInMgrMsg, "Loading default plug-in from %s...", defaultPlugIn.c_str()); std::pair pg = LoadFactory( defaultPlugIn, std::map() ); if( !pg.first ) { log->Debug( PlugInMgrMsg, "Failed to load default plug-in from %s", defaultPlugIn.c_str()); loadConfigs = false; } pDefaultFactory = new FactoryHelper(); pDefaultFactory->factory = pg.second; pDefaultFactory->plugin = pg.first; pDefaultFactory->isEnv = true; } //-------------------------------------------------------------------------- // If there is no default plug-in or it is invalid then load plug-in config // files //-------------------------------------------------------------------------- if( loadConfigs ) { log->Debug( PlugInMgrMsg, "No default plug-in, loading plug-in configs..." ); ProcessConfigDir( "/etc/xrootd/client.plugins.d" ); XrdSysPwd pwdHandler; passwd *pwd = pwdHandler.Get( getuid() ); if( !pwd ) return; std::string userPlugIns = pwd->pw_dir; userPlugIns += "/.xrootd/client.plugins.d"; ProcessConfigDir( userPlugIns ); std::string customPlugIns = DefaultPlugInConfDir; env->GetString( "PlugInConfDir", customPlugIns ); if( !customPlugIns.empty() ) ProcessConfigDir( customPlugIns ); } } //---------------------------------------------------------------------------- // Process the configuration directory and load plug in definitions //---------------------------------------------------------------------------- void PlugInManager::ProcessConfigDir( const std::string &dir ) { Log *log = DefaultEnv::GetLog(); log->Debug( PlugInMgrMsg, "Processing plug-in definitions in %s...", dir.c_str()); std::vector entries; std::vector::iterator it; Status st = Utils::GetDirectoryEntries( entries, dir ); if( !st.IsOK() ) { log->Debug( PlugInMgrMsg, "Unable to process directory %s: %s", dir.c_str(), st.ToString().c_str() ); return; } std::sort( entries.begin(), entries.end() ); for( it = entries.begin(); it != entries.end(); ++it ) { std::string confFile = dir + "/" + *it; std::string suffix = ".conf"; if( confFile.length() <= suffix.length() ) continue; if( !std::equal( suffix.rbegin(), suffix.rend(), confFile.rbegin() ) ) continue; ProcessPlugInConfig( confFile ); } } //---------------------------------------------------------------------------- // Process a plug-in config file and load the plug-in if possible //---------------------------------------------------------------------------- void PlugInManager::ProcessPlugInConfig( const std::string &confFile ) { Log *log = DefaultEnv::GetLog(); log->Dump( PlugInMgrMsg, "Processing: %s", confFile.c_str() ); //-------------------------------------------------------------------------- // Read the config //-------------------------------------------------------------------------- std::map config; Status st = Utils::ProcessConfig( config, confFile ); if( !st.IsOK() ) { log->Debug( PlugInMgrMsg, "Unable process config %s: %s", confFile.c_str(), st.ToString().c_str() ); return; } const char *keys[] = { "url", "lib", "enable", 0 }; for( int i = 0; keys[i]; ++i ) { if( config.find( keys[i] ) == config.end() ) { log->Debug( PlugInMgrMsg, "Unable to find '%s' key in the config file " "%s, ignoring this config", keys[i], confFile.c_str() ); return; } } //-------------------------------------------------------------------------- // Attempt to load the plug in and place it in the map //-------------------------------------------------------------------------- std::string url = config["url"]; std::string lib = config["lib"]; std::string enable = config["enable"]; log->Dump( PlugInMgrMsg, "Settings from '%s': url='%s', lib='%s', " "enable='%s'", confFile.c_str(), url.c_str(), lib.c_str(), enable.c_str() ); std::pair pg; pg.first = 0; pg.second = 0; if( enable == "true" ) { log->Debug( PlugInMgrMsg, "Trying to load a plug-in for '%s' from '%s'", url.c_str(), lib.c_str() ); pg = LoadFactory( lib, config ); if( !pg.first ) return; } else log->Debug( PlugInMgrMsg, "Trying to disable plug-in for '%s'", url.c_str() ); if( !RegisterFactory( url, lib, pg.second, pg.first ) ) { delete pg.first; delete pg.second; } } //---------------------------------------------------------------------------- // Load the plug-in and create the factory //---------------------------------------------------------------------------- std::pair PlugInManager::LoadFactory( const std::string &lib, const std::map &config ) { Log *log = DefaultEnv::GetLog(); char errorBuff[1024]; XrdOucPinLoader *pgHandler = new XrdOucPinLoader( errorBuff, 1024, &XrdVERSIONINFOVAR( XrdCl ), "client", lib.c_str() ); PlugInFunc_t pgFunc = (PlugInFunc_t)pgHandler->Resolve("XrdClGetPlugIn", -1); if( !pgFunc ) { log->Debug( PlugInMgrMsg, "Error while loading %s: %s", lib.c_str(), errorBuff ); pgHandler->Unload(); delete pgHandler; return std::make_pair( 0, 0 ); } PlugInFactory *f = (PlugInFactory*)pgFunc( &config ); if( !f ) { delete pgHandler; return std::make_pair( 0, 0 ); } return std::make_pair( pgHandler, f ); } //---------------------------------------------------------------------------- // Handle factory - register it or free all the memory //---------------------------------------------------------------------------- bool PlugInManager::RegisterFactory( const std::string &urlString, const std::string &lib, PlugInFactory *factory, XrdOucPinLoader *plugin ) { //-------------------------------------------------------------------------- // Process and normalize the URLs //-------------------------------------------------------------------------- Log *log = DefaultEnv::GetLog(); std::vector urls; std::vector normalizedURLs; std::vector::iterator it; if (urlString == "*") { if (pDefaultFactory) { if (pDefaultFactory->isEnv) { log->Debug(PlugInMgrMsg, "There is already an env default plugin " "loaded, skiping %s", lib.c_str()); return false; } else { log->Debug(PlugInMgrMsg, "There can be only one default plugin " "loaded, skipping %s", lib.c_str()); return false; } } else { pDefaultFactory = new FactoryHelper(); pDefaultFactory->factory = factory; pDefaultFactory->plugin = plugin; pDefaultFactory->isEnv = false; return true; } } Utils::splitString( urls, urlString, ";" ); for( it = urls.begin(); it != urls.end(); ++it ) { std::string normURL = NormalizeURL( *it ); if( normURL == "" ) { log->Debug( PlugInMgrMsg, "Url cannot be normalized: '%s', ignoring", it->c_str() ); continue; } normalizedURLs.push_back( normURL ); } std::sort( normalizedURLs.begin(), normalizedURLs.end() ); std::unique( normalizedURLs.begin(), normalizedURLs.end() ); if( normalizedURLs.empty() ) return false; //-------------------------------------------------------------------------- // Insert or remove from the map //-------------------------------------------------------------------------- FactoryHelper *h = 0; if( factory ) { h = new FactoryHelper(); h->isEnv = true; h->counter = normalizedURLs.size(); h->plugin = plugin; h->factory = factory; } std::map::iterator mapIt; for( it = normalizedURLs.begin(); it != normalizedURLs.end(); ++it ) { mapIt = pFactoryMap.find( *it ); if( mapIt != pFactoryMap.end() ) { mapIt->second->counter--; if( mapIt->second->counter == 0 ) delete mapIt->second; } if( h ) { log->Debug( PlugInMgrMsg, "Registering a factory for %s from %s", it->c_str(), lib.c_str() ); pFactoryMap[*it] = h; } else { if( mapIt != pFactoryMap.end() ) { log->Debug( PlugInMgrMsg, "Removing the factory for %s", it->c_str() ); pFactoryMap.erase( mapIt ); } } } return true; } //---------------------------------------------------------------------------- // Normalize a URL //---------------------------------------------------------------------------- std::string PlugInManager::NormalizeURL( const std::string url ) { URL urlObj = url; if( !urlObj.IsValid() ) return ""; std::string protocol = urlObj.GetProtocol(); std::string hostname = urlObj.GetHostName(); if( hostname == "*" ) return protocol; std::ostringstream o; o << protocol << "://" << hostname << ":"; o << urlObj.GetPort(); return o.str(); } };