]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_lua_utils.h
bump version to 18.2.4-pve3
[ceph.git] / ceph / src / rgw / rgw_lua_utils.h
CommitLineData
f67539c2
TL
1#pragma once
2
1e59de90 3#include <string.h>
f67539c2 4#include <memory>
1e59de90 5#include <map>
f67539c2
TL
6#include <string>
7#include <string_view>
8#include <ctime>
9#include <lua.hpp>
10
11#include "include/common_fwd.h"
1e59de90 12#include "rgw_perf_counters.h"
f67539c2
TL
13
14namespace rgw::lua {
15
16// push ceph time in string format: "%Y-%m-%d %H:%M:%S"
17template <typename CephTime>
18void pushtime(lua_State* L, const CephTime& tp)
19{
20 const auto tt = CephTime::clock::to_time_t(tp);
21 const auto tm = *std::localtime(&tt);
22 char buff[64];
23 std::strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", &tm);
24 lua_pushstring(L, buff);
25}
26
27static inline void pushstring(lua_State* L, std::string_view str)
28{
29 lua_pushlstring(L, str.data(), str.size());
30}
31
32static inline void unsetglobal(lua_State* L, const char* name)
33{
34 lua_pushnil(L);
35 lua_setglobal(L, name);
36}
37
38// dump the lua stack to stdout
39void stack_dump(lua_State* L);
40
41class lua_state_guard {
42 lua_State* l;
43public:
1e59de90
TL
44 lua_state_guard(lua_State* _l) : l(_l) {
45 if (perfcounter) {
46 perfcounter->inc(l_rgw_lua_current_vms, 1);
47 }
48 }
49 ~lua_state_guard() {
50 lua_close(l);
51 if (perfcounter) {
52 perfcounter->dec(l_rgw_lua_current_vms, 1);
53 }
54 }
f67539c2
TL
55 void reset(lua_State* _l=nullptr) {l = _l;}
56};
57
1e59de90
TL
58constexpr const int MAX_LUA_VALUE_SIZE = 1000;
59constexpr const int MAX_LUA_KEY_ENTRIES = 100000;
60
f67539c2
TL
61constexpr auto ONE_UPVAL = 1;
62constexpr auto TWO_UPVALS = 2;
63constexpr auto THREE_UPVALS = 3;
64constexpr auto FOUR_UPVALS = 4;
65constexpr auto FIVE_UPVALS = 5;
66
1e59de90
TL
67constexpr auto FIRST_UPVAL = 1;
68constexpr auto SECOND_UPVAL = 2;
69constexpr auto THIRD_UPVAL = 3;
70constexpr auto FOURTH_UPVAL = 4;
71constexpr auto FIFTH_UPVAL = 5;
72
f67539c2
TL
73constexpr auto NO_RETURNVAL = 0;
74constexpr auto ONE_RETURNVAL = 1;
75constexpr auto TWO_RETURNVALS = 2;
76constexpr auto THREE_RETURNVALS = 3;
77constexpr auto FOUR_RETURNVALS = 4;
78// utility functions to create a metatable
79// and tie it to an unnamed table
80//
81// add an __index method to it, to allow reading values
82// if "readonly" parameter is set to "false", it will also add
83// a __newindex method to it, to allow writing values
84// if the "toplevel" parameter is set to "true", it will name the
85// table as well as the metatable, this would allow direct access from
86// the lua script.
87//
88// The MetaTable is expected to be a class with the following members:
89// Name (static function returning the unique name of the metatable)
90// TableName (static function returning the unique name of the table - needed only for "toplevel" tables)
91// Type (typename) - the type of the "upvalue" (the type that the meta table represent)
92// IndexClosure (static function return "int" and accept "lua_State*")
93// NewIndexClosure (static function return "int" and accept "lua_State*")
94// e.g.
95// struct MyStructMetaTable {
96// static std::string TableName() {
97// return "MyStruct";
98// }
99//
100// using Type = MyStruct;
101//
102// static int IndexClosure(lua_State* L) {
1e59de90 103// const auto value = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
f67539c2
TL
104// ...
105// }
106
107// static int NewIndexClosure(lua_State* L) {
1e59de90 108// auto value = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
f67539c2
TL
109// ...
110// }
111// };
112//
113
114template<typename MetaTable, typename... Upvalues>
115void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues)
116{
117 constexpr auto upvals_size = sizeof...(upvalues);
118 const std::array<void*, upvals_size> upvalue_arr = {upvalues...};
119 // create table
120 lua_newtable(L);
121 if (toplevel) {
122 // duplicate the table to make sure it remain in the stack
123 lua_pushvalue(L, -1);
124 // give table a name (in cae of "toplevel")
125 lua_setglobal(L, MetaTable::TableName().c_str());
126 }
127 // create metatable
128 [[maybe_unused]] const auto rc = luaL_newmetatable(L, MetaTable::Name().c_str());
129 lua_pushliteral(L, "__index");
130 for (const auto upvalue : upvalue_arr) {
131 lua_pushlightuserdata(L, upvalue);
132 }
133 lua_pushcclosure(L, MetaTable::IndexClosure, upvals_size);
134 lua_rawset(L, -3);
135 lua_pushliteral(L, "__newindex");
136 for (const auto upvalue : upvalue_arr) {
137 lua_pushlightuserdata(L, upvalue);
138 }
139 lua_pushcclosure(L, MetaTable::NewIndexClosure, upvals_size);
140 lua_rawset(L, -3);
141 lua_pushliteral(L, "__pairs");
142 for (const auto upvalue : upvalue_arr) {
143 lua_pushlightuserdata(L, upvalue);
144 }
145 lua_pushcclosure(L, MetaTable::PairsClosure, upvals_size);
146 lua_rawset(L, -3);
147 lua_pushliteral(L, "__len");
148 for (const auto upvalue : upvalue_arr) {
149 lua_pushlightuserdata(L, upvalue);
150 }
151 lua_pushcclosure(L, MetaTable::LenClosure, upvals_size);
152 lua_rawset(L, -3);
153 // tie metatable and table
154 lua_setmetatable(L, -2);
155}
156
157template<typename MetaTable>
158void create_metatable(lua_State* L, bool toplevel, std::unique_ptr<typename MetaTable::Type>& ptr)
159{
160 if (ptr) {
161 create_metatable<MetaTable>(L, toplevel, reinterpret_cast<void*>(ptr.get()));
162 } else {
163 lua_pushnil(L);
164 }
165}
166
167// following struct may be used as a base class for other MetaTable classes
168// note, however, this is not mandatory to use it as a base
169struct EmptyMetaTable {
170 // by default everythinmg is "readonly"
171 // to change, overload this function in the derived
172 static int NewIndexClosure(lua_State* L) {
20effc67 173 return luaL_error(L, "trying to write to readonly field");
f67539c2
TL
174 }
175
176 // by default nothing is iterable
177 // to change, overload this function in the derived
178 static int PairsClosure(lua_State* L) {
20effc67 179 return luaL_error(L, "trying to iterate over non-iterable field");
f67539c2
TL
180 }
181
182 // by default nothing is iterable
183 // to change, overload this function in the derived
184 static int LenClosure(lua_State* L) {
20effc67 185 return luaL_error(L, "trying to get length of non-iterable field");
f67539c2
TL
186 }
187
20effc67
TL
188 static int error_unknown_field(lua_State* L, const std::string& index, const std::string& table) {
189 return luaL_error(L, "unknown field name: %s provided to: %s",
190 index.c_str(), table.c_str());
f67539c2
TL
191 }
192};
193
194// create a debug log action
195// it expects CephContext to be captured
196// it expects one string parameter, which is the message to log
197// could be executed from any context that has CephContext
198// e.g.
199// RGWDebugLog("hello world from lua")
200//
201void create_debug_action(lua_State* L, CephContext* cct);
202
203// set the packages search path according to:
204// package.path = "<install_dir>/share/lua/5.3/?.lua" │ LuaRocks.
205// package.cpath= "<install_dir>/lib/lua/5.3/?.so"
206void set_package_path(lua_State* L, const std::string& install_dir);
207
208// open standard lua libs and remove the following functions:
209// os.exit()
210// load()
211// loadfile()
212// loadstring()
213// dofile()
214// and the "debug" library
215void open_standard_libs(lua_State* L);
216
1e59de90
TL
217typedef int MetaTableClosure(lua_State* L);
218
219template<typename MapType=std::map<std::string, std::string>>
220int StringMapWriteableNewIndex(lua_State* L) {
221 const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
222
223 const char* index = luaL_checkstring(L, 2);
224
225 if (lua_isnil(L, 3) == 0) {
226 const char* value = luaL_checkstring(L, 3);
227 if (strnlen(value, MAX_LUA_VALUE_SIZE) + strnlen(index, MAX_LUA_VALUE_SIZE)
228 > MAX_LUA_VALUE_SIZE) {
229 return luaL_error(L, "Lua maximum size of entry limit exceeded");
230 } else if (map->size() > MAX_LUA_KEY_ENTRIES) {
231 return luaL_error(L, "Lua max number of entries limit exceeded");
232 } else {
233 map->insert_or_assign(index, value);
234 }
235 } else {
236 map->erase(std::string(index));
237 }
238
239 return NO_RETURNVAL;
f67539c2
TL
240}
241
1e59de90
TL
242template<typename MapType=std::map<std::string, std::string>,
243 MetaTableClosure NewIndex=EmptyMetaTable::NewIndexClosure>
244struct StringMapMetaTable : public EmptyMetaTable {
245
246 static std::string TableName() {return "StringMap";}
247 static std::string Name() {return TableName() + "Meta";}
248
249 static int IndexClosure(lua_State* L) {
250 const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
251
252 const char* index = luaL_checkstring(L, 2);
253
254 const auto it = map->find(std::string(index));
255 if (it == map->end()) {
256 lua_pushnil(L);
257 } else {
258 pushstring(L, it->second);
259 }
260 return ONE_RETURNVAL;
261 }
262
263 static int NewIndexClosure(lua_State* L) {
264 return NewIndex(L);
265 }
266
267 static int PairsClosure(lua_State* L) {
268 auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
269 ceph_assert(map);
270 lua_pushlightuserdata(L, map);
271 lua_pushcclosure(L, stateless_iter, ONE_UPVAL); // push the stateless iterator function
272 lua_pushnil(L); // indicate this is the first call
273 // return stateless_iter, nil
274
275 return TWO_RETURNVALS;
276 }
277
278 static int stateless_iter(lua_State* L) {
279 // based on: http://lua-users.org/wiki/GeneralizedPairsAndIpairs
280 auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
281 typename MapType::const_iterator next_it;
282 if (lua_isnil(L, -1)) {
283 next_it = map->begin();
284 } else {
285 const char* index = luaL_checkstring(L, 2);
286 const auto it = map->find(std::string(index));
287 ceph_assert(it != map->end());
288 next_it = std::next(it);
289 }
290
291 if (next_it == map->end()) {
292 // index of the last element was provided
293 lua_pushnil(L);
294 lua_pushnil(L);
295 // return nil, nil
296 } else {
297 pushstring(L, next_it->first);
298 pushstring(L, next_it->second);
299 // return key, value
300 }
301
302 return TWO_RETURNVALS;
303 }
304
305 static int LenClosure(lua_State* L) {
306 const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
307
308 lua_pushinteger(L, map->size());
309
310 return ONE_RETURNVAL;
311 }
312};
313
314} // namespace rgw::lua
315