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 <string>
42 #include <algorithm>
43 
45 
46 namespace XrdCl
47 {
48  //----------------------------------------------------------------------------
49  // Constructor
50  //----------------------------------------------------------------------------
52  pDefaultFactory(0)
53  {
54  }
55 
56  //----------------------------------------------------------------------------
57  // Destructor
58  //----------------------------------------------------------------------------
60  {
61  std::map<std::string, FactoryHelper*>::iterator it;
62  for( it = pFactoryMap.begin(); it != pFactoryMap.end(); ++it )
63  {
64  it->second->counter--;
65  if( it->second->counter == 0 )
66  delete it->second;
67  }
68 
69  delete pDefaultFactory;
70  }
71 
72  //----------------------------------------------------------------------------
73  // Register a plug-in favtory for the given url
74  //----------------------------------------------------------------------------
75  bool PlugInManager::RegisterFactory( const std::string &url,
76  PlugInFactory *factory )
77  {
78  Log *log = DefaultEnv::GetLog();
79  XrdSysMutexHelper scopedLock( pMutex );
80 
81  std::string normUrl = NormalizeURL( url );
82  if( normUrl == "" )
83  return false;
84 
85  std::map<std::string, FactoryHelper*>::iterator it;
86  it = pFactoryMap.find( normUrl );
87  if( it != pFactoryMap.end() )
88  {
89  if( it->second->isEnv )
90  return false;
91 
92  // we don't need to check the counter because it's valid only
93  // for environment plugins which cannot be replaced via
94  // this method
95  delete it->second;
96  }
97 
98  if( !factory )
99  {
100  log->Debug( PlugInMgrMsg, "Removing the factory for %s",
101  normUrl.c_str() );
102  pFactoryMap.erase( it );
103  return true;
104  }
105 
106  log->Debug( PlugInMgrMsg, "Registering a factory for %s",
107  normUrl.c_str() );
108 
109  FactoryHelper *h = new FactoryHelper();
110  h->factory = factory;
111  h->counter = 1;
112  pFactoryMap[normUrl] = h;
113  return true;
114  }
115 
116  //------------------------------------------------------------------------
118  //------------------------------------------------------------------------
120  {
121  Log *log = DefaultEnv::GetLog();
122  XrdSysMutexHelper scopedLock( pMutex );
123 
124  if( pDefaultFactory && pDefaultFactory->isEnv )
125  return false;
126 
127  delete pDefaultFactory;
128  pDefaultFactory = 0;
129 
130  if( factory )
131  {
132  log->Debug( PlugInMgrMsg, "Registering a default factory" );
133  pDefaultFactory = new FactoryHelper;
134  pDefaultFactory->factory = factory;
135  }
136  else
137  log->Debug( PlugInMgrMsg, "Removing the default factory" );
138 
139  return true;
140  }
141 
142  //----------------------------------------------------------------------------
143  // Retrieve the plug-in factory for the given URL
144  //----------------------------------------------------------------------------
145  PlugInFactory *PlugInManager::GetFactory( const std::string url )
146  {
147  XrdSysMutexHelper scopedLock( pMutex );
148 
149  if( pDefaultFactory && pDefaultFactory->isEnv )
150  return pDefaultFactory->factory;
151 
152  std::string normUrl = NormalizeURL( url );
153  if( normUrl.empty() )
154  {
155  if( pDefaultFactory )
156  return pDefaultFactory->factory;
157  return 0;
158  }
159 
160  std::map<std::string, FactoryHelper*>::iterator it;
161  it = pFactoryMap.find( normUrl );
162  if( it != pFactoryMap.end() && it->second->isEnv )
163  return it->second->factory;
164 
165  std::string protocol = URL( url ).GetProtocol();
166  std::map<std::string, FactoryHelper*>::iterator itProt;
167  itProt = pFactoryMap.find( protocol );
168  if( itProt != pFactoryMap.end() && itProt->second->isEnv )
169  return itProt->second->factory;
170 
171  if( pDefaultFactory )
172  return pDefaultFactory->factory;
173 
174  if( it != pFactoryMap.end() )
175  return it->second->factory;
176 
177  if( itProt != pFactoryMap.end() )
178  return itProt->second->factory;
179 
180  return 0;
181  }
182 
183  //----------------------------------------------------------------------------
184  // Process user environment to load plug-in settings.
185  //----------------------------------------------------------------------------
187  {
188  XrdSysMutexHelper scopedLock( pMutex );
189  Log *log = DefaultEnv::GetLog();
190  Env *env = DefaultEnv::GetEnv();
191 
192  log->Debug( PlugInMgrMsg, "Initializing plug-in manager..." );
193 
194  //--------------------------------------------------------------------------
195  // Check if a default plug-in has been specified in the environment
196  //--------------------------------------------------------------------------
197  bool loadConfigs = true;
198  std::string defaultPlugIn = DefaultPlugIn;
199  env->GetString( "PlugIn", defaultPlugIn );
200  if( !defaultPlugIn.empty() )
201  {
202  loadConfigs = false;
203  log->Debug( PlugInMgrMsg, "Loading default plug-in from %s...",
204  defaultPlugIn.c_str());
205 
206  std::pair<XrdOucPinLoader*, PlugInFactory *> pg = LoadFactory(
207  defaultPlugIn, std::map<std::string, std::string>() );
208 
209  if( !pg.first )
210  {
211  log->Debug( PlugInMgrMsg, "Failed to load default plug-in from %s",
212  defaultPlugIn.c_str());
213  loadConfigs = false;
214  }
215 
216  pDefaultFactory = new FactoryHelper();
217  pDefaultFactory->factory = pg.second;
218  pDefaultFactory->plugin = pg.first;
219  pDefaultFactory->isEnv = true;
220  }
221 
222  //--------------------------------------------------------------------------
223  // If there is no default plug-in or it is invalid then load plug-in config
224  // files
225  //--------------------------------------------------------------------------
226  if( loadConfigs )
227  {
228  log->Debug( PlugInMgrMsg,
229  "No default plug-in, loading plug-in configs..." );
230 
231  ProcessConfigDir( "/etc/xrootd/client.plugins.d" );
232 
233  XrdSysPwd pwdHandler;
234  passwd *pwd = pwdHandler.Get( getuid() );
235  if( pwd )
236  {
237  std::string userPlugIns = pwd->pw_dir;
238  userPlugIns += "/.xrootd/client.plugins.d";
239  ProcessConfigDir( userPlugIns );
240  }
241  std::string customPlugIns = DefaultPlugInConfDir;
242  env->GetString( "PlugInConfDir", customPlugIns );
243  if( !customPlugIns.empty() )
244  ProcessConfigDir( customPlugIns );
245  }
246  }
247 
248  //----------------------------------------------------------------------------
249  // Process the configuration directory and load plug in definitions
250  //----------------------------------------------------------------------------
251  void PlugInManager::ProcessConfigDir( const std::string &dir )
252  {
253  Log *log = DefaultEnv::GetLog();
254  log->Debug( PlugInMgrMsg, "Processing plug-in definitions in %s...",
255  dir.c_str());
256 
257  std::vector<std::string> entries;
258  std::vector<std::string>::iterator it;
259  Status st = Utils::GetDirectoryEntries( entries, dir );
260  if( !st.IsOK() )
261  {
262  log->Debug( PlugInMgrMsg, "Unable to process directory %s: %s",
263  dir.c_str(), st.ToString().c_str() );
264  return;
265  }
266  std::sort( entries.begin(), entries.end() );
267 
268  for( it = entries.begin(); it != entries.end(); ++it )
269  {
270  std::string confFile = dir + "/" + *it;
271  std::string suffix = ".conf";
272  if( confFile.length() <= suffix.length() )
273  continue;
274  if( !std::equal( suffix.rbegin(), suffix.rend(), confFile.rbegin() ) )
275  continue;
276 
277  ProcessPlugInConfig( confFile );
278  }
279  }
280 
281  //----------------------------------------------------------------------------
282  // Process a plug-in config file and load the plug-in if possible
283  //----------------------------------------------------------------------------
284  void PlugInManager::ProcessPlugInConfig( const std::string &confFile )
285  {
286  Log *log = DefaultEnv::GetLog();
287  log->Dump( PlugInMgrMsg, "Processing: %s", confFile.c_str() );
288 
289  //--------------------------------------------------------------------------
290  // Read the config
291  //--------------------------------------------------------------------------
292  std::map<std::string, std::string> config;
293  Status st = Utils::ProcessConfig( config, confFile );
294  if( !st.IsOK() )
295  {
296  log->Debug( PlugInMgrMsg, "Unable process config %s: %s",
297  confFile.c_str(), st.ToString().c_str() );
298  return;
299  }
300 
301  const char *keys[] = { "url", "lib", "enable", 0 };
302  for( int i = 0; keys[i]; ++i )
303  {
304  if( config.find( keys[i] ) == config.end() )
305  {
306  log->Debug( PlugInMgrMsg, "Unable to find '%s' key in the config file "
307  "%s, ignoring this config", keys[i], confFile.c_str() );
308  return;
309  }
310  }
311 
312  //--------------------------------------------------------------------------
313  // Attempt to load the plug in and place it in the map
314  //--------------------------------------------------------------------------
315  std::string url = config["url"];
316  std::string lib = config["lib"];
317  std::string enable = config["enable"];
318 
319  log->Dump( PlugInMgrMsg, "Settings from '%s': url='%s', lib='%s', "
320  "enable='%s'", confFile.c_str(), url.c_str(), lib.c_str(),
321  enable.c_str() );
322 
323  std::pair<XrdOucPinLoader*, PlugInFactory *> pg;
324  pg.first = 0; pg.second = 0;
325  if( enable == "true" )
326  {
327  log->Debug( PlugInMgrMsg, "Trying to load a plug-in for '%s' from '%s'",
328  url.c_str(), lib.c_str() );
329 
330  pg = LoadFactory( lib, config );
331 
332  if( !pg.second )
333  return;
334  }
335  else
336  log->Debug( PlugInMgrMsg, "Trying to disable plug-in for '%s'",
337  url.c_str() );
338 
339  if( !RegisterFactory( url, lib, pg.second, pg.first ) )
340  {
341  delete pg.first;
342  delete pg.second;
343  }
344  }
345 
346  //----------------------------------------------------------------------------
347  // Load the plug-in and create the factory
348  //----------------------------------------------------------------------------
349  std::pair<XrdOucPinLoader*,PlugInFactory*> PlugInManager::LoadFactory(
350  const std::string &lib, const std::map<std::string, std::string> &config )
351  {
352  Log *log = DefaultEnv::GetLog();
353 
354 #ifdef WITH_XRDEC
355  if( lib == "XrdEcDefault" )
356  {
357  auto itr = config.find( "nbdta" );
358  if( itr == config.end() )
359  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, nullptr );
360  uint8_t nbdta = std::stoul( itr->second );
361  itr = config.find( "nbprt" );
362  if( itr == config.end() )
363  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, nullptr );
364  uint8_t nbprt = std::stoul( itr->second );
365  itr = config.find( "chsz" );
366  if( itr == config.end() )
367  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, nullptr );
368  uint64_t chsz = std::stoul( itr->second );
369  std::vector<std::string> plgr;
370  itr = config.find( "plgr" );
371  if( itr != config.end() )
372  Utils::splitString( plgr, itr->second, "," );
373 
374  std::string xrdclECenv = std::to_string(nbdta) + "," +
375  std::to_string(nbprt) + "," +
376  std::to_string(chsz);
377  setenv("XRDCL_EC", xrdclECenv.c_str(), 1);
378 
379  EcPlugInFactory *ecHandler = new EcPlugInFactory( nbdta, nbprt, chsz, std::move( plgr ) );
380  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( nullptr, ecHandler );
381  }
382 #endif
383 
384  char errorBuff[1024];
385  XrdOucPinLoader *pgHandler = new XrdOucPinLoader( errorBuff, 1024,
386  &XrdVERSIONINFOVAR( XrdCl ),
387  "client", lib.c_str() );
388 
389  PlugInFunc_t pgFunc = (PlugInFunc_t)pgHandler->Resolve("XrdClGetPlugIn", -1);
390 
391  if( !pgFunc )
392  {
393  log->Debug( PlugInMgrMsg, "Error while loading %s: %s", lib.c_str(),
394  errorBuff );
395  pgHandler->Unload();
396  delete pgHandler;
397  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( 0, 0 );
398  }
399 
400  PlugInFactory *f = (PlugInFactory*)pgFunc( &config );
401 
402  if( !f )
403  {
404  delete pgHandler;
405  return std::make_pair<XrdOucPinLoader*, PlugInFactory*>( 0, 0 );
406  }
407 
408  return std::make_pair( pgHandler, f );
409  }
410 
411  //----------------------------------------------------------------------------
412  // Handle factory - register it or free all the memory
413  //----------------------------------------------------------------------------
414  bool PlugInManager::RegisterFactory( const std::string &urlString,
415  const std::string &lib,
416  PlugInFactory *factory,
417  XrdOucPinLoader *plugin )
418  {
419  //--------------------------------------------------------------------------
420  // Process and normalize the URLs
421  //--------------------------------------------------------------------------
422  Log *log = DefaultEnv::GetLog();
423  std::vector<std::string> urls;
424  std::vector<std::string> normalizedURLs;
425  std::vector<std::string>::iterator it;
426 
427  if (urlString == "*") {
428  if (pDefaultFactory) {
429  if (pDefaultFactory->isEnv) {
430  log->Debug(PlugInMgrMsg, "There is already an env default plugin "
431  "loaded, skipping %s", lib.c_str());
432  return false;
433  } else {
434  log->Debug(PlugInMgrMsg, "There can be only one default plugin "
435  "loaded, skipping %s", lib.c_str());
436  return false;
437  }
438  } else {
439  pDefaultFactory = new FactoryHelper();
440  pDefaultFactory->factory = factory;
441  pDefaultFactory->plugin = plugin;
442  pDefaultFactory->isEnv = false;
443  return true;
444  }
445  }
446 
447  Utils::splitString( urls, urlString, ";" );
448 
449  for( it = urls.begin(); it != urls.end(); ++it )
450  {
451  std::string normURL = NormalizeURL( *it );
452  if( normURL == "" )
453  {
454  log->Debug( PlugInMgrMsg, "Url cannot be normalized: '%s', ignoring",
455  it->c_str() );
456  continue;
457  }
458  normalizedURLs.push_back( normURL );
459  }
460 
461  std::sort( normalizedURLs.begin(), normalizedURLs.end() );
462 
463  auto last = std::unique( normalizedURLs.begin(), normalizedURLs.end() );
464  normalizedURLs.erase( last, normalizedURLs.end() );
465 
466  if( normalizedURLs.empty() )
467  return false;
468 
469  //--------------------------------------------------------------------------
470  // Insert or remove from the map
471  //--------------------------------------------------------------------------
472  FactoryHelper *h = 0;
473 
474  if( factory )
475  {
476  h = new FactoryHelper();
477  h->isEnv = true;
478  h->counter = normalizedURLs.size();
479  h->plugin = plugin;
480  h->factory = factory;
481  }
482 
483  std::map<std::string, FactoryHelper*>::iterator mapIt;
484  for( it = normalizedURLs.begin(); it != normalizedURLs.end(); ++it )
485  {
486  mapIt = pFactoryMap.find( *it );
487  if( mapIt != pFactoryMap.end() )
488  {
489  mapIt->second->counter--;
490  if( mapIt->second->counter == 0 )
491  delete mapIt->second;
492  }
493 
494  if( h )
495  {
496  log->Debug( PlugInMgrMsg, "Registering a factory for %s from %s",
497  it->c_str(), lib.c_str() );
498  pFactoryMap[*it] = h;
499  }
500  else
501  {
502  if( mapIt != pFactoryMap.end() )
503  {
504  log->Debug( PlugInMgrMsg, "Removing the factory for %s",
505  it->c_str() );
506  pFactoryMap.erase( mapIt );
507  }
508  }
509  }
510 
511  return true;
512  }
513 
514  //----------------------------------------------------------------------------
515  // Normalize a URL
516  //----------------------------------------------------------------------------
517  std::string PlugInManager::NormalizeURL( const std::string url )
518  {
519  URL urlObj = url;
520  if( !urlObj.IsValid() )
521  return "";
522 
523  std::string protocol = urlObj.GetProtocol();
524  std::string hostname = urlObj.GetHostName();
525 
526  if( hostname == "*" )
527  return protocol;
528 
529  std::ostringstream o;
530  o << protocol << "://" << hostname << ":";
531  o << urlObj.GetPort();
532  return o.str();
533  }
534 };
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:112
Procedure execution status.
Definition: XrdClStatus.hh:115
bool IsOK() const
We're fine.
Definition: XrdClStatus.hh:124
std::string ToString() const
Create a string representation.
Definition: XrdClStatus.cc:97