// system headers #include #include #include #include #include // library headers #include #include #include // local headers #include "log.h" #include "util.h" #include "process.h" #include "plugin_process_handler.h" #pragma once // implementation of PluginBase in a header to solve issues like // https://bytefreaks.net/programming-2/c/c-undefined-reference-to-templated-class-function namespace linuxdeploy { namespace plugin { namespace base { using namespace linuxdeploy::core::log; template class PluginBase::PrivateData { public: const boost::filesystem::path pluginPath; std::string name; int apiLevel; PLUGIN_TYPE pluginType; public: explicit PrivateData(const boost::filesystem::path& path) : pluginPath(path) { if (!boost::filesystem::exists(path)) { throw PluginError("No such file or directory: " + path.string()); } apiLevel = getApiLevelFromExecutable(); pluginType = getPluginTypeFromExecutable(); boost::cmatch res; boost::regex_match(path.filename().c_str(), res, PLUGIN_EXPR); name = res[1].str(); }; private: int getApiLevelFromExecutable() { const auto arg = "--plugin-api-version"; const subprocess::subprocess proc({pluginPath.string(), arg}); const auto stdoutOutput = proc.check_output(); if (stdoutOutput.empty()) { ldLog() << LD_WARNING << "received empty response from plugin" << pluginPath << "while trying to fetch data for" << "--plugin-api-version" << std::endl; return -1; } try { auto apiLevel = std::stoi(stdoutOutput); return apiLevel; } catch (const std::exception&) { return -1; } } PLUGIN_TYPE getPluginTypeFromExecutable() { // assume input type auto type = INPUT_TYPE; // check whether plugin implements --plugin-type try { const subprocess::subprocess proc({pluginPath.c_str(), "--plugin-type"}); const auto stdoutOutput = proc.check_output(); // the specification requires a single line, but we'll silently accept more than that, too if (std::count(stdoutOutput.begin(), stdoutOutput.end(), '\n') >= 1) { auto firstLine = stdoutOutput.substr(0, stdoutOutput.find_first_of('\n')); if (firstLine == "input") type = INPUT_TYPE; else if (firstLine == "output") type = OUTPUT_TYPE; } } catch (const std::logic_error&) {} return type; } }; template PluginBase::PluginBase(const boost::filesystem::path& path) : IPlugin(path) { d = new PrivateData(path); if (d->apiLevel != API_LEVEL) { std::stringstream msg; msg << "This class only supports API level " << API_LEVEL << ", not " << d->apiLevel; throw WrongApiLevelError(msg.str()); } } template PluginBase::~PluginBase() { delete d; } template boost::filesystem::path PluginBase::path() const { return d->pluginPath; } template PLUGIN_TYPE PluginBase::pluginType() const { return d->pluginType; } template std::string PluginBase::pluginTypeString() const { switch ((int) d->pluginType) { case INPUT_TYPE: return "input"; case OUTPUT_TYPE: return "output"; default: return ""; } } template int PluginBase::apiLevel() const { return d->apiLevel; } template int PluginBase::run(const boost::filesystem::path& appDirPath) { plugin_process_handler handler(d->name, path()); return handler.run(appDirPath); } } } }