Fawkes API  Fawkes Development Version
module.cpp
00001 
00002 /***************************************************************************
00003  *  module.cpp - interface for modules (i.e. shared object, dynamic library)
00004  *
00005  *  Created: Wed May 09 11:03:40 2007
00006  *  Copyright  2006-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <utils/system/dynamic_module/module.h>
00025 #include <utils/system/file.h>
00026 
00027 #include <cstring>
00028 #include <dlfcn.h>
00029 
00030 namespace fawkes {
00031 
00032 /** @class ModuleOpenException <utils/system/dynamic_module/module.h>
00033  * Opening a module failed.
00034  * Thrown if a call to Module::open() failed.
00035  */
00036 
00037 /** Constructor.
00038  * @param msg message
00039  */
00040 ModuleOpenException::ModuleOpenException(const char *msg)
00041   : Exception(msg)
00042 {
00043 }
00044 
00045 /** @class Module <utils/system/dynamic_module/module.h>
00046  * Dynamic module loader for Linux, FreeBSD, and MacOS X.
00047  * A Module implementation for the dl dynamic loader library that comes
00048  * with glibc, applicable for Linux, FreeBSD, and MacOS X Systems.
00049  *
00050  * For nice reading and hints about using dynamic module loading with C++ you
00051  * should have a look at
00052  * http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html
00053  * @author Tim Niemueller
00054  */
00055 
00056 // SOEXT is a macro passed in from the build system and set in config.mk or
00057 // a build type specific config file.
00058 const char * Module::FILE_EXTENSION = SOEXT;
00059 
00060 /** Constructor.
00061  * @param filename full filename of the module
00062  * @param flags module flags
00063  */
00064 Module::Module(std::string filename, Module::ModuleFlags flags)
00065 {
00066   __filename = filename;
00067   __flags    = flags;
00068 
00069   __handle         = NULL;
00070 
00071   __is_resident    = false;
00072   __ref_count      = 0;
00073 }
00074 
00075 
00076 /** Destructor.
00077  * Closes the module. */
00078 Module::~Module()
00079 {
00080   close();
00081 }
00082 
00083 /** Open the module
00084  * @return true if the module could be opened, false otherwise
00085  * @exception ModuleOpenException thrown if there was any problem
00086  * while loading the module
00087  */
00088 void
00089 Module::open()
00090 {
00091   if ( __handle != NULL )  return;
00092 
00093   // Note: We assume Linux-style shared objects
00094   std::string full_filename = "";
00095   full_filename = __filename;
00096   //                                                                .   SOEXT
00097   if ( full_filename.find("."SOEXT, 0) != (full_filename.length() - 1 - strlen(FILE_EXTENSION)) ) {
00098     // filename has no proper ending
00099     full_filename += "."SOEXT;
00100   }
00101 
00102   int tflags = 0;
00103   tflags |= ((__flags & MODULE_BIND_LAZY)   != 0) ? RTLD_LAZY : RTLD_NOW;
00104   tflags |= ((__flags & MODULE_BIND_NOW)    != 0) ? RTLD_NOW : 0;
00105   tflags |= ((__flags & MODULE_BIND_LOCAL)  != 0) ? RTLD_LOCAL : 0;
00106   tflags |= ((__flags & MODULE_BIND_GLOBAL) != 0) ? RTLD_GLOBAL : 0;
00107   tflags |= ((__flags & MODULE_NODELETE)    != 0) ? RTLD_NODELETE : 0;
00108 #ifdef linux
00109   tflags |= ((__flags & MODULE_BIND_DEEP)   != 0) ? RTLD_DEEPBIND : 0;
00110 #endif
00111 
00112   if ( full_filename == "") {
00113     __handle = dlopen (NULL, tflags);
00114 
00115     __filename    = "main";
00116     __is_resident = true;
00117     __ref_count   = 1;
00118   } else {
00119 
00120     // check whether we have a readable file right away
00121     if (File::is_regular(full_filename.c_str())) {
00122       // ok, try loading the module
00123       __handle = dlopen(full_filename.c_str(), tflags);
00124 
00125       if ( NULL == __handle) {
00126         const char *err = dlerror();
00127         if ( NULL == err ) {
00128           throw ModuleOpenException("dlopen failed with an unknown error");
00129         } else {
00130           ModuleOpenException e("dlopen failed");
00131           e.append("dlerror: %s", err);
00132           throw e;
00133         }
00134       } else {
00135         __is_resident = false;
00136         __ref_count   = 1;
00137       }
00138     } else {
00139       ModuleOpenException e("Cannot open module");
00140       e.append("File '%s' does not exist", full_filename.c_str());
00141       throw e;
00142     }
00143   }
00144 }
00145 
00146 
00147 /** Close the module
00148  * @return Returns true if the module could be closed, false otherwise
00149  */
00150 bool
00151 Module::close()
00152 {
00153   if ( __handle == NULL )  return true;
00154 
00155   if ( __ref_count > 0 )  --__ref_count;
00156 
00157   if ( (__ref_count == 0) && ! __is_resident ) {
00158     if ( dlclose(__handle) != 0 ) {
00159       __handle = NULL;
00160       return false;
00161     }
00162     __handle = NULL;
00163   }
00164 
00165   return true;
00166 }
00167 
00168 
00169 /** Increment the reference count of this module */
00170 void
00171 Module::ref()
00172 {
00173   ++__ref_count;
00174 }
00175 
00176 
00177 /** Decrease the reference count of this module */
00178 void
00179 Module::unref()
00180 {
00181   if ( __ref_count > 0 ) {
00182     --__ref_count;
00183   }
00184 }
00185 
00186 
00187 /** Check if there are no reference to this module
00188  * @return Returns true if there are no references to this module,
00189  * false if there is at least one reference
00190  */
00191 bool
00192 Module::notref()
00193 {
00194   return (__ref_count == 0);
00195 }
00196 
00197 
00198 /** Get the reference count of this module
00199  * @return Returns the number of references to this module
00200  */
00201 unsigned int
00202 Module::get_ref_count()
00203 {
00204   return __ref_count;
00205 }
00206 
00207 
00208 /** Compare to another Module instance
00209  * @param cmod a reference to the other comparison instance
00210  * @return Returns true, if the full file names of both modules are the
00211  * same, false otherwise
00212  */
00213 bool
00214 Module::operator==(const Module &cmod)
00215 {
00216   return (__filename == cmod.__filename);
00217 }
00218 
00219 
00220 /** Check if the module has the given symbol
00221  * @param symbol_name The name of the symbol.
00222  * NOTE: C++ symbols are mangled with type info and thus are not plainly
00223  * available as symbol name. Use extern "C" to avoid this.
00224  * Read
00225  * http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html
00226  * for more information on this topic.
00227  * @return Returns true if the symbol was found, false otherwise
00228  */
00229 bool
00230 Module::has_symbol(const char *symbol_name)
00231 {
00232   if( symbol_name == NULL ) {
00233     return false;
00234   }
00235   if ( __handle == NULL ) {
00236     return false;
00237   }
00238 
00239   return ( dlsym( __handle, symbol_name ) != NULL );
00240 }
00241 
00242 
00243 /** Get a symbol from the module
00244  * @param symbol_name The name of the symbol.
00245  * NOTE: C++ symbols are mangled with type info and thus are not plainly
00246  * available as symbol name. Use extern "C" to avoid this.
00247  * Read
00248  * http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html
00249  * for more information on this topic.
00250  * @return Returns a pointer to the symbol or NULL if symbol was not found
00251  */
00252 void *
00253 Module::get_symbol(const char *symbol_name)
00254 {
00255   if( symbol_name == NULL ) return NULL;
00256   if ( __handle == NULL ) return NULL;
00257 
00258   return dlsym( __handle, symbol_name );
00259 }
00260 
00261 
00262 /** Get file extension for dl modules
00263  * @return Returns the file extension for dl modules, this is "so" on Linux
00264  * and FreeBSD systems, and dylib on MacOS X. It is defined at compile time
00265  * in config.mk.
00266  */
00267 const char *
00268 Module::get_file_extension()
00269 {
00270   return FILE_EXTENSION;
00271 }
00272 
00273 
00274 /** Get the full file name of the module
00275  * @return Returns a string with the full file name of the module
00276  */
00277 std::string
00278 Module::get_filename()
00279 {
00280   return __filename;
00281 }
00282 
00283 
00284 /** Get the base file name of the module
00285  * @return Returns the base file name of the module. On Unix systems this is
00286  * everything after the last slash
00287  */
00288 std::string
00289 Module::get_base_filename()
00290 {
00291   if ( __filename.find("/", 0) != std::string::npos ) {
00292     std::string rv = __filename.substr(__filename.rfind("/", __filename.length()) + 1, __filename.length());
00293     return rv;
00294   } else {
00295     return __filename.c_str();
00296   }
00297 }
00298 
00299 } // end namespace fawkes