]> git.saurik.com Git - redis.git/blobdiff - src/scripting.c
Fixed a small memory leak I found with valgrind
[redis.git] / src / scripting.c
index e952c7c6cb89449d97c09f0a36cb5fdba9272659..99ca700cf41e47a5e13b6ba2bd1e9d03a3c00cd3 100644 (file)
@@ -158,24 +158,37 @@ int luaRedisCommand(lua_State *lua) {
         return 1;
     }
 
+    /* Setup our fake client for command execution */
+    c->argv = argv;
+    c->argc = argc;
+
     /* Command lookup */
     cmd = lookupCommand(argv[0]->ptr);
     if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||
                    (argc < -cmd->arity)))
     {
-        for (j = 0; j < argc; j++) decrRefCount(argv[j]);
-        zfree(argv);
         if (cmd)
             luaPushError(lua,
                 "Wrong number of args calling Redis command From Lua script");
         else
             luaPushError(lua,"Unknown Redis command called from Lua script");
-        return 1;
+        goto cleanup;
     }
 
-    /* Run the command in the context of a fake client */
-    c->argv = argv;
-    c->argc = argc;
+    if (cmd->flags & REDIS_CMD_NOSCRIPT) {
+        luaPushError(lua, "This Redis command is not allowed from scripts");
+        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);
 
     /* Convert the result of the Redis command into a suitable Lua type.
@@ -195,6 +208,7 @@ int luaRedisCommand(lua_State *lua) {
     redisProtocolToLuaType(lua,reply);
     sdsfree(reply);
 
+cleanup:
     /* Clean up. Command code may have changed argv/argc so we use the
      * argv/argc of the client instead of the local variables. */
     for (j = 0; j < c->argc; j++)
@@ -251,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
@@ -419,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;
@@ -488,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]);
         }
     }
@@ -540,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);