X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/c2a7dd85ae007f157da3a5bedf1392a23fc38bc4..82c6b8257a5f465aa257160930572c0ef257e5a8:/src/scripting.c diff --git a/src/scripting.c b/src/scripting.c index 26e33bc7..21b3214d 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -68,7 +68,7 @@ char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply) { string2ll(reply+1,p-reply-1,&bulklen); if (bulklen == -1) { - lua_pushnil(lua); + lua_pushboolean(lua,0); return p+2; } else { lua_pushlstring(lua,p+2,bulklen); @@ -104,7 +104,7 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply) { string2ll(reply+1,p-reply-1,&mbulklen); p += 2; if (mbulklen == -1) { - lua_pushnil(lua); + lua_pushboolean(lua,0); return p; } lua_newtable(lua); @@ -199,6 +199,18 @@ int luaRedisCommand(lua_State *lua) { return 1; } +void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { + long long elapsed; + REDIS_NOTUSED(ar); + + elapsed = (ustime()/1000) - server.lua_time_start; + if (elapsed >= server.lua_time_limit) { + redisLog(REDIS_NOTICE,"Lua script aborted for max execution time after %lld milliseconds of running time.",elapsed); + lua_pushstring(lua,"Script aborted for max execution time."); + lua_error(lua); + } +} + void scriptingInit(void) { lua_State *lua = lua_open(); luaL_openlibs(lua); @@ -236,17 +248,17 @@ void hashScript(char *digest, char *script, size_t len) { } void luaReplyToRedisReply(redisClient *c, lua_State *lua) { - int t = lua_type(lua,1); + int t = lua_type(lua,-1); switch(t) { case LUA_TSTRING: - addReplyBulkCBuffer(c,(char*)lua_tostring(lua,1),lua_strlen(lua,1)); + addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1)); break; case LUA_TBOOLEAN: - addReply(c,lua_toboolean(lua,1) ? shared.cone : shared.czero); + addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.nullbulk); break; case LUA_TNUMBER: - addReplyLongLong(c,(long long)lua_tonumber(lua,1)); + addReplyLongLong(c,(long long)lua_tonumber(lua,-1)); break; case LUA_TTABLE: /* We need to check if it is an array, an error, or a status reply. @@ -282,17 +294,9 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) { if (t == LUA_TNIL) { lua_pop(lua,1); break; - } else if (t == LUA_TSTRING) { - size_t len; - char *s = (char*) lua_tolstring(lua,-1,&len); - - addReplyBulkCBuffer(c,s,len); - mbulklen++; - } else if (t == LUA_TNUMBER) { - addReplyLongLong(c,(long long)lua_tonumber(lua,-1)); - mbulklen++; } - lua_pop(lua,1); + luaReplyToRedisReply(c, lua); + mbulklen++; } setDeferredMultiBulkLength(c,replylen,mbulklen); } @@ -372,6 +376,17 @@ void evalCommand(redisClient *c) { /* Select the right DB in the context of the Lua client */ selectDb(server.lua_client,c->db->id); + /* Set an hook in order to be able to stop the script execution if it + * is running for too much time. + * We set the hook only if the time limit is enabled as the hook will + * make the Lua script execution slower. */ + if (server.lua_time_limit > 0) { + lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000); + server.lua_time_start = ustime()/1000; + } else { + lua_sethook(lua,luaMaskCountHook,0,0); + } + /* At this point whatever this script was never seen before or if it was * already defined, we can call it. We have zero arguments and expect * a single return value. */ @@ -380,8 +395,10 @@ void evalCommand(redisClient *c) { addReplyErrorFormat(c,"Error running script (call to %s): %s\n", funcname, lua_tostring(lua,-1)); lua_pop(lua,1); + lua_gc(lua,LUA_GCCOLLECT,0); return; } selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */ luaReplyToRedisReply(c,lua); + lua_gc(lua,LUA_GCSTEP,1); }