/** * * @file SharedLibManager.cc * @author An Tao * * Copyright 2018, An Tao. All rights reserved. * https://github.com/an-tao/drogon * Use of this source code is governed by a MIT license * that can be found in the License file. * * Drogon * */ #include "SharedLibManager.h" #include #include #include #include #include #include #include static void forEachFileIn( const std::string &path, const std::function &cb) { DIR *dp; struct dirent *dirp; struct stat st; /* open dirent directory */ if ((dp = opendir(path.c_str())) == NULL) { // perror("opendir:"); LOG_ERROR << "can't open dir,path:" << path; return; } /** * read all files in this dir **/ while ((dirp = readdir(dp)) != NULL) { /* ignore hidden files */ if (dirp->d_name[0] == '.') continue; /* get dirent status */ std::string filename = dirp->d_name; std::string fullname = path; fullname.append("/").append(filename); if (stat(fullname.c_str(), &st) == -1) { perror("stat"); closedir(dp); return; } /* if dirent is a directory, find files recursively */ if (S_ISDIR(st.st_mode)) { forEachFileIn(fullname, cb); } else { cb(fullname, st); } } closedir(dp); return; } using namespace drogon; SharedLibManager::SharedLibManager(const std::vector &libPaths, const std::string &outputPath) : libPaths_(libPaths), outputPath_(outputPath) { workingThread_.run(); timeId_ = workingThread_.getLoop()->runEvery(5.0, [this]() { managerLibs(); }); } SharedLibManager::~SharedLibManager() { workingThread_.getLoop()->invalidateTimer(timeId_); } void SharedLibManager::managerLibs() { for (auto const &libPath : libPaths_) { forEachFileIn( libPath, [this, libPath](const std::string &filename, const struct stat &st) { auto pos = filename.rfind('.'); if (pos != std::string::npos) { auto exName = filename.substr(pos + 1); if (exName == "csp") { // compile auto lockFile = filename + ".lock"; std::ifstream fin(lockFile); if (fin) { return; } void *oldHandle = nullptr; if (dlMap_.find(filename) != dlMap_.end()) { #if defined __linux__ || defined __HAIKU__ if (st.st_mtim.tv_sec > dlMap_[filename].mTime.tv_sec) #elif defined _WIN32 if (st.st_mtime > dlMap_[filename].mTime.tv_sec) #else if (st.st_mtimespec.tv_sec > dlMap_[filename].mTime.tv_sec) #endif { LOG_TRACE << "new csp file:" << filename; oldHandle = dlMap_[filename].handle; } else return; } { std::ofstream fout(lockFile); } auto srcFile = filename.substr(0, pos); if (!outputPath_.empty()) { pos = srcFile.rfind("/"); if (pos != std::string::npos) { srcFile = srcFile.substr(pos + 1); } srcFile = outputPath_ + "/" + srcFile; } auto soFile = srcFile + ".so"; DLStat dlStat; if (!shouldCompileLib(soFile, st)) { LOG_TRACE << "Using already compiled library:" << soFile; dlStat.handle = loadLib(soFile, oldHandle); } else { // generate source code and compile it. std::string cmd = "drogon_ctl create view "; if (!outputPath_.empty()) { cmd.append(filename).append(" -o ").append( outputPath_); } else { cmd.append(filename).append(" -o ").append( libPath); } srcFile.append(".cc"); LOG_TRACE << cmd; auto r = system(cmd.c_str()); // TODO: handle r (void)(r); dlStat.handle = compileAndLoadLib(srcFile, oldHandle); } #if defined __linux__ || defined __HAIKU__ dlStat.mTime = st.st_mtim; #elif defined _WIN32 dlStat.mTime.tv_sec = st.st_mtime; #else dlStat.mTime = st.st_mtimespec; #endif if (dlStat.handle) { dlMap_[filename] = dlStat; } else { dlStat.handle = dlMap_[filename].handle; dlMap_[filename] = dlStat; } workingThread_.getLoop()->runAfter(3.5, [lockFile]() { LOG_TRACE << "remove file " << lockFile; if (unlink(lockFile.c_str()) == -1) perror(""); }); } } }); } } void *SharedLibManager::compileAndLoadLib(const std::string &sourceFile, void *oldHld) { LOG_TRACE << "src:" << sourceFile; std::string cmd = COMPILER_COMMAND; cmd.append(" ") .append(sourceFile) .append(" ") .append(COMPILATION_FLAGS) .append(" ") .append(INCLUDING_DIRS); if (std::string(COMPILER_ID).find("Clang") != std::string::npos) cmd.append(" -shared -fPIC -undefined dynamic_lookup -o "); else cmd.append(" -shared -fPIC --no-gnu-unique -o "); auto pos = sourceFile.rfind('.'); auto soFile = sourceFile.substr(0, pos); soFile.append(".so"); cmd.append(soFile); LOG_TRACE << cmd; if (system(cmd.c_str()) == 0) { LOG_TRACE << "Compiled successfully:" << soFile; return loadLib(soFile, oldHld); } else { LOG_DEBUG << "Could not compile library."; return nullptr; } } bool SharedLibManager::shouldCompileLib(const std::string &soFile, const struct stat &sourceStat) { #if defined __linux__ || defined __HAIKU__ auto sourceModifiedTime = sourceStat.st_mtim.tv_sec; #elif defined _WIN32 auto sourceModifiedTime = sourceStat.st_mtime; #else auto sourceModifiedTime = sourceStat.st_mtimespec.tv_sec; #endif struct stat soStat; if (stat(soFile.c_str(), &soStat) == -1) { LOG_TRACE << "Cannot determine modification time for:" << soFile; return true; } #if defined __linux__ || defined __HAIKU__ auto soModifiedTime = soStat.st_mtim.tv_sec; #elif defined _WIN32 auto soModifiedTime = soStat.st_mtime; #else auto soModifiedTime = soStat.st_mtimespec.tv_sec; #endif return (sourceModifiedTime > soModifiedTime); } void *SharedLibManager::loadLib(const std::string &soFile, void *oldHld) { if (oldHld) { if (dlclose(oldHld) == 0) { LOG_TRACE << "Successfully closed dynamic library:" << oldHld; } else { LOG_TRACE << dlerror(); } } auto Handle = dlopen(soFile.c_str(), RTLD_LAZY); if (!Handle) { LOG_ERROR << "load " << soFile << " error!"; LOG_ERROR << dlerror(); } else { LOG_TRACE << "Successfully loaded library file " << soFile; } return Handle; }