LCOV - code coverage report
Current view: directory - redis/src - scripting.c (source / functions) Found Hit Coverage
Test: redis.info Lines: 456 361 79.2 %
Date: 2012-04-04 Functions: 29 25 86.2 %
Colors: not hit hit

       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                 : }

Generated by: LCOV version 1.7