Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 10 commits
  • 7 files changed
  • 0 comments
  • 1 contributor
10  src/Makefile
@@ -6,12 +6,12 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
6 6
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
7 7
 OPTIMIZATION?=-O2
8 8
 ifeq ($(uname_S),SunOS)
9  
-  CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
10  
-  CCLINK?= -ldl -lnsl -lsocket -lm -lpthread
  9
+  CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6 `pkg-config --cflags lua`
  10
+  CCLINK?= -ldl -lnsl -lsocket -lm -lpthread `pkg-config --libs lua`
11 11
   DEBUG?= -g -ggdb 
12 12
 else
13  
-  CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF)
14  
-  CCLINK?= -lm -pthread
  13
+  CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF) `pkg-config --cflags lua`
  14
+  CCLINK?= -lm -pthread `pkg-config --libs lua`
15 15
   DEBUG?= -g -rdynamic -ggdb 
16 16
 endif
17 17
 
@@ -25,7 +25,7 @@ PREFIX= /usr/local
25 25
 INSTALL_BIN= $(PREFIX)/bin
26 26
 INSTALL= cp -p
27 27
 
28  
-OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o
  28
+OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o lua.o
29 29
 BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
30 30
 CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
31 31
 CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
147  src/lua.c
... ...
@@ -0,0 +1,147 @@
  1
+#include "redis.h"
  2
+#include "lua.h"
  3
+
  4
+#include <lua.h>
  5
+#include <lualib.h>
  6
+#include <lauxlib.h>
  7
+
  8
+// lua interpretter state
  9
+lua_State *luaState;
  10
+
  11
+// a fake client, used to capture command replies
  12
+redisClient *fakeClient;
  13
+
  14
+// wrapper function to allow lua to use our zalloc
  15
+static void *luaZalloc (void *ud, void *ptr, size_t osize, size_t nsize) {
  16
+    (void)ud;
  17
+    (void)osize;
  18
+    if (nsize == 0) {
  19
+        zfree(ptr);
  20
+        return NULL;
  21
+    }
  22
+    else
  23
+        return zrealloc(ptr, nsize);
  24
+}
  25
+
  26
+// last-chance lua error capture
  27
+static int luaPanic (lua_State *L) {
  28
+    (void)L;  /* to avoid warnings */
  29
+    redisPanic(sprintf("PANIC: unprotected error in call to Lua API (%s)\n",
  30
+                lua_tostring(L, -1)));
  31
+    return 0;
  32
+}
  33
+
  34
+// set up the lua interpretter, called when redis starts
  35
+void initLua(void) {
  36
+    // allocate and initialize just the essential parts
  37
+    // of our fake client
  38
+    fakeClient = zmalloc(sizeof(redisClient));
  39
+    selectDb(fakeClient, 0);
  40
+    fakeClient->flags = REDIS_LUA;
  41
+    fakeClient->reply = listCreate();
  42
+    listSetFreeMethod(fakeClient->reply,decrRefCount);
  43
+    listSetDupMethod(fakeClient->reply,dupClientReplyValue);
  44
+
  45
+    luaState = lua_newstate(luaZalloc, NULL);
  46
+    if (!luaState) {
  47
+        redisPanic("Unable to initialize Lua!");
  48
+    }
  49
+    lua_atpanic(luaState, &luaPanic);
  50
+    luaL_openlibs(luaState);
  51
+
  52
+    // provide lua script with a hook to call redis commands
  53
+    lua_pushcfunction(luaState, luaExecRedisCommand);
  54
+    lua_setglobal(luaState, "redis");
  55
+}
  56
+
  57
+void luaexecCommand(redisClient *c) {
  58
+    luaGenericExecCommand(c, c->argv[1]->ptr);
  59
+}
  60
+
  61
+void luaexeckeyCommand(redisClient *c) {
  62
+    robj *o;
  63
+
  64
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) {
  65
+        return;
  66
+    }
  67
+
  68
+    if (o->type != REDIS_STRING) {
  69
+        addReply(c,shared.wrongtypeerr);
  70
+        return;
  71
+    }
  72
+
  73
+    luaGenericExecCommand(c, o->ptr);
  74
+}
  75
+
  76
+// execute lua code
  77
+void luaGenericExecCommand(redisClient *c, const char *code) {
  78
+    int err;
  79
+
  80
+    // load the code into lua
  81
+    if ((err = luaL_loadstring(luaState, code))) {
  82
+        if (err == LUA_ERRSYNTAX) {
  83
+            addReply(c, shared.syntaxerr);
  84
+        } else if (err == LUA_ERRMEM) {
  85
+            addReply(c, createObject(REDIS_STRING,sdsnew("-ERR Memory allocation error in LUA script")));
  86
+        }
  87
+        return;
  88
+    }
  89
+
  90
+    // call the lua code, capturing output / error
  91
+    sds output;
  92
+    if ((err = lua_pcall(luaState, 0, 1, 0))) {
  93
+        output = sdsnew("-ERR Lua error: ");
  94
+        output = sdscat(output, lua_tostring(luaState, -1));
  95
+    } else {
  96
+        output = sdsnew(lua_tostring(luaState, -1));
  97
+    }
  98
+
  99
+    // send the lua output back as a single string
  100
+    // TODO handle multiple return values
  101
+    addReply(c,shared.plus);
  102
+    addReplySds(c, output);
  103
+    addReply(c,shared.crlf);
  104
+}
  105
+
  106
+// lua hook to execute redis commands
  107
+int luaExecRedisCommand(lua_State *L) {
  108
+    // find the redis command and check arity
  109
+    int nargs = lua_gettop(L);
  110
+    const char *cmdname = lua_tostring(L, 1);
  111
+    struct redisCommand *cmd = lookupCommandByCString(cmdname);
  112
+    if (nargs != cmd->arity) {
  113
+        lua_pushstring(L, "incorrect number of arguments");
  114
+        lua_error(L);
  115
+    }
  116
+
  117
+    // set up argc and argv as if they'd been read from a real client
  118
+    fakeClient->argc = nargs;
  119
+    fakeClient->argv = zmalloc(sizeof(robj) * nargs);
  120
+    for (int i = 0; i < nargs; i++) {
  121
+        const char *param = lua_tostring(L, i + 1);
  122
+        fakeClient->argv[i] = createStringObject(param, strlen(param));
  123
+    }
  124
+    lua_pop(L, nargs);
  125
+
  126
+    // execute the redis command
  127
+    call(fakeClient, cmd);
  128
+    for (int i = 0; i < nargs; i++) {
  129
+        decrRefCount(fakeClient->argv[i]);
  130
+    }
  131
+    zfree(fakeClient->argv);
  132
+    fakeClient->argv = NULL;
  133
+
  134
+    // read the response and return to lua
  135
+    int returnedValues = 0;
  136
+    listNode *node;
  137
+    while (listLength(fakeClient->reply)) {
  138
+        node = listFirst(fakeClient->reply);
  139
+        robj *o = listNodeValue(node);
  140
+        lua_pushstring(L, o->ptr);
  141
+        listDelNode(fakeClient->reply, node);
  142
+        returnedValues++;
  143
+    }
  144
+
  145
+    return returnedValues;
  146
+}
  147
+
11  src/lua.h
... ...
@@ -0,0 +1,11 @@
  1
+#ifndef __LUA_H
  2
+#define __LUA_H
  3
+
  4
+#include <lua.h>
  5
+#include "redis.h"
  6
+
  7
+void initLua(void);
  8
+void luaGenericExecCommand(redisClient *c, const char *);
  9
+int luaExecRedisCommand(lua_State *L);
  10
+
  11
+#endif
2  src/networking.c
@@ -60,6 +60,7 @@ redisClient *createClient(int fd) {
60 60
 /* Set the event loop to listen for write events on the client's socket.
61 61
  * Typically gets called every time a reply is built. */
62 62
 int _installWriteEvent(redisClient *c) {
  63
+    if (c->flags & REDIS_LUA) { return REDIS_OK; }
63 64
     /* When CLOSE_AFTER_REPLY is set, no more replies may be added! */
64 65
     redisAssert(!(c->flags & REDIS_CLOSE_AFTER_REPLY));
65 66
 
@@ -89,6 +90,7 @@ robj *dupLastObjectIfNeeded(list *reply) {
89 90
 }
90 91
 
91 92
 int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
  93
+    if (c->flags & REDIS_LUA) { return REDIS_ERR; }
92 94
     size_t available = sizeof(c->buf)-c->bufpos;
93 95
 
94 96
     /* If there already are entries in the reply list, we cannot
4  src/redis-benchmark.c
@@ -465,6 +465,10 @@ int main(int argc, char **argv) {
465 465
         benchmark("PING",cmd,len);
466 466
         free(cmd);
467 467
 
  468
+        len = redisFormatCommand(&cmd,"LUAEXEC redis('ping')");
  469
+        benchmark("PING (Lua)",cmd,len);
  470
+        free(cmd);
  471
+
468 472
         const char *argv[21];
469 473
         argv[0] = "MSET";
470 474
         for (i = 1; i < 21; i += 2) {
6  src/redis.c
@@ -52,6 +52,7 @@
52 52
 #include <math.h>
53 53
 #include <pthread.h>
54 54
 #include <sys/resource.h>
  55
+#include "lua.h"
55 56
 
56 57
 /* Our shared "common" objects */
57 58
 
@@ -187,7 +188,9 @@ struct redisCommand readonlyCommandTable[] = {
187 188
     {"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0},
188 189
     {"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
189 190
     {"watch",watchCommand,-2,0,NULL,0,0,0},
190  
-    {"unwatch",unwatchCommand,1,0,NULL,0,0,0}
  191
+    {"unwatch",unwatchCommand,1,0,NULL,0,0,0},
  192
+    {"luaexec",luaexecCommand,2,REDIS_CMD_FORCE_REPLICATION|REDIS_CMD_DENYOOM,NULL,0,0,0},
  193
+    {"luaexeckey",luaexeckeyCommand,2,REDIS_CMD_FORCE_REPLICATION|REDIS_CMD_DENYOOM,NULL,1,1,1}
191 194
 };
192 195
 
193 196
 /*============================ Utility functions ============================ */
@@ -1528,6 +1531,7 @@ int main(int argc, char **argv) {
1528 1531
     if (server.daemonize) daemonize();
1529 1532
     initServer();
1530 1533
     if (server.daemonize) createPidFile();
  1534
+    initLua();
1531 1535
     redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);
1532 1536
 #ifdef __linux__
1533 1537
     linuxOvercommitMemoryWarning();
3  src/redis.h
@@ -146,6 +146,7 @@
146 146
 #define REDIS_IO_WAIT 32    /* The client is waiting for Virtual Memory I/O */
147 147
 #define REDIS_DIRTY_CAS 64  /* Watched keys modified. EXEC will fail. */
148 148
 #define REDIS_CLOSE_AFTER_REPLY 128 /* Close after writing entire reply. */
  149
+#define REDIS_LUA 256
149 150
 
150 151
 /* Client request types */
151 152
 #define REDIS_REQ_INLINE 1
@@ -1011,6 +1012,8 @@ void punsubscribeCommand(redisClient *c);
1011 1012
 void publishCommand(redisClient *c);
1012 1013
 void watchCommand(redisClient *c);
1013 1014
 void unwatchCommand(redisClient *c);
  1015
+void luaexecCommand(redisClient *c);
  1016
+void luaexeckeyCommand(redisClient *c);
1014 1017
 
1015 1018
 #if defined(__GNUC__)
1016 1019
 void *calloc(size_t count, size_t size) __attribute__ ((deprecated));

No commit comments for this range

Something went wrong with that request. Please try again.