#pragma once #include #include #include #include #include #include #include #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 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(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); // ... // } // static int NewIndexClosure(lua_State* L) { // auto value = reinterpret_cast(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); // ... // } // }; // template void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues) { constexpr auto upvals_size = sizeof...(upvalues); const std::array 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 void create_metatable(lua_State* L, bool toplevel, std::unique_ptr& ptr) { if (ptr) { create_metatable(L, toplevel, reinterpret_cast(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 = "/share/lua/5.3/?.lua" │ LuaRocks. // package.cpath= "/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> int StringMapWriteableNewIndex(lua_State* L) { const auto map = reinterpret_cast(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, 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(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(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(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(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL))); lua_pushinteger(L, map->size()); return ONE_RETURNVAL; } }; } // namespace rgw::lua