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