]>
Commit | Line | Data |
---|---|---|
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 | |
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: | |
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 |
58 | constexpr const int MAX_LUA_VALUE_SIZE = 1000; |
59 | constexpr const int MAX_LUA_KEY_ENTRIES = 100000; | |
60 | ||
f67539c2 TL |
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 | ||
1e59de90 TL |
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 | ||
f67539c2 TL |
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) { | |
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 | ||
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) { | |
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 | // | |
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 | ||
1e59de90 TL |
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; | |
f67539c2 TL |
240 | } |
241 | ||
1e59de90 TL |
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 |