summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_lua_data_filter.cc
blob: 9ebaf345388c7a4e7c053760a7bff93669a0e7a2 (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
#include "rgw_lua_data_filter.h"
#include "rgw_lua_utils.h"
#include "rgw_lua_request.h"
#include "rgw_lua_background.h"
#include "rgw_process_env.h"
#include <lua.hpp>

namespace rgw::lua {

void push_bufferlist_byte(lua_State* L, bufferlist::iterator& it) {
    char byte[1];
    it.copy(1, byte);
    lua_pushlstring(L, byte, 1);
}

struct BufferlistMetaTable : public EmptyMetaTable {

  static std::string TableName() {return "Data";}
  static std::string Name() {return TableName() + "Meta";}
  
  static int IndexClosure(lua_State* L) {
    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(1)));
    const auto index = luaL_checkinteger(L, 2);
    if (index <= 0 || index > bl->length()) {
      // lua arrays start from 1
      lua_pushnil(L);
      return ONE_RETURNVAL;
    }
    auto it = bl->begin(index-1);
    if (it != bl->end()) {
      push_bufferlist_byte(L, it);
    } else {
      lua_pushnil(L);
    }
    
    return ONE_RETURNVAL;
  }

  static int PairsClosure(lua_State* L) {
    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(1)));
    ceph_assert(bl);
    lua_pushlightuserdata(L, bl);
    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 bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(1)));
    lua_Integer index;
    if (lua_isnil(L, -1)) {
      index = 1;
    } else {
      index = luaL_checkinteger(L, -1) + 1;
    }

    // lua arrays start from 1
    auto it = bl->begin(index-1);

    if (index > bl->length()) {
      // index of the last element was provided
      lua_pushnil(L);
      lua_pushnil(L);
      // return nil, nil
    } else {
      lua_pushinteger(L, index);
      push_bufferlist_byte(L, it);
      // return key, value
    }

    return TWO_RETURNVALS;
  }
  
  static int LenClosure(lua_State* L) {
    const auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(1)));

    lua_pushinteger(L, bl->length());

    return ONE_RETURNVAL;
  }
};

int RGWObjFilter::execute(bufferlist& bl, off_t offset, const char* op_name) const {
  auto L = luaL_newstate();
  lua_state_guard lguard(L);

  open_standard_libs(L);

  create_debug_action(L, s->cct);  

  // create the "Data" table
  create_metatable<BufferlistMetaTable>(L, true, &bl);
  lua_getglobal(L, BufferlistMetaTable::TableName().c_str());
  ceph_assert(lua_istable(L, -1));

  // create the "Request" table
  request::create_top_metatable(L, s, op_name);

  // create the "Offset" variable
  lua_pushinteger(L, offset);
  lua_setglobal(L, "Offset");

  if (s->penv.lua.background) {
    // create the "RGW" table
    s->penv.lua.background->create_background_metatable(L);
    lua_getglobal(L, rgw::lua::RGWTable::TableName().c_str());
    ceph_assert(lua_istable(L, -1));
  }

  try {
    // execute the lua script
    if (luaL_dostring(L, script.c_str()) != LUA_OK) {
      const std::string err(lua_tostring(L, -1));
      ldpp_dout(s, 1) << "Lua ERROR: " << err << dendl;
      return -EINVAL;
    }
  } catch (const std::runtime_error& e) {
    ldpp_dout(s, 1) << "Lua ERROR: " << e.what() << dendl;
    return -EINVAL;
  }

  return 0;
}

int RGWGetObjFilter::handle_data(bufferlist& bl,
                  off_t bl_ofs,
                  off_t bl_len) {
  filter.execute(bl, bl_ofs, "get_obj");
  // return value is ignored since we don't want to fail execution if lua script fails
  return RGWGetObj_Filter::handle_data(bl, bl_ofs, bl_len);
}

int RGWPutObjFilter::process(bufferlist&& data, uint64_t logical_offset) {
  filter.execute(data, logical_offset, "put_obj");
  // return value is ignored since we don't want to fail execution if lua script fails
  return rgw::putobj::Pipe::process(std::move(data), logical_offset); 
}

} // namespace rgw::lua