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 if (boost::filesystem::is_regular(test_path)) {
00205 final_path = test_path.file_string();
00206 return true;
00207 }
00208
00209
00210 if (name.empty()) {
00211
00212 test_path = boost::filesystem::path(start_path + extension);
00213
00214 checkCygwinPath(test_path, start_path + extension);
00215 } else {
00216
00217 test_path = cygwin_safe_path /
00218 boost::filesystem::path(name + extension);
00219 }
00220
00221
00222 if (boost::filesystem::is_regular(test_path)) {
00223 final_path = test_path.file_string();
00224 return true;
00225 }
00226
00227
00228 return false;
00229 }
00230
00231 void PionPlugin::openPlugin(const std::string& plugin_file,
00232 PionPluginData& plugin_data)
00233 {
00234
00235 plugin_data.m_plugin_name = getPluginName(plugin_file);
00236
00237
00238
00239 plugin_data.m_lib_handle = loadDynamicLibrary(plugin_file.c_str());
00240 if (plugin_data.m_lib_handle == NULL) {
00241 #ifndef PION_WIN32
00242 char *error_msg = dlerror();
00243 if (error_msg != NULL) {
00244 std::string error_str(plugin_file);
00245 error_str += " (";
00246 error_str += error_msg;
00247 error_str += ')';
00248 throw OpenPluginException(error_str);
00249 } else
00250 #endif
00251 throw OpenPluginException(plugin_file);
00252 }
00253
00254
00255 plugin_data.m_create_func =
00256 getLibrarySymbol(plugin_data.m_lib_handle,
00257 PION_PLUGIN_CREATE + plugin_data.m_plugin_name);
00258 if (plugin_data.m_create_func == NULL) {
00259 closeDynamicLibrary(plugin_data.m_lib_handle);
00260 throw PluginMissingCreateException(plugin_file);
00261 }
00262
00263
00264 plugin_data.m_destroy_func =
00265 getLibrarySymbol(plugin_data.m_lib_handle,
00266 PION_PLUGIN_DESTROY + plugin_data.m_plugin_name);
00267 if (plugin_data.m_destroy_func == NULL) {
00268 closeDynamicLibrary(plugin_data.m_lib_handle);
00269 throw PluginMissingDestroyException(plugin_file);
00270 }
00271 }
00272
00273 std::string PionPlugin::getPluginName(const std::string& plugin_file)
00274 {
00275 return boost::filesystem::basename(boost::filesystem::path(plugin_file));
00276 }
00277
00278 void PionPlugin::getAllPluginNames(std::vector<std::string>& plugin_names)
00279 {
00280
00281 std::vector<std::string>::iterator it;
00282 for (it = m_plugin_dirs.begin(); it != m_plugin_dirs.end(); ++it) {
00283
00284 boost::filesystem::directory_iterator end;
00285 for (boost::filesystem::directory_iterator it2(*it); it2 != end; ++it2) {
00286 if (boost::filesystem::is_regular(*it2)) {
00287 if (boost::filesystem::extension(it2->path()) == PionPlugin::PION_PLUGIN_EXTENSION) {
00288 plugin_names.push_back(PionPlugin::getPluginName(it2->path().leaf()));
00289 }
00290 }
00291 }
00292 }
00293 }
00294
00295 void *PionPlugin::loadDynamicLibrary(const std::string& plugin_file)
00296 {
00297 #ifdef PION_WIN32
00298 #ifdef _MSC_VER
00299 return LoadLibraryA(plugin_file.c_str());
00300 #else
00301 return LoadLibrary(plugin_file.c_str());
00302 #endif
00303 #else
00304
00305
00306 const boost::filesystem::path full_path = boost::filesystem::complete(plugin_file);
00307
00308
00309
00310 return dlopen(full_path.file_string().c_str(), RTLD_LAZY | RTLD_GLOBAL);
00311 #endif
00312 }
00313
00314 void PionPlugin::closeDynamicLibrary(void *lib_handle)
00315 {
00316 #ifdef PION_WIN32
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327 #else
00328 dlclose(lib_handle);
00329 #endif
00330 }
00331
00332 void *PionPlugin::getLibrarySymbol(void *lib_handle, const std::string& symbol)
00333 {
00334 #ifdef PION_WIN32
00335 return (void*)GetProcAddress((HINSTANCE) lib_handle, symbol.c_str());
00336 #else
00337 return dlsym(lib_handle, symbol.c_str());
00338 #endif
00339 }
00340
00341 bool PionPlugin::findStaticEntryPoint(const std::string& plugin_name,
00342 void **create_func,
00343 void **destroy_func)
00344 {
00345
00346 if (m_entry_points_ptr == NULL || m_entry_points_ptr->empty())
00347 return false;
00348
00349
00350 for (std::list<StaticEntryPoint>::const_iterator i = m_entry_points_ptr->begin();
00351 i != m_entry_points_ptr->end(); ++i) {
00352 if (i->m_plugin_name==plugin_name) {
00353 *create_func = i->m_create_func;
00354 *destroy_func = i->m_destroy_func;
00355 return true;
00356 }
00357 }
00358 return false;
00359 }
00360
00361 void PionPlugin::addStaticEntryPoint(const std::string& plugin_name,
00362 void *create_func,
00363 void *destroy_func)
00364 {
00365
00366 static boost::mutex entrypoint_mutex;
00367 boost::mutex::scoped_lock entrypoint_lock(entrypoint_mutex);
00368
00369
00370 if (m_entry_points_ptr == NULL)
00371 m_entry_points_ptr = new StaticEntryPointList;
00372
00373
00374 m_entry_points_ptr->push_back(StaticEntryPoint(plugin_name, create_func, destroy_func));
00375 }
00376
00377 }