diff options
Diffstat (limited to '')
-rw-r--r-- | src/rgw/rgw_lua.cc | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/rgw/rgw_lua.cc b/src/rgw/rgw_lua.cc new file mode 100644 index 000000000..33af60370 --- /dev/null +++ b/src/rgw/rgw_lua.cc @@ -0,0 +1,214 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#include <lua.hpp> +#include "services/svc_zone.h" +#include "rgw_lua_utils.h" +#include "rgw_sal_rados.h" +#include "rgw_lua.h" +#ifdef WITH_RADOSGW_LUA_PACKAGES +#include <filesystem> +#include <boost/process.hpp> +#endif + +#define dout_subsys ceph_subsys_rgw + +namespace rgw::lua { + +context to_context(const std::string& s) +{ + if (strcasecmp(s.c_str(), "prerequest") == 0) { + return context::preRequest; + } + if (strcasecmp(s.c_str(), "postrequest") == 0) { + return context::postRequest; + } + if (strcasecmp(s.c_str(), "background") == 0) { + return context::background; + } + if (strcasecmp(s.c_str(), "getdata") == 0) { + return context::getData; + } + if (strcasecmp(s.c_str(), "putdata") == 0) { + return context::putData; + } + return context::none; +} + +std::string to_string(context ctx) +{ + switch (ctx) { + case context::preRequest: + return "prerequest"; + case context::postRequest: + return "postrequest"; + case context::background: + return "background"; + case context::getData: + return "getdata"; + case context::putData: + return "putdata"; + case context::none: + break; + } + return "none"; +} + +bool verify(const std::string& script, std::string& err_msg) +{ + lua_State *L = luaL_newstate(); + lua_state_guard guard(L); + open_standard_libs(L); + try { + if (luaL_loadstring(L, script.c_str()) != LUA_OK) { + err_msg.assign(lua_tostring(L, -1)); + return false; + } + } catch (const std::runtime_error& e) { + err_msg = e.what(); + return false; + } + err_msg = ""; + return true; +} + +std::string script_oid(context ctx, const std::string& tenant) { + static const std::string SCRIPT_OID_PREFIX("script."); + return SCRIPT_OID_PREFIX + to_string(ctx) + "." + tenant; +} + + +int read_script(const DoutPrefixProvider *dpp, sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx, std::string& script) +{ + return manager ? manager->get_script(dpp, y, script_oid(ctx, tenant), script) : -ENOENT; +} + +int write_script(const DoutPrefixProvider *dpp, sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx, const std::string& script) +{ + return manager ? manager->put_script(dpp, y, script_oid(ctx, tenant), script) : -ENOENT; +} + +int delete_script(const DoutPrefixProvider *dpp, sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx) +{ + return manager ? manager->del_script(dpp, y, script_oid(ctx, tenant)) : -ENOENT; +} + +#ifdef WITH_RADOSGW_LUA_PACKAGES + +namespace bp = boost::process; + +int add_package(const DoutPrefixProvider *dpp, rgw::sal::Driver* driver, optional_yield y, const std::string& package_name, bool allow_compilation) +{ + // verify that luarocks can load this package + const auto p = bp::search_path("luarocks"); + if (p.empty()) { + return -ECHILD; + } + bp::ipstream is; + const auto cmd = p.string() + " search --porcelain" + (allow_compilation ? " " : " --binary ") + package_name; + bp::child c(cmd, + bp::std_in.close(), + bp::std_err > bp::null, + bp::std_out > is); + + std::string line; + bool package_found = false; + while (c.running() && std::getline(is, line) && !line.empty()) { + package_found = true; + } + c.wait(); + auto ret = c.exit_code(); + if (ret) { + return -ret; + } + + if (!package_found) { + return -EINVAL; + } + + //replace previous versions of the package + const std::string package_name_no_version = package_name.substr(0, package_name.find(" ")); + ret = remove_package(dpp, driver, y, package_name_no_version); + if (ret < 0) { + return ret; + } + + auto lua_mgr = driver->get_lua_manager(); + + return lua_mgr->add_package(dpp, y, package_name); +} + +int remove_package(const DoutPrefixProvider *dpp, rgw::sal::Driver* driver, optional_yield y, const std::string& package_name) +{ + auto lua_mgr = driver->get_lua_manager(); + + return lua_mgr->remove_package(dpp, y, package_name); +} + +namespace bp = boost::process; + +int list_packages(const DoutPrefixProvider *dpp, rgw::sal::Driver* driver, optional_yield y, packages_t& packages) +{ + auto lua_mgr = driver->get_lua_manager(); + + return lua_mgr->list_packages(dpp, y, packages); +} + +int install_packages(const DoutPrefixProvider *dpp, rgw::sal::Driver* driver, + optional_yield y, const std::string& luarocks_path, + packages_t& failed_packages, std::string& output) { + // luarocks directory cleanup + std::error_code ec; + if (std::filesystem::remove_all(luarocks_path, ec) + == static_cast<std::uintmax_t>(-1) && + ec != std::errc::no_such_file_or_directory) { + output.append("failed to clear luarock directory: "); + output.append(ec.message()); + output.append("\n"); + return ec.value(); + } + + packages_t packages; + auto ret = list_packages(dpp, driver, y, packages); + if (ret == -ENOENT) { + // allowlist is empty + return 0; + } + if (ret < 0) { + return ret; + } + // verify that luarocks exists + const auto p = bp::search_path("luarocks"); + if (p.empty()) { + return -ECHILD; + } + + // the lua rocks install dir will be created by luarocks the first time it is called + for (const auto& package : packages) { + bp::ipstream is; + const auto cmd = p.string() + " install --lua-version " + CEPH_LUA_VERSION + " --tree " + luarocks_path + " --deps-mode one " + package; + bp::child c(cmd, bp::std_in.close(), (bp::std_err & bp::std_out) > is); + + // once package reload is supported, code should yield when reading output + std::string line = std::string("CMD: ") + cmd; + + do { + if (!line.empty()) { + output.append(line); + output.append("\n"); + } + } while (c.running() && std::getline(is, line)); + + c.wait(); + if (c.exit_code()) { + failed_packages.insert(package); + } + } + + return 0; +} + +#endif + +} + |