]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_lua_utils.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rgw / rgw_lua_utils.h
1 #pragma once
2
3 #include <string.h>
4 #include <memory>
5 #include <map>
6 #include <string>
7 #include <string_view>
8 #include <ctime>
9 #include <lua.hpp>
10
11 #include "include/common_fwd.h"
12 #include "rgw_perf_counters.h"
13
14 namespace rgw::lua {
15
16 // push ceph time in string format: "%Y-%m-%d %H:%M:%S"
17 template <typename CephTime>
18 void 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
27 static inline void pushstring(lua_State* L, std::string_view str)
28 {
29 lua_pushlstring(L, str.data(), str.size());
30 }
31
32 static 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
39 void stack_dump(lua_State* L);
40
41 class lua_state_guard {
42 lua_State* l;
43 public:
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 }
55 void reset(lua_State* _l=nullptr) {l = _l;}
56 };
57
58 constexpr const int MAX_LUA_VALUE_SIZE = 1000;
59 constexpr const int MAX_LUA_KEY_ENTRIES = 100000;
60
61 constexpr auto ONE_UPVAL = 1;
62 constexpr auto TWO_UPVALS = 2;
63 constexpr auto THREE_UPVALS = 3;
64 constexpr auto FOUR_UPVALS = 4;
65 constexpr auto FIVE_UPVALS = 5;
66
67 constexpr auto FIRST_UPVAL = 1;
68 constexpr auto SECOND_UPVAL = 2;
69 constexpr auto THIRD_UPVAL = 3;
70 constexpr auto FOURTH_UPVAL = 4;
71 constexpr auto FIFTH_UPVAL = 5;
72
73 constexpr auto NO_RETURNVAL = 0;
74 constexpr auto ONE_RETURNVAL = 1;
75 constexpr auto TWO_RETURNVALS = 2;
76 constexpr auto THREE_RETURNVALS = 3;
77 constexpr 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) {
103 // const auto value = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
104 // ...
105 // }
106
107 // static int NewIndexClosure(lua_State* L) {
108 // auto value = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
109 // ...
110 // }
111 // };
112 //
113
114 template<typename MetaTable, typename... Upvalues>
115 void 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
157 template<typename MetaTable>
158 void 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
169 struct EmptyMetaTable {
170 // by default everythinmg is "readonly"
171 // to change, overload this function in the derived
172 static int NewIndexClosure(lua_State* L) {
173 return luaL_error(L, "trying to write to readonly field");
174 }
175
176 // by default nothing is iterable
177 // to change, overload this function in the derived
178 static int PairsClosure(lua_State* L) {
179 return luaL_error(L, "trying to iterate over non-iterable field");
180 }
181
182 // by default nothing is iterable
183 // to change, overload this function in the derived
184 static int LenClosure(lua_State* L) {
185 return luaL_error(L, "trying to get length of non-iterable field");
186 }
187
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());
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 //
201 void 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"
206 void 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
215 void open_standard_libs(lua_State* L);
216
217 typedef int MetaTableClosure(lua_State* L);
218
219 template<typename MapType=std::map<std::string, std::string>>
220 int 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;
240 }
241
242 template<typename MapType=std::map<std::string, std::string>,
243 MetaTableClosure NewIndex=EmptyMetaTable::NewIndexClosure>
244 struct 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