summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_lua_request.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rgw/rgw_lua_request.cc906
1 files changed, 906 insertions, 0 deletions
diff --git a/src/rgw/rgw_lua_request.cc b/src/rgw/rgw_lua_request.cc
new file mode 100644
index 000000000..6d324d4fc
--- /dev/null
+++ b/src/rgw/rgw_lua_request.cc
@@ -0,0 +1,906 @@
+#include <sstream>
+#include <stdexcept>
+#include <lua.hpp>
+#include "common/dout.h"
+#include "services/svc_zone.h"
+#include "rgw_lua_utils.h"
+#include "rgw_lua.h"
+#include "rgw_common.h"
+#include "rgw_log.h"
+#include "rgw_op.h"
+#include "rgw_process_env.h"
+#include "rgw_zone.h"
+#include "rgw_acl.h"
+#include "rgw_sal_rados.h"
+#include "rgw_lua_background.h"
+#include "rgw_perf_counters.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+namespace rgw::lua::request {
+
+// closure that perform ops log action
+// e.g.
+// Request.Log()
+//
+constexpr const char* RequestLogAction{"Log"};
+
+int RequestLog(lua_State* L)
+{
+ const auto rest = reinterpret_cast<RGWREST*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ const auto olog = reinterpret_cast<OpsLogSink*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(THIRD_UPVAL)));
+ const auto op(reinterpret_cast<RGWOp*>(lua_touserdata(L, lua_upvalueindex(FOURTH_UPVAL))));
+ if (s) {
+ const auto rc = rgw_log_op(rest, s, op, olog);
+ lua_pushinteger(L, rc);
+ } else {
+ ldpp_dout(s, 1) << "Lua ERROR: missing request state, cannot use ops log" << dendl;
+ lua_pushinteger(L, -EINVAL);
+ }
+
+ return ONE_RETURNVAL;
+}
+
+int SetAttribute(lua_State* L) {
+ auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(1)));
+
+ if (!s->trace || !s->trace->IsRecording()) {
+ return 0;
+ }
+
+ auto key = luaL_checkstring(L, 1);
+ int value_type = lua_type(L, 2);
+
+ switch (value_type) {
+ case LUA_TSTRING:
+ s->trace->SetAttribute(key, lua_tostring(L, 2));
+ break;
+
+ case LUA_TNUMBER:
+ if (lua_isinteger(L, 2)) {
+ s->trace->SetAttribute(key, static_cast<int64_t>(lua_tointeger(L, 2)));
+ } else {
+ s->trace->SetAttribute(key, static_cast<double>(lua_tonumber(L, 2)));
+ }
+ break;
+
+ default:
+ luaL_error(L, "unsupported value type for SetAttribute");
+ }
+ return 0;
+}
+
+int AddEvent(lua_State* L) {
+ auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(1)));
+
+ if (!s->trace || !s->trace->IsRecording()) {
+ return 0;
+ }
+
+ int args = lua_gettop(L);
+ if (args == 1) {
+ auto log = luaL_checkstring(L, 1);
+ s->trace->AddEvent(log);
+ } else if(args == 2) {
+ auto event_name = luaL_checkstring(L, 1);
+ std::unordered_map<const char*, jspan_attribute> event_values;
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0) {
+ if (lua_type(L, -2) != LUA_TSTRING) {
+ // skip pair if key is not a string
+ lua_pop(L, 1);
+ continue;
+ }
+
+ auto key = luaL_checkstring(L, -2);
+ int value_type = lua_type(L, -1);
+ switch (value_type) {
+ case LUA_TSTRING:
+ event_values.emplace(key, lua_tostring(L, -1));
+ break;
+
+ case LUA_TNUMBER:
+ if (lua_isinteger(L, -1)) {
+ event_values.emplace(key, static_cast<int64_t>(lua_tointeger(L, -1)));
+ } else {
+ event_values.emplace(key, static_cast<double>(lua_tonumber(L, -1)));
+ }
+ break;
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ s->trace->AddEvent(event_name, event_values);
+ }
+ return 0;
+}
+
+struct ResponseMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Response";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto err = reinterpret_cast<const rgw_err*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "HTTPStatusCode") == 0) {
+ lua_pushinteger(L, err->http_ret);
+ } else if (strcasecmp(index, "RGWCode") == 0) {
+ lua_pushinteger(L, err->ret);
+ } else if (strcasecmp(index, "HTTPStatus") == 0) {
+ pushstring(L, err->err_code);
+ } else if (strcasecmp(index, "Message") == 0) {
+ pushstring(L, err->message);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int NewIndexClosure(lua_State* L) {
+ auto err = reinterpret_cast<rgw_err*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "HTTPStatusCode") == 0) {
+ err->http_ret = luaL_checkinteger(L, 3);
+ } else if (strcasecmp(index, "RGWCode") == 0) {
+ err->ret = luaL_checkinteger(L, 3);
+ } else if (strcasecmp(index, "HTTPStatus") == 0) {
+ err->err_code.assign(luaL_checkstring(L, 3));
+ } else if (strcasecmp(index, "Message") == 0) {
+ err->message.assign(luaL_checkstring(L, 3));
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return NO_RETURNVAL;
+ }
+};
+
+struct QuotaMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Quota";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto info = reinterpret_cast<RGWQuotaInfo*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "MaxSize") == 0) {
+ lua_pushinteger(L, info->max_size);
+ } else if (strcasecmp(index, "MaxObjects") == 0) {
+ lua_pushinteger(L, info->max_objects);
+ } else if (strcasecmp(index, "Enabled") == 0) {
+ lua_pushboolean(L, info->enabled);
+ } else if (strcasecmp(index, "Rounded") == 0) {
+ lua_pushboolean(L, !info->check_on_raw);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct PlacementRuleMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "PlacementRule";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto rule = reinterpret_cast<rgw_placement_rule*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Name") == 0) {
+ pushstring(L, rule->name);
+ } else if (strcasecmp(index, "StorageClass") == 0) {
+ pushstring(L, rule->storage_class);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct UserMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "User";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto user = reinterpret_cast<const rgw_user*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Tenant") == 0) {
+ pushstring(L, user->tenant);
+ } else if (strcasecmp(index, "Id") == 0) {
+ pushstring(L, user->id);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct TraceMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Trace";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Enable") == 0) {
+ lua_pushboolean(L, s->trace_enabled);
+ } else if(strcasecmp(index, "SetAttribute") == 0) {
+ lua_pushlightuserdata(L, s);
+ lua_pushcclosure(L, SetAttribute, ONE_UPVAL);
+ } else if(strcasecmp(index, "AddEvent") == 0) {
+ lua_pushlightuserdata(L, s);
+ lua_pushcclosure(L, AddEvent, ONE_UPVAL);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int NewIndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Enable") == 0) {
+ s->trace_enabled = lua_toboolean(L, 3);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return NO_RETURNVAL;
+ }
+};
+
+struct OwnerMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Owner";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto owner = reinterpret_cast<ACLOwner*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "DisplayName") == 0) {
+ pushstring(L, owner->get_display_name());
+ } else if (strcasecmp(index, "User") == 0) {
+ create_metatable<UserMetaTable>(L, false, &(owner->get_id()));
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct BucketMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Bucket";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ const auto bucket = s->bucket.get();
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (rgw::sal::Bucket::empty(bucket)) {
+ if (strcasecmp(index, "Name") == 0) {
+ pushstring(L, s->init_state.url_bucket);
+ } else {
+ lua_pushnil(L);
+ }
+ } else if (strcasecmp(index, "Tenant") == 0) {
+ pushstring(L, bucket->get_tenant());
+ } else if (strcasecmp(index, "Name") == 0) {
+ pushstring(L, bucket->get_name());
+ } else if (strcasecmp(index, "Marker") == 0) {
+ pushstring(L, bucket->get_marker());
+ } else if (strcasecmp(index, "Id") == 0) {
+ pushstring(L, bucket->get_bucket_id());
+ } else if (strcasecmp(index, "Count") == 0) {
+ lua_pushinteger(L, bucket->get_count());
+ } else if (strcasecmp(index, "Size") == 0) {
+ lua_pushinteger(L, bucket->get_size());
+ } else if (strcasecmp(index, "ZoneGroupId") == 0) {
+ pushstring(L, bucket->get_info().zonegroup);
+ } else if (strcasecmp(index, "CreationTime") == 0) {
+ pushtime(L, bucket->get_creation_time());
+ } else if (strcasecmp(index, "MTime") == 0) {
+ pushtime(L, bucket->get_modification_time());
+ } else if (strcasecmp(index, "Quota") == 0) {
+ create_metatable<QuotaMetaTable>(L, false, &(bucket->get_info().quota));
+ } else if (strcasecmp(index, "PlacementRule") == 0) {
+ create_metatable<PlacementRuleMetaTable>(L, false, &(bucket->get_info().placement_rule));
+ } else if (strcasecmp(index, "User") == 0) {
+ create_metatable<UserMetaTable>(L, false, &(bucket->get_info().owner));
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int NewIndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ const auto bucket = s->bucket.get();
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (rgw::sal::Bucket::empty(bucket)) {
+ if (strcasecmp(index, "Name") == 0) {
+ s->init_state.url_bucket = luaL_checkstring(L, 3);
+ return NO_RETURNVAL;
+ }
+ }
+ return error_unknown_field(L, index, TableName());
+ }
+};
+
+struct ObjectMetaTable : public EmptyMetaTable {
+ static const std::string TableName() {return "Object";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ using Type = rgw::sal::Object;
+
+ static int IndexClosure(lua_State* L) {
+ const auto obj = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Name") == 0) {
+ pushstring(L, obj->get_name());
+ } else if (strcasecmp(index, "Instance") == 0) {
+ pushstring(L, obj->get_instance());
+ } else if (strcasecmp(index, "Id") == 0) {
+ pushstring(L, obj->get_oid());
+ } else if (strcasecmp(index, "Size") == 0) {
+ lua_pushinteger(L, obj->get_obj_size());
+ } else if (strcasecmp(index, "MTime") == 0) {
+ pushtime(L, obj->get_mtime());
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct GrantMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Grant";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto grant = reinterpret_cast<ACLGrant*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Type") == 0) {
+ lua_pushinteger(L, grant->get_type().get_type());
+ } else if (strcasecmp(index, "User") == 0) {
+ const auto id_ptr = grant->get_id();
+ if (id_ptr) {
+ create_metatable<UserMetaTable>(L, false, const_cast<rgw_user*>(id_ptr));
+ } else {
+ lua_pushnil(L);
+ }
+ } else if (strcasecmp(index, "Permission") == 0) {
+ lua_pushinteger(L, grant->get_permission().get_permissions());
+ } else if (strcasecmp(index, "GroupType") == 0) {
+ lua_pushinteger(L, grant->get_group());
+ } else if (strcasecmp(index, "Referer") == 0) {
+ pushstring(L, grant->get_referer());
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct GrantsMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Grants";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto map = reinterpret_cast<ACLGrantMap*>(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 {
+ create_metatable<GrantMetaTable>(L, false, &(it->second));
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int PairsClosure(lua_State* L) {
+ auto map = reinterpret_cast<ACLGrantMap*>(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<ACLGrantMap*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ ACLGrantMap::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 TWO_RETURNVALS;
+ // return nil, nil
+ }
+
+ while (next_it->first.empty()) {
+ // this is a multimap and the next element does not have a unique key
+ ++next_it;
+ if (next_it == map->end()) {
+ // index of the last element was provided
+ lua_pushnil(L);
+ lua_pushnil(L);
+ return TWO_RETURNVALS;
+ // return nil, nil
+ }
+ }
+
+ pushstring(L, next_it->first);
+ create_metatable<GrantMetaTable>(L, false, &(next_it->second));
+ // return key, value
+
+ return TWO_RETURNVALS;
+ }
+
+ static int LenClosure(lua_State* L) {
+ const auto map = reinterpret_cast<ACLGrantMap*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ lua_pushinteger(L, map->size());
+
+ return ONE_RETURNVAL;
+ }
+};
+
+struct ACLMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "ACL";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ using Type = RGWAccessControlPolicy;
+
+ static int IndexClosure(lua_State* L) {
+ const auto acl = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Owner") == 0) {
+ create_metatable<OwnerMetaTable>(L, false, &(acl->get_owner()));
+ } else if (strcasecmp(index, "Grants") == 0) {
+ create_metatable<GrantsMetaTable>(L, false, &(acl->get_acl().get_grant_map()));
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct StatementsMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Statements";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ using Type = std::vector<rgw::IAM::Statement>;
+
+ static std::string statement_to_string(const rgw::IAM::Statement& statement) {
+ std::stringstream ss;
+ ss << statement;
+ return ss.str();
+ }
+
+ static int IndexClosure(lua_State* L) {
+ const auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const auto index = luaL_checkinteger(L, 2);
+
+ if (index >= (int)statements->size() || index < 0) {
+ lua_pushnil(L);
+ } else {
+ // TODO: policy language could be interpreted to lua and executed as such
+ pushstring(L, statement_to_string((*statements)[index]));
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int PairsClosure(lua_State* L) {
+ auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ ceph_assert(statements);
+ lua_pushlightuserdata(L, statements);
+ 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) {
+ auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ size_t next_it;
+ if (lua_isnil(L, -1)) {
+ next_it = 0;
+ } else {
+ const auto it = luaL_checkinteger(L, -1);
+ next_it = it+1;
+ }
+
+ if (next_it >= statements->size()) {
+ // index of the last element was provided
+ lua_pushnil(L);
+ lua_pushnil(L);
+ // return nil, nil
+ } else {
+ lua_pushinteger(L, next_it);
+ pushstring(L, statement_to_string((*statements)[next_it]));
+ // return key, value
+ }
+
+ return TWO_RETURNVALS;
+ }
+
+ static int LenClosure(lua_State* L) {
+ const auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ lua_pushinteger(L, statements->size());
+
+ return ONE_RETURNVAL;
+ }
+};
+
+struct PolicyMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Policy";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto policy = reinterpret_cast<rgw::IAM::Policy*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Text") == 0) {
+ pushstring(L, policy->text);
+ } else if (strcasecmp(index, "Id") == 0) {
+ // TODO create pushstring for std::unique_ptr
+ if (!policy->id) {
+ lua_pushnil(L);
+ } else {
+ pushstring(L, policy->id.get());
+ }
+ } else if (strcasecmp(index, "Statements") == 0) {
+ create_metatable<StatementsMetaTable>(L, false, &(policy->statements));
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct PoliciesMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Policies";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ using Type = std::vector<rgw::IAM::Policy>;
+
+ static int IndexClosure(lua_State* L) {
+ const auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const auto index = luaL_checkinteger(L, 2);
+
+ if (index >= (int)policies->size() || index < 0) {
+ lua_pushnil(L);
+ } else {
+ create_metatable<PolicyMetaTable>(L, false, &((*policies)[index]));
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int PairsClosure(lua_State* L) {
+ auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ ceph_assert(policies);
+ lua_pushlightuserdata(L, policies);
+ 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) {
+ auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ size_t next_it;
+ if (lua_isnil(L, -1)) {
+ next_it = 0;
+ } else {
+ ceph_assert(lua_isinteger(L, -1));
+ const auto it = luaL_checkinteger(L, -1);
+ next_it = it+1;
+ }
+
+ if (next_it >= policies->size()) {
+ // index of the last element was provided
+ lua_pushnil(L);
+ lua_pushnil(L);
+ // return nil, nil
+ } else {
+ lua_pushinteger(L, next_it);
+ create_metatable<PolicyMetaTable>(L, false, &((*policies)[next_it]));
+ // return key, value
+ }
+
+ return TWO_RETURNVALS;
+ }
+
+ static int LenClosure(lua_State* L) {
+ const auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ lua_pushinteger(L, policies->size());
+
+ return ONE_RETURNVAL;
+ }
+};
+
+struct HTTPMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "HTTP";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto info = reinterpret_cast<req_info*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Parameters") == 0) {
+ create_metatable<StringMapMetaTable<>>(L, false, &(info->args.get_params()));
+ } else if (strcasecmp(index, "Resources") == 0) {
+ // TODO: add non-const api to get resources
+ create_metatable<StringMapMetaTable<>>(L, false,
+ const_cast<std::map<std::string, std::string>*>(&(info->args.get_sub_resources())));
+ } else if (strcasecmp(index, "Metadata") == 0) {
+ create_metatable<StringMapMetaTable<meta_map_t, StringMapWriteableNewIndex<meta_map_t>>>(L, false, &(info->x_meta_map));
+ } else if (strcasecmp(index, "Host") == 0) {
+ pushstring(L, info->host);
+ } else if (strcasecmp(index, "Method") == 0) {
+ pushstring(L, info->method);
+ } else if (strcasecmp(index, "URI") == 0) {
+ pushstring(L, info->request_uri);
+ } else if (strcasecmp(index, "QueryString") == 0) {
+ pushstring(L, info->request_params);
+ } else if (strcasecmp(index, "Domain") == 0) {
+ pushstring(L, info->domain);
+ } else if (strcasecmp(index, "StorageClass") == 0) {
+ pushstring(L, info->storage_class);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+
+ static int NewIndexClosure(lua_State* L) {
+ auto info = reinterpret_cast<req_info*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "StorageClass") == 0) {
+ info->storage_class = luaL_checkstring(L, 3);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return NO_RETURNVAL;
+ }
+};
+
+struct CopyFromMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "CopyFrom";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Tenant") == 0) {
+ pushstring(L, s->src_tenant_name);
+ } else if (strcasecmp(index, "Bucket") == 0) {
+ pushstring(L, s->src_bucket_name);
+ } else if (strcasecmp(index, "Object") == 0) {
+ create_metatable<ObjectMetaTable>(L, false, s->src_object);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct ZoneGroupMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "ZoneGroup";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ static int IndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "Name") == 0) {
+ pushstring(L, s->zonegroup_name);
+ } else if (strcasecmp(index, "Endpoint") == 0) {
+ pushstring(L, s->zonegroup_endpoint);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+struct RequestMetaTable : public EmptyMetaTable {
+ static std::string TableName() {return "Request";}
+ static std::string Name() {return TableName() + "Meta";}
+
+ // __index closure that expect req_state to be captured
+ static int IndexClosure(lua_State* L) {
+ const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+ const auto op_name = reinterpret_cast<const char*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+
+ const char* index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, "RGWOp") == 0) {
+ pushstring(L, op_name);
+ } else if (strcasecmp(index, "DecodedURI") == 0) {
+ pushstring(L, s->decoded_uri);
+ } else if (strcasecmp(index, "ContentLength") == 0) {
+ lua_pushinteger(L, s->content_length);
+ } else if (strcasecmp(index, "GenericAttributes") == 0) {
+ create_metatable<StringMapMetaTable<>>(L, false, &(s->generic_attrs));
+ } else if (strcasecmp(index, "Response") == 0) {
+ create_metatable<ResponseMetaTable>(L, false, &(s->err));
+ } else if (strcasecmp(index, "SwiftAccountName") == 0) {
+ if (s->dialect == "swift") {
+ pushstring(L, s->account_name);
+ } else {
+ lua_pushnil(L);
+ }
+ } else if (strcasecmp(index, "Bucket") == 0) {
+ create_metatable<BucketMetaTable>(L, false, s);
+ } else if (strcasecmp(index, "Object") == 0) {
+ create_metatable<ObjectMetaTable>(L, false, s->object);
+ } else if (strcasecmp(index, "CopyFrom") == 0) {
+ if (s->op_type == RGW_OP_COPY_OBJ) {
+ create_metatable<CopyFromMetaTable>(L, s);
+ } else {
+ lua_pushnil(L);
+ }
+ } else if (strcasecmp(index, "ObjectOwner") == 0) {
+ create_metatable<OwnerMetaTable>(L, false, &(s->owner));
+ } else if (strcasecmp(index, "ZoneGroup") == 0) {
+ create_metatable<ZoneGroupMetaTable>(L, false, s);
+ } else if (strcasecmp(index, "UserACL") == 0) {
+ create_metatable<ACLMetaTable>(L, false, s->user_acl);
+ } else if (strcasecmp(index, "BucketACL") == 0) {
+ create_metatable<ACLMetaTable>(L, false, s->bucket_acl);
+ } else if (strcasecmp(index, "ObjectACL") == 0) {
+ create_metatable<ACLMetaTable>(L, false, s->object_acl);
+ } else if (strcasecmp(index, "Environment") == 0) {
+ create_metatable<StringMapMetaTable<rgw::IAM::Environment>>(L, false, &(s->env));
+ } else if (strcasecmp(index, "Policy") == 0) {
+ // TODO: create a wrapper to std::optional
+ if (!s->iam_policy) {
+ lua_pushnil(L);
+ } else {
+ create_metatable<PolicyMetaTable>(L, false, s->iam_policy.get_ptr());
+ }
+ } else if (strcasecmp(index, "UserPolicies") == 0) {
+ create_metatable<PoliciesMetaTable>(L, false, &(s->iam_user_policies));
+ } else if (strcasecmp(index, "RGWId") == 0) {
+ pushstring(L, s->host_id);
+ } else if (strcasecmp(index, "HTTP") == 0) {
+ create_metatable<HTTPMetaTable>(L, false, &(s->info));
+ } else if (strcasecmp(index, "Time") == 0) {
+ pushtime(L, s->time);
+ } else if (strcasecmp(index, "Dialect") == 0) {
+ pushstring(L, s->dialect);
+ } else if (strcasecmp(index, "Id") == 0) {
+ pushstring(L, s->req_id);
+ } else if (strcasecmp(index, "TransactionId") == 0) {
+ pushstring(L, s->trans_id);
+ } else if (strcasecmp(index, "Tags") == 0) {
+ create_metatable<StringMapMetaTable<RGWObjTags::tag_map_t>>(L, false, &(s->tagset.get_tags()));
+ } else if (strcasecmp(index, "User") == 0) {
+ if (!s->user) {
+ lua_pushnil(L);
+ } else {
+ create_metatable<UserMetaTable>(L, false, const_cast<rgw_user*>(&(s->user->get_id())));
+ }
+ } else if (strcasecmp(index, "Trace") == 0) {
+ create_metatable<TraceMetaTable>(L, false, s);
+ } else {
+ return error_unknown_field(L, index, TableName());
+ }
+ return ONE_RETURNVAL;
+ }
+};
+
+void create_top_metatable(lua_State* L, req_state* s, const char* op_name) {
+ create_metatable<RequestMetaTable>(L, true, s, const_cast<char*>(op_name));
+ lua_getglobal(L, RequestMetaTable::TableName().c_str());
+ ceph_assert(lua_istable(L, -1));
+}
+
+int execute(
+ rgw::sal::Driver* driver,
+ RGWREST* rest,
+ OpsLogSink* olog,
+ req_state* s,
+ RGWOp* op,
+ const std::string& script)
+{
+ auto L = luaL_newstate();
+ const char* op_name = op ? op->name() : "Unknown";
+ lua_state_guard lguard(L);
+
+ open_standard_libs(L);
+ set_package_path(L, s->penv.lua.luarocks_path);
+
+ create_debug_action(L, s->cct);
+
+ create_metatable<RequestMetaTable>(L, true, s, const_cast<char*>(op_name));
+
+ lua_getglobal(L, RequestMetaTable::TableName().c_str());
+ ceph_assert(lua_istable(L, -1));
+
+ // add the ops log action
+ pushstring(L, RequestLogAction);
+ lua_pushlightuserdata(L, rest);
+ lua_pushlightuserdata(L, olog);
+ lua_pushlightuserdata(L, s);
+ lua_pushlightuserdata(L, op);
+ lua_pushcclosure(L, RequestLog, FOUR_UPVALS);
+ lua_rawset(L, -3);
+
+ if (s->penv.lua.background) {
+ s->penv.lua.background->create_background_metatable(L);
+ lua_getglobal(L, rgw::lua::RGWTable::TableName().c_str());
+ ceph_assert(lua_istable(L, -1));
+ }
+
+ int rc = 0;
+ 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;
+ rc = -1;
+ }
+ } catch (const std::runtime_error& e) {
+ ldpp_dout(s, 1) << "Lua ERROR: " << e.what() << dendl;
+ rc = -1;
+ }
+ if (perfcounter) {
+ perfcounter->inc((rc == -1 ? l_rgw_lua_script_fail : l_rgw_lua_script_ok), 1);
+ }
+
+ return rc;
+}
+
+} // namespace rgw::lua::request
+