]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_lua.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / rgw / rgw_lua.cc
CommitLineData
f67539c2
TL
1#include <lua.hpp>
2#include "services/svc_zone.h"
3#include "services/svc_sys_obj.h"
4#include "common/dout.h"
5#include "rgw_lua_utils.h"
6#include "rgw_sal_rados.h"
7#include "rgw_lua.h"
8#ifdef WITH_RADOSGW_LUA_PACKAGES
20effc67 9#include <filesystem>
f67539c2 10#include <boost/process.hpp>
f67539c2
TL
11#include "rgw_lua_version.h"
12#endif
13
14#define dout_subsys ceph_subsys_rgw
15
16namespace rgw::lua {
17
18context to_context(const std::string& s)
19{
20 if (strcasecmp(s.c_str(), "prerequest") == 0) {
21 return context::preRequest;
22 }
23 if (strcasecmp(s.c_str(), "postrequest") == 0) {
24 return context::postRequest;
25 }
26 return context::none;
27}
28
29std::string to_string(context ctx)
30{
31 switch (ctx) {
32 case context::preRequest:
33 return "prerequest";
34 case context::postRequest:
35 return "postrequest";
36 case context::none:
37 break;
38 }
39 return "none";
40}
41
42bool verify(const std::string& script, std::string& err_msg)
43{
44 lua_State *L = luaL_newstate();
45 lua_state_guard guard(L);
46 open_standard_libs(L);
47 try {
48 if (luaL_loadstring(L, script.c_str()) != LUA_OK) {
49 err_msg.assign(lua_tostring(L, -1));
50 return false;
51 }
52 } catch (const std::runtime_error& e) {
53 err_msg = e.what();
54 return false;
55 }
56 err_msg = "";
57 return true;
58}
59
60std::string script_oid(context ctx, const std::string& tenant) {
61 static const std::string SCRIPT_OID_PREFIX("script.");
62 return SCRIPT_OID_PREFIX + to_string(ctx) + "." + tenant;
63}
64
65
20effc67 66int read_script(const DoutPrefixProvider *dpp, rgw::sal::Store* store, const std::string& tenant, optional_yield y, context ctx, std::string& script)
f67539c2 67{
20effc67 68 auto lua_script = store->get_lua_script_manager();
f67539c2 69
20effc67 70 return lua_script->get(dpp, y, script_oid(ctx, tenant), script);
f67539c2
TL
71}
72
20effc67 73int write_script(const DoutPrefixProvider *dpp, rgw::sal::Store* store, const std::string& tenant, optional_yield y, context ctx, const std::string& script)
f67539c2 74{
20effc67 75 auto lua_script = store->get_lua_script_manager();
f67539c2 76
20effc67 77 return lua_script->put(dpp, y, script_oid(ctx, tenant), script);
f67539c2
TL
78}
79
20effc67 80int delete_script(const DoutPrefixProvider *dpp, rgw::sal::Store* store, const std::string& tenant, optional_yield y, context ctx)
f67539c2 81{
20effc67 82 auto lua_script = store->get_lua_script_manager();
f67539c2 83
20effc67 84 return lua_script->del(dpp, y, script_oid(ctx, tenant));
f67539c2
TL
85}
86
87#ifdef WITH_RADOSGW_LUA_PACKAGES
88
89const std::string PACKAGE_LIST_OBJECT_NAME = "lua_package_allowlist";
90
91namespace bp = boost::process;
92
20effc67
TL
93int add_package(const DoutPrefixProvider *dpp, rgw::sal::RadosStore* store, optional_yield y, const std::string& package_name, bool allow_compilation) {
94 // verify that luarocks can load this package
f67539c2
TL
95 const auto p = bp::search_path("luarocks");
96 if (p.empty()) {
97 return -ECHILD;
98 }
99 bp::ipstream is;
100 const auto cmd = p.string() + " search --porcelain" + (allow_compilation ? " " : " --binary ") + package_name;
101 bp::child c(cmd,
102 bp::std_in.close(),
103 bp::std_err > bp::null,
104 bp::std_out > is);
105
106 std::string line;
107 bool package_found = false;
108 while (c.running() && std::getline(is, line) && !line.empty()) {
109 package_found = true;
110 }
111 c.wait();
112 auto ret = c.exit_code();
113 if (ret) {
114 return -ret;
115 }
116
117 if (!package_found) {
118 return -EINVAL;
119 }
120
20effc67
TL
121 //replace previous versions of the package
122 const std::string package_name_no_version = package_name.substr(0, package_name.find(" "));
123 ret = remove_package(dpp, store, y, package_name_no_version);
124 if (ret < 0) {
125 return ret;
126 }
127
f67539c2
TL
128 // add package to list
129 const bufferlist empty_bl;
130 std::map<std::string, bufferlist> new_package{{package_name, empty_bl}};
131 librados::ObjectWriteOperation op;
132 op.omap_set(new_package);
20effc67 133 ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
f67539c2
TL
134 PACKAGE_LIST_OBJECT_NAME, &op, y);
135
136 if (ret < 0) {
137 return ret;
138 }
139 return 0;
140}
141
20effc67 142int remove_package(const DoutPrefixProvider *dpp, rgw::sal::RadosStore* store, optional_yield y, const std::string& package_name) {
f67539c2 143 librados::ObjectWriteOperation op;
20effc67
TL
144 size_t pos = package_name.find(" ");
145 if (pos != package_name.npos) {
146 // remove specfic version of the the package
147 op.omap_rm_keys(std::set<std::string>({package_name}));
148 auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
149 PACKAGE_LIST_OBJECT_NAME, &op, y);
150 if (ret < 0) {
151 return ret;
152 }
153 return 0;
154 }
155 // otherwise, remove any existing versions of the package
156 packages_t packages;
157 auto ret = list_packages(dpp, store, y, packages);
158 if (ret < 0 && ret != -ENOENT) {
f67539c2
TL
159 return ret;
160 }
20effc67
TL
161 for(const auto& package : packages) {
162 const std::string package_no_version = package.substr(0, package.find(" "));
163 if (package_no_version.compare(package_name) == 0) {
164 op.omap_rm_keys(std::set<std::string>({package}));
165 ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
166 PACKAGE_LIST_OBJECT_NAME, &op, y);
167 if (ret < 0) {
168 return ret;
169 }
170 }
171 }
f67539c2
TL
172 return 0;
173}
174
20effc67 175int list_packages(const DoutPrefixProvider *dpp, rgw::sal::RadosStore* store, optional_yield y, packages_t& packages) {
f67539c2
TL
176 constexpr auto max_chunk = 1024U;
177 std::string start_after;
178 bool more = true;
179 int rval;
180 while (more) {
181 librados::ObjectReadOperation op;
182 packages_t packages_chunk;
183 op.omap_get_keys2(start_after, max_chunk, &packages_chunk, &more, &rval);
b3b6e05e 184 const auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
f67539c2
TL
185 PACKAGE_LIST_OBJECT_NAME, &op, nullptr, y);
186
187 if (ret < 0) {
188 return ret;
189 }
190
191 packages.merge(packages_chunk);
192 }
193
194 return 0;
195}
196
20effc67 197int install_packages(const DoutPrefixProvider *dpp, rgw::sal::RadosStore* store, optional_yield y, packages_t& failed_packages, std::string& output) {
f67539c2 198 // luarocks directory cleanup
20effc67 199 std::error_code ec;
f67539c2 200 const auto& luarocks_path = store->get_luarocks_path();
20effc67
TL
201 if (std::filesystem::remove_all(luarocks_path, ec)
202 == static_cast<std::uintmax_t>(-1) &&
203 ec != std::errc::no_such_file_or_directory) {
f67539c2
TL
204 output.append("failed to clear luarock directory: ");
205 output.append(ec.message());
206 output.append("\n");
207 return ec.value();
208 }
209
210 packages_t packages;
b3b6e05e 211 auto ret = list_packages(dpp, store, y, packages);
f67539c2
TL
212 if (ret == -ENOENT) {
213 // allowlist is empty
214 return 0;
215 }
216 if (ret < 0) {
217 return ret;
218 }
219 // verify that luarocks exists
220 const auto p = bp::search_path("luarocks");
221 if (p.empty()) {
222 return -ECHILD;
223 }
224
225 // the lua rocks install dir will be created by luarocks the first time it is called
226 for (const auto& package : packages) {
227 bp::ipstream is;
20effc67
TL
228 const auto cmd = p.string() + " install --lua-version " + CEPH_LUA_VERSION + " --tree " + luarocks_path + " --deps-mode one " + package;
229 bp::child c(cmd, bp::std_in.close(), (bp::std_err & bp::std_out) > is);
f67539c2
TL
230
231 // once package reload is supported, code should yield when reading output
20effc67 232 std::string line = std::string("CMD: ") + cmd;
f67539c2
TL
233
234 do {
235 if (!line.empty()) {
236 output.append(line);
237 output.append("\n");
238 }
239 } while (c.running() && std::getline(is, line));
240
241 c.wait();
242 if (c.exit_code()) {
243 failed_packages.insert(package);
244 }
245 }
246
247 return 0;
248}
249
250#endif
251
252}
253