Orocos Real-Time Toolkit  2.8.3
PluginLoader.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  tag: The SourceWorks Tue Sep 7 00:55:18 CEST 2010 PluginLoader.cpp
3 
4  PluginLoader.cpp - description
5  -------------------
6  begin : Tue September 07 2010
7  copyright : (C) 2010 The SourceWorks
8  email : peter@thesourceworks.com
9 
10  ***************************************************************************
11  * This library is free software; you can redistribute it and/or *
12  * modify it under the terms of the GNU General Public *
13  * License as published by the Free Software Foundation; *
14  * version 2 of the License. *
15  * *
16  * As a special exception, you may use this file as part of a free *
17  * software library without restriction. Specifically, if other files *
18  * instantiate templates or use macros or inline functions from this *
19  * file, or you compile this file and link it with other files to *
20  * produce an executable, this file does not by itself cause the *
21  * resulting executable to be covered by the GNU General Public *
22  * License. This exception does not however invalidate any other *
23  * reasons why the executable file might be covered by the GNU General *
24  * Public License. *
25  * *
26  * This library is distributed in the hope that it will be useful, *
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
29  * Lesser General Public License for more details. *
30  * *
31  * You should have received a copy of the GNU General Public *
32  * License along with this library; if not, write to the Free Software *
33  * Foundation, Inc., 59 Temple Place, *
34  * Suite 330, Boston, MA 02111-1307 USA *
35  * *
36  ***************************************************************************/
37 
38 
46 #include "PluginLoader.hpp"
47 #include "../TaskContext.hpp"
48 #include "../Logger.hpp"
49 #include <boost/filesystem.hpp>
50 #include <boost/version.hpp>
51 #include "../os/StartStopManager.hpp"
52 #include "../os/MutexLock.hpp"
53 #include "../internal/GlobalService.hpp"
54 
55 #include <cstdlib>
56 #include <dlfcn.h>
57 
58 #include <vector>
59 #include <set>
60 
61 using namespace RTT;
62 using namespace RTT::detail;
63 using namespace plugin;
64 using namespace std;
65 using namespace boost::filesystem;
66 
67 // chose the file extension and debug postfix applicable to the O/S
68 #ifdef __APPLE__
69 static const std::string SO_EXT(".dylib");
70 static const std::string SO_POSTFIX("");
71 #else
72 # ifdef _WIN32
73 static const std::string SO_EXT(".dll");
74 # ifdef _DEBUG
75 static const std::string SO_POSTFIX("d");
76 # else
77 static const std::string SO_POSTFIX("");
78 # endif // _DEBUG
79 # else
80 static const std::string SO_EXT(".so");
81 static const std::string SO_POSTFIX("");
82 # endif
83 #endif
84 
85 // The full library suffix must be enforced by the UseOrocos macros
86 static const std::string FULL_PLUGINS_SUFFIX(string("-") + string(OROCOS_TARGET_NAME) + SO_POSTFIX + SO_EXT);
87 
88 // choose how the PATH looks like
89 # ifdef _WIN32
90 static const std::string delimiters(";");
91 static const std::string default_delimiter(";");
92 # else
93 static const std::string delimiters(":;");
94 static const std::string default_delimiter(":");
95 # endif
96 
97 // define RTT_UNUSED macro
98 #ifndef RTT_UNUSED
99  #ifdef __GNUC__
100  #define RTT_UNUSED __attribute__((unused))
101  #else
102  #define RTT_UNUSED
103  #endif
104 #endif
105 
114 RTT_API bool isExtensionVersion(const std::string& ext)
115 {
116  bool isExtensionVersion = false;
117 
118  if (!ext.empty() && ('.' == ext[0]))
119  {
120  std::istringstream iss;
121  int i;
122 
123  iss.str(ext.substr((size_t)1)); // take all after the '.'
124  iss >> std::dec >> std::noskipws >> i;
125  isExtensionVersion = !iss.fail() && iss.eof();
126  }
127 
128  return isExtensionVersion;
129 }
130 
131 /* Is this a dynamic library that we should load from within a directory scan?
132 
133  Versioned libraries are not loaded, to prevent loading both libfoo.so and
134  libfoo.so.1 (which is typically a symlink to libfoo.so, and so loading
135  the same library twice).
136 
137  Libraries are either (NB x.y.z is version, and could also be x or x.y)
138 
139  Linux
140  libfoo.so = load this
141  libfoo.so.x.y.z = don't load this
142 
143  Windows
144  libfoo.dll = load this
145 
146  Mac OS X
147  libfoo.dylib = load this
148  libfoo.x.y.z.dylib = don't load this
149 
150  All the above also apply without the "lib" prefix.
151 */
152 RTT_API bool isLoadableLibrary(const path& filename)
153 {
154  bool isLoadable = false;
155 
156 #if defined(__APPLE__)
157  std::string ext;
158 #if BOOST_VERSION >= 104600
159  ext = filename.extension().string();
160 #else
161  ext = filename.extension();
162 #endif
163  // ends in SO_EXT?
164  if (0 == ext.compare(SO_EXT))
165  {
166  // Ends in SO_EXT and so must not be a link for us to load
167  // Links are of the form abc.x.dylib or abc.x.y.dylib or abc.x.y.z.dylib,
168  // where x,y,z are positive numbers
169  path name = filename.stem(); // drop SO_EXT
170  path ext = name.extension();
171  isLoadable =
172  // wasn't just SO_EXT
173  !name.empty() &&
174  // and if there is and extension then it is not a number
175  (ext.empty() || !isExtensionVersion(ext.string()));
176  }
177  // else is not loadable
178 
179 #else
180  // Linux or Windows
181 
182  // must end in SO_EXT and have a non-extension part
183  isLoadable =
184  (filename.extension() == SO_EXT) &&
185  !filename.stem().empty();
186 #endif
187 
188  return isLoadable;
189 }
190 
191 namespace {
192 
193 static vector<string> splitPaths(string const& str)
194 {
195  vector<string> paths;
196 
197  // Skip delimiters at beginning.
198  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
199  // Find first "non-delimiter".
200  string::size_type pos = str.find_first_of(delimiters, lastPos);
201 
202  while (string::npos != pos || string::npos != lastPos)
203  {
204  // Found a token, add it to the vector.
205  if ( !str.substr(lastPos, pos - lastPos).empty() )
206  paths.push_back(str.substr(lastPos, pos - lastPos));
207  // Skip delimiters. Note the "not_of"
208  lastPos = str.find_first_not_of(delimiters, pos);
209  // Find next "non-delimiter"
210  pos = str.find_first_of(delimiters, lastPos);
211  }
212  if ( paths.empty() )
213  paths.push_back(".");
214  return paths;
215 }
216 
217 static void removeDuplicates(string& path_list)
218 {
219  vector<string> paths;
220  set<string> seen;
221  string result;
222 
223  // split path_lists
224  paths = splitPaths( path_list );
225 
226  // iterate over paths and append to result
227  for(vector<string>::const_iterator it = paths.begin(); it != paths.end(); ++it)
228  {
229  if (seen.count(*it))
230  continue;
231  else
232  seen.insert(*it);
233 
234  result = result + *it + default_delimiter;
235  }
236 
237  // remove trailing delimiter
238  if (result.size() >= default_delimiter.size() && result.substr(result.size() - default_delimiter.size()) == default_delimiter)
239  result = result.substr(0, result.size() - default_delimiter.size());
240 
241  path_list.swap(result);
242 }
243 
250 static string makeShortFilename(string const& str) {
251  string ret = str;
252  if (str.substr(0,3) == "lib")
253  ret = str.substr(3);
254  if (ret.rfind(FULL_PLUGINS_SUFFIX) != string::npos)
255  ret = ret.substr(0, ret.rfind(FULL_PLUGINS_SUFFIX) );
256  return ret;
257 }
258 
259 }
260 
261 static RTT_UNUSED bool hasEnding(string const &fullString, string const &ending)
262 {
263  if (fullString.length() > ending.length()) {
264  return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
265  } else {
266  return false;
267  }
268 }
269 
270 namespace RTT { namespace plugin {
271  extern char const* default_plugin_path;
272 }}
273 
274 namespace {
278  int loadPlugins()
279  {
281 
282  char* paths = getenv("RTT_COMPONENT_PATH");
283  string plugin_paths;
284  if (paths) {
285  plugin_paths = paths;
286  // prepend the default search path.
287  if ( !default_plugin_path.empty() )
288  plugin_paths = plugin_paths + default_delimiter + default_plugin_path;
289  removeDuplicates( plugin_paths );
290  log(Info) <<"RTT_COMPONENT_PATH was set to: " << paths << " . Searching in: "<< plugin_paths<< endlog();
291  } else {
292  plugin_paths = default_plugin_path;
293  removeDuplicates( plugin_paths );
294  log(Info) <<"No RTT_COMPONENT_PATH set. Using default: " << plugin_paths <<endlog();
295  }
296  // we set the plugin path such that we can search for sub-directories/projects lateron
297  PluginLoader::Instance()->setPluginPath(plugin_paths);
298  // we load the plugins/typekits which are in each plugin path directory (but not subdirectories).
299  try {
300  PluginLoader::Instance()->loadPlugin("rtt", plugin_paths);
301  PluginLoader::Instance()->loadTypekit("rtt", plugin_paths);
302  } catch(std::exception& e) {
303  log(Warning) << e.what() <<endlog();
304  log(Warning) << "Corrupted files found in '" << plugin_paths << "'. Fix or remove these plugins."<<endlog();
305  }
306  return 0;
307  }
308 
309  os::InitFunction plugin_loader( &loadPlugins );
310 
311  void unloadPlugins()
312  {
314  }
315 
316  os::CleanupFunction plugin_unloader( &unloadPlugins );
317 }
318 
319 static boost::shared_ptr<PluginLoader> instance2;
320 
323 
324 
325 boost::shared_ptr<PluginLoader> PluginLoader::Instance() {
326  if (!instance2) {
327  instance2.reset( new PluginLoader() );
328  }
329  return instance2;
330 }
331 
333  instance2.reset();
334 }
335 
336 bool PluginLoader::loadTypekits(string const& path_list) {
337  MutexLock lock( listlock );
338  return loadPluginsInternal( path_list, "types", "typekit");
339 }
340 
341 bool PluginLoader::loadTypekit(std::string const& name, std::string const& path_list) {
342  MutexLock lock( listlock );
343  return loadPluginInternal(name, path_list, "types", "typekit");
344 }
345 
346 bool PluginLoader::loadPlugin(std::string const& name, std::string const& path_list) {
347  MutexLock lock( listlock );
348  return loadPluginInternal(name, path_list, "plugins", "plugin");
349 }
350 
351 bool PluginLoader::loadPlugins(string const& path_list) {
352  MutexLock lock( listlock );
353  return loadPluginsInternal( path_list, "plugins", "plugin");
354 }
355 
356 bool PluginLoader::loadService(string const& servicename, TaskContext* tc) {
357  MutexLock lock( listlock );
358  for(vector<LoadedLib>::iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
359  if (it->filename == servicename || it->plugname == servicename || it->shortname == servicename) {
360  if (tc) {
361  log(Info) << "Loading Service or Plugin " << servicename << " in TaskContext " << tc->getName() <<endlog();
362  try {
363  return it->loadPlugin( tc );
364  } catch(std::exception& e) {
365  log(Error) << "Service or Plugin "<< servicename <<" threw an exception during loading in " << tc->getName() << endlog();
366  log(Error) << "Exception: "<< e.what() << endlog();
367  return false;
368  } catch(...) {
369  log(Error) << "Service or Plugin "<< servicename <<" threw an unknown exception during loading in " << tc->getName() << endlog();
370  return false;
371  }
372  } else {
373  // loadPlugin( 0 ) was already called. So drop the service in the global service.
374  if (it->is_service)
375  try {
376  return internal::GlobalService::Instance()->addService( it->createService() );
377  } catch(std::exception& e) {
378  log(Error) << "Service "<< servicename <<" threw an exception during loading in global service." << endlog();
379  log(Error) << "Exception: "<< e.what() << endlog();
380  return false;
381  } catch(...) {
382  log(Error) << "Service "<< servicename <<" threw an unknown exception during loading in global service. " << endlog();
383  return false;
384  }
385  log(Error) << "Plugin "<< servicename << " was found, but it's not a Service." <<endlog();
386  }
387  }
388  }
389  log(Error) << "No such service or plugin: '"<< servicename << "'"<< endlog();
390  return false;
391 }
392 
393 // This is a DUMB function and does not scan subdirs, possible filenames etc.
394 bool PluginLoader::loadPluginsInternal( std::string const& path_list, std::string const& subdir, std::string const& kind )
395 {
396  // If exact match, load it directly:
397  path arg( path_list );
398  if (is_regular_file(arg)) {
399 #if BOOST_VERSION >= 104600
400  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename().string()), kind, true) == false)
401 #else
402  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename()), kind, true) == false)
403 #endif
404  throw std::runtime_error("The plugin "+path_list+" was found but could not be loaded !");
405  log(Warning) << "You supplied a filename to 'loadPlugins(path)' or 'loadTypekits(path)'."<< nlog();
406  log(Warning) << "Please use 'loadLibrary(filename)' instead since the function you use will only scan directories in future releases."<<endlog();
407  return true;
408  }
409 
410  // prepare search path:
411  vector<string> paths;
412  if (path_list.empty())
413  return false; // nop: if no path is given, nothing to be searched.
414  else
415  paths = splitPaths( path_list );
416 
417  bool all_good = true, found = false;
418  // perform search in paths:
419  for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
420  {
421  // Scan path/types/* (non recursive)
422  path p = path(*it) / subdir;
423  if (is_directory(p))
424  {
425  log(Info) << "Loading "<<kind<<" libraries from directory " << p.string() << " ..."<<endlog();
426  for (directory_iterator itr(p); itr != directory_iterator(); ++itr)
427  {
428  log(Debug) << "Scanning file " << itr->path().string() << " ...";
429  if (is_regular_file(itr->status()) && isLoadableLibrary(itr->path()) ) {
430  found = true;
431  std::string libname;
432 #if BOOST_VERSION >= 104600
433  libname = itr->path().filename().string();
434 #else
435  libname = itr->path().filename();
436 #endif
437  if(!isCompatiblePlugin(libname))
438  {
439  log(Debug) << "not a compatible plugin: ignored."<<endlog();
440  }
441  else
442  {
443  found = true;
444  all_good = loadInProcess( itr->path().string(), makeShortFilename(libname), kind, true) && all_good;
445  }
446  } else {
447  if (!is_regular_file(itr->status()))
448  log(Debug) << "not a regular file: ignored."<<endlog();
449  else
450  log(Debug) << "not a " + SO_EXT + " library: ignored."<<endlog();
451  }
452  }
453  }
454  else
455  log(Debug) << "No such directory: " << p << endlog();
456  }
457  if (!all_good)
458  throw std::runtime_error("Some found plugins could not be loaded !");
459  return found;
460 }
461 
462 bool PluginLoader::loadLibrary( std::string const& name )
463 {
464  // If exact match, load it directly:
465  path arg( name );
466  if (is_regular_file(arg)) {
467 #if BOOST_VERSION >= 104600
468  string subdir = arg.parent_path().filename().string();
469 #else
470  string subdir = arg.parent_path().leaf();
471 #endif
472  string kind;
473  // we only load it if it is in types or plugins subdir:
474  if (subdir == "types") kind = "typekit";
475  if (subdir == "plugins") kind = "plugin";
476  if ( !kind.empty() ) {
477 #if BOOST_VERSION >= 104600
478  string libname = arg.filename().string();
479 #else
480  string libname = arg.filename();
481 #endif
482  if(!isCompatiblePlugin(libname))
483  {
484  log(Error) << "The " << kind << " " << name << " was found but is incompatible." << endlog();
485  return false;
486  }
487 
488 #if BOOST_VERSION >= 104600
489  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename().string()), kind, true) == false)
490 #else
491  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename()), kind, true) == false)
492 #endif
493  throw std::runtime_error("The plugin "+name+" was found but could not be loaded !");
494  return true;
495  }
496 
497  log(Error) << "refusing to load " << name << " as I could not autodetect its type (name=" << name << ", path=" << arg.string() << ", subdir=" << subdir << ")" << endlog();
498  // file exists but not typekit or plugin:
499  return false;
500  }
501 
502  // bail out if absolute path
503  if ( arg.is_complete() )
504  return false;
505 
506  // try relative match:
507  path dir = arg.parent_path();
508 #if BOOST_VERSION >= 104600
509  string file = arg.filename().string();
510 #else
511  string file = arg.filename();
512 #endif
513  vector<string> paths = splitPaths(plugin_path);
514  vector<string> tryouts( paths.size() * 8 );
515  tryouts.clear();
516 
517  // search in plugins/types:
518  string subdir = "plugins"; string kind = "plugin";
519  while (true) {
520  for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
521  {
522  path p = path(*it) / dir / subdir / (file + FULL_PLUGINS_SUFFIX);
523  tryouts.push_back( p.string() );
524  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
525  return true;
526  p = path(*it) / dir / subdir / ("lib" + file + FULL_PLUGINS_SUFFIX);
527  tryouts.push_back( p.string() );
528  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
529  return true;
530  p = path(*it) / OROCOS_TARGET_NAME / dir / subdir / (file + FULL_PLUGINS_SUFFIX);
531  tryouts.push_back( p.string() );
532  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
533  return true;
534  p = path(*it) / OROCOS_TARGET_NAME / dir / subdir / ("lib" + file + FULL_PLUGINS_SUFFIX);
535  tryouts.push_back( p.string() );
536  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
537  return true;
538  }
539  if (subdir == "types")
540  break;
541  subdir = "types";
542  kind = "typekit";
543  }
544  log(Debug) << "No such "<< kind << " found in path: " << name << ". Tried:"<< endlog();
545  for(vector<string>::iterator it=tryouts.begin(); it != tryouts.end(); ++it)
546  log(Debug) << *it << endlog();
547 
548  return false;
549 }
550 
551 bool PluginLoader::loadPluginInternal( std::string const& name, std::string const& path_list, std::string const& subdir, std::string const& kind )
552 {
553  // If exact match, load it directly:
554  // special case for ourselves, rtt plugins are not in an 'rtt' subdir:
555  if (name != "rtt" && loadLibrary(name)) {
556  log(Warning) << "You supplied a filename as first argument to 'loadPlugin(name,path)' or 'loadTypekit(name,path)'."<<nlog();
557  log(Warning) << "Please use 'loadLibrary(filename)' instead since the function you use will only interprete 'name' as a directory name in future releases."<<endlog();
558  return true;
559  }
560 
561  if ( isLoadedInternal(name) ) {
562  log(Debug) <<kind << " '"<< name <<"' already loaded. Not reloading it." <<endlog();
563  return true;
564  } else {
565  log(Info) << kind << " '"<< name <<"' not loaded before." <<endlog();
566  }
567 
568  string paths, trypaths;
569  if (path_list.empty())
570  paths = plugin_path + default_delimiter + ".";
571  else
572  paths = path_list;
573 
574  // search in '.' if really no paths are given.
575  if (paths.empty())
576  paths = ".";
577 
578  // append '/name' to each plugin path in order to search all of them:
579  vector<string> vpaths = splitPaths(paths);
580  paths.clear();
581  bool path_found = false;
582  string plugin_dir = name;
583  if (name == "rtt" ) // special case for ourselves, rtt plugins are not in an 'rtt' subdir:
584  plugin_dir = ".";
585  for(vector<string>::iterator it = vpaths.begin(); it != vpaths.end(); ++it) {
586  path p(*it);
587  p = p / plugin_dir;
588  // we only search in existing directories:
589  if (is_directory( p )) {
590  path_found = true;
591  paths += p.string() + default_delimiter;
592  } else {
593  trypaths += p.string() + default_delimiter;
594  }
595  p = *it;
596  p = p / OROCOS_TARGET_NAME / plugin_dir;
597  // we only search in existing directories:
598  if (is_directory( p )) {
599  path_found = true;
600  paths += p.string() + default_delimiter;
601  } else {
602  trypaths += p.string() + default_delimiter;
603  }
604  }
605 
606  // when at least one directory exists:
607  if (path_found) {
608  paths.erase( paths.size() - 1 ); // remove trailing delimiter ';'
609  return loadPluginsInternal(paths,subdir,kind);
610  }
611  log(Error) << "No such "<< kind << " found in path: " << name << ". Looked for these directories: "<< endlog();
612  if ( !paths.empty() )
613  log(Error) << "Exist, but don't contain it: " << paths << endlog();
614  else
615  log(Error) << "None of the search paths exist !" << endlog();
616  if ( !trypaths.empty() )
617  log(Error) << "Don't exist: " << trypaths << endlog();
618  return false;
619 }
620 
621 bool PluginLoader::isLoaded(string file)
622 {
623  MutexLock lock( listlock );
624  return isLoadedInternal(file);
625 }
626 
627 bool PluginLoader::isLoadedInternal(string file)
628 {
629  path p(file);
630  std::vector<LoadedLib>::iterator lib = loadedLibs.begin();
631  while (lib != loadedLibs.end()) {
632  // there is already a library with the same name
633  if ( lib->filename == p.filename() || lib->plugname == file || lib->shortname == file ) {
634  return true;
635  }
636  ++lib;
637  }
638  return false;
639 }
640 
641 // loads a single plugin in the current process.
642 bool PluginLoader::loadInProcess(string file, string shortname, string kind, bool log_error) {
643  path p(file);
644  char* error;
645  void* handle;
646 
647  if ( isLoadedInternal(shortname) || isLoadedInternal(file) ) {
648  log(Debug) <<"plugin '"<< file <<"' already loaded. Not reloading it." <<endlog() ;
649  return true;
650  }
651 
652  // Last chance to validate plugin compatibility
653  if(!isCompatiblePlugin(file))
654  {
655  if(log_error)
656  log(Error) << "could not load library '"<< p.string() <<"': incompatible." <<endlog();
657  return false;
658  }
659 
660  handle = dlopen ( p.string().c_str(), RTLD_NOW | RTLD_GLOBAL );
661 
662  if (!handle) {
663  string e( dlerror() );
664  if (log_error)
665  log(Error) << "could not load library '"<< p.string() <<"': "<< e <<endlog();
666  else
667  endlog();
668  return false;
669  }
670 
671  //------------- if you get here, the library has been loaded -------------
672 #if BOOST_VERSION >= 104600
673  string libname = p.filename().string();
674 #else
675  string libname = p.filename();
676 #endif
677  log(Debug)<<"Found library "<<libname<<endlog();
678  LoadedLib loading_lib(libname,shortname,handle);
679  dlerror(); /* Clear any existing error */
680 
681  std::string(*pluginName)(void) = 0;
682  std::string(*targetName)(void) = 0;
683  loading_lib.loadPlugin = (bool(*)(RTT::TaskContext*))(dlsym(handle, "loadRTTPlugin") );
684  if ((error = dlerror()) == NULL) {
685  string plugname, targetname;
686  pluginName = (std::string(*)(void))(dlsym(handle, "getRTTPluginName") );
687  if ((error = dlerror()) == NULL) {
688  plugname = (*pluginName)();
689  } else {
690  plugname = libname;
691  }
692  loading_lib.plugname = plugname;
693  targetName = (std::string(*)(void))(dlsym(handle, "getRTTTargetName") );
694  if ((error = dlerror()) == NULL) {
695  targetname = (*targetName)();
696  } else {
697  targetname = OROCOS_TARGET_NAME;
698  }
699  if ( targetname != OROCOS_TARGET_NAME ) {
700  log(Error) << "Plugin "<< plugname <<" reports to be compiled for OROCOS_TARGET "<< targetname
701  << " while we are running on target "<< OROCOS_TARGET_NAME <<". Unloading."<<endlog();
702  dlclose(handle);
703  return false;
704  }
705 
706  // check if it is a service plugin:
707  loading_lib.createService = (Service::shared_ptr(*)(void))(dlsym(handle, "createService") );
708  if (loading_lib.createService)
709  loading_lib.is_service = true;
710 
711  // ok; try to load it.
712  bool success = false;
713  try {
714  // Load into process (TaskContext* == 0):
715  success = (*loading_lib.loadPlugin)( 0 );
716  } catch(std::exception& e) {
717  log(Error) << "Loading "<< plugname <<" threw an exception: "<< e.what() << endlog();
718  } catch(...) {
719  log(Error) << "Unexpected exception in loadRTTPlugin !"<<endlog();
720  }
721 
722  if ( !success ) {
723  log(Error) << "Failed to load RTT Plugin '" <<plugname<<"': plugin refused to load into this process. Unloading." <<endlog();
724  dlclose(handle);
725  return false;
726  }
727  if (kind == "typekit") {
728  log(Info) << "Loaded RTT TypeKit/Transport '" + plugname + "' from '" + shortname +"'"<<endlog();
729  loading_lib.is_typekit = true;
730  } else {
731  loading_lib.is_typekit = false;
732  if ( loading_lib.is_service ) {
733  log(Info) << "Loaded RTT Service '" + plugname + "' from '" + shortname +"'"<<endlog();
734  }
735  else {
736  log(Info) << "Loaded RTT Plugin '" + plugname + "' from '" + shortname +"'"<<endlog();
737  }
738  }
739  loadedLibs.push_back(loading_lib);
740  return true;
741  } else {
742  if (log_error)
743  log(Error) <<"Not a plugin: " << error << endlog();
744  }
745  dlclose(handle);
746  return false;
747 }
748 
749 std::vector<std::string> PluginLoader::listServices() const {
750  MutexLock lock( listlock );
751  vector<string> names;
752  for(vector<LoadedLib>::const_iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
753  if ( it->is_service )
754  names.push_back( it->plugname );
755  }
756  return names;
757 }
758 
759 std::vector<std::string> PluginLoader::listPlugins() const {
760  MutexLock lock( listlock );
761  vector<string> names;
762  for(vector<LoadedLib>::const_iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
763  names.push_back( it->plugname );
764  }
765  return names;
766 }
767 
768 std::vector<std::string> PluginLoader::listTypekits() const {
769  MutexLock lock( listlock );
770  vector<string> names;
771  for(vector<LoadedLib>::const_iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
772  if ( it->is_typekit )
773  names.push_back( it->plugname );
774  }
775  return names;
776 }
777 
778 std::string PluginLoader::getPluginPath() const {
779  MutexLock lock( listlock );
780  return plugin_path;
781 }
782 
783 void PluginLoader::setPluginPath( std::string const& newpath ) {
784  MutexLock lock( listlock );
785  plugin_path = newpath;
786 }
787 
788 bool PluginLoader::isCompatiblePlugin(std::string const& filepath)
789 {
790  path p(filepath);
791 
792 #if BOOST_VERSION >= 104600
793  string libname = p.filename().string();
794 #else
795  string libname = p.filename();
796 #endif
797 
798  //log(Debug) << "Validating compatility of plugin file '" + libname + "'" <<endlog();
799 
800 #ifdef OROCOS_TARGET_WIN32
801  // On WIN32 target:
802  // - look if the library name ends with "-win32.dll" on release mode
803  // - look if the library name ends with "-win32d.dll" on debug mode
804  if(!hasEnding(libname, FULL_PLUGINS_SUFFIX))
805  {
806  //log(Debug) << "Plugin file '" + libname + "' is incompatible because it doesn't have the suffix " << FULL_PLUGINS_SUFFIX << endlog();
807  return false;
808  }
809 #endif // OROCOS_TARGET_WIN32
810 
811  // There's no validation on other targets
812 
813  //log(Debug) << "Plugin file '" + libname + "' is compatible." <<endlog();
814 
815  return true;
816 }
Use this to register a global cleanup function to the StartStopManager.
#define RTLD_NOW
Definition: dlfcn.h:33
Use this to register a global init function to the StartStopManager.
#define OROCOS_TARGET_NAME
bool loadService(std::string const &servicename, TaskContext *tc)
Loads an earlier discovered service into a TaskContext.
RTT_API char * dlerror(void)
#define RTLD_GLOBAL
Definition: dlfcn.h:37
RTT_API bool isExtensionVersion(const std::string &ext)
Determine whether a file extension is actually part of a library version.
RTT_API void * dlsym(void *handle, const char *name)
STL namespace.
static void Release()
Release the PluginLoader, erasing all knowledge of loaded libraries.
#define RTT_API
Definition: rtt-config.h:97
RTT_API int dlclose(void *handle)
Loads plugins found on the filesystem and keeps track of found plugins, typekits and services...
boost::shared_ptr< Service > shared_ptr
Definition: Service.hpp:101
bool loadTypekits(std::string const &path_list)
Load any typekit found in the &#39;types/&#39; subdirectory of each path in path_list in the process...
Convenient short notation for every sub-namespace of RTT.
#define RTT_UNUSED
bool loadTypekit(std::string const &name, std::string const &path_list)
Load a typekit found in the &#39;types/&#39; subdirectory of a package present in path_list.
std::vector< std::string > listTypekits() const
Lists all typekits discovered by the PluginLoader.
bool loadLibrary(std::string const &path)
Loads a library as plugin or typekit.
char const * default_plugin_path
std::vector< std::string > listPlugins() const
Lists all plugins (= services + typekits) discovered by the PluginLoader.
void setPluginPath(std::string const &newpath)
Sets the plugin path list.
bool isLoaded(std::string name)
Checks if a given plugin or filename has been loaded.
The TaskContext is the C++ representation of an Orocos component.
Definition: TaskContext.hpp:93
static boost::shared_ptr< PluginLoader > Instance()
Create the instance of the PluginLoader.
std::vector< std::string > listServices() const
Lists all services discovered by the PluginLoader.
Contains TaskContext, Activity, OperationCaller, Operation, Property, InputPort, OutputPort, Attribute.
Definition: Activity.cpp:51
std::string getPluginPath() const
Returns the current plugin path list.
RTT_API bool isLoadableLibrary(const path &filename)
bool loadPlugin(std::string const &name, std::string const &path_list)
Loads a plugin found in the &#39;plugins/&#39; subdirectory of each path in path_list in the current process...
bool loadPlugins(std::string const &path_list)
Loads any plugin found in the &#39;plugins/&#39; subdirectory of each path in path_list in the current proces...
static RTT_API Service::shared_ptr Instance()
virtual const std::string & getName() const
Returns the name of this TaskContext.
RTT_API void * dlopen(const char *file, int mode)
MutexLock is a scope based Monitor, protecting critical sections with a Mutex object through locking ...
Definition: MutexLock.hpp:51