]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
b3b6e05e | 100 | int 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 | 128 | int 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 | ||
151 | const std::string PACKAGE_LIST_OBJECT_NAME = "lua_package_allowlist"; | |
152 | ||
153 | namespace bp = boost::process; | |
154 | ||
b3b6e05e | 155 | int 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 | 197 | int 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 | 210 | int 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 | 232 | int 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 |