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