modules.cpp

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 /** @file modules.cpp Implementation of Modules Management Subsystem */
00020 
00021 #include <hn/hnprec.h>
00022 #include <hn/modules.h>                    // Interface
00023 #include <hn/log.h>                        // Logging functions
00024 #include <hn/hydranode.h>                  // For HydraNode::instance()
00025 #include <boost/lexical_cast.hpp>          // Required at getError()
00026 
00027 //! Platform-specific stuff
00028 
00029 #ifdef WIN32
00030         #include <windows.h>
00031         static const std::string MOD_EXT = ".dll";
00032         static const std::string PATH_SEPARATOR = "\\";
00033         std::string getError() {
00034                 return boost::lexical_cast<std::string>(GetLastError());
00035         }
00036 #elif defined(HAVE_LIBDL)
00037         #include <dlfcn.h>
00038         static const std::string MOD_EXT = ".so";
00039         static const std::string PATH_SEPARATOR = "/";
00040         std::string getError() {
00041                 #ifndef STATIC_BUILD
00042                         return dlerror();
00043                 #else
00044                         return "";
00045                 #endif
00046         }
00047 #elif defined(__MAC__)
00048         #include <mach-o/dyld.h>
00049         static const std::string MOD_EXT = ".so";
00050         static const std::string PATH_SEPARATOR = "/";
00051         std::string getError() {
00052                 int null;
00053                 NSLinkEditErrors c;
00054                 const char *errorMsg;  // error message
00055                 const char *modName;   // module name
00056                 NSLinkEditError(&c, &null, &modName, &errorMsg);
00057                 return errorMsg;
00058         }
00059 #else
00060         #error No dynamic loading support implemented for your platform
00061 #endif
00062 
00063 /**
00064  * Built-in modules
00065  *
00066  * This function contains built-in modules initializers in static vector, and
00067  * allows accessing them. The reason for this kind of implementation is to have
00068  * at least some control of static initialization order; if implemented using
00069  * static file-scope variable, things go wrong during initialization.
00070  *
00071  * \param n      Request to return the initializer at this position
00072  * \param i      Add initializer to the vector
00073  * \return       0 when adding, or when requesting invalid position; nonzero
00074  *               pointer to InitializerBase object otherwise.
00075  */
00076 Detail::InitializerBase* builtIn(uint8_t n = 0, Detail::InitializerBase *i = 0){
00077         static std::vector<Detail::InitializerBase*> s_vec;
00078         if (i) {
00079                 s_vec.push_back(i);
00080         } else if (n < s_vec.size()) {
00081                 return s_vec.at(n);
00082         }
00083         return 0;
00084 }
00085 
00086 namespace Detail {
00087         // InitializerBase class
00088         // ---------------------
00089         InitializerBase::InitializerBase() {
00090                 builtIn(0, this);
00091         }
00092         InitializerBase::~InitializerBase() {}
00093 }
00094 
00095 // ModuleBase class
00096 // ----------------
00097 ModuleBase::ModuleBase(const std::string &name)
00098 : Object(&ModManager::instance(), name), m_priority(PR_NORMAL), m_name(name) {}
00099 ModuleBase::~ModuleBase() {}
00100 std::string ModuleBase::getDesc() { return m_name; }
00101 
00102 // ModManager class
00103 // ----------------
00104 //! Constructor
00105 ModManager::ModManager() : Object(&HydraNode::instance(), "modules") {}
00106 //! Destructor
00107 ModManager::~ModManager() {}
00108 
00109 ModManager& ModManager::instance() {
00110         static ModManager mm;
00111         return mm;
00112 }
00113 
00114 //! Called from HydraNode::run(), perform ModManager-specific initialization
00115 void ModManager::onInit() {
00116         uint8_t i = 0;
00117         Detail::InitializerBase *toInit = 0;
00118         for (toInit = builtIn(i); toInit; toInit = builtIn(++i)) {
00119                 ModuleBase *mod = toInit->doInit();
00120                 logMsg(
00121                         boost::format("= Initializing built-in module `%s`...")
00122                         % mod->getName()
00123                 );
00124                 Log::instance().addPreStr("  [init_" + mod->getName() + "] ");
00125                 mod->onInit();
00126                 Log::instance().remPreStr("  [init_" + mod->getName() + "] ");
00127                 m_list.insert(std::make_pair(mod->getName(), mod));
00128         }
00129 }
00130 
00131 //! Unloads _all_ loaded modules
00132 void ModManager::onExit() {
00133         boost::format preStr;
00134         while (m_list.size()) {
00135                 preStr = boost::format("[exit_%s] ");
00136                 preStr % ((*m_list.begin()).first);
00137                 Log::instance().addPreStr(preStr.str());
00138                 (*m_list.begin()).second->onExit();
00139                 delete (*m_list.begin()).second;
00140                 Log::instance().remPreStr(preStr.str());
00141                 m_list.erase(m_list.begin());
00142         }
00143 }
00144 
00145 // Load a module [private]
00146 HNMODULE ModManager::load(const std::string &name) {
00147 #ifndef STATIC_BUILD
00148 
00149         CHECK(!name.empty());
00150         logTrace(TRACE_MOD, boost::format("Loading module %s") % name);
00151 
00152 #ifdef WIN32
00153         HNMODULE plg = LoadLibrary(name.c_str());
00154 #elif defined(HAVE_LIBDL)
00155         HNMODULE plg = dlopen(name.c_str(), RTLD_LAZY);
00156 #elif defined(__MAC__)
00157         NSObjectFileImage fileimage;
00158         NSObjectFileImageReturnCode returnCode;
00159         returnCode = NSCreateObjectFileImageFromFile(name.c_str(), &fileimage);
00160         HNMODULE plg;
00161         if (returnCode == NSObjectFileImageSuccess) {
00162                 plg = NSLinkModule(
00163                         &fileimage, name.c_str(),
00164                         NSLINKMODULE_OPTION_RETURN_ON_ERROR
00165                         | NSLINKMODULE_OPTION_NONE
00166                 );
00167                 NSDestroyObjectFileImage(&fileimage);
00168         }
00169 #endif
00170         if (plg == 0) {
00171                 throw std::runtime_error(getError());
00172         } else {
00173                 return plg;
00174         }
00175 
00176 #else // STATIC_BUILD
00177         return 0;
00178 #endif
00179 }
00180 
00181 // Initialize a module [private]
00182 ModuleBase* ModManager::initialize(HNMODULE plg) {
00183 #ifndef STATIC_BUILD
00184 
00185         CHECK_THROW(plg != 0);
00186 
00187         logTrace(TRACE_MOD, "Initializing module.");
00188 
00189         ModuleBase* (*initMod)() = 0;
00190 #ifdef WIN32
00191         initMod = (ModuleBase*(*)())(GetProcAddress(plg, "onInit"));
00192 #elif defined(HAVE_LIBDL)
00193         initMod = (ModuleBase*(*)())(dlsym(plg, "onInit"));
00194 #elif defined(__MAC__)
00195         NSSymbol sym = NSLookupSymbolInModule(plg, "onInit");
00196         initMod = (ModuleBase*(*)())(NSAddressOfSymbol(sym));
00197 #endif
00198         if (!initMod) {
00199                 throw std::runtime_error("Failed to get symbol `onInit`.");
00200         }
00201         ModuleBase *module = initMod();
00202         if (!module) {
00203                 throw std::runtime_error("Module initialization failed.");
00204         }
00205         if (module->onInit() == false) {
00206                 throw std::runtime_error("Module initialization failed.");
00207         }
00208         return module;
00209 #else // STATIC_BUILD
00210         return 0;
00211 #endif
00212 }
00213 
00214 // \todo Clean this up, this is ugly
00215 bool ModManager::loadModule(const std::string &name) try {
00216 #ifdef STATIC_BUILD // No loading in static builds
00217         logWarning("Cannot load modules in static build!");
00218         return false;
00219 #endif
00220         if (m_list.find(name) != m_list.end()) {
00221                 logWarning(boost::format(
00222                         "Requested to load module `%s` which is already loaded."
00223                 ) % name);
00224                 return false;
00225         }
00226 
00227         logMsg(boost::format("- Loading module `%s`.") % name);
00228         Log::instance().addPreStr("  ");
00229 
00230         std::string mod = name;
00231         mod.append(MOD_EXT);
00232 #ifndef WIN32
00233         mod.insert(0, "lib");
00234 #endif
00235         std::vector<std::string> paths;
00236         paths.push_back("." + PATH_SEPARATOR);
00237         paths.push_back("modules" + PATH_SEPARATOR);
00238 #ifdef MODULE_DIR
00239         paths.push_back(MODULE_DIR + PATH_SEPARATOR);
00240 #endif
00241         paths.push_back("");
00242 
00243         HNMODULE plg = 0;
00244         std::vector<std::string>::iterator i = paths.begin();
00245         std::string lastError;
00246         while (i != paths.end()) {
00247                 try {
00248                         plg = load((*i++) + mod);
00249                         break;
00250                 } catch (std::exception &e) {
00251                         lastError = e.what();
00252                 }
00253         }
00254         if (!plg) {
00255                 logError(
00256                         boost::format("Failed to load module %s: %s")
00257                         % name % lastError
00258                 );
00259                 throw std::runtime_error(lastError);
00260         }
00261 
00262         const std::string preStr = (boost::format("[init_%s] ") % name).str();
00263         ModuleBase* module;
00264         try {
00265                 Log::instance().addPreStr(preStr);
00266                 module = initialize(plg);
00267                 Log::instance().remPreStr(preStr);
00268         } catch (std::exception &err) {
00269                 logError(
00270                         boost::format("Failed to init module %s: %s")
00271                         % name % err.what()
00272                 );
00273                 Log::instance().remPreStr(preStr);
00274                 throw;
00275         }
00276 
00277         m_list.insert(std::make_pair(name, module));
00278         logMsg(boost::format("Module `%s` loaded successfully.") % name);
00279         Log::instance().remPreStr("  ");
00280         return true;
00281 } catch (std::exception&) {
00282         Log::instance().remPreStr("  ");
00283         return false;
00284 }
00285 MSVC_ONLY(;)
00286 
00287 // Modules unloading... this has a myriad of problems. First we have dlclose()
00288 // which corrupts stack. If we disable dlclose() call, then unload+load causes
00289 // segfault due to singleton's re-instanciating in modules. So - disabling this
00290 // functionality altogether for now.
00291 bool ModManager::unloadModule(const std::string &name) {
00292 #ifdef STATIC_BUILD // No unloading in static builds
00293         return false;
00294 #endif
00295 
00296         logWarning("Sorry, modules unloading is not supported right now.");
00297         return false;
00298 
00299         logTrace(TRACE_MOD, boost::format("Unloading module %s") % name);
00300 
00301         Iter i = m_list.find(name);
00302         if (i == m_list.end()) {
00303                 logWarning(boost::format(
00304                         "Attempt to unload module `%s` which is not loaded."
00305                 ) % name);
00306                 return false;
00307         }
00308 
00309         //! Call module's exit function
00310         (*i).second->onExit();
00311         HNMODULE handle = (*i).second->m_handle;
00312         delete (*i).second;
00313         (*i).second = 0;
00314 
00315         // \todo Something is broken about modules unloading ... closing the
00316         // handle corrupts the stack.
00317         int ret = 0;
00318 #ifdef WIN32
00319 //      ret = FreeLibrary(handle);
00320 #elif defined(HAVE_LIBDL)
00321 //      ret = dlclose(handle);
00322 #elif defined(__MAC__)
00323 //      ret = NSUnLinkModule(handle, 0);
00324 #endif
00325         (void)handle; // surpress compiler warning until the above line is fixed
00326 
00327         if (ret != 0) {
00328                 logError(
00329                         boost::format("Error unloading module `%s`: %s")
00330                         % getError()
00331                 );
00332                 return false;
00333         }
00334 
00335         // Important:
00336         // Keep this line above m_list.erase() call, since we are passed
00337         // reference to the list string entry from onEvent() when application
00338         // is shutting down, and if we erase the original and attempt to use
00339         // it then for logMsg() call, we end up using a dead reference.
00340         logMsg(boost::format("Module `%s` unloaded.") % name);
00341 
00342         // Remove from list _after_ emitting log message.
00343         m_list.erase(name);
00344 
00345         return true;
00346 }
00347 
00348 //! Fill the passed vector with all loaded modules and their descriptions
00349 void ModManager::getList(
00350         std::vector<std::pair<std::string, std::string> > *ret
00351 ) const {
00352         CHECK_THROW(ret != 0);
00353 
00354         for (CIter i = m_list.begin(); i != m_list.end(); ++i) {
00355                 ret->push_back(
00356                         std::make_pair((*i).first, (*i).second->getDesc())
00357                 );
00358         }
00359 }