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