summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_lua_utils.h
blob: 47f7437f3ab499f9ed5b13125a69edb9049482b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#pragma once

#include <memory>
#include <string>
#include <string_view>
#include <ctime>
#include <lua.hpp>

#include "include/common_fwd.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) {}
  ~lua_state_guard() {lua_close(l);}
  void reset(lua_State* _l=nullptr) {l = _l;}
};

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 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(1)));
//     ...
//   }

//   static int NewIndexClosure(lua_State* L) {
//     auto value = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(1)));
//     ...
//   }
// };
//

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) {
    throw std::runtime_error("trying to write to readonly field");
    return NO_RETURNVAL;
  }
  
  // by default nothing is iterable
  // to change, overload this function in the derived
  static int PairsClosure(lua_State* L) {
    throw std::runtime_error("trying to iterate over non-iterable field");
    return ONE_RETURNVAL;
  }
  
  // by default nothing is iterable
  // to change, overload this function in the derived
  static int LenClosure(lua_State* L) {
    throw std::runtime_error("trying to get length of non-iterable field");
    return ONE_RETURNVAL;
  }

  static void throw_unknown_field(const std::string& index, const std::string& table) {
    throw std::runtime_error("unknown field name: " + index + " provided to: " + table);
  }
};

// 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);

}