dynamic_loading.cpp
Go to the documentation of this file.
1 
7 #include "dynamic_loading.h"
8 
9 #include <dirent.h>
10 #include <cerrno>
11 
12 namespace argos {
13 
14  /****************************************/
15  /****************************************/
16 
17  CDynamicLoading::TDLHandleMap CDynamicLoading::m_tOpenLibs;
18  const std::string CDynamicLoading::DEFAULT_PLUGIN_PATH = ARGOS_INSTALL_PREFIX "/lib/argos3/";
19 
20  /****************************************/
21  /****************************************/
22 
23  /*
24  * Tries to load the given library
25  * 1. Tries to load the library as passed
26  * 2. If that fails, it appends the shared library extension and tries again;
27  * 3. If also that fails, it appends the module library extension and tries a last time.
28  * If all fails, t_handle is set to NULL and str_lib is left as-is;
29  * In case of success, it sets t_handle to the handle of the load library, and fixes str_lib to
30  * match the extension of the loaded library.
31  */
32  static CDynamicLoading::TDLHandle LoadLibraryTryingExtensions(std::string& str_lib,
33  std::string& str_msg) {
34  /* Try loading without changes to the given path */
35  CDynamicLoading::TDLHandle tHandle = ::dlopen(str_lib.c_str(), RTLD_GLOBAL | RTLD_LAZY);
36  str_msg = str_lib + ": ";
37  if(tHandle == NULL) {
38  str_msg += dlerror();
39  /* Try adding the shared lib extension to the path */
40  std::string strLibWExt = str_lib + "." + ARGOS_SHARED_LIBRARY_EXTENSION;
41  tHandle = ::dlopen(strLibWExt.c_str(), RTLD_GLOBAL | RTLD_LAZY);
42  str_msg += "\n" + strLibWExt + ": ";
43  if(tHandle != NULL) {
44  /* Success */
45  str_lib = strLibWExt;
46  }
47  else {
48  str_msg += dlerror();
49  /* Try adding the module lib extension to the path */
50  strLibWExt = str_lib + "." + ARGOS_MODULE_LIBRARY_EXTENSION;
51  tHandle = ::dlopen(strLibWExt.c_str(), RTLD_GLOBAL | RTLD_LAZY);
52  str_msg += "\n" + strLibWExt + ": ";
53  if(tHandle != NULL) {
54  /* Success */
55  str_lib = strLibWExt;
56  }
57  }
58  }
59  str_msg += "OK";
60  return tHandle;
61  }
62 
63  /****************************************/
64  /****************************************/
65 
67  TDLHandle tHandle;
68  /* Check if the provided path is absolute or relative */
69  if(str_lib[0] == '/') {
70  /*
71  * Absolute path
72  */
73  /* First check if the library is already loaded */
74  TDLHandleMap::iterator it = m_tOpenLibs.find(str_lib);
75  if(it != m_tOpenLibs.end()) {
76  /* Already loaded */
77  return m_tOpenLibs[str_lib];
78  }
79  /* Not already loaded, load the library and bomb out in case of failure */
80  std::string strLoadedLib = str_lib;
81  std::string strMsg;
82  tHandle = LoadLibraryTryingExtensions(strLoadedLib, strMsg);
83  if(tHandle == NULL) {
84  THROW_ARGOSEXCEPTION("Can't load library \""
85  << str_lib
86  << "\" even after trying to add extensions for shared library ("
87  << ARGOS_SHARED_LIBRARY_EXTENSION
88  << ") and module library ("
89  << ARGOS_MODULE_LIBRARY_EXTENSION
90  << "): "
91  << std::endl
92  << strMsg);
93  }
94  /* Store the handle to the loaded library */
95  m_tOpenLibs[strLoadedLib] = tHandle;
96  LOG << "[INFO] Loaded library \"" << strLoadedLib << "\"" << std::endl;
97  LOG.Flush();
98  return tHandle;
99  }
100  else {
101  /*
102  * Relative path, go through the plugin directories
103  */
104  /* String to store the full path to a library */
105  std::string strLibPath;
106  /* String to store the list of paths to search */
107  std::string strPluginPath = ".:" + DEFAULT_PLUGIN_PATH;
108  /* Get variable ARGOS_PLUGIN_PATH from the environment */
109  if(::getenv("ARGOS_PLUGIN_PATH") != NULL) {
110  /* Add value of the variable to list of paths to check */
111  strPluginPath = std::string(::getenv("ARGOS_PLUGIN_PATH")) + ":" + strPluginPath;
112  }
113  /* Add : at the end to make parsing easier */
114  if(strPluginPath[strPluginPath.length()-1] != ':') {
115  strPluginPath.append(":");
116  }
117  /*
118  * Go through paths and try to load the library
119  */
120  /* Parse the string */
121  std::istringstream issPluginPath(strPluginPath);
122  std::string strDir, strMsg;
123  while(std::getline(issPluginPath, strDir, ':')) {
124  /* Add '/' to dir if missing */
125  if(strDir[strDir.length()-1] != '/') {
126  strDir.append("/");
127  }
128  strLibPath = strDir + str_lib;
129  /* First check if the library is already loaded */
130  TDLHandleMap::iterator it = m_tOpenLibs.find(strLibPath);
131  if(it != m_tOpenLibs.end()) {
132  /* Already loaded */
133  return m_tOpenLibs[strLibPath];
134  }
135  /* Not already loaded, try and load the library */
136  tHandle = LoadLibraryTryingExtensions(strLibPath, strMsg);
137  if(tHandle != NULL) {
138  /* Store the handle to the loaded library */
139  m_tOpenLibs[strLibPath] = tHandle;
140  LOG << "[INFO] Loaded library \"" << strLibPath << "\"" << std::endl;
141  LOG.Flush();
142  return tHandle;
143  }
144  }
145  /* If we get here, it's because no directory worked */
146  THROW_ARGOSEXCEPTION("Can't load library \""
147  << str_lib
148  << "\" even after trying to add extensions for shared library ("
149  << ARGOS_SHARED_LIBRARY_EXTENSION
150  << ") and module library ("
151  << ARGOS_MODULE_LIBRARY_EXTENSION
152  << "): "
153  << std::endl
154  << strMsg);
155  }
156  }
157 
158  /****************************************/
159  /****************************************/
160 
161  void CDynamicLoading::UnloadLibrary(const std::string& str_lib) {
162  TDLHandleMap::iterator it = m_tOpenLibs.find(str_lib);
163  if(it != m_tOpenLibs.end()) {
164  if(::dlclose(it->second) != 0) {
165  LOGERR << "[WARNING] Can't unload library \""
166  << str_lib
167  << "\": "
168  << dlerror()
169  << std::endl;
170  LOGERR.Flush();
171  }
172  }
173  else {
174  THROW_ARGOSEXCEPTION("Can't unload library \""
175  << str_lib
176  << "\": library does not appear to have been loaded.");
177  }
178  }
179 
180  /****************************************/
181  /****************************************/
182 
184  /* String to store the full path to a library */
185  std::string strLibPath;
186  /* String to store the list of paths to search */
187  std::string strPluginPath = DEFAULT_PLUGIN_PATH;
188  /* Get variable ARGOS_PLUGIN_PATH from the environment */
189  if(::getenv("ARGOS_PLUGIN_PATH") != NULL) {
190  /* Add value of the variable to list of paths to check */
191  strPluginPath = std::string(::getenv("ARGOS_PLUGIN_PATH")) + ":" + strPluginPath;
192  }
193  /* Add : at the end to make parsing easier */
194  if(strPluginPath[strPluginPath.length()-1] != ':') {
195  strPluginPath.append(":");
196  }
197  /*
198  * Go through paths and load all the libraries
199  */
200  /* Directory info */
201  DIR* ptDir;
202  struct dirent* ptDirData;
203  /* Parse the string */
204  std::istringstream issPluginPath(strPluginPath);
205  std::string strDir;
206  while(std::getline(issPluginPath, strDir, ':')) {
207  /* Add '/' to dir if missing */
208  if(strDir[strDir.length()-1] != '/') {
209  strDir.append("/");
210  }
211  /* Try to open the directory */
212  ptDir = ::opendir(strDir.c_str());
213  if(ptDir != NULL) {
214  /* Directory open, now go through the files in the directory */
215  while((ptDirData = ::readdir(ptDir)) != NULL) {
216  /* We have a file, check that it is a library file */
217  if(strlen(ptDirData->d_name) > strlen(ARGOS_SHARED_LIBRARY_EXTENSION) &&
218  std::string(ptDirData->d_name).rfind("." ARGOS_SHARED_LIBRARY_EXTENSION) +
219  strlen(ARGOS_SHARED_LIBRARY_EXTENSION) + 1 == strlen(ptDirData->d_name)) {
220  /* It's a library file, load it */
221  LoadLibrary(strDir + ptDirData->d_name);
222  }
223  if(strcmp(ARGOS_SHARED_LIBRARY_EXTENSION, ARGOS_MODULE_LIBRARY_EXTENSION) != 0) {
224  if(strlen(ptDirData->d_name) > strlen(ARGOS_MODULE_LIBRARY_EXTENSION) &&
225  std::string(ptDirData->d_name).rfind("." ARGOS_MODULE_LIBRARY_EXTENSION) +
226  strlen(ARGOS_MODULE_LIBRARY_EXTENSION) + 1 == strlen(ptDirData->d_name)) {
227  /* It's a library file, load it */
228  LoadLibrary(strDir + ptDirData->d_name);
229  }
230  }
231  }
232  /* Close directory */
233  ::closedir(ptDir);
234  }
235  else {
236  /* Error opening directory open, inform user without bombing out */
237  LOGERR << "[WARNING] Error opening directory \""
238  << strDir
239  << "\": "
240  << ::strerror(errno)
241  << std::endl;
242  LOGERR.Flush();
243  }
244  }
245  }
246 
247  /****************************************/
248  /****************************************/
249 
251  for(TDLHandleMap::iterator it = m_tOpenLibs.begin();
252  it != m_tOpenLibs.end();
253  ++it) {
254  UnloadLibrary(it->first);
255  }
256  m_tOpenLibs.clear();
257  }
258 
259  /****************************************/
260  /****************************************/
261 
262 }
CARGoSLog LOG(std::cout, SLogColor(ARGOS_LOG_ATTRIBUTE_BRIGHT, ARGOS_LOG_COLOR_GREEN))
Definition: argos_log.h:179
#define THROW_ARGOSEXCEPTION(message)
This macro throws an ARGoS exception with the passed message.
CARGoSLog LOGERR(std::cerr, SLogColor(ARGOS_LOG_ATTRIBUTE_BRIGHT, ARGOS_LOG_COLOR_RED))
Definition: argos_log.h:180
static void LoadAllLibraries()
Loads all the dynamic libraries in the current ARGOS_PLUGIN_PATH.
void * TDLHandle
The handle to a loaded library.
static void UnloadLibrary(const std::string &str_lib)
Unloads a dynamic library.
The namespace containing all the ARGoS related code.
Definition: ci_actuator.h:12
static TDLHandle LoadLibrary(const std::string &str_lib)
Loads a dynamic library.
static void UnloadAllLibraries()
Unloads all the dynamic libraries.