00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <boost/filesystem.hpp>
00011 #include <boost/filesystem/operations.hpp>
00012 #include <boost/thread/mutex.hpp>
00013 #include <pion/PionConfig.hpp>
00014 #include <pion/PionPlugin.hpp>
00015
00016 #ifdef PION_WIN32
00017 #include <windows.h>
00018 #else
00019 #include <dlfcn.h>
00020 #endif
00021
00022
00023 namespace pion {
00024
00025
00026
00027 const std::string PionPlugin::PION_PLUGIN_CREATE("pion_create_");
00028 const std::string PionPlugin::PION_PLUGIN_DESTROY("pion_destroy_");
00029 #ifdef PION_WIN32
00030 const std::string PionPlugin::PION_PLUGIN_EXTENSION(".dll");
00031 #else
00032 const std::string PionPlugin::PION_PLUGIN_EXTENSION(".so");
00033 #endif
00034 const std::string PionPlugin::PION_CONFIG_EXTENSION(".conf");
00035 std::vector<std::string> PionPlugin::m_plugin_dirs;
00036 PionPlugin::PluginMap PionPlugin::m_plugin_map;
00037 boost::mutex PionPlugin::m_plugin_mutex;
00038 PionPlugin::StaticEntryPointList *PionPlugin::m_entry_points_ptr = NULL;
00039
00040
00041
00042
00043 void PionPlugin::checkCygwinPath(boost::filesystem::path& final_path,
00044 const std::string& start_path)
00045 {
00046 #if defined(PION_WIN32) && defined(PION_CYGWIN_DIRECTORY)
00047
00048 if (! final_path.is_complete() && final_path.has_root_directory()) {
00049 final_path = boost::filesystem::path(std::string(PION_CYGWIN_DIRECTORY) + start_path);
00050 }
00051 #endif
00052 }
00053
00054 void PionPlugin::addPluginDirectory(const std::string& dir)
00055 {
00056 boost::filesystem::path plugin_path = boost::filesystem::system_complete(dir);
00057 checkCygwinPath(plugin_path, dir);
00058 if (! boost::filesystem::exists(plugin_path) )
00059 throw DirectoryNotFoundException(dir);
00060 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00061 m_plugin_dirs.push_back(plugin_path.directory_string());
00062 }
00063
00064 void PionPlugin::resetPluginDirectories(void)
00065 {
00066 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00067 m_plugin_dirs.clear();
00068 }
00069
00070 void PionPlugin::open(const std::string& plugin_name)
00071 {
00072 std::string plugin_file;
00073
00074 if (!findPluginFile(plugin_file, plugin_name))
00075 throw PluginNotFoundException(plugin_name);
00076
00077 openFile(plugin_file);
00078 }
00079
00080 void PionPlugin::openFile(const std::string& plugin_file)
00081 {
00082 releaseData();
00083
00084
00085 PionPluginData plugin_data(getPluginName(plugin_file));
00086
00087
00088 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00089 PluginMap::iterator itr = m_plugin_map.find(plugin_data.m_plugin_name);
00090 if (itr == m_plugin_map.end()) {
00091
00092
00093
00094 openPlugin(plugin_file, plugin_data);
00095
00096
00097 m_plugin_data = new PionPluginData(plugin_data);
00098 m_plugin_map.insert( std::make_pair(m_plugin_data->m_plugin_name,
00099 m_plugin_data) );
00100 } else {
00101
00102 m_plugin_data = itr->second;
00103 }
00104
00105
00106 ++ m_plugin_data->m_references;
00107 }
00108
00109 void PionPlugin::openStaticLinked(const std::string& plugin_name,
00110 void *create_func,
00111 void *destroy_func)
00112 {
00113 releaseData();
00114
00115
00116 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00117 PluginMap::iterator itr = m_plugin_map.find(plugin_name);
00118 if (itr == m_plugin_map.end()) {
00119
00120
00121
00122 m_plugin_data = new PionPluginData(plugin_name);
00123 m_plugin_data->m_lib_handle = NULL;
00124 m_plugin_data->m_create_func = create_func;
00125 m_plugin_data->m_destroy_func = destroy_func;
00126 m_plugin_map.insert(std::make_pair(m_plugin_data->m_plugin_name,
00127 m_plugin_data));
00128 } else {
00129
00130 m_plugin_data = itr->second;
00131 }
00132
00133
00134 ++ m_plugin_data->m_references;
00135 }
00136
00137 void PionPlugin::releaseData(void)
00138 {
00139 if (m_plugin_data != NULL) {
00140 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00141
00142 if (m_plugin_data != NULL && --m_plugin_data->m_references == 0) {
00143
00144
00145
00146 closeDynamicLibrary(m_plugin_data->m_lib_handle);
00147
00148
00149 PluginMap::iterator itr = m_plugin_map.find(m_plugin_data->m_plugin_name);
00150
00151 if (itr != m_plugin_map.end())
00152 m_plugin_map.erase(itr);
00153
00154
00155 delete m_plugin_data;
00156 }
00157 m_plugin_data = NULL;
00158 }
00159 }
00160
00161 void PionPlugin::grabData(const PionPlugin& p)
00162 {
00163 releaseData();
00164 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00165 m_plugin_data = const_cast<PionPluginData*>(p.m_plugin_data);
00166 if (m_plugin_data != NULL) {
00167 ++ m_plugin_data->m_references;
00168 }
00169 }
00170
00171 bool PionPlugin::findFile(std::string& path_to_file, const std::string& name,
00172 const std::string& extension)
00173 {
00174
00175 if (checkForFile(path_to_file, name, "", extension))
00176 return true;
00177
00178
00179 boost::mutex::scoped_lock plugin_lock(m_plugin_mutex);
00180 for (std::vector<std::string>::iterator i = m_plugin_dirs.begin();
00181 i != m_plugin_dirs.end(); ++i)
00182 {
00183 if (checkForFile(path_to_file, *i, name, extension))
00184 return true;
00185 }
00186
00187
00188 return false;
00189 }
00190
00191 bool PionPlugin::checkForFile(std::string& final_path, const std::string& start_path,
00192 const std::string& name, const std::string& extension)
00193 {
00194
00195 boost::filesystem::path cygwin_safe_path(start_path);
00196 checkCygwinPath(cygwin_safe_path, start_path);
00197 boost::filesystem::path test_path(cygwin_safe_path);
00198
00199
00200 if (! name.empty())
00201 test_path /= name;
00202
00203
00204 try {
00205
00206 if (boost::filesystem::is_regular(test_path)) {
00207 final_path = test_path.file_string();
00208 return true;
00209 }
00210 } catch (...) {}
00211
00212
00213 if (name.empty()) {
00214
00215 test_path = boost::filesystem::path(start_path + extension);
00216
00217 checkCygwinPath(test_path, start_path + extension);
00218 } else {
00219
00220 test_path = cygwin_safe_path /
00221 boost::filesystem::path(name + extension);
00222 }
00223
00224
00225 try {
00226
00227 if (boost::filesystem::is_regular(test_path)) {
00228 final_path = test_path.file_string();
00229 return true;
00230 }
00231 } catch (...) {}
00232
00233
00234 return false;
00235 }
00236
00237 void PionPlugin::openPlugin(const std::string& plugin_file,
00238 PionPluginData& plugin_data)
00239 {
00240
00241 plugin_data.m_plugin_name = getPluginName(plugin_file);
00242
00243
00244
00245 plugin_data.m_lib_handle = loadDynamicLibrary(plugin_file.c_str());
00246 if (plugin_data.m_lib_handle == NULL) {
00247 #ifndef PION_WIN32
00248 const char *error_msg = dlerror();
00249 if (error_msg != NULL) {
00250 std::string error_str(plugin_file);
00251 error_str += " (";
00252 error_str += error_msg;
00253 error_str += ')';
00254 throw OpenPluginException(error_str);
00255 } else
00256 #endif
00257 throw OpenPluginException(plugin_file);
00258 }
00259
00260
00261 plugin_data.m_create_func =
00262 getLibrarySymbol(plugin_data.m_lib_handle,
00263 PION_PLUGIN_CREATE + plugin_data.m_plugin_name);
00264 if (plugin_data.m_create_func == NULL) {
00265 closeDynamicLibrary(plugin_data.m_lib_handle);
00266 throw PluginMissingCreateException(plugin_file);
00267 }
00268
00269
00270 plugin_data.m_destroy_func =
00271 getLibrarySymbol(plugin_data.m_lib_handle,
00272 PION_PLUGIN_DESTROY + plugin_data.m_plugin_name);
00273 if (plugin_data.m_destroy_func == NULL) {
00274 closeDynamicLibrary(plugin_data.m_lib_handle);
00275 throw PluginMissingDestroyException(plugin_file);
00276 }
00277 }
00278
00279 std::string PionPlugin::getPluginName(const std::string& plugin_file)
00280 {
00281 return boost::filesystem::basename(boost::filesystem::path(plugin_file));
00282 }
00283
00284 void PionPlugin::getAllPluginNames(std::vector<std::string>& plugin_names)
00285 {
00286
00287 std::vector<std::string>::iterator it;
00288 for (it = m_plugin_dirs.begin(); it != m_plugin_dirs.end(); ++it) {
00289
00290 boost::filesystem::directory_iterator end;
00291 for (boost::filesystem::directory_iterator it2(*it); it2 != end; ++it2) {
00292 if (boost::filesystem::is_regular(*it2)) {
00293 if (boost::filesystem::extension(it2->path()) == PionPlugin::PION_PLUGIN_EXTENSION) {
00294 plugin_names.push_back(PionPlugin::getPluginName(it2->path().leaf()));
00295 }
00296 }
00297 }
00298 }
00299 }
00300
00301 void *PionPlugin::loadDynamicLibrary(const std::string& plugin_file)
00302 {
00303 #ifdef PION_WIN32
00304 #ifdef _MSC_VER
00305 return LoadLibraryA(plugin_file.c_str());
00306 #else
00307 return LoadLibrary(plugin_file.c_str());
00308 #endif
00309 #else
00310
00311
00312 const boost::filesystem::path full_path = boost::filesystem::complete(plugin_file);
00313
00314
00315
00316 return dlopen(full_path.file_string().c_str(), RTLD_LAZY | RTLD_GLOBAL);
00317 #endif
00318 }
00319
00320 void PionPlugin::closeDynamicLibrary(void *lib_handle)
00321 {
00322 #ifdef PION_WIN32
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333 #else
00334 dlclose(lib_handle);
00335 #endif
00336 }
00337
00338 void *PionPlugin::getLibrarySymbol(void *lib_handle, const std::string& symbol)
00339 {
00340 #ifdef PION_WIN32
00341 return (void*)GetProcAddress((HINSTANCE) lib_handle, symbol.c_str());
00342 #else
00343 return dlsym(lib_handle, symbol.c_str());
00344 #endif
00345 }
00346
00347 bool PionPlugin::findStaticEntryPoint(const std::string& plugin_name,
00348 void **create_func,
00349 void **destroy_func)
00350 {
00351
00352 if (m_entry_points_ptr == NULL || m_entry_points_ptr->empty())
00353 return false;
00354
00355
00356 for (std::list<StaticEntryPoint>::const_iterator i = m_entry_points_ptr->begin();
00357 i != m_entry_points_ptr->end(); ++i) {
00358 if (i->m_plugin_name==plugin_name) {
00359 *create_func = i->m_create_func;
00360 *destroy_func = i->m_destroy_func;
00361 return true;
00362 }
00363 }
00364 return false;
00365 }
00366
00367 void PionPlugin::addStaticEntryPoint(const std::string& plugin_name,
00368 void *create_func,
00369 void *destroy_func)
00370 {
00371
00372 static boost::mutex entrypoint_mutex;
00373 boost::mutex::scoped_lock entrypoint_lock(entrypoint_mutex);
00374
00375
00376 if (m_entry_points_ptr == NULL)
00377 m_entry_points_ptr = new StaticEntryPointList;
00378
00379
00380 m_entry_points_ptr->push_back(StaticEntryPoint(plugin_name, create_func, destroy_func));
00381 }
00382
00383 }