From 91cc0b9266b4b41e748f22220e0775ef05c46f19 Mon Sep 17 00:00:00 2001 From: Parth Patel <661497+parthpatel@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:53:34 -0700 Subject: [PATCH] Fixed a bug in lua debugger for 'server *' invocations and Removing Redis from internal function names. * Fixed a bug where 'server *' invocation from lua debugger was not working. * I ran into difficulty understanding some fo the LUA functions due to lack of documentation. Trying to remedy that in this commit. * I removed Redis from function names in eval.c and script_lua.c files. I did not touch log messages in this commit. I also did not touch references to "RedisProtocol". Signed-off-by: Parth Patel <661497+parthpatel@users.noreply.github.com> --- src/eval.c | 66 +++++++++++----------- src/function_lua.c | 4 +- src/script_lua.c | 135 +++++++++++++++++++++++++-------------------- src/script_lua.h | 2 +- src/server.c | 2 + src/server.h | 2 +- 6 files changed, 113 insertions(+), 98 deletions(-) diff --git a/src/eval.c b/src/eval.c index 587fb3b7a..ce52f8dc2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -99,7 +99,7 @@ struct ldbState { int bp[LDB_BREAKPOINTS_MAX]; /* An array of breakpoints line numbers. */ int bpcount; /* Number of valid entries inside bp. */ int step; /* Stop at next line regardless of breakpoints. */ - int luabp; /* Stop at next line because redis.breakpoint() was called. */ + int luabp; /* Stop at next line because server.breakpoint() was called. */ sds *src; /* Lua script source code split by line. */ int lines; /* Number of lines in 'src'. */ int currentline; /* Current line number. */ @@ -114,7 +114,7 @@ struct ldbState { /* Perform the SHA1 of the input string. We use this both for hashing script * bodies in order to obtain the Lua function name, and in the implementation - * of redis.sha1(). + * of server.sha1(). * * 'digest' should point to a 41 bytes buffer: 40 for SHA1 converted into an * hexadecimal number, plus 1 byte for null term. */ @@ -135,12 +135,12 @@ void sha1hex(char *digest, char *script, size_t len) { digest[40] = '\0'; } -/* redis.breakpoint() +/* Adds server.breakpoint() function used by lua debugger. * * Allows to stop execution during a debugging session from within * the Lua code implementation, like if a breakpoint was set in the code * immediately after the function. */ -int luaRedisBreakpointCommand(lua_State *lua) { +int luaServerBreakpointCommand(lua_State *lua) { if (ldb.active) { ldb.luabp = 1; lua_pushboolean(lua,1); @@ -150,12 +150,12 @@ int luaRedisBreakpointCommand(lua_State *lua) { return 1; } -/* redis.debug() +/* Adds server.debug() function used by lua debugger * * Log a string message into the output console. * Can take multiple arguments that will be separated by commas. * Nothing is returned to the caller. */ -int luaRedisDebugCommand(lua_State *lua) { +int luaServerDebugCommand(lua_State *lua) { if (!ldb.active) return 0; int argc = lua_gettop(lua); sds log = sdscatprintf(sdsempty()," line %d: ", ldb.currentline); @@ -167,14 +167,14 @@ int luaRedisDebugCommand(lua_State *lua) { return 0; } -/* redis.replicate_commands() +/* Adds server.replicate_commands() * * DEPRECATED: Now do nothing and always return true. * Turn on single commands replication if the script never called * a write command so far, and returns true. Otherwise if the script * already started to write, returns false and stick to whole scripts * replication, which is our default. */ -int luaRedisReplicateCommandsCommand(lua_State *lua) { +int luaServerReplicateCommandsCommand(lua_State *lua) { lua_pushboolean(lua,1); return 1; } @@ -205,27 +205,27 @@ void scriptingInit(int setup) { lctx.lua_scripts_lru_list = listCreate(); lctx.lua_scripts_mem = 0; - luaRegisterRedisAPI(lua); + luaRegisterServerAPI(lua); - /* register debug commands */ - lua_getglobal(lua,"redis"); + /* register debug commands. we only need to add it under 'server' as 'redis' is effectively aliased to 'server' table at this point. */ + lua_getglobal(lua,"server"); - /* redis.breakpoint */ + /* server.breakpoint */ lua_pushstring(lua,"breakpoint"); - lua_pushcfunction(lua,luaRedisBreakpointCommand); + lua_pushcfunction(lua,luaServerBreakpointCommand); lua_settable(lua,-3); - /* redis.debug */ + /* server.debug */ lua_pushstring(lua,"debug"); - lua_pushcfunction(lua,luaRedisDebugCommand); + lua_pushcfunction(lua,luaServerDebugCommand); lua_settable(lua,-3); - /* redis.replicate_commands */ + /* server.replicate_commands */ lua_pushstring(lua, "replicate_commands"); - lua_pushcfunction(lua, luaRedisReplicateCommandsCommand); + lua_pushcfunction(lua, luaServerReplicateCommandsCommand); lua_settable(lua, -3); - lua_setglobal(lua,"redis"); + lua_setglobal(lua,"server"); /* Add a helper function we use for pcall error reporting. * Note that when the error is in the C function we want to report the @@ -234,7 +234,7 @@ void scriptingInit(int setup) { { char *errh_func = "local dbg = debug\n" "debug = nil\n" - "function __redis__err__handler(err)\n" + "function __server__err__handler(err)\n" " local i = dbg.getinfo(2,'nSl')\n" " if i and i.what == 'C' then\n" " i = dbg.getinfo(3,'nSl')\n" @@ -583,7 +583,7 @@ void evalGenericCommand(client *c, int evalsha) { evalCalcFunctionName(evalsha, c->argv[1]->ptr, funcname); /* Push the pcall error handler function on the stack. */ - lua_getglobal(lua, "__redis__err__handler"); + lua_getglobal(lua, "__server__err__handler"); /* Try to lookup the Lua function */ lua_getfield(lua, LUA_REGISTRYINDEX, funcname); @@ -1375,7 +1375,7 @@ char *ldbRedisProtocolToHuman_Double(sds *o, char *reply) { /* Log a RESP reply as debugger output, in a human readable format. * If the resulting string is longer than 'len' plus a few more chars * used as prefix, it gets truncated. */ -void ldbLogRedisReply(char *reply) { +void ldbLogServerReply(char *reply) { sds log = sdsnew(" "); ldbRedisProtocolToHuman(&log,reply); ldbLogWithMaxLen(log); @@ -1516,30 +1516,30 @@ void ldbEval(lua_State *lua, sds *argv, int argc) { } /* Implement the debugger "server" command. We use a trick in order to make - * the implementation very simple: we just call the Lua redis.call() command + * the implementation very simple: we just call the Lua server.call() command * implementation, with ldb.step enabled, so as a side effect the command * and its reply are logged. */ -void ldbRedis(lua_State *lua, sds *argv, int argc) { +void ldbServer(lua_State *lua, sds *argv, int argc) { int j; if (!lua_checkstack(lua, argc + 1)) { /* Increase the Lua stack if needed to make sure there is enough room * to push 'argc + 1' elements to the stack. On failure, return error. * Notice that we need, in worst case, 'argc + 1' elements because we push all the arguments - * given by the user (without the first argument) and we also push the 'redis' global table and - * 'redis.call' function so: - * (1 (redis table)) + (1 (redis.call function)) + (argc - 1 (all arguments without the first)) = argc + 1*/ - ldbLogRedisReply("max lua stack reached"); + * given by the user (without the first argument) and we also push the 'server' global table and + * 'server.call' function so: + * (1 (server table)) + (1 (server.call function)) + (argc - 1 (all arguments without the first)) = argc + 1*/ + ldbLogServerReply("max lua stack reached"); return; } - lua_getglobal(lua,"redis"); + lua_getglobal(lua,"server"); lua_pushstring(lua,"call"); - lua_gettable(lua,-2); /* Stack: redis, redis.call */ + lua_gettable(lua,-2); /* Stack: server, server.call */ for (j = 1; j < argc; j++) lua_pushlstring(lua,argv[j],sdslen(argv[j])); - ldb.step = 1; /* Force redis.call() to log. */ - lua_pcall(lua,argc-1,1,0); /* Stack: redis, result */ + ldb.step = 1; /* Force server.call() to log. */ + lua_pcall(lua,argc-1,1,0); /* Stack: server, result */ ldb.step = 0; /* Disable logging. */ lua_pop(lua,2); /* Discard the result and clean the stack. */ } @@ -1677,8 +1677,8 @@ ldbLog(sdsnew(" next line of code.")); luaPushError(lua, "script aborted for user request"); luaError(lua); } else if (argc > 1 && - (!strcasecmp(argv[0],"r") || !strcasecmp(argv[0],"redis"))) { - ldbRedis(lua,argv,argc); + (!strcasecmp(argv[0],"r") || !strcasecmp(argv[0],"redis") || !strcasecmp(argv[0],"server"))) { + ldbServer(lua,argv,argc); ldbSendLogs(); } else if ((!strcasecmp(argv[0],"p") || !strcasecmp(argv[0],"print"))) { if (argc == 2) diff --git a/src/function_lua.c b/src/function_lua.c index 044f40bff..1dad1d909 100644 --- a/src/function_lua.c +++ b/src/function_lua.c @@ -423,11 +423,11 @@ int luaEngineInitEngine(void) { luaEngineCtx *lua_engine_ctx = zmalloc(sizeof(*lua_engine_ctx)); lua_engine_ctx->lua = lua_open(); - luaRegisterRedisAPI(lua_engine_ctx->lua); + luaRegisterServerAPI(lua_engine_ctx->lua); /* Register the library commands table and fields and store it to registry */ lua_newtable(lua_engine_ctx->lua); /* load library globals */ - lua_newtable(lua_engine_ctx->lua); /* load library `redis` table */ + lua_newtable(lua_engine_ctx->lua); /* load library `server` table */ lua_pushstring(lua_engine_ctx->lua, "register_function"); lua_pushcfunction(lua_engine_ctx->lua, luaRegisterFunction); diff --git a/src/script_lua.c b/src/script_lua.c index 24784c1d8..635fb7c9f 100644 --- a/src/script_lua.c +++ b/src/script_lua.c @@ -56,10 +56,10 @@ static char *libraries_allow_list[] = { }; /* Lua API globals */ -static char *redis_api_allow_list[] = { +static char *server_api_allow_list[] = { SERVER_API_NAME, REDIS_API_NAME, - "__redis__err__handler", /* error handler for eval, currently located on globals. + "__server__err__handler", /* error handler for eval, currently located on globals. Should move to registry. */ NULL, }; @@ -117,7 +117,7 @@ static char *lua_builtins_removed_after_initialization_allow_list[] = { * after that the global table is locked so not need to check anything.*/ static char **allow_lists[] = { libraries_allow_list, - redis_api_allow_list, + server_api_allow_list, lua_builtins_allow_list, lua_builtins_not_documented_allow_list, lua_builtins_removed_after_initialization_allow_list, @@ -135,8 +135,8 @@ static char *deny_list[] = { NULL, }; -static int redis_math_random (lua_State *L); -static int redis_math_randomseed (lua_State *L); +static int server_math_random (lua_State *L); +static int server_math_randomseed (lua_State *L); static void redisProtocolToLuaType_Int(void *ctx, long long val, const char *proto, size_t proto_len); static void redisProtocolToLuaType_BulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len); static void redisProtocolToLuaType_NullBulkString(void *ctx, const char *proto, size_t proto_len); @@ -152,7 +152,7 @@ static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto static void redisProtocolToLuaType_BigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len); static void redisProtocolToLuaType_VerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len); static void redisProtocolToLuaType_Attribute(struct ReplyParser *parser, void *ctx, size_t len, const char *proto); -static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua); +static void luaReplyToServerReply(client *c, client* script_client, lua_State *lua); /* * Save the give pointer on Lua registry, used to save the Lua context and @@ -197,7 +197,7 @@ void* luaGetFromRegistry(lua_State* lua, const char* name) { /* Take a server reply in the RESP format and convert it into a * Lua type. Thanks to this function, and the introduction of not connected - * clients, it is trivial to implement the redis() lua function. + * clients, it is trivial to implement the server() lua function. * * Basically we take the arguments, execute the command in the context * of a non connected client, then take the generated reply and convert it @@ -525,7 +525,7 @@ static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto } /* This function is used in order to push an error on the Lua stack in the - * format used by redis.pcall to return errors, which is a lua table + * format used by server.pcall to return errors, which is a lua table * with an "err" field set to the error string including the error code. * Note that this table is never a valid reply by proper commands, * since the returned tables are otherwise always indexed by integers, never by strings. @@ -544,7 +544,7 @@ void luaPushErrorBuff(lua_State *lua, sds err_buffer) { /* There are two possible formats for the received `error` string: * 1) "-CODE msg": in this case we remove the leading '-' since we don't store it as part of the lua error format. * 2) "msg": in this case we prepend a generic 'ERR' code since all error statuses need some error code. - * We support format (1) so this function can reuse the error messages used in other places in redis. + * We support format (1) so this function can reuse the error messages used in other places. * We support format (2) so it'll be easy to pass descriptive errors to this function without worrying about format. */ if (err_buffer[0] == '-') { @@ -582,7 +582,7 @@ void luaPushError(lua_State *lua, const char *error) { } /* In case the error set into the Lua stack by luaPushError() was generated - * by the non-error-trapping version of redis.pcall(), which is redis.call(), + * by the non-error-trapping version of server.pcall(), which is server.call(), * this function will raise the Lua error so that the execution of the * script will be halted. */ int luaError(lua_State *lua) { @@ -596,7 +596,7 @@ int luaError(lua_State *lua) { /* Reply to client 'c' converting the top element in the Lua stack to a * server reply. As a side effect the element is consumed from the stack. */ -static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua) { +static void luaReplyToServerReply(client *c, client* script_client, lua_State *lua) { int t = lua_type(lua,-1); if (!lua_checkstack(lua, 4)) { @@ -725,8 +725,8 @@ static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lu while (lua_next(lua,-2)) { /* Stack now: table, key, value */ lua_pushvalue(lua,-2); /* Dup key before consuming. */ - luaReplyToRedisReply(c, script_client, lua); /* Return key. */ - luaReplyToRedisReply(c, script_client, lua); /* Return value. */ + luaReplyToServerReply(c, script_client, lua); /* Return key. */ + luaReplyToServerReply(c, script_client, lua); /* Return value. */ /* Stack now: table, key. */ maplen++; } @@ -749,7 +749,7 @@ static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lu /* Stack now: table, key, true */ lua_pop(lua,1); /* Discard the boolean value. */ lua_pushvalue(lua,-1); /* Dup key before consuming. */ - luaReplyToRedisReply(c, script_client, lua); /* Return key. */ + luaReplyToServerReply(c, script_client, lua); /* Return key. */ /* Stack now: table, key. */ setlen++; } @@ -771,7 +771,7 @@ static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lu lua_pop(lua,1); break; } - luaReplyToRedisReply(c, script_client, lua); + luaReplyToServerReply(c, script_client, lua); mbulklen++; } setDeferredArrayLen(c,replylen,mbulklen); @@ -783,9 +783,9 @@ static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lu } /* --------------------------------------------------------------------------- - * Lua redis.* functions implementations. + * Lua server.* functions implementations. * ------------------------------------------------------------------------- */ -void freeLuaRedisArgv(robj **argv, int argc, int argv_len); +void freeLuaServerArgv(robj **argv, int argc, int argv_len); /* Cached argv array across calls. */ static robj **lua_argv = NULL; @@ -795,7 +795,7 @@ static int lua_argv_size = 0; static robj *lua_args_cached_objects[LUA_CMD_OBJCACHE_SIZE]; static size_t lua_args_cached_objects_len[LUA_CMD_OBJCACHE_SIZE]; -static robj **luaArgsToRedisArgv(lua_State *lua, int *argc, int *argv_len) { +static robj **luaArgsToServerArgv(lua_State *lua, int *argc, int *argv_len) { int j; /* Require at least one argument */ *argc = lua_gettop(lua); @@ -858,7 +858,7 @@ static robj **luaArgsToRedisArgv(lua_State *lua, int *argc, int *argv_len) { * is not a string or an integer (lua_isstring() return true for * integers as well). */ if (j != *argc) { - freeLuaRedisArgv(lua_argv, j, lua_argv_size); + freeLuaServerArgv(lua_argv, j, lua_argv_size); luaPushError(lua, "Lua redis lib command arguments must be strings or integers"); return NULL; } @@ -866,7 +866,7 @@ static robj **luaArgsToRedisArgv(lua_State *lua, int *argc, int *argv_len) { return lua_argv; } -void freeLuaRedisArgv(robj **argv, int argc, int argv_len) { +void freeLuaServerArgv(robj **argv, int argc, int argv_len) { int j; for (j = 0; j < argc; j++) { robj *o = argv[j]; @@ -896,7 +896,7 @@ void freeLuaRedisArgv(robj **argv, int argc, int argv_len) { } } -static int luaRedisGenericCommand(lua_State *lua, int raise_error) { +static int luaServerGenericCommand(lua_State *lua, int raise_error) { int j; scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME); serverAssert(rctx); /* Only supported inside script invocation */ @@ -904,7 +904,7 @@ static int luaRedisGenericCommand(lua_State *lua, int raise_error) { client* c = rctx->c; sds reply; - c->argv = luaArgsToRedisArgv(lua, &c->argc, &c->argv_len); + c->argv = luaArgsToServerArgv(lua, &c->argc, &c->argv_len); if (c->argv == NULL) { return raise_error ? luaError(lua) : 1; } @@ -912,7 +912,7 @@ static int luaRedisGenericCommand(lua_State *lua, int raise_error) { static int inuse = 0; /* Recursive calls detection. */ /* By using Lua debug hooks it is possible to trigger a recursive call - * to luaRedisGenericCommand(), which normally should never happen. + * to luaServerGenericCommand(), which normally should never happen. * To make this function reentrant is futile and makes it slower, but * we should at least detect such a misuse, and abort. */ if (inuse) { @@ -978,7 +978,7 @@ static int luaRedisGenericCommand(lua_State *lua, int raise_error) { /* If the debugger is active, log the reply from the server. */ if (ldbIsEnabled()) - ldbLogRedisReply(reply); + ldbLogServerReply(reply); if (reply != c->buf) sdsfree(reply); c->reply_bytes = 0; @@ -986,7 +986,7 @@ static int luaRedisGenericCommand(lua_State *lua, int raise_error) { cleanup: /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ - freeLuaRedisArgv(c->argv, c->argc, c->argv_len); + freeLuaServerArgv(c->argv, c->argc, c->argv_len); c->argc = c->argv_len = 0; c->user = NULL; c->argv = NULL; @@ -1031,17 +1031,17 @@ static int luaRedisPcall(lua_State *lua) { } -/* redis.call() */ +/* server.call() */ static int luaRedisCallCommand(lua_State *lua) { - return luaRedisGenericCommand(lua,1); + return luaServerGenericCommand(lua,1); } -/* redis.pcall() */ +/* server.pcall() */ static int luaRedisPCallCommand(lua_State *lua) { - return luaRedisGenericCommand(lua,0); + return luaServerGenericCommand(lua,0); } -/* This adds redis.sha1hex(string) to Lua scripts using the same hashing +/* This adds server.sha1hex(string) to Lua scripts using the same hashing * function used for sha1ing lua scripts. */ static int luaRedisSha1hexCommand(lua_State *lua) { int argc = lua_gettop(lua); @@ -1064,8 +1064,8 @@ static int luaRedisSha1hexCommand(lua_State *lua) { * passed as argument. This helper function is handy when returning * a RESP error or status reply from Lua: * - * return redis.error_reply("ERR Some Error") - * return redis.status_reply("ERR Some Error") + * return server.error_reply("ERR Some Error") + * return server.status_reply("ERR Some Error") */ static int luaRedisReturnSingleFieldTable(lua_State *lua, char *field) { if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) { @@ -1080,7 +1080,7 @@ static int luaRedisReturnSingleFieldTable(lua_State *lua, char *field) { return 1; } -/* redis.error_reply() */ +/* server.error_reply() */ static int luaRedisErrorReplyCommand(lua_State *lua) { if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) { luaPushError(lua, "wrong number or type of arguments"); @@ -1099,12 +1099,12 @@ static int luaRedisErrorReplyCommand(lua_State *lua) { return 1; } -/* redis.status_reply() */ +/* server.status_reply() */ static int luaRedisStatusReplyCommand(lua_State *lua) { return luaRedisReturnSingleFieldTable(lua,"ok"); } -/* redis.set_repl() +/* server.set_repl() * * Set the propagation of write commands executed in the context of the * script to on/off for AOF and slaves. */ @@ -1129,7 +1129,7 @@ static int luaRedisSetReplCommand(lua_State *lua) { return 0; } -/* redis.acl_check_cmd() +/* server.acl_check_cmd() * * Checks ACL permissions for given command for the current user. */ static int luaRedisAclCheckCmdPermissionsCommand(lua_State *lua) { @@ -1138,7 +1138,7 @@ static int luaRedisAclCheckCmdPermissionsCommand(lua_State *lua) { int raise_error = 0; int argc, argv_len; - robj **argv = luaArgsToRedisArgv(lua, &argc, &argv_len); + robj **argv = luaArgsToServerArgv(lua, &argc, &argv_len); /* Require at least one argument */ if (argv == NULL) return luaError(lua); @@ -1157,7 +1157,7 @@ static int luaRedisAclCheckCmdPermissionsCommand(lua_State *lua) { } } - freeLuaRedisArgv(argv, argc, argv_len); + freeLuaServerArgv(argv, argc, argv_len); if (raise_error) return luaError(lua); else @@ -1165,7 +1165,7 @@ static int luaRedisAclCheckCmdPermissionsCommand(lua_State *lua) { } -/* redis.log() */ +/* server.log() */ static int luaLogCommand(lua_State *lua) { int j, argc = lua_gettop(lua); int level; @@ -1202,7 +1202,7 @@ static int luaLogCommand(lua_State *lua) { return 0; } -/* redis.setresp() */ +/* server.setresp() */ static int luaSetResp(lua_State *lua) { scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME); serverAssert(rctx); /* Only supported inside script invocation */ @@ -1408,7 +1408,7 @@ void luaRegisterVersion(lua_State* lua) { } void luaRegisterLogFunction(lua_State* lua) { - /* redis.log and log levels. */ + /* server.log and log levels. */ lua_pushstring(lua,"log"); lua_pushcfunction(lua,luaLogCommand); lua_settable(lua,-3); @@ -1430,44 +1430,57 @@ void luaRegisterLogFunction(lua_State* lua) { lua_settable(lua,-3); } -void luaRegisterRedisAPI(lua_State* lua) { +/* + * Adds server.* functions/fields to lua such as server.call etc. + * This function only handles fields common between Functions and LUA scripting. + * scriptingInit() and functionsInit() may add additional fields specific to each. + */ +void luaRegisterServerAPI(lua_State* lua) { + /* In addition to registering server.call/pcall API, we will throw a customer message when a script accesses undefined global variable. + * LUA stored global variables in the global table, accessible to us on stack at virtual index = LUA_GLOBALSINDEX. + * We will set __index handler in global table's metatable to a custom C function to achieve this - handled by luaSetAllowListProtection. + * Refer to https://www.lua.org/pil/13.4.1.html for documentation on __index and https://www.lua.org/pil/contents.html#13 for documentation on metatables. + * We need to pass global table to lua invocations as parameters. To achieve this, lua_pushvalue invocation brings global variable table to the top of + * the stack by pushing value from global index onto the stack. And lua_pop invocation after luaSetAllowListProtection removes it - resetting the stack to its original state. */ lua_pushvalue(lua, LUA_GLOBALSINDEX); luaSetAllowListProtection(lua); lua_pop(lua, 1); + /* Add default C functions provided in deps/lua codebase to handle basic data types such as table, string etc. */ luaLoadLibraries(lua); + /* Before Valkey 7, LUA used to return error messages as strings from pcall function. With Valkey 7, LUA now returns error messages as tables. + * To keep backwards compatibility, we wrap the LUA pcall function with our own implementation of C function that converts table to string. */ lua_pushcfunction(lua,luaRedisPcall); lua_setglobal(lua, "pcall"); - /* Register the commands table and fields */ + /* Create a top level table object on the stack to temporarily hold fields for 'server' table. + * We will name it as 'server' and send it to LUA at the very end. Also add 'call' and 'pcall' functions to the table. */ lua_newtable(lua); - - /* redis.call */ lua_pushstring(lua,"call"); lua_pushcfunction(lua,luaRedisCallCommand); lua_settable(lua,-3); - - /* redis.pcall */ lua_pushstring(lua,"pcall"); lua_pushcfunction(lua,luaRedisPCallCommand); lua_settable(lua,-3); + /* Add server.log function and debug level constants. LUA scripts use it to print messages to server log. */ luaRegisterLogFunction(lua); + /* Add SERVER_VERSION_NUM, SERVER_VERSION and SERVER_NAME fields with appropriate values. */ luaRegisterVersion(lua); - /* redis.setresp */ + /* Add server.setresp function to allow LUA scripts to change the RESP version for server.call and server.pcall invocations. */ lua_pushstring(lua,"setresp"); lua_pushcfunction(lua,luaSetResp); lua_settable(lua,-3); - /* redis.sha1hex */ + /* Add server.sha1hex function. */ lua_pushstring(lua, "sha1hex"); lua_pushcfunction(lua, luaRedisSha1hexCommand); lua_settable(lua, -3); - /* redis.error_reply and redis.status_reply */ + /* Add server.error_reply and server.status_reply functions. */ lua_pushstring(lua, "error_reply"); lua_pushcfunction(lua, luaRedisErrorReplyCommand); lua_settable(lua, -3); @@ -1475,7 +1488,7 @@ void luaRegisterRedisAPI(lua_State* lua) { lua_pushcfunction(lua, luaRedisStatusReplyCommand); lua_settable(lua, -3); - /* redis.set_repl and associated flags. */ + /* Add server.set_repl function and associated flags. */ lua_pushstring(lua,"set_repl"); lua_pushcfunction(lua,luaRedisSetReplCommand); lua_settable(lua,-3); @@ -1500,7 +1513,7 @@ void luaRegisterRedisAPI(lua_State* lua) { lua_pushnumber(lua,PROPAGATE_AOF|PROPAGATE_REPL); lua_settable(lua,-3); - /* redis.acl_check_cmd */ + /* Add server.acl_check_cmd function. */ lua_pushstring(lua,"acl_check_cmd"); lua_pushcfunction(lua,luaRedisAclCheckCmdPermissionsCommand); lua_settable(lua,-3); @@ -1513,17 +1526,17 @@ void luaRegisterRedisAPI(lua_State* lua) { * This is not a deep copy but is enough for our purpose here. */ lua_getglobal(lua,SERVER_API_NAME); lua_setglobal(lua,REDIS_API_NAME); - /* Replace math.random and math.randomseed with our implementations. */ + + /* Replace math.random and math.randomseed with custom implementations. */ lua_getglobal(lua,"math"); - + /* Add random and randomseed functions. */ lua_pushstring(lua,"random"); - lua_pushcfunction(lua,redis_math_random); + lua_pushcfunction(lua,server_math_random); lua_settable(lua,-3); - lua_pushstring(lua,"randomseed"); - lua_pushcfunction(lua,redis_math_randomseed); + lua_pushcfunction(lua,server_math_randomseed); lua_settable(lua,-3); - + /* overwrite value of global variable 'math' with this modified math table present on top of stack. */ lua_setglobal(lua,"math"); } @@ -1549,7 +1562,7 @@ static void luaCreateArray(lua_State *lua, robj **elev, int elec) { /* The following implementation is the one shipped with Lua itself but with * rand() replaced by serverLrand48(). */ -static int redis_math_random (lua_State *L) { +static int server_math_random (lua_State *L) { /* the `%' avoids the (rare) case of r==1, and is needed also because on some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ lua_Number r = (lua_Number)(serverLrand48()%REDIS_LRAND48_MAX) / @@ -1577,7 +1590,7 @@ static int redis_math_random (lua_State *L) { return 1; } -static int redis_math_randomseed (lua_State *L) { +static int server_math_randomseed (lua_State *L) { serverSrand48(luaL_checkint(L, 1)); return 0; } @@ -1737,7 +1750,7 @@ void luaCallFunction(scriptRunCtx* run_ctx, lua_State *lua, robj** keys, size_t } else { /* On success convert the Lua return value into RESP, and * send it to * the client. */ - luaReplyToRedisReply(c, run_ctx->c, lua); /* Convert and consume the reply. */ + luaReplyToServerReply(c, run_ctx->c, lua); /* Convert and consume the reply. */ } /* Perform some cleanup that we need to do both on error and success. */ diff --git a/src/script_lua.h b/src/script_lua.h index 73210e6b3..ebdb363c2 100644 --- a/src/script_lua.h +++ b/src/script_lua.h @@ -66,7 +66,7 @@ typedef struct errorInfo { int ignore_err_stats_update; }errorInfo; -void luaRegisterRedisAPI(lua_State* lua); +void luaRegisterServerAPI(lua_State* lua); sds luaGetStringSds(lua_State *lua, int index); void luaRegisterGlobalProtectionFunction(lua_State *lua); void luaSetErrorMetatable(lua_State *lua); diff --git a/src/server.c b/src/server.c index a6069c016..c3b0055c5 100644 --- a/src/server.c +++ b/src/server.c @@ -2775,7 +2775,9 @@ void initServer(void) { server.maxmemory_policy = MAXMEMORY_NO_EVICTION; } + /* Initialize the LUA scripting engine. */ scriptingInit(1); + /* Initialize the functions engine based off of LUA initialization. */ if (functionsInit() == C_ERR) { serverPanic("Functions initialization failed, check the server logs."); exit(1); diff --git a/src/server.h b/src/server.h index d891fccfb..3cfcf05e3 100644 --- a/src/server.h +++ b/src/server.h @@ -3393,7 +3393,7 @@ void freeLuaScriptsAsync(dict *lua_scripts, list *lua_scripts_lru_list, lua_Stat void freeFunctionsAsync(functionsLibCtx *lib_ctx); int ldbIsEnabled(void); void ldbLog(sds entry); -void ldbLogRedisReply(char *reply); +void ldbLogServerReply(char *reply); void sha1hex(char *digest, char *script, size_t len); unsigned long evalMemory(void); dict* evalScriptsDict(void);