X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/15ef6053404686995024adb373854a009a3d2d39..42a6fcd6c59f7a51d0bda011e55e50960a28cfb5:/src/scripting.c diff --git a/src/scripting.c b/src/scripting.c index 0c18d916..99ca700c 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -180,6 +180,14 @@ int luaRedisCommand(lua_State *lua) { goto cleanup; } + if (cmd->flags & REDIS_CMD_WRITE && server.lua_random_dirty) { + luaPushError(lua, + "Write commands not allowed after non deterministic commands"); + goto cleanup; + } + + if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1; + /* Run the command */ cmd->proc(c); @@ -257,9 +265,28 @@ void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { } } +void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) { + lua_pushcfunction(lua, luafunc); + lua_pushstring(lua, libname); + lua_call(lua, 1, 0); +} + +void luaLoadLibraries(lua_State *lua) { + luaLoadLib(lua, "", luaopen_base); + luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table); + luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string); + luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math); + luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug); + +#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */ + luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package); + luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os); +#endif +} + void scriptingInit(void) { lua_State *lua = lua_open(); - luaL_openlibs(lua); + luaLoadLibraries(lua); /* Initialize a dictionary we use to map SHAs to scripts. * This is useful for replication, as we need to replicate EVALSHA @@ -425,6 +452,16 @@ void evalGenericCommand(redisClient *c, int evalsha) { * not affected by external state. */ redisSrand48(0); + /* We set this flag to zero to remember that so far no random command + * was called. This way we can allow the user to call commands like + * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command + * is called (otherwise the replication and AOF would end with non + * deterministic sequences). + * + * Thanks to this flag we'll raise an error every time a write command + * is called after a random command was used. */ + server.lua_random_dirty = 0; + /* Get the number of arguments that are keys */ if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK) return; @@ -494,7 +531,7 @@ void evalGenericCommand(redisClient *c, int evalsha) { { int retval = dictAdd(server.lua_scripts, sdsnewlen(funcname+2,40),c->argv[1]); - redisAssert(retval == DICT_OK); + redisAssertWithInfo(c,NULL,retval == DICT_OK); incrRefCount(c->argv[1]); } } @@ -546,7 +583,7 @@ void evalGenericCommand(redisClient *c, int evalsha) { if (evalsha) { robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr); - redisAssert(script != NULL); + redisAssertWithInfo(c,NULL,script != NULL); rewriteClientCommandArgument(c,0, resetRefCount(createStringObject("EVAL",4))); rewriteClientCommandArgument(c,1,script);