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