/******************************************************************************* * libmodman - A library for extending applications * Copyright (C) 2009 Nathaniel McCallum * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include // For sort() #include // For stat() #include #include #ifdef WIN32 #include #else #include // For dlopen(), etc... #include // For opendir(), readdir(), closedir() #endif #include "module_manager.hpp" using namespace libmodman; #include #define _LOAD_FAIL -1 #define _LOAD_LAZY 0 #define _LOAD_SUCC 1 #ifdef WIN32 #define pdlmtype HMODULE #define pdlopenl(filename) LoadLibraryEx(filename, NULL, DONT_RESOLVE_DLL_REFERENCES) #define pdlclose(module) FreeLibrary((pdlmtype) module) static void* pdlsym(pdlmtype mod, const string &sym) { return (void *) GetProcAddress(mod, sym.c_str()); } static pdlmtype pdlreopen(const char* filename, pdlmtype module) { pdlclose(module); return LoadLibrary(filename); } static string pdlerror() { std::string e; LPTSTR msg; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL); e = std::string((const char*) msg); LocalFree(msg); return e; } static bool pdlsymlinked(const char* modn, const char* symb) { return (GetProcAddress(GetModuleHandle(modn), symb) != NULL || \ GetProcAddress(GetModuleHandle(NULL), symb) != NULL); } static string prep_type_name(string name) { string prefix = " >::iterator i=this->extensions.begin() ; i != this->extensions.end() ; i++) { for (vector::iterator j=i->second.begin() ; j != i->second.end() ; j++) delete *j; i->second.clear(); } this->extensions.clear(); // Free all modules for (set::iterator i=this->modules.begin() ; i != this->modules.end() ; i++) pdlclose(*i); this->modules.clear(); } static int load(map >& extensions, set& singletons, mm_module *mod, bool lazy, bool symbreq) { const char* debug = getenv("_MM_DEBUG"); if (!mod || mod->vers != __MM_MODULE_VERSION || !mod->type || !mod->init) { if (debug) cerr << "failed!" << endl << "\tUnable to find basic module info!" << endl; return _LOAD_FAIL; } // Get the module type string types = mod->type(); // Make sure the type is registered if (extensions.find(types) == extensions.end()) { if (debug) cerr << "failed!" << endl << "\tUnknown extension type: " << prep_type_name(types) << endl; return _LOAD_FAIL; } // If this is a singleton and we already have an instance, don't instantiate if (singletons.find(types) != singletons.end() && extensions[types].size() > 0) { if (debug) cerr << "failed!" << endl << "\tNot loading subsequent singleton for: " << prep_type_name(types) << endl; return _LOAD_FAIL; } // If a symbol is defined, we'll search for it in the main process if (mod->symb && mod->smod && !pdlsymlinked(mod->smod, mod->symb)) { // If the symbol is not found and the symbol is required, error if (symbreq) { if (debug) cerr << "failed!" << endl << "\tUnable to find required symbol: " << mod->symb << endl; return _LOAD_FAIL; } // If the symbol is not found and not required, we'll load only // if there are no other modules of this type else if (extensions[types].size() > 0) { if (debug) cerr << "failed!" << endl << "\tUnable to find required symbol: " << mod->symb << endl; return _LOAD_FAIL; } } // We've passed all the tests this far, do it again in non-lazy mode if (lazy) return _LOAD_LAZY; // If our execution test succeeds, call init() if ((mod->test && mod->test()) || !mod->test) { base_extension** exts = mod->init(); if (!exts) { if (debug) cerr << "failed!" << endl << "\tinit() returned no extensions!" << endl; return _LOAD_FAIL; } if (debug) cerr << "success" << endl; // init() returned extensions we need to register for (unsigned int i=0 ; exts[i] ; i++) { if (debug) cerr << "\tRegistering " << typeid(*exts[i]).name() << "(" << prep_type_name(exts[i]->get_base_type()) << ")" << endl; extensions[exts[i]->get_base_type()].push_back(exts[i]); } delete[] exts; return _LOAD_SUCC; } if (debug) cerr << "failed!" << endl << "\tTest execution failed." << endl; return _LOAD_FAIL; } bool module_manager::load_builtin(mm_module *mod) { const char* debug = getenv("_MM_DEBUG"); if (debug) cerr << "loading : builtin module " << mod->name << CR; // Do the load with the specified prefix int status = load(this->extensions, this->singletons, mod, false, false); return status == _LOAD_SUCC; } bool module_manager::load_file(const string &filename, bool symbreq) { const char* debug = getenv("_MM_DEBUG"); // Stat the file to make sure it is a file struct stat st; if (stat(filename.c_str(), &st) != 0) return false; if ((st.st_mode & S_IFMT) != S_IFREG) return false; if (debug) cerr << "loading : " << filename << CR; // Open the module pdlmtype dlobj = pdlopenl(filename.c_str()); if (!dlobj) { if (debug) cerr << "failed!" << endl << "\t" << pdlerror() << endl; return false; } // If we have already loaded this module, return true if (this->modules.find((void*) dlobj) != this->modules.end()) { if (debug) cerr << "preload" << endl; pdlclose(dlobj); return true; } // Try and finish the load struct mm_module *mod_info = (mm_module*) pdlsym(dlobj, __str(__MM_MODULE_VARNAME(info))); int status = load(this->extensions, this->singletons, mod_info, true, symbreq); if (status == _LOAD_LAZY) { // Reload the module in non-lazy mode dlobj = pdlreopen(filename.c_str(), dlobj); if (!dlobj) { if (debug) cerr << "failed!" << endl << "\tUnable to reload module: " << pdlerror() << endl; return false; } mod_info = (mm_module*) pdlsym(dlobj, __str(__MM_MODULE_VARNAME(info))); status = load(this->extensions, this->singletons, mod_info, false, symbreq); } if (status == _LOAD_FAIL) { pdlclose(dlobj); return false; } // Add the dlobject to our known modules this->modules.insert((void*) dlobj); // Yay, we did it! return true; } bool module_manager::load_dir(const string &dirname, bool symbreq) { vector files; #ifdef WIN32 WIN32_FIND_DATA fd; HANDLE search; string srch = dirname + "\\*." + _MOD_SUFFIX; search = FindFirstFile(srch.c_str(), &fd); if (search != INVALID_HANDLE_VALUE) { do { files.push_back(dirname + "\\" + fd.cFileName); } while (FindNextFile(search, &fd)); FindClose(search); } #else struct dirent *ent; DIR *moduledir = opendir(dirname.c_str()); if (moduledir) { while((ent = readdir(moduledir))) { string tmp = ent->d_name; if (tmp.find(_MOD_SUFFIX, tmp.size() - string(_MOD_SUFFIX).size()) != tmp.npos) files.push_back(dirname + "/" + tmp); } closedir(moduledir); } #endif // Perform our load alphabetically sort(files.begin(), files.end()); // Try to do the load bool loaded = false; for (vector::iterator it = files.begin() ; it != files.end() ; it++) loaded = this->load_file(*it, symbreq) || loaded; return loaded; }