scripting.c 21.1 KB
Newer Older
1 2
#include "redis.h"
#include "sha1.h"
3
#include "rand.h"
4 5 6 7

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
A
antirez 已提交
8
#include <ctype.h>
9
#include <math.h>
10

11 12 13
char *redisProtocolToLuaType_Int(lua_State *lua, char *reply);
char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply);
char *redisProtocolToLuaType_Status(lua_State *lua, char *reply);
14 15
char *redisProtocolToLuaType_Error(lua_State *lua, char *reply);
char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply);
16 17
int redis_math_random (lua_State *L);
int redis_math_randomseed (lua_State *L);
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

/* Take a Redis reply in the Redis protocol format and convert it into a
 * Lua type. Thanks to this function, and the introduction of not connected
 * clients, it is trvial to implement the redis() lua function.
 *
 * Basically we take the arguments, execute the Redis command in the context
 * of a non connected client, then take the generated reply and convert it
 * into a suitable Lua type. With this trick the scripting feature does not
 * need the introduction of a full Redis internals API. Basically the script
 * is like a normal client that bypasses all the slow I/O paths.
 *
 * Note: in this function we do not do any sanity check as the reply is
 * generated by Redis directly. This allows use to go faster.
 * The reply string can be altered during the parsing as it is discared
 * after the conversion is completed.
 *
 * Errors are returned as a table with a single 'err' field set to the
 * error string.
 */

char *redisProtocolToLuaType(lua_State *lua, char* reply) {
    char *p = reply;

    switch(*p) {
    case ':':
        p = redisProtocolToLuaType_Int(lua,reply);
        break;
    case '$':
        p = redisProtocolToLuaType_Bulk(lua,reply);
        break;
    case '+':
        p = redisProtocolToLuaType_Status(lua,reply);
        break;
51 52 53 54 55 56
    case '-':
        p = redisProtocolToLuaType_Error(lua,reply);
        break;
    case '*':
        p = redisProtocolToLuaType_MultiBulk(lua,reply);
        break;
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    }
    return p;
}

char *redisProtocolToLuaType_Int(lua_State *lua, char *reply) {
    char *p = strchr(reply+1,'\r');
    long long value;

    string2ll(reply+1,p-reply-1,&value);
    lua_pushnumber(lua,(lua_Number)value);
    return p+2;
}

char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply) {
    char *p = strchr(reply+1,'\r');
    long long bulklen;

    string2ll(reply+1,p-reply-1,&bulklen);
75
    if (bulklen == -1) {
76
        lua_pushboolean(lua,0);
77 78 79 80 81 82 83 84 85 86
        return p+2;
    } else {
        lua_pushlstring(lua,p+2,bulklen);
        return p+2+bulklen+2;
    }
}

char *redisProtocolToLuaType_Status(lua_State *lua, char *reply) {
    char *p = strchr(reply+1,'\r');

87 88
    lua_newtable(lua);
    lua_pushstring(lua,"ok");
89
    lua_pushlstring(lua,reply+1,p-reply-1);
90
    lua_settable(lua,-3);
91 92 93
    return p+2;
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
char *redisProtocolToLuaType_Error(lua_State *lua, char *reply) {
    char *p = strchr(reply+1,'\r');

    lua_newtable(lua);
    lua_pushstring(lua,"err");
    lua_pushlstring(lua,reply+1,p-reply-1);
    lua_settable(lua,-3);
    return p+2;
}

char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply) {
    char *p = strchr(reply+1,'\r');
    long long mbulklen;
    int j = 0;

    string2ll(reply+1,p-reply-1,&mbulklen);
    p += 2;
    if (mbulklen == -1) {
112
        lua_pushboolean(lua,0);
113 114 115 116
        return p;
    }
    lua_newtable(lua);
    for (j = 0; j < mbulklen; j++) {
A
antirez 已提交
117
        lua_pushnumber(lua,j+1);
118 119 120 121 122 123
        p = redisProtocolToLuaType(lua,p);
        lua_settable(lua,-3);
    }
    return p;
}

124 125 126 127 128 129 130
void luaPushError(lua_State *lua, char *error) {
    lua_newtable(lua);
    lua_pushstring(lua,"err");
    lua_pushstring(lua, error);
    lua_settable(lua,-3);
}

131
int luaRedisGenericCommand(lua_State *lua, int raise_error) {
132 133 134 135 136 137
    int j, argc = lua_gettop(lua);
    struct redisCommand *cmd;
    robj **argv;
    redisClient *c = server.lua_client;
    sds reply;

138
    /* Build the arguments vector */
139
    argv = zmalloc(sizeof(robj*)*argc);
140 141
    for (j = 0; j < argc; j++) {
        if (!lua_isstring(lua,j+1)) break;
142 143
        argv[j] = createStringObject((char*)lua_tostring(lua,j+1),
                                     lua_strlen(lua,j+1));
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    }
    
    /* Check if one of the arguments passed by the Lua script
     * is not a string or an integer (lua_isstring() return true for
     * integers as well). */
    if (j != argc) {
        j--;
        while (j >= 0) {
            decrRefCount(argv[j]);
            j--;
        }
        zfree(argv);
        luaPushError(lua,
            "Lua redis() command arguments must be strings or integers");
        return 1;
    }
160

161 162 163 164
    /* Setup our fake client for command execution */
    c->argv = argv;
    c->argc = argc;

165 166
    /* Command lookup */
    cmd = lookupCommand(argv[0]->ptr);
167 168 169 170
    if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||
                   (argc < -cmd->arity)))
    {
        if (cmd)
171
            luaPushError(lua,
172 173
                "Wrong number of args calling Redis command From Lua script");
        else
174
            luaPushError(lua,"Unknown Redis command called from Lua script");
175
        goto cleanup;
176
    }
177

178 179 180 181 182
    if (cmd->flags & REDIS_CMD_NOSCRIPT) {
        luaPushError(lua, "This Redis command is not allowed from scripts");
        goto cleanup;
    }

183 184 185 186 187 188 189 190
    if (cmd->flags & REDIS_CMD_WRITE && server.lua_random_dirty) {
        luaPushError(lua,
            "Write commands not allowed after non deterministic commands");
        goto cleanup;
    }

    if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1;

191
    /* Run the command */
192 193 194 195 196 197 198
    cmd->proc(c);

    /* Convert the result of the Redis command into a suitable Lua type.
     * The first thing we need is to create a single string from the client
     * output buffers. */
    reply = sdsempty();
    if (c->bufpos) {
199
        reply = sdscatlen(reply,c->buf,c->bufpos);
200 201 202 203 204
        c->bufpos = 0;
    }
    while(listLength(c->reply)) {
        robj *o = listNodeValue(listFirst(c->reply));

205
        reply = sdscatlen(reply,o->ptr,sdslen(o->ptr));
206 207
        listDelNode(c->reply,listFirst(c->reply));
    }
208
    if (raise_error && reply[0] != '-') raise_error = 0;
209
    redisProtocolToLuaType(lua,reply);
210
    sdsfree(reply);
211

212
cleanup:
213 214 215 216 217 218
    /* Clean up. Command code may have changed argv/argc so we use the
     * argv/argc of the client instead of the local variables. */
    for (j = 0; j < c->argc; j++)
        decrRefCount(c->argv[j]);
    zfree(c->argv);

219 220 221 222 223 224 225 226
    if (raise_error) {
        /* If we are here we should have an error in the stack, in the
         * form of a table with an "err" field. Extract the string to
         * return the plain error. */
        lua_pushstring(lua,"err");
        lua_gettable(lua,-2);
        return lua_error(lua);
    }
227 228 229
    return 1;
}

230 231 232 233 234 235 236 237
int luaRedisCallCommand(lua_State *lua) {
    return luaRedisGenericCommand(lua,1);
}

int luaRedisPCallCommand(lua_State *lua) {
    return luaRedisGenericCommand(lua,0);
}

238 239 240 241 242 243 244 245 246
int luaLogCommand(lua_State *lua) {
    int j, argc = lua_gettop(lua);
    int level;
    sds log;

    if (argc < 2) {
        luaPushError(lua, "redis.log() requires two arguments or more.");
        return 1;
    } else if (!lua_isnumber(lua,-argc)) {
247
        luaPushError(lua, "First argument must be a number (log level).");
248 249 250
        return 1;
    }
    level = lua_tonumber(lua,-argc);
251
    if (level < REDIS_DEBUG || level > REDIS_WARNING) {
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
        luaPushError(lua, "Invalid debug level.");
        return 1;
    }

    /* Glue together all the arguments */
    log = sdsempty();
    for (j = 1; j < argc; j++) {
        size_t len;
        char *s;

        s = (char*)lua_tolstring(lua,(-argc)+j,&len);
        if (s) {
            if (j != 1) log = sdscatlen(log," ",1);
            log = sdscatlen(log,s,len);
        }
    }
    redisLogRaw(level,log);
    sdsfree(log);
    return 0;
}

A
antirez 已提交
273 274 275 276 277 278
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
    long long elapsed;
    REDIS_NOTUSED(ar);

    elapsed = (ustime()/1000) - server.lua_time_start;
    if (elapsed >= server.lua_time_limit) {
279 280
        redisLog(REDIS_NOTICE,"Lua script aborted for max execution time after %lld milliseconds of running time.",elapsed);
        lua_pushstring(lua,"Script aborted for max execution time.");
A
antirez 已提交
281 282 283 284
        lua_error(lua);
    }
}

285 286 287 288 289 290
void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
  lua_pushcfunction(lua, luafunc);
  lua_pushstring(lua, libname);
  lua_call(lua, 1, 0);
}

291 292
LUALIB_API int (luaopen_cjson) (lua_State *L);

293 294 295 296 297 298
void luaLoadLibraries(lua_State *lua) {
    luaLoadLib(lua, "", luaopen_base);
    luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);
    luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);
    luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);
    luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug); 
299
    luaLoadLib(lua, "cjson", luaopen_cjson); 
300 301 302 303 304 305 306

#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */
    luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);
    luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os);
#endif
}

307 308
void scriptingInit(void) {
    lua_State *lua = lua_open();
309
    luaLoadLibraries(lua);
310

311 312 313 314 315
    /* Initialize a dictionary we use to map SHAs to scripts.
     * This is useful for replication, as we need to replicate EVALSHA
     * as EVAL, so we need to remember the associated script. */
    server.lua_scripts = dictCreate(&dbDictType,NULL);

316 317 318 319 320
    /* Register the redis commands table and fields */
    lua_newtable(lua);

    /* redis.call */
    lua_pushstring(lua,"call");
321 322 323 324 325 326
    lua_pushcfunction(lua,luaRedisCallCommand);
    lua_settable(lua,-3);

    /* redis.pcall */
    lua_pushstring(lua,"pcall");
    lua_pushcfunction(lua,luaRedisPCallCommand);
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
    lua_settable(lua,-3);

    /* redis.log and log levels. */
    lua_pushstring(lua,"log");
    lua_pushcfunction(lua,luaLogCommand);
    lua_settable(lua,-3);

    lua_pushstring(lua,"LOG_DEBUG");
    lua_pushnumber(lua,REDIS_DEBUG);
    lua_settable(lua,-3);

    lua_pushstring(lua,"LOG_VERBOSE");
    lua_pushnumber(lua,REDIS_VERBOSE);
    lua_settable(lua,-3);

    lua_pushstring(lua,"LOG_NOTICE");
    lua_pushnumber(lua,REDIS_NOTICE);
    lua_settable(lua,-3);

    lua_pushstring(lua,"LOG_WARNING");
    lua_pushnumber(lua,REDIS_WARNING);
    lua_settable(lua,-3);

    /* Finally set the table as 'redis' global var. */
351
    lua_setglobal(lua,"redis");
352

353 354 355 356 357 358 359 360 361 362 363 364 365
    /* Replace math.random and math.randomseed with our implementations. */
    lua_getglobal(lua,"math");

    lua_pushstring(lua,"random");
    lua_pushcfunction(lua,redis_math_random);
    lua_settable(lua,-3);

    lua_pushstring(lua,"randomseed");
    lua_pushcfunction(lua,redis_math_randomseed);
    lua_settable(lua,-3);

    lua_setglobal(lua,"math");

366 367 368
    /* Create the (non connected) client that we use to execute Redis commands
     * inside the Lua interpreter */
    server.lua_client = createClient(-1);
369
    server.lua_client->flags |= REDIS_LUA_CLIENT;
370

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
    server.lua = lua;
}

/* Hash the scripit into a SHA1 digest. We use this as Lua function name.
 * Digest should point to a 41 bytes buffer: 40 for SHA1 converted into an
 * hexadecimal number, plus 1 byte for null term. */
void hashScript(char *digest, char *script, size_t len) {
    SHA1_CTX ctx;
    unsigned char hash[20];
    char *cset = "0123456789abcdef";
    int j;

    SHA1Init(&ctx);
    SHA1Update(&ctx,(unsigned char*)script,len);
    SHA1Final(hash,&ctx);

    for (j = 0; j < 20; j++) {
        digest[j*2] = cset[((hash[j]&0xF0)>>4)];
        digest[j*2+1] = cset[(hash[j]&0xF)];
    }
    digest[40] = '\0';
}

void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
395
    int t = lua_type(lua,-1);
396 397 398

    switch(t) {
    case LUA_TSTRING:
399
        addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1));
400 401
        break;
    case LUA_TBOOLEAN:
402
        addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.nullbulk);
403 404
        break;
    case LUA_TNUMBER:
405
        addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
406
        break;
407
    case LUA_TTABLE:
408 409 410
        /* We need to check if it is an array, an error, or a status reply.
         * Error are returned as a single element table with 'err' field.
         * Status replies are returned as single elment table with 'ok' field */
411 412 413 414
        lua_pushstring(lua,"err");
        lua_gettable(lua,-2);
        t = lua_type(lua,-1);
        if (t == LUA_TSTRING) {
415 416 417 418
            sds err = sdsnew(lua_tostring(lua,-1));
            sdsmapchars(err,"\r\n","  ",2);
            addReplySds(c,sdscatprintf(sdsempty(),"-%s\r\n",err));
            sdsfree(err);
419 420 421 422 423 424 425 426 427
            lua_pop(lua,2);
            return;
        }

        lua_pop(lua,1);
        lua_pushstring(lua,"ok");
        lua_gettable(lua,-2);
        t = lua_type(lua,-1);
        if (t == LUA_TSTRING) {
428 429 430 431
            sds ok = sdsnew(lua_tostring(lua,-1));
            sdsmapchars(ok,"\r\n","  ",2);
            addReplySds(c,sdscatprintf(sdsempty(),"+%s\r\n",ok));
            sdsfree(ok);
432 433 434 435 436
            lua_pop(lua,1);
        } else {
            void *replylen = addDeferredMultiBulkLength(c);
            int j = 1, mbulklen = 0;

437
            lua_pop(lua,1); /* Discard the 'ok' field value we popped */
438 439 440 441 442 443 444 445
            while(1) {
                lua_pushnumber(lua,j++);
                lua_gettable(lua,-2);
                t = lua_type(lua,-1);
                if (t == LUA_TNIL) {
                    lua_pop(lua,1);
                    break;
                }
446 447
                luaReplyToRedisReply(c, lua);
                mbulklen++;
448 449 450 451
            }
            setDeferredMultiBulkLength(c,replylen,mbulklen);
        }
        break;
452 453 454 455 456 457
    default:
        addReply(c,shared.nullbulk);
    }
    lua_pop(lua,1);
}

458 459 460 461 462 463 464 465 466 467 468 469 470
/* Set an array of Redis String Objects as a Lua array (table) stored into a
 * global variable. */
void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) {
    int j;

    lua_newtable(lua);
    for (j = 0; j < elec; j++) {
        lua_pushlstring(lua,(char*)elev[j]->ptr,sdslen(elev[j]->ptr));
        lua_rawseti(lua,-2,j+1);
    }
    lua_setglobal(lua,var);
}

A
antirez 已提交
471
void evalGenericCommand(redisClient *c, int evalsha) {
472 473
    lua_State *lua = server.lua;
    char funcname[43];
474 475
    long long numkeys;

476 477 478 479
    /* We want the same PRNG sequence at every call so that our PRNG is
     * not affected by external state. */
    redisSrand48(0);

480 481 482 483 484 485 486 487 488 489
    /* We set this flag to zero to remember that so far no random command
     * was called. This way we can allow the user to call commands like
     * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
     * is called (otherwise the replication and AOF would end with non
     * deterministic sequences).
     *
     * Thanks to this flag we'll raise an error every time a write command
     * is called after a random command was used. */
    server.lua_random_dirty = 0;

490 491 492 493 494 495 496
    /* Get the number of arguments that are keys */
    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)
        return;
    if (numkeys > (c->argc - 3)) {
        addReplyError(c,"Number of keys can't be greater than number of args");
        return;
    }
497 498 499 500 501

    /* We obtain the script SHA1, then check if this function is already
     * defined into the Lua state */
    funcname[0] = 'f';
    funcname[1] = '_';
A
antirez 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514
    if (!evalsha) {
        /* Hash the code if this is an EVAL call */
        hashScript(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
    } else {
        /* We already have the SHA if it is a EVALSHA */
        int j;
        char *sha = c->argv[1]->ptr;

        for (j = 0; j < 40; j++)
            funcname[j+2] = tolower(sha[j]);
        funcname[42] = '\0';
    }

515 516
    lua_getglobal(lua, funcname);
    if (lua_isnil(lua,1)) {
A
antirez 已提交
517 518 519 520 521 522 523 524 525 526 527
        sds funcdef;
      
        /* Function not defined... let's define it if we have the
         * body of the funciton. If this is an EVALSHA call we can just
         * return an error. */
        if (evalsha) {
            addReply(c, shared.noscripterr);
            lua_pop(lua,1); /* remove the nil from the stack */
            return;
        }
        funcdef = sdsempty();
528 529 530 531 532 533 534

        lua_pop(lua,1); /* remove the nil from the stack */
        funcdef = sdscat(funcdef,"function ");
        funcdef = sdscatlen(funcdef,funcname,42);
        funcdef = sdscatlen(funcdef," ()\n",4);
        funcdef = sdscatlen(funcdef,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
        funcdef = sdscatlen(funcdef,"\nend\n",5);
535
        /* printf("Defining:\n%s\n",funcdef); */
536 537 538 539 540

        if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"func definition")) {
            addReplyErrorFormat(c,"Error compiling script (new function): %s\n",
                lua_tostring(lua,-1));
            lua_pop(lua,1);
541
            sdsfree(funcdef);
542 543
            return;
        }
544
        sdsfree(funcdef);
545 546 547 548 549 550 551
        if (lua_pcall(lua,0,0,0)) {
            addReplyErrorFormat(c,"Error running script (new function): %s\n",
                lua_tostring(lua,-1));
            lua_pop(lua,1);
            return;
        }
        lua_getglobal(lua, funcname);
552 553 554 555 556 557 558

        /* We also save a SHA1 -> Original script map in a dictionary
         * so that we can replicate / write in the AOF all the
         * EVALSHA commands as EVAL using the original script. */
        {
            int retval = dictAdd(server.lua_scripts,
                                 sdsnewlen(funcname+2,40),c->argv[1]);
559
            redisAssertWithInfo(c,NULL,retval == DICT_OK);
560 561
            incrRefCount(c->argv[1]);
        }
562
    }
563 564 565 566 567

    /* Populate the argv and keys table accordingly to the arguments that
     * EVAL received. */
    luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys);
    luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);
568 569 570

    /* Select the right DB in the context of the Lua client */
    selectDb(server.lua_client,c->db->id);
571
    
572 573 574 575 576 577 578 579 580 581 582
    /* Set an hook in order to be able to stop the script execution if it
     * is running for too much time.
     * We set the hook only if the time limit is enabled as the hook will
     * make the Lua script execution slower. */
    if (server.lua_time_limit > 0) {
        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        server.lua_time_start = ustime()/1000;
    } else {
        lua_sethook(lua,luaMaskCountHook,0,0);
    }

583 584 585 586
    /* At this point whatever this script was never seen before or if it was
     * already defined, we can call it. We have zero arguments and expect
     * a single return value. */
    if (lua_pcall(lua,0,1,0)) {
587
        selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
588 589 590
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
            funcname, lua_tostring(lua,-1));
        lua_pop(lua,1);
591
        lua_gc(lua,LUA_GCCOLLECT,0);
592 593
        return;
    }
594
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
595
    luaReplyToRedisReply(c,lua);
596
    lua_gc(lua,LUA_GCSTEP,1);
597 598 599 600 601 602 603 604 605 606 607 608 609 610

    /* If we have slaves attached we want to replicate this command as
     * EVAL instead of EVALSHA. We do this also in the AOF as currently there
     * is no easy way to propagate a command in a different way in the AOF
     * and in the replication link.
     *
     * IMPROVEMENT POSSIBLE:
     * 1) Replicate this command as EVALSHA in the AOF.
     * 2) Remember what slave already received a given script, and replicate
     *    the EVALSHA against this slaves when possible.
     */
    if (evalsha) {
        robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);

611
        redisAssertWithInfo(c,NULL,script != NULL);
612 613 614 615
        rewriteClientCommandArgument(c,0,
            resetRefCount(createStringObject("EVAL",4)));
        rewriteClientCommandArgument(c,1,script);
    }
616
}
A
antirez 已提交
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632

void evalCommand(redisClient *c) {
    evalGenericCommand(c,0);
}

void evalShaCommand(redisClient *c) {
    if (sdslen(c->argv[1]->ptr) != 40) {
        /* We know that a match is not possible if the provided SHA is
         * not the right length. So we return an error ASAP, this way
         * evalGenericCommand() can be implemented without string length
         * sanity check */
        addReply(c, shared.noscripterr);
        return;
    }
    evalGenericCommand(c,1);
}
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671

/* We replace math.random() with our implementation that is not affected
 * by specific libc random() implementations and will output the same sequence
 * (for the same seed) in every arch. */

/* The following implementation is the one shipped with Lua itself but with
 * rand() replaced by redisLrand48(). */
int redis_math_random (lua_State *L) {
  /* the `%' avoids the (rare) case of r==1, and is needed also because on
     some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
  lua_Number r = (lua_Number)(redisLrand48()%REDIS_LRAND48_MAX) /
                                (lua_Number)REDIS_LRAND48_MAX;
  switch (lua_gettop(L)) {  /* check number of arguments */
    case 0: {  /* no arguments */
      lua_pushnumber(L, r);  /* Number between 0 and 1 */
      break;
    }
    case 1: {  /* only upper limit */
      int u = luaL_checkint(L, 1);
      luaL_argcheck(L, 1<=u, 1, "interval is empty");
      lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and `u' */
      break;
    }
    case 2: {  /* lower and upper limits */
      int l = luaL_checkint(L, 1);
      int u = luaL_checkint(L, 2);
      luaL_argcheck(L, l<=u, 2, "interval is empty");
      lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */
      break;
    }
    default: return luaL_error(L, "wrong number of arguments");
  }
  return 1;
}

int redis_math_randomseed (lua_State *L) {
  redisSrand48(luaL_checkint(L, 1));
  return 0;
}