ARGoS 3
A parallel, multi-engine simulator for swarm robotics
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
12namespace 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.