XRootD
XrdClPlugInManager.cc
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // Copyright (c) 2014 by European Organization for Nuclear Research (CERN)
3 // Author: Lukasz Janyst <ljanyst@cern.ch>
4 //------------------------------------------------------------------------------
5 // This file is part of the XRootD software suite.
6 //
7 // XRootD is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Lesser General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // XRootD is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
19 //
20 // In applying this licence, CERN does not waive the privileges and immunities
21 // granted to it by virtue of its status as an Intergovernmental Organization
22 // or submit itself to any jurisdiction.
23 //------------------------------------------------------------------------------
24 
26 #include "XrdCl/XrdClDefaultEnv.hh"
27 #include "XrdCl/XrdClLog.hh"
28 #include "XrdCl/XrdClConstants.hh"
29 #include "XrdCl/XrdClUtils.hh"
30 #include "XrdCl/XrdClURL.hh"
31 #include "XrdSys/XrdSysPwd.hh"
32 #include "XrdVersion.hh"
33 
34 #ifdef WITH_XRDEC
35 #include "XrdCl/XrdClEcHandler.hh"
36 #endif
37 
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <vector>
41 #include <set>
42 #include <string>
43 #include <algorithm>
44 
46 
47 namespace XrdCl
48 {
49  //----------------------------------------------------------------------------
50  // Constructor
51  //----------------------------------------------------------------------------
53  pDefaultFactory(0)
54  {
55  }
56 
57  //----------------------------------------------------------------------------
58  // Destructor
59  //----------------------------------------------------------------------------
61  {
62  std::map<std::string, FactoryHelper*>::iterator it;
63  for( it = pFactoryMap.begin(); it != pFactoryMap.end(); ++it )
64  {
65  it->second->counter--;
66  if( it->second->counter == 0 )
67  delete it->second;
68  }
69 
70  delete pDefaultFactory;
71  }
72 
73  //----------------------------------------------------------------------------
74  // Register a plug-in favtory for the given url
75  //----------------------------------------------------------------------------
76  bool PlugInManager::RegisterFactory( const std::string &url,
77  PlugInFactory *factory )
78  {
79  Log *log = DefaultEnv::GetLog();
80  XrdSysMutexHelper scopedLock( pMutex );
81 
82  std::string normUrl = NormalizeURL( url );
83  if( normUrl == "" )
84  return false;
85 
86  std::map<std::string, FactoryHelper*>::iterator it;
87  it = pFactoryMap.find( normUrl );
88  if( it != pFactoryMap.end() )
89  {
90  if( it->second->isEnv )
91  return false;
92 
93  // we don't need to check the counter because it's valid only
94  // for environment plugins which cannot be replaced via
95  // this method
96  delete it->second;
97  }
98 
99  if( !factory )
100  {
101  log->Debug( PlugInMgrMsg, "Removing the factory for %s",
102  normUrl.c_str() );
103  pFactoryMap.erase( it );
104  return true;
105  }
106 
107  log->Debug( PlugInMgrMsg, "Registering a factory for %s",
108  normUrl.c_str() );
109 
110  FactoryHelper *h = new FactoryHelper();
111  h->factory = factory;
112  h->counter = 1;
113  pFactoryMap[normUrl] = h;
114  return true;
115  }
116 
117  //------------------------------------------------------------------------
119  //------------------------------------------------------------------------
121  {
122  Log *log = DefaultEnv::GetLog();
123  XrdSysMutexHelper scopedLock( pMutex );
124 
125  if( pDefaultFactory && pDefaultFactory->isEnv )
126  return false;
127 
128  delete pDefaultFactory;
129  pDefaultFactory = 0;
130 
131  if( factory )
132  {
133  log->Debug( PlugInMgrMsg, "Registering a default factory" );
134  pDefaultFactory = new FactoryHelper;
135  pDefaultFactory->factory = factory;
136  }
137  else
138  log->Debug( PlugInMgrMsg, "Removing the default factory" );
139 
140  return true;
141  }
142 
143  //----------------------------------------------------------------------------
144  // Retrieve the plug-in factory for the given URL
145  //----------------------------------------------------------------------------
146  PlugInFactory *PlugInManager::GetFactory( const std::string url )
147  {
148  XrdSysMutexHelper scopedLock( pMutex );
149 
150  if( pDefaultFactory && pDefaultFactory->isEnv )
151  return pDefaultFactory->factory;
152 
153  std::string normUrl = NormalizeURL( url );
154  if( normUrl.empty() )
155  {
156  if( pDefaultFactory )
157  return pDefaultFactory->factory;
158  return 0;
159  }
160 
161  std::map<std::string, FactoryHelper*>::iterator it;
162  it = pFactoryMap.find( normUrl );
163  if( it != pFactoryMap.end() && it->second->isEnv )
164  return it->second->factory;
165 
166  std::string protocol = URL( url ).GetProtocol();
167  std::map<std::string, FactoryHelper*>::iterator itProt;
168  itProt = pFactoryMap.find( protocol );
169  if( itProt != pFactoryMap.end() && itProt->second->isEnv )
170  return itProt->second->factory;
171 
172  if( pDefaultFactory )
173  return pDefaultFactory->factory;
174 
175  if( it != pFactoryMap.end() )
176  return it->second->factory;
177 
178  if( itProt != pFactoryMap.end() )
179  return itProt->second->factory;
180 
181  return 0;
182  }
183 
184  //----------------------------------------------------------------------------
185  // Process user environment to load plug-in settings.
186  //----------------------------------------------------------------------------
188  {
189  XrdSysMutexHelper scopedLock( pMutex );
190  Log *log = DefaultEnv::GetLog();
191  Env *env = DefaultEnv::GetEnv();
192 
193  log->Debug( PlugInMgrMsg, "Initializing plug-in manager..." );
194 
195  //--------------------------------------------------------------------------
196  // Check if a default plug-in has been specified in the environment
197  //--------------------------------------------------------------------------
198  bool loadConfigs = true;
199  std::string defaultPlugIn = DefaultPlugIn;
200  env->GetString( "PlugIn", defaultPlugIn );
201  if( !defaultPlugIn.empty() )
202  {
203  loadConfigs = false;
204  log->Debug( PlugInMgrMsg, "Loading default plug-in from %s...",
205  defaultPlugIn.c_str());
206 
207  std::pair<XrdOucPinLoader*, PlugInFactory *> pg = LoadFactory(
208  defaultPlugIn, std::map<std::string, std::string>() );
209 
210  if( !pg.first )
211  {
212  log->Debug( PlugInMgrMsg, "Failed to load default plug-in from %s",
213  defaultPlugIn.c_str());
214  loadConfigs = false;
215  }
216 
217  pDefaultFactory = new FactoryHelper();
218  pDefaultFactory->factory = pg.second;
219  pDefaultFactory->plugin = pg.first;
220  pDefaultFactory->isEnv = true;
221  }
222 
223  //--------------------------------------------------------------------------
224  // If there is no default plug-in or it is invalid then load plug-in config
225  // files
226  //--------------------------------------------------------------------------
227  if( loadConfigs )
228  {
229  log->Debug( PlugInMgrMsg,
230  "No default plug-in, loading plug-in configs..." );
231 
232  std::vector<std::string> dirs;
233 
234  std::string customPlugIns = DefaultPlugInConfDir;
235  env->GetString( "PlugInConfDir", customPlugIns );
236  if( !customPlugIns.empty() )
237  dirs.push_back( customPlugIns );
238 
239  XrdSysPwd pwdHandler;
240  passwd *pwd = pwdHandler.Get( getuid() );
241  if( pwd )
242  dirs.push_back( std::string( pwd->pw_dir ) + "/.xrootd/client.plugins.d" );
243 
244  dirs.push_back( "/etc/xrootd/client.plugins.d" );
245 
246  ProcessConfigDirs( dirs );
247  }
248  }
249 
250  //----------------------------------------------------------------------------
251  // Process the configuration directories in order of priority
252  //----------------------------------------------------------------------------
253  void PlugInManager::ProcessConfigDirs( const std::vector<std::string> &dirs )
254  {
255  std::set<std::string> settled;
256  for( const auto &dir : dirs )
257  ProcessConfigDir( dir, settled );
258  }
259 
260  //----------------------------------------------------------------------------
261  // Process the configuration directory and load plug in definitions
262  //----------------------------------------------------------------------------
263  void PlugInManager::ProcessConfigDir( const std::string &dir )
264  {
265  std::set<std::string> settled;
266  ProcessConfigDir( dir, settled );
267  }
268 
269  //----------------------------------------------------------------------------
270  // Process the configuration directory, skipping the settled URLs
271  //----------------------------------------------------------------------------
272  void PlugInManager::ProcessConfigDir( const std::string &dir,
273  std::set<std::string> &settled )
274  {
275  Log *log = DefaultEnv::GetLog();
276  log->Debug( PlugInMgrMsg, "Processing plug-in definitions in %s...",
277  dir.c_str());
278 
279  std::vector<std::string> entries;
280  std::vector<std::string>::iterator it;
281  Status st = Utils::GetDirectoryEntries( entries, dir );
282  if( !st.IsOK() )
283  {
284  log->Debug( PlugInMgrMsg, "Unable to process directory %s: %s",
285  dir.c_str(), st.ToString().c_str() );
286  return;
287  }
288  std::sort( entries.begin(), entries.end() );
289 
290  std::set<std::string> decided;
291  for( it = entries.begin(); it != entries.end(); ++it )
292  {
293  std::string confFile = dir + "/" + *it;
294  std::string suffix = ".conf";
295  if( confFile.length() <= suffix.length() )
296  continue;
297  if( !std::equal( suffix.rbegin(), suffix.rend(), confFile.rbegin() ) )
298  continue;
299 
300  ProcessPlugInConfig( confFile, settled, decided );
301  }
302 
303  settled.insert( decided.begin(), decided.end() );
304  }
305 
306  //----------------------------------------------------------------------------
307  // Process a plug-in config file and load the plug-in if possible
308  //----------------------------------------------------------------------------
309  void PlugInManager::ProcessPlugInConfig( const std::string &confFile )
310  {
311  std::set<std::string> settled, decided;
312  ProcessPlugInConfig( confFile, settled, decided );
313  }
314 
315  //----------------------------------------------------------------------------
316  // Process a plug-in config file, skipping the settled URLs
317  //----------------------------------------------------------------------------
318  void PlugInManager::ProcessPlugInConfig( const std::string &confFile,
319  const std::set<std::string> &settled,
320  std::set<std::string> &decided )
321  {
322  Log *log = DefaultEnv::GetLog();
323  log->Dump( PlugInMgrMsg, "Processing: %s", confFile.c_str() );
324 
325  //--------------------------------------------------------------------------
326  // Read the config
327  //--------------------------------------------------------------------------
328  std::map<std::string, std::string> config;
329  Status st = Utils::ProcessConfig( config, confFile );
330  if( !st.IsOK() )
331  {
332  log->Debug( PlugInMgrMsg, "Unable process config %s: %s",
333  confFile.c_str(), st.ToString().c_str() );
334  return;
335  }
336 
337  const char *keys[] = { "url", "lib", "enable", 0 };
338  for( int i = 0; keys[i]; ++i )
339  {
340  if( config.find( keys[i] ) == config.end() )
341  {
342  log->Debug( PlugInMgrMsg, "Unable to find '%s' key in the config file "
343  "%s, ignoring this config", keys[i], confFile.c_str() );
344  return;
345  }
346  }
347 
348  //--------------------------------------------------------------------------
349  // Attempt to load the plug in and place it in the map
350  //--------------------------------------------------------------------------
351  std::string url = config["url"];
352  std::string lib = config["lib"];
353  std::string enable = config["enable"];
354 
355  log->Dump( PlugInMgrMsg, "Settings from '%s': url='%s', lib='%s', "
356  "enable='%s'", confFile.c_str(), url.c_str(), lib.c_str(),
357  enable.c_str() );
358 
359  //--------------------------------------------------------------------------
360  // Skip the URLs decided by a directory with higher priority
361  //--------------------------------------------------------------------------
362  std::vector<std::string> tokens;
363  if( url == "*" )
364  tokens.push_back( url );
365  else
366  Utils::splitString( tokens, url, ";" );
367 
368  std::vector<std::string> normURLs;
369  std::string urls;
370  for( const auto &token : tokens )
371  {
372  std::string key = token == "*" ? token : NormalizeURL( token );
373  if( settled.count( key ) )
374  {
375  log->Debug( PlugInMgrMsg, "Skipping %s for '%s', set by a directory "
376  "with higher priority", lib.c_str(), token.c_str() );
377  continue;
378  }
379  normURLs.push_back( key );
380  if( !urls.empty() )
381  urls += ";";
382  urls += token;
383  }
384 
385  if( urls.empty() )
386  return;
387 
388  std::pair<XrdOucPinLoader*, PlugInFactory *> pg;
389  pg.first = 0; pg.second = 0;
390  if( enable == "true" )
391  {
392  log->Debug( PlugInMgrMsg, "Trying to load a plug-in for '%s' from '%s'",
393  urls.c_str(), lib.c_str() );
394 
395  pg = LoadFactory( lib, config );
396 
397  if( !pg.second )
398  return;
399  }
400  else
401  log->Debug( PlugInMgrMsg, "Trying to disable plug-in for '%s'",
402  urls.c_str() );
403 
404  decided.insert( normURLs.begin(), normURLs.end() );
405 
406  if( !RegisterFactory( urls, lib, pg.second, pg.first ) )
407  {
408  delete pg.first;
409  delete pg.second;
410  }
411  }
412 
413  //----------------------------------------------------------------------------
414  // Load the plug-in and create the factory
415  //----------------------------------------------------------------------------
416  std::pair<XrdOucPinLoader*,PlugInFactory*> PlugInManager::LoadFactory(
417  const std::string &lib, const std::map<std::string, std::string> &config )
418  {
419  Log *log = DefaultEnv::GetLog();
420 
421 #ifdef WITH_XRDEC
422  if( lib == "XrdEcDefault" )
423  {
424  auto itr = config.find( "nbdta" );
425  if( itr == config.end() )
426  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, nullptr );
427  uint8_t nbdta = std::stoul( itr->second );
428  itr = config.find( "nbprt" );
429  if( itr == config.end() )
430  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, nullptr );
431  uint8_t nbprt = std::stoul( itr->second );
432  itr = config.find( "chsz" );
433  if( itr == config.end() )
434  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, nullptr );
435  uint64_t chsz = std::stoul( itr->second );
436  std::vector<std::string> plgr;
437  itr = config.find( "plgr" );
438  if( itr != config.end() )
439  Utils::splitString( plgr, itr->second, "," );
440 
441  std::string xrdclECenv = std::to_string(nbdta) + "," +
442  std::to_string(nbprt) + "," +
443  std::to_string(chsz);
444  setenv("XRDCL_EC", xrdclECenv.c_str(), 1);
445 
446  EcPlugInFactory *ecHandler = new EcPlugInFactory( nbdta, nbprt, chsz, std::move( plgr ) );
447  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, ecHandler );
448  }
449 #endif
450 
451  char errorBuff[1024];
452  XrdOucPinLoader *pgHandler = new XrdOucPinLoader( errorBuff, 1024,
453  &XrdVERSIONINFOVAR( XrdCl ),
454  "client", lib.c_str() );
455 
456  PlugInFunc_t pgFunc = (PlugInFunc_t)pgHandler->Resolve("XrdClGetPlugIn", -1);
457 
458  if( !pgFunc )
459  {
460  log->Debug( PlugInMgrMsg, "Error while loading %s: %s", lib.c_str(),
461  errorBuff );
462  pgHandler->Unload();
463  delete pgHandler;
464  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( 0, 0 );
465  }
466 
467  PlugInFactory *f = (PlugInFactory*)pgFunc( &config );
468 
469  if( !f )
470  {
471  delete pgHandler;
472  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( 0, 0 );
473  }
474 
475  return std::make_pair( pgHandler, f );
476  }
477 
478  //----------------------------------------------------------------------------
479  // Handle factory - register it or free all the memory
480  //----------------------------------------------------------------------------
481  bool PlugInManager::RegisterFactory( const std::string &urlString,
482  const std::string &lib,
483  PlugInFactory *factory,
484  XrdOucPinLoader *plugin )
485  {
486  //--------------------------------------------------------------------------
487  // Process and normalize the URLs
488  //--------------------------------------------------------------------------
489  Log *log = DefaultEnv::GetLog();
490  std::vector<std::string> urls;
491  std::vector<std::string> normalizedURLs;
492  std::vector<std::string>::iterator it;
493 
494  if (urlString == "*") {
495  if (pDefaultFactory) {
496  if (pDefaultFactory->isEnv) {
497  log->Debug(PlugInMgrMsg, "There is already an env default plugin "
498  "loaded, skipping %s", lib.c_str());
499  return false;
500  } else {
501  log->Debug(PlugInMgrMsg, "There can be only one default plugin "
502  "loaded, skipping %s", lib.c_str());
503  return false;
504  }
505  } else if (!factory) {
506  // a disabled default plug-in config; there is nothing to disable and
507  // installing a helper holding a null factory would shadow factories
508  // registered for specific URLs
509  log->Debug(PlugInMgrMsg, "No default plugin loaded, nothing to "
510  "disable for %s", lib.c_str());
511  return false;
512  } else {
513  pDefaultFactory = new FactoryHelper();
514  pDefaultFactory->factory = factory;
515  pDefaultFactory->plugin = plugin;
516  pDefaultFactory->isEnv = false;
517  return true;
518  }
519  }
520 
521  Utils::splitString( urls, urlString, ";" );
522 
523  for( it = urls.begin(); it != urls.end(); ++it )
524  {
525  std::string normURL = NormalizeURL( *it );
526  if( normURL == "" )
527  {
528  log->Debug( PlugInMgrMsg, "Url cannot be normalized: '%s', ignoring",
529  it->c_str() );
530  continue;
531  }
532  normalizedURLs.push_back( normURL );
533  }
534 
535  std::sort( normalizedURLs.begin(), normalizedURLs.end() );
536 
537  auto last = std::unique( normalizedURLs.begin(), normalizedURLs.end() );
538  normalizedURLs.erase( last, normalizedURLs.end() );
539 
540  if( normalizedURLs.empty() )
541  return false;
542 
543  //--------------------------------------------------------------------------
544  // Insert or remove from the map
545  //--------------------------------------------------------------------------
546  FactoryHelper *h = 0;
547 
548  if( factory )
549  {
550  h = new FactoryHelper();
551  h->isEnv = true;
552  h->counter = normalizedURLs.size();
553  h->plugin = plugin;
554  h->factory = factory;
555  }
556 
557  std::map<std::string, FactoryHelper*>::iterator mapIt;
558  for( it = normalizedURLs.begin(); it != normalizedURLs.end(); ++it )
559  {
560  mapIt = pFactoryMap.find( *it );
561  if( mapIt != pFactoryMap.end() )
562  {
563  mapIt->second->counter--;
564  if( mapIt->second->counter == 0 )
565  delete mapIt->second;
566  }
567 
568  if( h )
569  {
570  log->Debug( PlugInMgrMsg, "Registering a factory for %s from %s",
571  it->c_str(), lib.c_str() );
572  pFactoryMap[*it] = h;
573  }
574  else
575  {
576  if( mapIt != pFactoryMap.end() )
577  {
578  log->Debug( PlugInMgrMsg, "Removing the factory for %s",
579  it->c_str() );
580  pFactoryMap.erase( mapIt );
581  }
582  }
583  }
584 
585  return true;
586  }
587 
588  //----------------------------------------------------------------------------
589  // Normalize a URL
590  //----------------------------------------------------------------------------
591  std::string PlugInManager::NormalizeURL( const std::string url )
592  {
593  URL urlObj = url;
594  if( !urlObj.IsValid() )
595  return "";
596 
597  std::string protocol = urlObj.GetProtocol();
598  std::string hostname = urlObj.GetHostName();
599 
600  if( hostname == "*" )
601  return protocol;
602 
603  std::ostringstream o;
604  o << protocol << "://" << hostname << ":";
605  o << urlObj.GetPort();
606  return o.str();
607  }
608 };
XrdVERSIONINFOREF(XrdCl)
static Log * GetLog()
Get default log.
static Env * GetEnv()
Get default client environment.
bool GetString(const std::string &key, std::string &value)
Definition: XrdClEnv.cc:31
Handle diagnostics.
Definition: XrdClLog.hh:101
void Debug(uint64_t topic, const char *format,...)
Print a debug message.
Definition: XrdClLog.cc:282
bool RegisterFactory(const std::string &url, PlugInFactory *factory)
bool RegisterDefaultFactory(PlugInFactory *factory)
Register a plug-in factory applying to all URLs.
PlugInFactory * GetFactory(const std::string url)
URL representation.
Definition: XrdClURL.hh:31
const std::string & GetProtocol() const
Get the protocol.
Definition: XrdClURL.hh:118
static Status ProcessConfig(std::map< std::string, std::string > &config, const std::string &file)
Process a config file and return key-value pairs.
Definition: XrdClUtils.cc:534
static void splitString(Container &result, const std::string &input, const std::string &delimiter)
Split a string.
Definition: XrdClUtils.hh:56
static Status GetDirectoryEntries(std::vector< std::string > &entries, const std::string &path)
Get directory entries.
Definition: XrdClUtils.cc:506
void * Resolve(const char *symbl, int mcnt=1)
void Unload(bool dodel=false)
struct passwd * Get(const char *Usr)
Definition: XrdSysPwd.hh:42
const char *const DefaultPlugIn
const char *const DefaultPlugInConfDir
const uint64_t PlugInMgrMsg
XrdSysError Log
Definition: XrdConfig.cc:113