diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/rgw/rgw_lua_utils.h | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/rgw/rgw_lua_utils.h')
-rw-r--r-- | src/rgw/rgw_lua_utils.h | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/rgw/rgw_lua_utils.h b/src/rgw/rgw_lua_utils.h new file mode 100644 index 000000000..cc77dae7a --- /dev/null +++ b/src/rgw/rgw_lua_utils.h @@ -0,0 +1,315 @@ +#pragma once + +#include <string.h> +#include <memory> +#include <map> +#include <string> +#include <string_view> +#include <ctime> +#include <lua.hpp> + +#include "include/common_fwd.h" +#include "rgw_perf_counters.h" + +namespace rgw::lua { + +// push ceph time in string format: "%Y-%m-%d %H:%M:%S" +template <typename CephTime> +void pushtime(lua_State* L, const CephTime& tp) +{ + const auto tt = CephTime::clock::to_time_t(tp); + const auto tm = *std::localtime(&tt); + char buff[64]; + std::strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", &tm); + lua_pushstring(L, buff); +} + +static inline void pushstring(lua_State* L, std::string_view str) +{ + lua_pushlstring(L, str.data(), str.size()); +} + +static inline void unsetglobal(lua_State* L, const char* name) +{ + lua_pushnil(L); + lua_setglobal(L, name); +} + +// dump the lua stack to stdout +void stack_dump(lua_State* L); + +class lua_state_guard { + lua_State* l; +public: + lua_state_guard(lua_State* _l) : l(_l) { + if (perfcounter) { + perfcounter->inc(l_rgw_lua_current_vms, 1); + } + } + ~lua_state_guard() { + lua_close(l); + if (perfcounter) { + perfcounter->dec(l_rgw_lua_current_vms, 1); + } + } + void reset(lua_State* _l=nullptr) {l = _l;} +}; + +constexpr const int MAX_LUA_VALUE_SIZE = 1000; +constexpr const int MAX_LUA_KEY_ENTRIES = 100000; + +constexpr auto ONE_UPVAL = 1; +constexpr auto TWO_UPVALS = 2; +constexpr auto THREE_UPVALS = 3; +constexpr auto FOUR_UPVALS = 4; +constexpr auto FIVE_UPVALS = 5; + +constexpr auto FIRST_UPVAL = 1; +constexpr auto SECOND_UPVAL = 2; +constexpr auto THIRD_UPVAL = 3; +constexpr auto FOURTH_UPVAL = 4; +constexpr auto FIFTH_UPVAL = 5; + +constexpr auto NO_RETURNVAL = 0; +constexpr auto ONE_RETURNVAL = 1; +constexpr auto TWO_RETURNVALS = 2; +constexpr auto THREE_RETURNVALS = 3; +constexpr auto FOUR_RETURNVALS = 4; +// utility functions to create a metatable +// and tie it to an unnamed table +// +// add an __index method to it, to allow reading values +// if "readonly" parameter is set to "false", it will also add +// a __newindex method to it, to allow writing values +// if the "toplevel" parameter is set to "true", it will name the +// table as well as the metatable, this would allow direct access from +// the lua script. +// +// The MetaTable is expected to be a class with the following members: +// Name (static function returning the unique name of the metatable) +// TableName (static function returning the unique name of the table - needed only for "toplevel" tables) +// Type (typename) - the type of the "upvalue" (the type that the meta table represent) +// IndexClosure (static function return "int" and accept "lua_State*") +// NewIndexClosure (static function return "int" and accept "lua_State*") +// e.g. +// struct MyStructMetaTable { +// static std::string TableName() { +// return "MyStruct"; +// } +// +// using Type = MyStruct; +// +// static int IndexClosure(lua_State* L) { +// const auto value = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); +// ... +// } + +// static int NewIndexClosure(lua_State* L) { +// auto value = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); +// ... +// } +// }; +// + +template<typename MetaTable, typename... Upvalues> +void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues) +{ + constexpr auto upvals_size = sizeof...(upvalues); + const std::array<void*, upvals_size> upvalue_arr = {upvalues...}; + // create table + lua_newtable(L); + if (toplevel) { + // duplicate the table to make sure it remain in the stack + lua_pushvalue(L, -1); + // give table a name (in cae of "toplevel") + lua_setglobal(L, MetaTable::TableName().c_str()); + } + // create metatable + [[maybe_unused]] const auto rc = luaL_newmetatable(L, MetaTable::Name().c_str()); + lua_pushliteral(L, "__index"); + for (const auto upvalue : upvalue_arr) { + lua_pushlightuserdata(L, upvalue); + } + lua_pushcclosure(L, MetaTable::IndexClosure, upvals_size); + lua_rawset(L, -3); + lua_pushliteral(L, "__newindex"); + for (const auto upvalue : upvalue_arr) { + lua_pushlightuserdata(L, upvalue); + } + lua_pushcclosure(L, MetaTable::NewIndexClosure, upvals_size); + lua_rawset(L, -3); + lua_pushliteral(L, "__pairs"); + for (const auto upvalue : upvalue_arr) { + lua_pushlightuserdata(L, upvalue); + } + lua_pushcclosure(L, MetaTable::PairsClosure, upvals_size); + lua_rawset(L, -3); + lua_pushliteral(L, "__len"); + for (const auto upvalue : upvalue_arr) { + lua_pushlightuserdata(L, upvalue); + } + lua_pushcclosure(L, MetaTable::LenClosure, upvals_size); + lua_rawset(L, -3); + // tie metatable and table + lua_setmetatable(L, -2); +} + +template<typename MetaTable> +void create_metatable(lua_State* L, bool toplevel, std::unique_ptr<typename MetaTable::Type>& ptr) +{ + if (ptr) { + create_metatable<MetaTable>(L, toplevel, reinterpret_cast<void*>(ptr.get())); + } else { + lua_pushnil(L); + } +} + +// following struct may be used as a base class for other MetaTable classes +// note, however, this is not mandatory to use it as a base +struct EmptyMetaTable { + // by default everythinmg is "readonly" + // to change, overload this function in the derived + static int NewIndexClosure(lua_State* L) { + return luaL_error(L, "trying to write to readonly field"); + } + + // by default nothing is iterable + // to change, overload this function in the derived + static int PairsClosure(lua_State* L) { + return luaL_error(L, "trying to iterate over non-iterable field"); + } + + // by default nothing is iterable + // to change, overload this function in the derived + static int LenClosure(lua_State* L) { + return luaL_error(L, "trying to get length of non-iterable field"); + } + + static int error_unknown_field(lua_State* L, const std::string& index, const std::string& table) { + return luaL_error(L, "unknown field name: %s provided to: %s", + index.c_str(), table.c_str()); + } +}; + +// create a debug log action +// it expects CephContext to be captured +// it expects one string parameter, which is the message to log +// could be executed from any context that has CephContext +// e.g. +// RGWDebugLog("hello world from lua") +// +void create_debug_action(lua_State* L, CephContext* cct); + +// set the packages search path according to: +// package.path = "<install_dir>/share/lua/5.3/?.lua" │ LuaRocks. +// package.cpath= "<install_dir>/lib/lua/5.3/?.so" +void set_package_path(lua_State* L, const std::string& install_dir); + +// open standard lua libs and remove the following functions: +// os.exit() +// load() +// loadfile() +// loadstring() +// dofile() +// and the "debug" library +void open_standard_libs(lua_State* L); + +typedef int MetaTableClosure(lua_State* L); + +template<typename MapType=std::map<std::string, std::string>> +int StringMapWriteableNewIndex(lua_State* L) { + const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); + + const char* index = luaL_checkstring(L, 2); + + if (lua_isnil(L, 3) == 0) { + const char* value = luaL_checkstring(L, 3); + if (strnlen(value, MAX_LUA_VALUE_SIZE) + strnlen(index, MAX_LUA_VALUE_SIZE) + > MAX_LUA_VALUE_SIZE) { + return luaL_error(L, "Lua maximum size of entry limit exceeded"); + } else if (map->size() > MAX_LUA_KEY_ENTRIES) { + return luaL_error(L, "Lua max number of entries limit exceeded"); + } else { + map->insert_or_assign(index, value); + } + } else { + map->erase(std::string(index)); + } + + return NO_RETURNVAL; +} + +template<typename MapType=std::map<std::string, std::string>, + MetaTableClosure NewIndex=EmptyMetaTable::NewIndexClosure> +struct StringMapMetaTable : public EmptyMetaTable { + + static std::string TableName() {return "StringMap";} + static std::string Name() {return TableName() + "Meta";} + + static int IndexClosure(lua_State* L) { + const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); + + const char* index = luaL_checkstring(L, 2); + + const auto it = map->find(std::string(index)); + if (it == map->end()) { + lua_pushnil(L); + } else { + pushstring(L, it->second); + } + return ONE_RETURNVAL; + } + + static int NewIndexClosure(lua_State* L) { + return NewIndex(L); + } + + static int PairsClosure(lua_State* L) { + auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); + ceph_assert(map); + lua_pushlightuserdata(L, map); + lua_pushcclosure(L, stateless_iter, ONE_UPVAL); // push the stateless iterator function + lua_pushnil(L); // indicate this is the first call + // return stateless_iter, nil + + return TWO_RETURNVALS; + } + + static int stateless_iter(lua_State* L) { + // based on: http://lua-users.org/wiki/GeneralizedPairsAndIpairs + auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); + typename MapType::const_iterator next_it; + if (lua_isnil(L, -1)) { + next_it = map->begin(); + } else { + const char* index = luaL_checkstring(L, 2); + const auto it = map->find(std::string(index)); + ceph_assert(it != map->end()); + next_it = std::next(it); + } + + if (next_it == map->end()) { + // index of the last element was provided + lua_pushnil(L); + lua_pushnil(L); + // return nil, nil + } else { + pushstring(L, next_it->first); + pushstring(L, next_it->second); + // return key, value + } + + return TWO_RETURNVALS; + } + + static int LenClosure(lua_State* L) { + const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); + + lua_pushinteger(L, map->size()); + + return ONE_RETURNVAL; + } +}; + +} // namespace rgw::lua + |