diff --git a/src/networking.c b/src/networking.c index 4a6a8afd8..b26c20595 100644 --- a/src/networking.c +++ b/src/networking.c @@ -14,14 +14,20 @@ redisClient *createClient(int fd) { redisClient *c = zmalloc(sizeof(redisClient)); c->bufpos = 0; - anetNonBlock(NULL,fd); - anetTcpNoDelay(NULL,fd); - if (aeCreateFileEvent(server.el,fd,AE_READABLE, - readQueryFromClient, c) == AE_ERR) - { - close(fd); - zfree(c); - return NULL; + /* passing -1 as fd it is possible to create a non connected client. + * This is useful since all the Redis commands needs to be executed + * in the context of a client. When commands are executed in other + * contexts (for instance a Lua script) we need a non connected client. */ + if (fd != -1) { + anetNonBlock(NULL,fd); + anetTcpNoDelay(NULL,fd); + if (aeCreateFileEvent(server.el,fd,AE_READABLE, + readQueryFromClient, c) == AE_ERR) + { + close(fd); + zfree(c); + return NULL; + } } selectDb(c,0); diff --git a/src/redis.h b/src/redis.h index 4848de95b..0071c0aa4 100644 --- a/src/redis.h +++ b/src/redis.h @@ -657,6 +657,7 @@ struct redisServer { clusterState cluster; /* Scripting */ lua_State *lua; + redisClient *lua_client; }; typedef struct pubsubPattern { diff --git a/src/scripting.c b/src/scripting.c index 42ba3bf95..9b957c92d 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -5,9 +5,67 @@ #include #include +int luaRedisCommand(lua_State *lua) { + int j, argc = lua_gettop(lua); + struct redisCommand *cmd; + robj **argv; + redisClient *c = server.lua_client; + sds reply; + + argv = zmalloc(sizeof(robj*)*argc); + for (j = 0; j < argc; j++) + argv[j] = createStringObject(lua_tostring(lua,j+1),lua_strlen(lua,j+1)); + + /* Command lookup */ + cmd = lookupCommand(argv[0]->ptr); + if (!cmd) { + zfree(argv); + lua_pushnil(lua); + lua_pushstring(lua,"Unknown Redis command called from Lua script"); + return 2; + } + /* Run the command in the context of a fake client */ + c->argv = argv; + c->argc = argc; + cmd->proc(c); + + /* Convert the result of the Redis command into a suitable Lua type. + * The first thing we need is to create a single string from the client + * output buffers. */ + reply = sdsempty(); + if (c->bufpos) { + reply = sdscatlen(reply,c->bufpos,c->buf); + c->bufpos = 0; + } + while(listLength(c->reply)) { + robj *o = listNodeValue(listFirst(c->reply)); + + sdscatlen(reply,o->ptr,sdslen(o->ptr)); + listDelNode(c->reply,listFirst(c->reply)); + } + lua_pushnumber(lua,1); + + /* 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++) + decrRefCount(c->argv[j]); + zfree(c->argv); + + return 1; +} + void scriptingInit(void) { lua_State *lua = lua_open(); luaL_openlibs(lua); + + /* Register the 'r' command */ + lua_pushcfunction(lua,luaRedisCommand); + lua_setglobal(lua,"r"); + + /* Create the (non connected) client that we use to execute Redis commands + * inside the Lua interpreter */ + server.lua_client = createClient(-1); + server.lua = lua; }