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