]> git.saurik.com Git - redis.git/blame - src/scripting.c
Merge pull request #544 from dvirsky/2.6
[redis.git] / src / scripting.c
CommitLineData
7585836e 1#include "redis.h"
2#include "sha1.h"
e108bab0 3#include "rand.h"
7585836e 4
5#include <lua.h>
6#include <lauxlib.h>
7#include <lualib.h>
7229d60d 8#include <ctype.h>
e108bab0 9#include <math.h>
7585836e 10
532e0f5d 11char *redisProtocolToLuaType_Int(lua_State *lua, char *reply);
12char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply);
13char *redisProtocolToLuaType_Status(lua_State *lua, char *reply);
3791000f 14char *redisProtocolToLuaType_Error(lua_State *lua, char *reply);
15char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply);
e108bab0 16int redis_math_random (lua_State *L);
17int redis_math_randomseed (lua_State *L);
2f2e6ad4 18void sha1hex(char *digest, char *script, size_t len);
532e0f5d 19
20/* Take a Redis reply in the Redis protocol format and convert it into a
21 * Lua type. Thanks to this function, and the introduction of not connected
22 * clients, it is trvial to implement the redis() lua function.
23 *
24 * Basically we take the arguments, execute the Redis command in the context
25 * of a non connected client, then take the generated reply and convert it
26 * into a suitable Lua type. With this trick the scripting feature does not
27 * need the introduction of a full Redis internals API. Basically the script
28 * is like a normal client that bypasses all the slow I/O paths.
29 *
30 * Note: in this function we do not do any sanity check as the reply is
548efd91 31 * generated by Redis directly. This allows us to go faster.
532e0f5d 32 * The reply string can be altered during the parsing as it is discared
33 * after the conversion is completed.
34 *
35 * Errors are returned as a table with a single 'err' field set to the
36 * error string.
37 */
38
39char *redisProtocolToLuaType(lua_State *lua, char* reply) {
40 char *p = reply;
41
42 switch(*p) {
43 case ':':
44 p = redisProtocolToLuaType_Int(lua,reply);
45 break;
46 case '$':
47 p = redisProtocolToLuaType_Bulk(lua,reply);
48 break;
49 case '+':
50 p = redisProtocolToLuaType_Status(lua,reply);
51 break;
3791000f 52 case '-':
53 p = redisProtocolToLuaType_Error(lua,reply);
54 break;
55 case '*':
56 p = redisProtocolToLuaType_MultiBulk(lua,reply);
57 break;
532e0f5d 58 }
59 return p;
60}
61
62char *redisProtocolToLuaType_Int(lua_State *lua, char *reply) {
63 char *p = strchr(reply+1,'\r');
64 long long value;
65
66 string2ll(reply+1,p-reply-1,&value);
67 lua_pushnumber(lua,(lua_Number)value);
68 return p+2;
69}
70
71char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply) {
72 char *p = strchr(reply+1,'\r');
73 long long bulklen;
74
75 string2ll(reply+1,p-reply-1,&bulklen);
379789cc 76 if (bulklen == -1) {
82c6b825 77 lua_pushboolean(lua,0);
532e0f5d 78 return p+2;
79 } else {
80 lua_pushlstring(lua,p+2,bulklen);
81 return p+2+bulklen+2;
82 }
83}
84
85char *redisProtocolToLuaType_Status(lua_State *lua, char *reply) {
86 char *p = strchr(reply+1,'\r');
87
0d916763 88 lua_newtable(lua);
89 lua_pushstring(lua,"ok");
532e0f5d 90 lua_pushlstring(lua,reply+1,p-reply-1);
0d916763 91 lua_settable(lua,-3);
532e0f5d 92 return p+2;
93}
94
3791000f 95char *redisProtocolToLuaType_Error(lua_State *lua, char *reply) {
96 char *p = strchr(reply+1,'\r');
97
98 lua_newtable(lua);
99 lua_pushstring(lua,"err");
100 lua_pushlstring(lua,reply+1,p-reply-1);
101 lua_settable(lua,-3);
102 return p+2;
103}
104
105char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply) {
106 char *p = strchr(reply+1,'\r');
107 long long mbulklen;
108 int j = 0;
109
3791000f 110 string2ll(reply+1,p-reply-1,&mbulklen);
111 p += 2;
112 if (mbulklen == -1) {
82c6b825 113 lua_pushboolean(lua,0);
3791000f 114 return p;
115 }
3791000f 116 lua_newtable(lua);
117 for (j = 0; j < mbulklen; j++) {
10a6da7a 118 lua_pushnumber(lua,j+1);
3791000f 119 p = redisProtocolToLuaType(lua,p);
120 lua_settable(lua,-3);
121 }
122 return p;
123}
124
379789cc 125void luaPushError(lua_State *lua, char *error) {
126 lua_newtable(lua);
127 lua_pushstring(lua,"err");
128 lua_pushstring(lua, error);
129 lua_settable(lua,-3);
130}
131
548efd91 132/* Sort the array currently in the stack. We do this to make the output
133 * of commands like KEYS or SMEMBERS something deterministic when called
134 * from Lua (to play well with AOf/replication).
135 *
136 * The array is sorted using table.sort itself, and assuming all the
137 * list elements are strings. */
138void luaSortArray(lua_State *lua) {
139 /* Initial Stack: array */
140 lua_getglobal(lua,"table");
141 lua_pushstring(lua,"sort");
142 lua_gettable(lua,-2); /* Stack: array, table, table.sort */
143 lua_pushvalue(lua,-3); /* Stack: array, table, table.sort, array */
2c861050 144 if (lua_pcall(lua,1,0,0)) {
145 /* Stack: array, table, error */
146
147 /* We are not interested in the error, we assume that the problem is
148 * that there are 'false' elements inside the array, so we try
149 * again with a slower function but able to handle this case, that
150 * is: table.sort(table, __redis__compare_helper) */
151 lua_pop(lua,1); /* Stack: array, table */
152 lua_pushstring(lua,"sort"); /* Stack: array, table, sort */
153 lua_gettable(lua,-2); /* Stack: array, table, table.sort */
154 lua_pushvalue(lua,-3); /* Stack: array, table, table.sort, array */
155 lua_getglobal(lua,"__redis__compare_helper");
156 /* Stack: array, table, table.sort, array, __redis__compare_helper */
157 lua_call(lua,2,0);
158 }
159 /* Stack: array (sorted), table */
548efd91 160 lua_pop(lua,1); /* Stack: array (sorted) */
161}
162
9ed32ba0 163int luaRedisGenericCommand(lua_State *lua, int raise_error) {
0f1d64ca 164 int j, argc = lua_gettop(lua);
165 struct redisCommand *cmd;
166 robj **argv;
167 redisClient *c = server.lua_client;
168 sds reply;
169
edfaa64f 170 /* Require at least one argument */
171 if (argc == 0) {
172 luaPushError(lua,
173 "Please specify at least one argument for redis.call()");
174 return 1;
175 }
176
532e0f5d 177 /* Build the arguments vector */
0f1d64ca 178 argv = zmalloc(sizeof(robj*)*argc);
379789cc 179 for (j = 0; j < argc; j++) {
180 if (!lua_isstring(lua,j+1)) break;
532e0f5d 181 argv[j] = createStringObject((char*)lua_tostring(lua,j+1),
182 lua_strlen(lua,j+1));
379789cc 183 }
184
185 /* Check if one of the arguments passed by the Lua script
186 * is not a string or an integer (lua_isstring() return true for
187 * integers as well). */
188 if (j != argc) {
189 j--;
190 while (j >= 0) {
191 decrRefCount(argv[j]);
192 j--;
193 }
194 zfree(argv);
195 luaPushError(lua,
196 "Lua redis() command arguments must be strings or integers");
197 return 1;
198 }
0f1d64ca 199
15ef6053 200 /* Setup our fake client for command execution */
201 c->argv = argv;
202 c->argc = argc;
203
0f1d64ca 204 /* Command lookup */
205 cmd = lookupCommand(argv[0]->ptr);
3791000f 206 if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||
207 (argc < -cmd->arity)))
208 {
3791000f 209 if (cmd)
379789cc 210 luaPushError(lua,
3791000f 211 "Wrong number of args calling Redis command From Lua script");
212 else
379789cc 213 luaPushError(lua,"Unknown Redis command called from Lua script");
15ef6053 214 goto cleanup;
0f1d64ca 215 }
532e0f5d 216
05406168 217 /* There are commands that are not allowed inside scripts. */
15ef6053 218 if (cmd->flags & REDIS_CMD_NOSCRIPT) {
219 luaPushError(lua, "This Redis command is not allowed from scripts");
220 goto cleanup;
221 }
222
05406168 223 /* Write commands are forbidden against read-only slaves, or if a
224 * command marked as non-deterministic was already called in the context
225 * of this script. */
226 if (cmd->flags & REDIS_CMD_WRITE) {
227 if (server.lua_random_dirty) {
228 luaPushError(lua,
229 "Write commands not allowed after non deterministic commands");
230 goto cleanup;
231 } else if (server.masterhost && server.repl_slave_ro &&
232 !(server.lua_caller->flags & REDIS_MASTER))
233 {
234 luaPushError(lua, shared.roslaveerr->ptr);
235 goto cleanup;
236 } else if (server.stop_writes_on_bgsave_err &&
237 server.saveparamslen > 0 &&
238 server.lastbgsave_status == REDIS_ERR)
239 {
240 luaPushError(lua, shared.bgsaveerr->ptr);
241 goto cleanup;
242 }
243 }
244
245 /* If we reached the memory limit configured via maxmemory, commands that
246 * could enlarge the memory usage are not allowed, but only if this is the
247 * first write in the context of this script, otherwise we can't stop
248 * in the middle. */
249 if (server.maxmemory && server.lua_write_dirty == 0 &&
250 (cmd->flags & REDIS_CMD_DENYOOM))
251 {
252 if (freeMemoryIfNeeded() == REDIS_ERR) {
253 luaPushError(lua, shared.oomerr->ptr);
254 goto cleanup;
255 }
9f772cc2 256 }
257
258 if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1;
4ab8695d 259 if (cmd->flags & REDIS_CMD_WRITE) server.lua_write_dirty = 1;
9f772cc2 260
15ef6053 261 /* Run the command */
ce8b772b 262 c->cmd = cmd;
263 call(c,REDIS_CALL_SLOWLOG | REDIS_CALL_STATS);
0f1d64ca 264
265 /* Convert the result of the Redis command into a suitable Lua type.
266 * The first thing we need is to create a single string from the client
267 * output buffers. */
268 reply = sdsempty();
269 if (c->bufpos) {
7156f43c 270 reply = sdscatlen(reply,c->buf,c->bufpos);
0f1d64ca 271 c->bufpos = 0;
272 }
273 while(listLength(c->reply)) {
274 robj *o = listNodeValue(listFirst(c->reply));
275
09ab5591 276 reply = sdscatlen(reply,o->ptr,sdslen(o->ptr));
0f1d64ca 277 listDelNode(c->reply,listFirst(c->reply));
278 }
9ed32ba0 279 if (raise_error && reply[0] != '-') raise_error = 0;
532e0f5d 280 redisProtocolToLuaType(lua,reply);
548efd91 281 /* Sort the output array if needed, assuming it is a non-null multi bulk
282 * reply as expected. */
283 if ((cmd->flags & REDIS_CMD_SORT_FOR_SCRIPT) &&
284 (reply[0] == '*' && reply[1] != '-')) {
2c861050 285 luaSortArray(lua);
548efd91 286 }
7156f43c 287 sdsfree(reply);
42a239b8 288 c->reply_bytes = 0;
0f1d64ca 289
15ef6053 290cleanup:
0f1d64ca 291 /* Clean up. Command code may have changed argv/argc so we use the
292 * argv/argc of the client instead of the local variables. */
293 for (j = 0; j < c->argc; j++)
294 decrRefCount(c->argv[j]);
295 zfree(c->argv);
296
9ed32ba0 297 if (raise_error) {
298 /* If we are here we should have an error in the stack, in the
299 * form of a table with an "err" field. Extract the string to
300 * return the plain error. */
301 lua_pushstring(lua,"err");
302 lua_gettable(lua,-2);
303 return lua_error(lua);
304 }
0f1d64ca 305 return 1;
306}
307
9ed32ba0 308int luaRedisCallCommand(lua_State *lua) {
309 return luaRedisGenericCommand(lua,1);
310}
311
312int luaRedisPCallCommand(lua_State *lua) {
313 return luaRedisGenericCommand(lua,0);
314}
315
2f2e6ad4
NF
316/* This adds redis.sha1hex(string) to Lua scripts using the same hashing
317 * function used for sha1ing lua scripts. */
318int luaRedisSha1hexCommand(lua_State *lua) {
319 int argc = lua_gettop(lua);
320 char digest[41];
321 size_t len;
322 char *s;
323
324 if (argc != 1) {
325 luaPushError(lua, "wrong number of arguments");
326 return 1;
327 }
328
329 s = (char*)lua_tolstring(lua,1,&len);
330 sha1hex(digest,s,len);
331 lua_pushstring(lua,digest);
332 return 1;
333}
334
f1466e11 335/* Returns a table with a single field 'field' set to the string value
336 * passed as argument. This helper function is handy when returning
337 * a Redis Protocol error or status reply from Lua:
338 *
339 * return redis.error_reply("ERR Some Error")
340 * return redis.status_reply("ERR Some Error")
341 */
342int luaRedisReturnSingleFieldTable(lua_State *lua, char *field) {
343 if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) {
344 luaPushError(lua, "wrong number or type of arguments");
345 return 1;
346 }
347
348 lua_newtable(lua);
349 lua_pushstring(lua, field);
350 lua_pushvalue(lua, -3);
351 lua_settable(lua, -3);
352 return 1;
353}
354
355int luaRedisErrorReplyCommand(lua_State *lua) {
356 return luaRedisReturnSingleFieldTable(lua,"err");
357}
358
359int luaRedisStatusReplyCommand(lua_State *lua) {
360 return luaRedisReturnSingleFieldTable(lua,"ok");
361}
362
288f811f 363int luaLogCommand(lua_State *lua) {
364 int j, argc = lua_gettop(lua);
365 int level;
366 sds log;
367
368 if (argc < 2) {
369 luaPushError(lua, "redis.log() requires two arguments or more.");
370 return 1;
371 } else if (!lua_isnumber(lua,-argc)) {
e927a246 372 luaPushError(lua, "First argument must be a number (log level).");
288f811f 373 return 1;
374 }
375 level = lua_tonumber(lua,-argc);
e927a246 376 if (level < REDIS_DEBUG || level > REDIS_WARNING) {
288f811f 377 luaPushError(lua, "Invalid debug level.");
378 return 1;
379 }
380
381 /* Glue together all the arguments */
382 log = sdsempty();
383 for (j = 1; j < argc; j++) {
384 size_t len;
385 char *s;
386
387 s = (char*)lua_tolstring(lua,(-argc)+j,&len);
388 if (s) {
389 if (j != 1) log = sdscatlen(log," ",1);
390 log = sdscatlen(log,s,len);
391 }
392 }
393 redisLogRaw(level,log);
394 sdsfree(log);
395 return 0;
396}
397
eeffcf38 398void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
399 long long elapsed;
400 REDIS_NOTUSED(ar);
115e3ff3 401 REDIS_NOTUSED(lua);
eeffcf38 402
eeffcf38 403 elapsed = (ustime()/1000) - server.lua_time_start;
115e3ff3 404 if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
4ab8695d 405 redisLog(REDIS_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed);
115e3ff3 406 server.lua_timedout = 1;
4ab8695d 407 /* Once the script timeouts we reenter the event loop to permit others
408 * to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason
409 * we need to mask the client executing the script from the event loop.
410 * If we don't do that the client may disconnect and could no longer be
411 * here when the EVAL command will return. */
412 aeDeleteFileEvent(server.el, server.lua_caller->fd, AE_READABLE);
eeffcf38 413 }
115e3ff3 414 if (server.lua_timedout)
415 aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
4ab8695d 416 if (server.lua_kill) {
417 redisLog(REDIS_WARNING,"Lua script killed by user with SCRIPT KILL.");
418 lua_pushstring(lua,"Script killed by user with SCRIPT KILL...");
419 lua_error(lua);
420 }
eeffcf38 421}
422
002d5626 423void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
424 lua_pushcfunction(lua, luafunc);
425 lua_pushstring(lua, libname);
426 lua_call(lua, 1, 0);
427}
428
15108778 429LUALIB_API int (luaopen_cjson) (lua_State *L);
2f75bbab 430LUALIB_API int (luaopen_struct) (lua_State *L);
63505e0b 431LUALIB_API int (luaopen_cmsgpack) (lua_State *L);
15108778 432
002d5626 433void luaLoadLibraries(lua_State *lua) {
434 luaLoadLib(lua, "", luaopen_base);
435 luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);
436 luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);
437 luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);
438 luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug);
2f75bbab 439 luaLoadLib(lua, "cjson", luaopen_cjson);
440 luaLoadLib(lua, "struct", luaopen_struct);
63505e0b 441 luaLoadLib(lua, "cmsgpack", luaopen_cmsgpack);
002d5626 442
443#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */
444 luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);
445 luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os);
446#endif
447}
448
537dafab 449/* Remove a functions that we don't want to expose to the Redis scripting
450 * environment. */
451void luaRemoveUnsupportedFunctions(lua_State *lua) {
452 lua_pushnil(lua);
453 lua_setglobal(lua,"loadfile");
454}
455
3e6a4463 456/* This function installs metamethods in the global table _G that prevent
457 * the creation of globals accidentally.
458 *
459 * It should be the last to be called in the scripting engine initialization
97cab309 460 * sequence, because it may interact with creation of globals. */
3e6a4463 461void scriptingEnableGlobalsProtection(lua_State *lua) {
462 char *s[32];
430602b2 463 sds code = sdsempty();
3e6a4463 464 int j = 0;
430602b2 465
3e6a4463 466 /* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
467 * Modified to be adapted to Redis. */
6255a5ae 468 s[j++]="local mt = {}\n";
3e6a4463 469 s[j++]="setmetatable(_G, mt)\n";
3e6a4463 470 s[j++]="mt.__newindex = function (t, n, v)\n";
d2906893 471 s[j++]=" if debug.getinfo(2) then\n";
3e6a4463 472 s[j++]=" local w = debug.getinfo(2, \"S\").what\n";
473 s[j++]=" if w ~= \"main\" and w ~= \"C\" then\n";
97cab309 474 s[j++]=" error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n";
3e6a4463 475 s[j++]=" end\n";
3e6a4463 476 s[j++]=" end\n";
477 s[j++]=" rawset(t, n, v)\n";
478 s[j++]="end\n";
479 s[j++]="mt.__index = function (t, n)\n";
d2906893 480 s[j++]=" if debug.getinfo(2) and debug.getinfo(2, \"S\").what ~= \"C\" then\n";
c9853f53 481 s[j++]=" error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n";
3e6a4463 482 s[j++]=" end\n";
483 s[j++]=" return rawget(t, n)\n";
484 s[j++]="end\n";
3e6a4463 485 s[j++]=NULL;
486
487 for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));
9a2dd1ef 488 luaL_loadbuffer(lua,code,sdslen(code),"@enable_strict_lua");
430602b2 489 lua_pcall(lua,0,0,0);
490 sdsfree(code);
491}
492
070e3945 493/* Initialize the scripting environment.
494 * It is possible to call this function to reset the scripting environment
495 * assuming that we call scriptingRelease() before.
496 * See scriptingReset() for more information. */
7585836e 497void scriptingInit(void) {
498 lua_State *lua = lua_open();
537dafab 499
002d5626 500 luaLoadLibraries(lua);
537dafab 501 luaRemoveUnsupportedFunctions(lua);
0f1d64ca 502
4dd444bb 503 /* Initialize a dictionary we use to map SHAs to scripts.
504 * This is useful for replication, as we need to replicate EVALSHA
505 * as EVAL, so we need to remember the associated script. */
506 server.lua_scripts = dictCreate(&dbDictType,NULL);
507
288f811f 508 /* Register the redis commands table and fields */
509 lua_newtable(lua);
510
511 /* redis.call */
512 lua_pushstring(lua,"call");
9ed32ba0 513 lua_pushcfunction(lua,luaRedisCallCommand);
514 lua_settable(lua,-3);
515
516 /* redis.pcall */
517 lua_pushstring(lua,"pcall");
518 lua_pushcfunction(lua,luaRedisPCallCommand);
288f811f 519 lua_settable(lua,-3);
520
521 /* redis.log and log levels. */
522 lua_pushstring(lua,"log");
523 lua_pushcfunction(lua,luaLogCommand);
524 lua_settable(lua,-3);
525
526 lua_pushstring(lua,"LOG_DEBUG");
527 lua_pushnumber(lua,REDIS_DEBUG);
528 lua_settable(lua,-3);
529
530 lua_pushstring(lua,"LOG_VERBOSE");
531 lua_pushnumber(lua,REDIS_VERBOSE);
532 lua_settable(lua,-3);
533
534 lua_pushstring(lua,"LOG_NOTICE");
535 lua_pushnumber(lua,REDIS_NOTICE);
536 lua_settable(lua,-3);
537
538 lua_pushstring(lua,"LOG_WARNING");
539 lua_pushnumber(lua,REDIS_WARNING);
540 lua_settable(lua,-3);
541
2f2e6ad4
NF
542 /* redis.sha1hex */
543 lua_pushstring(lua, "sha1hex");
544 lua_pushcfunction(lua, luaRedisSha1hexCommand);
545 lua_settable(lua, -3);
546
f1466e11 547 /* redis.error_reply and redis.status_reply */
548 lua_pushstring(lua, "error_reply");
549 lua_pushcfunction(lua, luaRedisErrorReplyCommand);
550 lua_settable(lua, -3);
551 lua_pushstring(lua, "status_reply");
552 lua_pushcfunction(lua, luaRedisStatusReplyCommand);
553 lua_settable(lua, -3);
554
288f811f 555 /* Finally set the table as 'redis' global var. */
00b7541b 556 lua_setglobal(lua,"redis");
0f1d64ca 557
e108bab0 558 /* Replace math.random and math.randomseed with our implementations. */
559 lua_getglobal(lua,"math");
560
561 lua_pushstring(lua,"random");
562 lua_pushcfunction(lua,redis_math_random);
563 lua_settable(lua,-3);
564
565 lua_pushstring(lua,"randomseed");
566 lua_pushcfunction(lua,redis_math_randomseed);
567 lua_settable(lua,-3);
568
569 lua_setglobal(lua,"math");
570
2c861050 571 /* Add a helper funciton that we use to sort the multi bulk output of non
572 * deterministic commands, when containing 'false' elements. */
573 {
574 char *compare_func = "function __redis__compare_helper(a,b)\n"
575 " if a == false then a = '' end\n"
576 " if b == false then b = '' end\n"
577 " return a<b\n"
578 "end\n";
9a2dd1ef 579 luaL_loadbuffer(lua,compare_func,strlen(compare_func),"@cmp_func_def");
2c861050 580 lua_pcall(lua,0,0,0);
581 }
582
0f1d64ca 583 /* Create the (non connected) client that we use to execute Redis commands
070e3945 584 * inside the Lua interpreter.
585 * Note: there is no need to create it again when this function is called
586 * by scriptingReset(). */
587 if (server.lua_client == NULL) {
588 server.lua_client = createClient(-1);
589 server.lua_client->flags |= REDIS_LUA_CLIENT;
590 }
0f1d64ca 591
430602b2 592 /* Lua beginners ofter don't use "local", this is likely to introduce
593 * subtle bugs in their code. To prevent problems we protect accesses
594 * to global variables. */
97cab309 595 scriptingEnableGlobalsProtection(lua);
430602b2 596
7585836e 597 server.lua = lua;
598}
599
070e3945 600/* Release resources related to Lua scripting.
601 * This function is used in order to reset the scripting environment. */
602void scriptingRelease(void) {
603 dictRelease(server.lua_scripts);
604 lua_close(server.lua);
605}
606
607void scriptingReset(void) {
608 scriptingRelease();
609 scriptingInit();
610}
611
2f2e6ad4
NF
612/* Perform the SHA1 of the input string. We use this both for hasing script
613 * bodies in order to obtain the Lua function name, and in the implementation
614 * of redis.sha1().
615 *
616 * 'digest' should point to a 41 bytes buffer: 40 for SHA1 converted into an
7585836e 617 * hexadecimal number, plus 1 byte for null term. */
2f2e6ad4 618void sha1hex(char *digest, char *script, size_t len) {
7585836e 619 SHA1_CTX ctx;
620 unsigned char hash[20];
621 char *cset = "0123456789abcdef";
622 int j;
623
624 SHA1Init(&ctx);
625 SHA1Update(&ctx,(unsigned char*)script,len);
626 SHA1Final(hash,&ctx);
627
628 for (j = 0; j < 20; j++) {
629 digest[j*2] = cset[((hash[j]&0xF0)>>4)];
630 digest[j*2+1] = cset[(hash[j]&0xF)];
631 }
632 digest[40] = '\0';
633}
634
635void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
82c6b825 636 int t = lua_type(lua,-1);
7585836e 637
638 switch(t) {
639 case LUA_TSTRING:
82c6b825 640 addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1));
7585836e 641 break;
642 case LUA_TBOOLEAN:
82c6b825 643 addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.nullbulk);
7585836e 644 break;
645 case LUA_TNUMBER:
82c6b825 646 addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
7585836e 647 break;
532e0f5d 648 case LUA_TTABLE:
151b606c 649 /* We need to check if it is an array, an error, or a status reply.
650 * Error are returned as a single element table with 'err' field.
651 * Status replies are returned as single elment table with 'ok' field */
532e0f5d 652 lua_pushstring(lua,"err");
653 lua_gettable(lua,-2);
654 t = lua_type(lua,-1);
655 if (t == LUA_TSTRING) {
3bb818df 656 sds err = sdsnew(lua_tostring(lua,-1));
657 sdsmapchars(err,"\r\n"," ",2);
658 addReplySds(c,sdscatprintf(sdsempty(),"-%s\r\n",err));
659 sdsfree(err);
0d916763 660 lua_pop(lua,2);
661 return;
662 }
e061d797 663
151b606c 664 lua_pop(lua,1);
0d916763 665 lua_pushstring(lua,"ok");
666 lua_gettable(lua,-2);
667 t = lua_type(lua,-1);
668 if (t == LUA_TSTRING) {
3bb818df 669 sds ok = sdsnew(lua_tostring(lua,-1));
670 sdsmapchars(ok,"\r\n"," ",2);
671 addReplySds(c,sdscatprintf(sdsempty(),"+%s\r\n",ok));
672 sdsfree(ok);
532e0f5d 673 lua_pop(lua,1);
674 } else {
675 void *replylen = addDeferredMultiBulkLength(c);
676 int j = 1, mbulklen = 0;
677
0d916763 678 lua_pop(lua,1); /* Discard the 'ok' field value we popped */
532e0f5d 679 while(1) {
680 lua_pushnumber(lua,j++);
681 lua_gettable(lua,-2);
682 t = lua_type(lua,-1);
683 if (t == LUA_TNIL) {
684 lua_pop(lua,1);
685 break;
532e0f5d 686 }
82c6b825 687 luaReplyToRedisReply(c, lua);
688 mbulklen++;
532e0f5d 689 }
690 setDeferredMultiBulkLength(c,replylen,mbulklen);
691 }
692 break;
7585836e 693 default:
694 addReply(c,shared.nullbulk);
695 }
696 lua_pop(lua,1);
697}
698
4ae5b5e1 699/* Set an array of Redis String Objects as a Lua array (table) stored into a
700 * global variable. */
701void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) {
702 int j;
703
704 lua_newtable(lua);
705 for (j = 0; j < elec; j++) {
706 lua_pushlstring(lua,(char*)elev[j]->ptr,sdslen(elev[j]->ptr));
707 lua_rawseti(lua,-2,j+1);
708 }
709 lua_setglobal(lua,var);
710}
711
a9b07ac4 712/* Define a lua function with the specified function name and body.
713 * The function name musts be a 2 characters long string, since all the
714 * functions we defined in the Lua context are in the form:
715 *
716 * f_<hex sha1 sum>
717 *
718 * On success REDIS_OK is returned, and nothing is left on the Lua stack.
719 * On error REDIS_ERR is returned and an appropriate error is set in the
720 * client context. */
721int luaCreateFunction(redisClient *c, lua_State *lua, char *funcname, robj *body) {
722 sds funcdef = sdsempty();
723
a9b07ac4 724 funcdef = sdscat(funcdef,"function ");
725 funcdef = sdscatlen(funcdef,funcname,42);
4d776dba 726 funcdef = sdscatlen(funcdef,"() ",3);
a9b07ac4 727 funcdef = sdscatlen(funcdef,body->ptr,sdslen(body->ptr));
4d776dba 728 funcdef = sdscatlen(funcdef," end",4);
a9b07ac4 729
9a2dd1ef 730 if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script")) {
a9b07ac4 731 addReplyErrorFormat(c,"Error compiling script (new function): %s\n",
732 lua_tostring(lua,-1));
733 lua_pop(lua,1);
734 sdsfree(funcdef);
735 return REDIS_ERR;
736 }
737 sdsfree(funcdef);
738 if (lua_pcall(lua,0,0,0)) {
739 addReplyErrorFormat(c,"Error running script (new function): %s\n",
740 lua_tostring(lua,-1));
741 lua_pop(lua,1);
742 return REDIS_ERR;
743 }
744
745 /* We also save a SHA1 -> Original script map in a dictionary
746 * so that we can replicate / write in the AOF all the
747 * EVALSHA commands as EVAL using the original script. */
748 {
749 int retval = dictAdd(server.lua_scripts,
750 sdsnewlen(funcname+2,40),body);
751 redisAssertWithInfo(c,NULL,retval == DICT_OK);
752 incrRefCount(body);
753 }
754 return REDIS_OK;
755}
756
7229d60d 757void evalGenericCommand(redisClient *c, int evalsha) {
7585836e 758 lua_State *lua = server.lua;
759 char funcname[43];
4ae5b5e1 760 long long numkeys;
77a75fde 761 int delhook = 0;
4ae5b5e1 762
e108bab0 763 /* We want the same PRNG sequence at every call so that our PRNG is
764 * not affected by external state. */
765 redisSrand48(0);
766
9f772cc2 767 /* We set this flag to zero to remember that so far no random command
768 * was called. This way we can allow the user to call commands like
769 * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
770 * is called (otherwise the replication and AOF would end with non
771 * deterministic sequences).
772 *
773 * Thanks to this flag we'll raise an error every time a write command
774 * is called after a random command was used. */
775 server.lua_random_dirty = 0;
4ab8695d 776 server.lua_write_dirty = 0;
9f772cc2 777
4ae5b5e1 778 /* Get the number of arguments that are keys */
779 if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)
780 return;
781 if (numkeys > (c->argc - 3)) {
782 addReplyError(c,"Number of keys can't be greater than number of args");
783 return;
784 }
7585836e 785
786 /* We obtain the script SHA1, then check if this function is already
787 * defined into the Lua state */
788 funcname[0] = 'f';
789 funcname[1] = '_';
7229d60d 790 if (!evalsha) {
791 /* Hash the code if this is an EVAL call */
2f2e6ad4 792 sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
7229d60d 793 } else {
794 /* We already have the SHA if it is a EVALSHA */
795 int j;
796 char *sha = c->argv[1]->ptr;
797
798 for (j = 0; j < 40; j++)
799 funcname[j+2] = tolower(sha[j]);
800 funcname[42] = '\0';
801 }
802
a9b07ac4 803 /* Try to lookup the Lua function */
7585836e 804 lua_getglobal(lua, funcname);
805 if (lua_isnil(lua,1)) {
e8c993f0 806 lua_pop(lua,1); /* remove the nil from the stack */
7229d60d 807 /* Function not defined... let's define it if we have the
808 * body of the funciton. If this is an EVALSHA call we can just
809 * return an error. */
810 if (evalsha) {
811 addReply(c, shared.noscripterr);
7229d60d 812 return;
813 }
a9b07ac4 814 if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) return;
815 /* Now the following is guaranteed to return non nil */
7585836e 816 lua_getglobal(lua, funcname);
a9b07ac4 817 redisAssert(!lua_isnil(lua,1));
7585836e 818 }
4ae5b5e1 819
820 /* Populate the argv and keys table accordingly to the arguments that
821 * EVAL received. */
822 luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys);
823 luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);
c2a7dd85 824
825 /* Select the right DB in the context of the Lua client */
826 selectDb(server.lua_client,c->db->id);
7585836e 827
da386cdf 828 /* Set an hook in order to be able to stop the script execution if it
829 * is running for too much time.
830 * We set the hook only if the time limit is enabled as the hook will
831 * make the Lua script execution slower. */
77a75fde 832 server.lua_caller = c;
833 server.lua_time_start = ustime()/1000;
834 server.lua_kill = 0;
115e3ff3 835 if (server.lua_time_limit > 0 && server.masterhost == NULL) {
da386cdf 836 lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
77a75fde 837 delhook = 1;
da386cdf 838 }
839
7585836e 840 /* At this point whatever this script was never seen before or if it was
841 * already defined, we can call it. We have zero arguments and expect
842 * a single return value. */
843 if (lua_pcall(lua,0,1,0)) {
77a75fde 844 if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
4ab8695d 845 if (server.lua_timedout) {
846 server.lua_timedout = 0;
847 /* Restore the readable handler that was unregistered when the
848 * script timeout was detected. */
849 aeCreateFileEvent(server.el,c->fd,AE_READABLE,
850 readQueryFromClient,c);
851 }
852 server.lua_caller = NULL;
c2a7dd85 853 selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
7585836e 854 addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
855 funcname, lua_tostring(lua,-1));
856 lua_pop(lua,1);
40531be0 857 lua_gc(lua,LUA_GCCOLLECT,0);
7585836e 858 return;
859 }
77a75fde 860 if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
115e3ff3 861 server.lua_timedout = 0;
4ab8695d 862 server.lua_caller = NULL;
c2a7dd85 863 selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
7585836e 864 luaReplyToRedisReply(c,lua);
40531be0 865 lua_gc(lua,LUA_GCSTEP,1);
4dd444bb 866
867 /* If we have slaves attached we want to replicate this command as
868 * EVAL instead of EVALSHA. We do this also in the AOF as currently there
869 * is no easy way to propagate a command in a different way in the AOF
870 * and in the replication link.
871 *
872 * IMPROVEMENT POSSIBLE:
873 * 1) Replicate this command as EVALSHA in the AOF.
874 * 2) Remember what slave already received a given script, and replicate
875 * the EVALSHA against this slaves when possible.
876 */
877 if (evalsha) {
878 robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);
879
eab0e26e 880 redisAssertWithInfo(c,NULL,script != NULL);
4dd444bb 881 rewriteClientCommandArgument(c,0,
882 resetRefCount(createStringObject("EVAL",4)));
883 rewriteClientCommandArgument(c,1,script);
884 }
7585836e 885}
7229d60d 886
887void evalCommand(redisClient *c) {
888 evalGenericCommand(c,0);
889}
890
891void evalShaCommand(redisClient *c) {
892 if (sdslen(c->argv[1]->ptr) != 40) {
893 /* We know that a match is not possible if the provided SHA is
894 * not the right length. So we return an error ASAP, this way
895 * evalGenericCommand() can be implemented without string length
896 * sanity check */
897 addReply(c, shared.noscripterr);
898 return;
899 }
900 evalGenericCommand(c,1);
901}
e108bab0 902
903/* We replace math.random() with our implementation that is not affected
904 * by specific libc random() implementations and will output the same sequence
905 * (for the same seed) in every arch. */
906
907/* The following implementation is the one shipped with Lua itself but with
908 * rand() replaced by redisLrand48(). */
909int redis_math_random (lua_State *L) {
910 /* the `%' avoids the (rare) case of r==1, and is needed also because on
911 some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
912 lua_Number r = (lua_Number)(redisLrand48()%REDIS_LRAND48_MAX) /
913 (lua_Number)REDIS_LRAND48_MAX;
914 switch (lua_gettop(L)) { /* check number of arguments */
915 case 0: { /* no arguments */
916 lua_pushnumber(L, r); /* Number between 0 and 1 */
917 break;
918 }
919 case 1: { /* only upper limit */
920 int u = luaL_checkint(L, 1);
921 luaL_argcheck(L, 1<=u, 1, "interval is empty");
922 lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
923 break;
924 }
925 case 2: { /* lower and upper limits */
926 int l = luaL_checkint(L, 1);
927 int u = luaL_checkint(L, 2);
928 luaL_argcheck(L, l<=u, 2, "interval is empty");
929 lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
930 break;
931 }
932 default: return luaL_error(L, "wrong number of arguments");
933 }
934 return 1;
935}
936
937int redis_math_randomseed (lua_State *L) {
938 redisSrand48(luaL_checkint(L, 1));
939 return 0;
940}
070e3945 941
942/* ---------------------------------------------------------------------------
943 * SCRIPT command for script environment introspection and control
944 * ------------------------------------------------------------------------- */
945
946void scriptCommand(redisClient *c) {
947 if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"flush")) {
948 scriptingReset();
949 addReply(c,shared.ok);
950 server.dirty++; /* Replicating this command is a good idea. */
951 } else if (c->argc >= 2 && !strcasecmp(c->argv[1]->ptr,"exists")) {
952 int j;
953
954 addReplyMultiBulkLen(c, c->argc-2);
955 for (j = 2; j < c->argc; j++) {
956 if (dictFind(server.lua_scripts,c->argv[j]->ptr))
957 addReply(c,shared.cone);
958 else
959 addReply(c,shared.czero);
960 }
a9b07ac4 961 } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,"load")) {
a9b07ac4 962 char funcname[43];
e8c993f0 963 sds sha;
a9b07ac4 964
965 funcname[0] = 'f';
966 funcname[1] = '_';
2f2e6ad4 967 sha1hex(funcname+2,c->argv[2]->ptr,sdslen(c->argv[2]->ptr));
e8c993f0 968 sha = sdsnewlen(funcname+2,40);
969 if (dictFind(server.lua_scripts,sha) == NULL) {
970 if (luaCreateFunction(c,server.lua,funcname,c->argv[2])
971 == REDIS_ERR) {
972 sdsfree(sha);
973 return;
974 }
975 }
e5abf6ef 976 addReplyBulkCBuffer(c,funcname+2,40);
e8c993f0 977 sdsfree(sha);
4ab8695d 978 } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"kill")) {
979 if (server.lua_caller == NULL) {
980 addReplyError(c,"No scripts in execution right now.");
981 } else if (server.lua_write_dirty) {
982 addReplyError(c, "Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in an hard way using the SHUTDOWN NOSAVE command.");
983 } else {
984 server.lua_kill = 1;
985 addReply(c,shared.ok);
986 }
070e3945 987 } else {
988 addReplyError(c, "Unknown SCRIPT subcommand or wrong # of args.");
989 }
990}