XRootD
Loading...
Searching...
No Matches
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
27#include "XrdCl/XrdClLog.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
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
47namespace 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.
EcPlugInFactory(uint8_t nbdta, uint8_t nbprt, uint64_t chsz, std::vector< std::string > &&plgr)
Constructor.
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()
Default constructor.
Definition XrdClURL.cc:36
static Status ProcessConfig(std::map< std::string, std::string > &config, const std::string &file)
Process a config file and return key-value pairs.
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.
XrdOucPinLoader(XrdSysError *errP, XrdVersionInfo *vInfo, const char *drctv, const char *plib)
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