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 }