Orocos Real-Time Toolkit  2.9.0
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 
105 RTT_API bool isExtensionVersion(const std::string& ext)
106 {
107  bool isExtensionVersion = false;
108 
109  if (!ext.empty() && ('.' == ext[0]))
110  {
111  std::istringstream iss;
112  int i;
113 
114  iss.str(ext.substr((size_t)1)); // take all after the '.'
115  iss >> std::dec >> std::noskipws >> i;
116  isExtensionVersion = !iss.fail() && iss.eof();
117  }
118 
119  return isExtensionVersion;
120 }
121 
122 /* Is this a dynamic library that we should load from within a directory scan?
123 
124  Versioned libraries are not loaded, to prevent loading both libfoo.so and
125  libfoo.so.1 (which is typically a symlink to libfoo.so, and so loading
126  the same library twice).
127 
128  Libraries are either (NB x.y.z is version, and could also be x or x.y)
129 
130  Linux
131  libfoo.so = load this
132  libfoo.so.x.y.z = don't load this
133 
134  Windows
135  libfoo.dll = load this
136 
137  Mac OS X
138  libfoo.dylib = load this
139  libfoo.x.y.z.dylib = don't load this
140 
141  All the above also apply without the "lib" prefix.
142 */
143 RTT_API bool isLoadableLibrary(const path& filename)
144 {
145  bool isLoadable = false;
146 
147 #if defined(__APPLE__)
148  std::string ext;
149 #if BOOST_VERSION >= 104600
150  ext = filename.extension().string();
151 #else
152  ext = filename.extension();
153 #endif
154  // ends in SO_EXT?
155  if (0 == ext.compare(SO_EXT))
156  {
157  // Ends in SO_EXT and so must not be a link for us to load
158  // Links are of the form abc.x.dylib or abc.x.y.dylib or abc.x.y.z.dylib,
159  // where x,y,z are positive numbers
160  path name = filename.stem(); // drop SO_EXT
161  path ext = name.extension();
162  isLoadable =
163  // wasn't just SO_EXT
164  !name.empty() &&
165  // and if there is and extension then it is not a number
166  (ext.empty() || !isExtensionVersion(ext.string()));
167  }
168  // else is not loadable
169 
170 #else
171  // Linux or Windows
172 
173  // must end in SO_EXT and have a non-extension part
174  isLoadable =
175  (filename.extension() == SO_EXT) &&
176  !filename.stem().empty();
177 #endif
178 
179  return isLoadable;
180 }
181 
182 namespace {
183 
184 static vector<string> splitPaths(string const& str)
185 {
186  vector<string> paths;
187 
188  // Skip delimiters at beginning.
189  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
190  // Find first "non-delimiter".
191  string::size_type pos = str.find_first_of(delimiters, lastPos);
192 
193  while (string::npos != pos || string::npos != lastPos)
194  {
195  // Found a token, add it to the vector.
196  if ( !str.substr(lastPos, pos - lastPos).empty() )
197  paths.push_back(str.substr(lastPos, pos - lastPos));
198  // Skip delimiters. Note the "not_of"
199  lastPos = str.find_first_not_of(delimiters, pos);
200  // Find next "non-delimiter"
201  pos = str.find_first_of(delimiters, lastPos);
202  }
203  if ( paths.empty() )
204  paths.push_back(".");
205  return paths;
206 }
207 
208 static void removeDuplicates(string& path_list)
209 {
210  vector<string> paths;
211  set<string> seen;
212  string result;
213 
214  // split path_lists
215  paths = splitPaths( path_list );
216 
217  // iterate over paths and append to result
218  for(vector<string>::const_iterator it = paths.begin(); it != paths.end(); ++it)
219  {
220  if (seen.count(*it))
221  continue;
222  else
223  seen.insert(*it);
224 
225  result = result + *it + default_delimiter;
226  }
227 
228  // remove trailing delimiter
229  if (result.size() >= default_delimiter.size() && result.substr(result.size() - default_delimiter.size()) == default_delimiter)
230  result = result.substr(0, result.size() - default_delimiter.size());
231 
232  path_list.swap(result);
233 }
234 
241 static string makeShortFilename(string const& str) {
242  string ret = str;
243  if (str.substr(0,3) == "lib")
244  ret = str.substr(3);
245  if (ret.rfind(FULL_PLUGINS_SUFFIX) != string::npos)
246  ret = ret.substr(0, ret.rfind(FULL_PLUGINS_SUFFIX) );
247  return ret;
248 }
249 
250 }
251 
252 static RTT_UNUSED bool hasEnding(string const &fullString, string const &ending)
253 {
254  if (fullString.length() > ending.length()) {
255  return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
256  } else {
257  return false;
258  }
259 }
260 
261 namespace RTT { namespace plugin {
262  extern char const* default_plugin_path;
263 }}
264 
265 namespace {
269  int loadPlugins()
270  {
272 
273  char* paths = getenv("RTT_COMPONENT_PATH");
274  string plugin_paths;
275  if (paths) {
276  plugin_paths = paths;
277  // prepend the default search path.
278  if ( !default_plugin_path.empty() )
279  plugin_paths = plugin_paths + default_delimiter + default_plugin_path;
280  removeDuplicates( plugin_paths );
281  log(Info) <<"RTT_COMPONENT_PATH was set to: " << paths << " . Searching in: "<< plugin_paths<< endlog();
282  } else {
283  plugin_paths = default_plugin_path;
284  removeDuplicates( plugin_paths );
285  log(Info) <<"No RTT_COMPONENT_PATH set. Using default: " << plugin_paths <<endlog();
286  }
287  // we set the plugin path such that we can search for sub-directories/projects lateron
288  PluginLoader::Instance()->setPluginPath(plugin_paths);
289  // we load the plugins/typekits which are in each plugin path directory (but not subdirectories).
290  try {
291  PluginLoader::Instance()->loadPlugin("rtt", plugin_paths);
292  PluginLoader::Instance()->loadTypekit("rtt", plugin_paths);
293  } catch(std::exception& e) {
294  log(Warning) << e.what() <<endlog();
295  log(Warning) << "Corrupted files found in '" << plugin_paths << "'. Fix or remove these plugins."<<endlog();
296  }
297  return 0;
298  }
299 
300  os::InitFunction plugin_loader( &loadPlugins );
301 
302  void unloadPlugins()
303  {
305  }
306 
307  os::CleanupFunction plugin_unloader( &unloadPlugins );
308 }
309 
310 static boost::shared_ptr<PluginLoader> instance2;
311 
314 
315 
316 boost::shared_ptr<PluginLoader> PluginLoader::Instance() {
317  if (!instance2) {
318  instance2.reset( new PluginLoader() );
319  }
320  return instance2;
321 }
322 
324  instance2.reset();
325 }
326 
327 bool PluginLoader::loadTypekits(string const& path_list) {
328  MutexLock lock( listlock );
329  return loadPluginsInternal( path_list, "types", "typekit");
330 }
331 
332 bool PluginLoader::loadTypekit(std::string const& name, std::string const& path_list) {
333  MutexLock lock( listlock );
334  return loadPluginInternal(name, path_list, "types", "typekit");
335 }
336 
337 bool PluginLoader::loadPlugin(std::string const& name, std::string const& path_list) {
338  MutexLock lock( listlock );
339  return loadPluginInternal(name, path_list, "plugins", "plugin");
340 }
341 
342 bool PluginLoader::loadPlugins(string const& path_list) {
343  MutexLock lock( listlock );
344  return loadPluginsInternal( path_list, "plugins", "plugin");
345 }
346 
347 bool PluginLoader::loadService(string const& servicename, TaskContext* tc) {
348  MutexLock lock( listlock );
349  for(vector<LoadedLib>::iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
350  if (it->filename == servicename || it->plugname == servicename || it->shortname == servicename) {
351  if (tc) {
352  log(Info) << "Loading Service or Plugin " << servicename << " in TaskContext " << tc->getName() <<endlog();
353  try {
354  return it->loadPlugin( tc );
355  } catch(std::exception& e) {
356  log(Error) << "Service or Plugin "<< servicename <<" threw an exception during loading in " << tc->getName() << endlog();
357  log(Error) << "Exception: "<< e.what() << endlog();
358  return false;
359  } catch(...) {
360  log(Error) << "Service or Plugin "<< servicename <<" threw an unknown exception during loading in " << tc->getName() << endlog();
361  return false;
362  }
363  } else {
364  // loadPlugin( 0 ) was already called. So drop the service in the global service.
365  if (it->is_service) {
366  try {
367  Service::shared_ptr service = it->createService();
368  if (service) {
369  return internal::GlobalService::Instance()->addService( service );
370  } else {
371  log(Error) << "Service " << servicename << " cannot be loaded into the global service." << endlog();
372  return false;
373  }
374  } catch(std::exception& e) {
375  log(Error) << "Service "<< servicename <<" threw an exception during loading in global service." << endlog();
376  log(Error) << "Exception: "<< e.what() << endlog();
377  return false;
378  } catch(...) {
379  log(Error) << "Service "<< servicename <<" threw an unknown exception during loading in global service. " << endlog();
380  return false;
381  }
382  } else {
383  log(Error) << "Plugin "<< servicename << " was found, but it's not a Service." <<endlog();
384  return false;
385  }
386  }
387  }
388  }
389 
390  log(Error) << "No such service or plugin: '"<< servicename << "'"<< endlog();
391  return false;
392 }
393 
394 // This is a DUMB function and does not scan subdirs, possible filenames etc.
395 bool PluginLoader::loadPluginsInternal( std::string const& path_list, std::string const& subdir, std::string const& kind )
396 {
397  // If exact match, load it directly:
398  path arg( path_list );
399  if (is_regular_file(arg)) {
400 #if BOOST_VERSION >= 104600
401  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename().string()), kind, true) == false)
402 #else
403  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename()), kind, true) == false)
404 #endif
405  throw std::runtime_error("The plugin "+path_list+" was found but could not be loaded !");
406  log(Warning) << "You supplied a filename to 'loadPlugins(path)' or 'loadTypekits(path)'."<< nlog();
407  log(Warning) << "Please use 'loadLibrary(filename)' instead since the function you use will only scan directories in future releases."<<endlog();
408  return true;
409  }
410 
411  // prepare search path:
412  vector<string> paths;
413  if (path_list.empty())
414  return false; // nop: if no path is given, nothing to be searched.
415  else
416  paths = splitPaths( path_list );
417 
418  bool all_good = true, found = false;
419  // perform search in paths:
420  for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
421  {
422  // Scan path/types/* (non recursive)
423  path p = path(*it) / subdir;
424  if (is_directory(p))
425  {
426  log(Info) << "Loading "<<kind<<" libraries from directory " << p.string() << " ..."<<endlog();
427  for (directory_iterator itr(p); itr != directory_iterator(); ++itr)
428  {
429  log(Debug) << "Scanning file " << itr->path().string() << " ...";
430  if (is_regular_file(itr->status()) && isLoadableLibrary(itr->path()) ) {
431  found = true;
432  std::string libname;
433 #if BOOST_VERSION >= 104600
434  libname = itr->path().filename().string();
435 #else
436  libname = itr->path().filename();
437 #endif
438  if(!isCompatiblePlugin(libname))
439  {
440  log(Debug) << "not a compatible plugin: ignored."<<endlog();
441  }
442  else
443  {
444  found = true;
445  all_good = loadInProcess( itr->path().string(), makeShortFilename(libname), kind, true) && all_good;
446  }
447  } else {
448  if (!is_regular_file(itr->status()))
449  log(Debug) << "not a regular file: ignored."<<endlog();
450  else
451  log(Debug) << "not a " + SO_EXT + " library: ignored."<<endlog();
452  }
453  }
454  }
455  else
456  log(Debug) << "No such directory: " << p << endlog();
457  }
458  if (!all_good)
459  throw std::runtime_error("Some found plugins could not be loaded !");
460  return found;
461 }
462 
463 bool PluginLoader::loadLibrary( std::string const& name )
464 {
465  // If exact match, load it directly:
466  path arg( name );
467  if (is_regular_file(arg)) {
468 #if BOOST_VERSION >= 104600
469  string subdir = arg.parent_path().filename().string();
470 #else
471  string subdir = arg.parent_path().leaf();
472 #endif
473  string kind;
474  // we only load it if it is in types or plugins subdir:
475  if (subdir == "types") kind = "typekit";
476  if (subdir == "plugins") kind = "plugin";
477  if ( !kind.empty() ) {
478 #if BOOST_VERSION >= 104600
479  string libname = arg.filename().string();
480 #else
481  string libname = arg.filename();
482 #endif
483  if(!isCompatiblePlugin(libname))
484  {
485  log(Error) << "The " << kind << " " << name << " was found but is incompatible." << endlog();
486  return false;
487  }
488 
489 #if BOOST_VERSION >= 104600
490  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename().string()), kind, true) == false)
491 #else
492  if ( loadInProcess(arg.string(), makeShortFilename(arg.filename()), kind, true) == false)
493 #endif
494  throw std::runtime_error("The plugin "+name+" was found but could not be loaded !");
495  return true;
496  }
497 
498  log(Error) << "refusing to load " << name << " as I could not autodetect its type (name=" << name << ", path=" << arg.string() << ", subdir=" << subdir << ")" << endlog();
499  // file exists but not typekit or plugin:
500  return false;
501  }
502 
503  // bail out if absolute path
504  if ( arg.is_complete() )
505  return false;
506 
507  // try relative match:
508  path dir = arg.parent_path();
509 #if BOOST_VERSION >= 104600
510  string file = arg.filename().string();
511 #else
512  string file = arg.filename();
513 #endif
514  vector<string> paths = splitPaths(plugin_path);
515  vector<string> tryouts( paths.size() * 8 );
516  tryouts.clear();
517 
518  // search in plugins/types:
519  string subdir = "plugins"; string kind = "plugin";
520  while (true) {
521  for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
522  {
523  path p = path(*it) / dir / subdir / (file + FULL_PLUGINS_SUFFIX);
524  tryouts.push_back( p.string() );
525  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
526  return true;
527  p = path(*it) / dir / subdir / ("lib" + file + FULL_PLUGINS_SUFFIX);
528  tryouts.push_back( p.string() );
529  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
530  return true;
531  p = path(*it) / OROCOS_TARGET_NAME / dir / subdir / (file + FULL_PLUGINS_SUFFIX);
532  tryouts.push_back( p.string() );
533  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
534  return true;
535  p = path(*it) / OROCOS_TARGET_NAME / dir / subdir / ("lib" + file + FULL_PLUGINS_SUFFIX);
536  tryouts.push_back( p.string() );
537  if (is_regular_file( p ) && loadInProcess( p.string(), name, kind, true ) )
538  return true;
539  }
540  if (subdir == "types")
541  break;
542  subdir = "types";
543  kind = "typekit";
544  }
545  log(Debug) << "No such "<< kind << " found in path: " << name << ". Tried:"<< endlog();
546  for(vector<string>::iterator it=tryouts.begin(); it != tryouts.end(); ++it)
547  log(Debug) << *it << endlog();
548 
549  return false;
550 }
551 
552 bool PluginLoader::loadPluginInternal( std::string const& name, std::string const& path_list, std::string const& subdir, std::string const& kind )
553 {
554  // If exact match, load it directly:
555  // special case for ourselves, rtt plugins are not in an 'rtt' subdir:
556  if (name != "rtt" && loadLibrary(name)) {
557  log(Warning) << "You supplied a filename as first argument to 'loadPlugin(name,path)' or 'loadTypekit(name,path)'."<<nlog();
558  log(Warning) << "Please use 'loadLibrary(filename)' instead since the function you use will only interprete 'name' as a directory name in future releases."<<endlog();
559  return true;
560  }
561 
562  if ( isLoadedInternal(name) ) {
563  log(Debug) <<kind << " '"<< name <<"' already loaded. Not reloading it." <<endlog();
564  return true;
565  } else {
566  log(Info) << kind << " '"<< name <<"' not loaded before." <<endlog();
567  }
568 
569  string paths, trypaths;
570  if (path_list.empty())
571  paths = plugin_path + default_delimiter + ".";
572  else
573  paths = path_list;
574 
575  // search in '.' if really no paths are given.
576  if (paths.empty())
577  paths = ".";
578 
579  // append '/name' to each plugin path in order to search all of them:
580  vector<string> vpaths = splitPaths(paths);
581  paths.clear();
582  bool path_found = false;
583  string plugin_dir = name;
584  if (name == "rtt" ) // special case for ourselves, rtt plugins are not in an 'rtt' subdir:
585  plugin_dir = ".";
586  for(vector<string>::iterator it = vpaths.begin(); it != vpaths.end(); ++it) {
587  path p(*it);
588  p = p / plugin_dir;
589  // we only search in existing directories:
590  if (is_directory( p )) {
591  path_found = true;
592  paths += p.string() + default_delimiter;
593  } else {
594  trypaths += p.string() + default_delimiter;
595  }
596  p = *it;
597  p = p / OROCOS_TARGET_NAME / plugin_dir;
598  // we only search in existing directories:
599  if (is_directory( p )) {
600  path_found = true;
601  paths += p.string() + default_delimiter;
602  } else {
603  trypaths += p.string() + default_delimiter;
604  }
605  }
606 
607  // when at least one directory exists:
608  if (path_found) {
609  paths.erase( paths.size() - 1 ); // remove trailing delimiter ';'
610  return loadPluginsInternal(paths,subdir,kind);
611  }
612  log(Error) << "No such "<< kind << " found in path: " << name << ". Looked for these directories: "<< endlog();
613  if ( !paths.empty() )
614  log(Error) << "Exist, but don't contain it: " << paths << endlog();
615  else
616  log(Error) << "None of the search paths exist !" << endlog();
617  if ( !trypaths.empty() )
618  log(Error) << "Don't exist: " << trypaths << endlog();
619  return false;
620 }
621 
622 bool PluginLoader::isLoaded(string file)
623 {
624  MutexLock lock( listlock );
625  return isLoadedInternal(file);
626 }
627 
628 bool PluginLoader::isLoadedInternal(string file)
629 {
630  path p(file);
631  std::vector<LoadedLib>::iterator lib = loadedLibs.begin();
632  while (lib != loadedLibs.end()) {
633  // there is already a library with the same name
634  if ( lib->filename == p.filename() || lib->plugname == file || lib->shortname == file ) {
635  return true;
636  }
637  ++lib;
638  }
639  return false;
640 }
641 
642 // loads a single plugin in the current process.
643 bool PluginLoader::loadInProcess(string file, string shortname, string kind, bool log_error) {
644  path p(file);
645  char* error;
646  void* handle;
647 
648  if ( isLoadedInternal(shortname) || isLoadedInternal(file) ) {
649  log(Debug) <<"plugin '"<< file <<"' already loaded. Not reloading it." <<endlog() ;
650  return true;
651  }
652 
653  // Last chance to validate plugin compatibility
654  if(!isCompatiblePlugin(file))
655  {
656  if(log_error)
657  log(Error) << "could not load library '"<< p.string() <<"': incompatible." <<endlog();
658  return false;
659  }
660 
661  handle = dlopen ( p.string().c_str(), RTLD_NOW | RTLD_GLOBAL );
662 
663  if (!handle) {
664  string e( dlerror() );
665  if (log_error)
666  log(Error) << "could not load library '"<< p.string() <<"': "<< e <<endlog();
667  else
668  endlog();
669  return false;
670  }
671 
672  //------------- if you get here, the library has been loaded -------------
673 #if BOOST_VERSION >= 104600
674  string libname = p.filename().string();
675 #else
676  string libname = p.filename();
677 #endif
678  log(Debug)<<"Found library "<<libname<<endlog();
679  LoadedLib loading_lib(libname,shortname,handle);
680  dlerror(); /* Clear any existing error */
681 
682  std::string(*pluginName)(void) = 0;
683  std::string(*targetName)(void) = 0;
684  loading_lib.loadPlugin = (bool(*)(RTT::TaskContext*))(dlsym(handle, "loadRTTPlugin") );
685  if ((error = dlerror()) == NULL) {
686  string plugname, targetname;
687  pluginName = (std::string(*)(void))(dlsym(handle, "getRTTPluginName") );
688  if ((error = dlerror()) == NULL) {
689  plugname = (*pluginName)();
690  } else {
691  plugname = libname;
692  }
693  loading_lib.plugname = plugname;
694  targetName = (std::string(*)(void))(dlsym(handle, "getRTTTargetName") );
695  if ((error = dlerror()) == NULL) {
696  targetname = (*targetName)();
697  } else {
698  targetname = OROCOS_TARGET_NAME;
699  }
700  if ( targetname != OROCOS_TARGET_NAME ) {
701  log(Error) << "Plugin "<< plugname <<" reports to be compiled for OROCOS_TARGET "<< targetname
702  << " while we are running on target "<< OROCOS_TARGET_NAME <<". Unloading."<<endlog();
703  dlclose(handle);
704  return false;
705  }
706 
707  // check if it is a service plugin:
708  loading_lib.createService = (Service::shared_ptr(*)(void))(dlsym(handle, "createService") );
709  if (loading_lib.createService)
710  loading_lib.is_service = true;
711 
712  // ok; try to load it.
713  bool success = false;
714  try {
715  // Load into process (TaskContext* == 0):
716  success = (*loading_lib.loadPlugin)( 0 );
717  } catch(std::exception& e) {
718  log(Error) << "Loading "<< plugname <<" threw an exception: "<< e.what() << endlog();
719  } catch(...) {
720  log(Error) << "Unexpected exception in loadRTTPlugin !"<<endlog();
721  }
722 
723  if ( !success ) {
724  log(Error) << "Failed to load RTT Plugin '" <<plugname<<"': plugin refused to load into this process. Unloading." <<endlog();
725  dlclose(handle);
726  return false;
727  }
728  if (kind == "typekit") {
729  log(Info) << "Loaded RTT TypeKit/Transport '" + plugname + "' from '" + shortname +"'"<<endlog();
730  loading_lib.is_typekit = true;
731  } else {
732  loading_lib.is_typekit = false;
733  if ( loading_lib.is_service ) {
734  log(Info) << "Loaded RTT Service '" + plugname + "' from '" + shortname +"'"<<endlog();
735  }
736  else {
737  log(Info) << "Loaded RTT Plugin '" + plugname + "' from '" + shortname +"'"<<endlog();
738  }
739  }
740  loadedLibs.push_back(loading_lib);
741  return true;
742  } else {
743  if (log_error)
744  log(Error) <<"Not a plugin: " << error << endlog();
745  }
746  dlclose(handle);
747  return false;
748 }
749 
750 std::vector<std::string> PluginLoader::listServices() const {
751  MutexLock lock( listlock );
752  vector<string> names;
753  for(vector<LoadedLib>::const_iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
754  if ( it->is_service )
755  names.push_back( it->plugname );
756  }
757  return names;
758 }
759 
760 std::vector<std::string> PluginLoader::listPlugins() const {
761  MutexLock lock( listlock );
762  vector<string> names;
763  for(vector<LoadedLib>::const_iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
764  names.push_back( it->plugname );
765  }
766  return names;
767 }
768 
769 std::vector<std::string> PluginLoader::listTypekits() const {
770  MutexLock lock( listlock );
771  vector<string> names;
772  for(vector<LoadedLib>::const_iterator it= loadedLibs.begin(); it != loadedLibs.end(); ++it) {
773  if ( it->is_typekit )
774  names.push_back( it->plugname );
775  }
776  return names;
777 }
778 
779 std::string PluginLoader::getPluginPath() const {
780  MutexLock lock( listlock );
781  return plugin_path;
782 }
783 
784 void PluginLoader::setPluginPath( std::string const& newpath ) {
785  MutexLock lock( listlock );
786  plugin_path = newpath;
787 }
788 
789 bool PluginLoader::isCompatiblePlugin(std::string const& filepath)
790 {
791  path p(filepath);
792 
793 #if BOOST_VERSION >= 104600
794  string libname = p.filename().string();
795 #else
796  string libname = p.filename();
797 #endif
798 
799  //log(Debug) << "Validating compatility of plugin file '" + libname + "'" <<endlog();
800 
801 #ifdef OROCOS_TARGET_WIN32
802  // On WIN32 target:
803  // - look if the library name ends with "-win32.dll" on release mode
804  // - look if the library name ends with "-win32d.dll" on debug mode
805  if(!hasEnding(libname, FULL_PLUGINS_SUFFIX))
806  {
807  //log(Debug) << "Plugin file '" + libname + "' is incompatible because it doesn't have the suffix " << FULL_PLUGINS_SUFFIX << endlog();
808  return false;
809  }
810 #endif // OROCOS_TARGET_WIN32
811 
812  // There's no validation on other targets
813 
814  //log(Debug) << "Plugin file '" + libname + "' is compatible." <<endlog();
815 
816  return true;
817 }
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.
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:52
std::string getPluginPath() const
Returns the current plugin path list.
#define RTT_UNUSED
Definition: rtt-config.h:125
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