]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_lua.cc
import ceph pacific 16.2.5
[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
9#include <boost/process.hpp>
10#include <boost/filesystem.hpp>
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
66int read_script(rgw::sal::RGWRadosStore* store, const std::string& tenant, optional_yield y, context ctx, std::string& script)
67{
68 RGWSysObjectCtx obj_ctx(store->svc()->sysobj->init_obj_ctx());
69 RGWObjVersionTracker objv_tracker;
70
71 rgw_raw_obj obj(store->svc()->zone->get_zone_params().log_pool, script_oid(ctx, tenant));
72
73 bufferlist bl;
74
75 const auto rc = rgw_get_system_obj(
76 obj_ctx,
77 obj.pool,
78 obj.oid,
79 bl,
80 &objv_tracker,
81 nullptr,
82 y,
83 nullptr,
84 nullptr);
85
86 if (rc < 0) {
87 return rc;
88 }
89
90 auto iter = bl.cbegin();
91 try {
92 ceph::decode(script, iter);
93 } catch (buffer::error& err) {
94 return -EIO;
95 }
96
97 return 0;
98}
99
b3b6e05e 100int write_script(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, const std::string& tenant, optional_yield y, context ctx, const std::string& script)
f67539c2
TL
101{
102 RGWSysObjectCtx obj_ctx(store->svc()->sysobj->init_obj_ctx());
103 RGWObjVersionTracker objv_tracker;
104
105 rgw_raw_obj obj(store->svc()->zone->get_zone_params().log_pool, script_oid(ctx, tenant));
106
107 bufferlist bl;
108 ceph::encode(script, bl);
109
110 const auto rc = rgw_put_system_obj(
b3b6e05e 111 dpp,
f67539c2
TL
112 obj_ctx,
113 obj.pool,
114 obj.oid,
115 bl,
116 false,
117 &objv_tracker,
118 real_time(),
119 y);
120
121 if (rc < 0) {
122 return rc;
123 }
124
125 return 0;
126}
127
b3b6e05e 128int delete_script(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, const std::string& tenant, optional_yield y, context ctx)
f67539c2
TL
129{
130 RGWObjVersionTracker objv_tracker;
131
132 rgw_raw_obj obj(store->svc()->zone->get_zone_params().log_pool, script_oid(ctx, tenant));
133
134 const auto rc = rgw_delete_system_obj(
b3b6e05e 135 dpp,
f67539c2
TL
136 store->svc()->sysobj,
137 obj.pool,
138 obj.oid,
139 &objv_tracker,
140 y);
141
142 if (rc < 0 && rc != -ENOENT) {
143 return rc;
144 }
145
146 return 0;
147}
148
149#ifdef WITH_RADOSGW_LUA_PACKAGES
150
151const std::string PACKAGE_LIST_OBJECT_NAME = "lua_package_allowlist";
152
153namespace bp = boost::process;
154
b3b6e05e 155int add_package(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, optional_yield y, const std::string& package_name, bool allow_compilation) {
f67539c2
TL
156 // verify that luarocks can load this oackage
157 const auto p = bp::search_path("luarocks");
158 if (p.empty()) {
159 return -ECHILD;
160 }
161 bp::ipstream is;
162 const auto cmd = p.string() + " search --porcelain" + (allow_compilation ? " " : " --binary ") + package_name;
163 bp::child c(cmd,
164 bp::std_in.close(),
165 bp::std_err > bp::null,
166 bp::std_out > is);
167
168 std::string line;
169 bool package_found = false;
170 while (c.running() && std::getline(is, line) && !line.empty()) {
171 package_found = true;
172 }
173 c.wait();
174 auto ret = c.exit_code();
175 if (ret) {
176 return -ret;
177 }
178
179 if (!package_found) {
180 return -EINVAL;
181 }
182
183 // add package to list
184 const bufferlist empty_bl;
185 std::map<std::string, bufferlist> new_package{{package_name, empty_bl}};
186 librados::ObjectWriteOperation op;
187 op.omap_set(new_package);
b3b6e05e 188 ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
f67539c2
TL
189 PACKAGE_LIST_OBJECT_NAME, &op, y);
190
191 if (ret < 0) {
192 return ret;
193 }
194 return 0;
195}
196
b3b6e05e 197int remove_package(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, optional_yield y, const std::string& package_name) {
f67539c2
TL
198 librados::ObjectWriteOperation op;
199 op.omap_rm_keys(std::set<std::string>({package_name}));
b3b6e05e 200 const auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
f67539c2
TL
201 PACKAGE_LIST_OBJECT_NAME, &op, y);
202
203 if (ret < 0) {
204 return ret;
205 }
206
207 return 0;
208}
209
b3b6e05e 210int list_packages(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, optional_yield y, packages_t& packages) {
f67539c2
TL
211 constexpr auto max_chunk = 1024U;
212 std::string start_after;
213 bool more = true;
214 int rval;
215 while (more) {
216 librados::ObjectReadOperation op;
217 packages_t packages_chunk;
218 op.omap_get_keys2(start_after, max_chunk, &packages_chunk, &more, &rval);
b3b6e05e 219 const auto ret = rgw_rados_operate(dpp, *(store->getRados()->get_lc_pool_ctx()),
f67539c2
TL
220 PACKAGE_LIST_OBJECT_NAME, &op, nullptr, y);
221
222 if (ret < 0) {
223 return ret;
224 }
225
226 packages.merge(packages_chunk);
227 }
228
229 return 0;
230}
231
b3b6e05e 232int install_packages(const DoutPrefixProvider *dpp, rgw::sal::RGWRadosStore* store, optional_yield y, packages_t& failed_packages, std::string& output) {
f67539c2
TL
233 // luarocks directory cleanup
234 boost::system::error_code ec;
235 const auto& luarocks_path = store->get_luarocks_path();
236 boost::filesystem::remove_all(luarocks_path, ec);
237 if (ec.value() != 0 && ec.value() != ENOENT) {
238 output.append("failed to clear luarock directory: ");
239 output.append(ec.message());
240 output.append("\n");
241 return ec.value();
242 }
243
244 packages_t packages;
b3b6e05e 245 auto ret = list_packages(dpp, store, y, packages);
f67539c2
TL
246 if (ret == -ENOENT) {
247 // allowlist is empty
248 return 0;
249 }
250 if (ret < 0) {
251 return ret;
252 }
253 // verify that luarocks exists
254 const auto p = bp::search_path("luarocks");
255 if (p.empty()) {
256 return -ECHILD;
257 }
258
259 // the lua rocks install dir will be created by luarocks the first time it is called
260 for (const auto& package : packages) {
261 bp::ipstream is;
262 bp::child c(p, "install", "--lua-version", CEPH_LUA_VERSION, "--tree", luarocks_path, "--deps-mode", "one", package,
263 bp::std_in.close(),
264 (bp::std_err & bp::std_out) > is);
265
266 // once package reload is supported, code should yield when reading output
267 std::string line = "CMD: luarocks install --lua-version " + std::string(CEPH_LUA_VERSION) + std::string(" --tree ") +
268 luarocks_path + " --deps-mode one " + package;
269
270 do {
271 if (!line.empty()) {
272 output.append(line);
273 output.append("\n");
274 }
275 } while (c.running() && std::getline(is, line));
276
277 c.wait();
278 if (c.exit_code()) {
279 failed_packages.insert(package);
280 }
281 }
282
283 return 0;
284}
285
286#endif
287
288}
289