Exemplo n.º 1
0
/* Define a lua function with the specified function name and body.
 * The function name musts be a 2 characters long string, since all the
 * functions we defined in the Lua context are in the form:
 *
 *   f_<hex sha1 sum>
 *
 * On success REDIS_OK is returned, and nothing is left on the Lua stack.
 * On error REDIS_ERR is returned and an appropriate error is set in the
 * client context. */
int luaCreateFunction(redisClient *c, lua_State *lua, char *funcname, robj *body) {
    sds funcdef = sdsempty();

    funcdef = sdscat(funcdef,"function ");
    funcdef = sdscatlen(funcdef,funcname,42);
    funcdef = sdscatlen(funcdef,"() ",3);
    funcdef = sdscatlen(funcdef,body->ptr,sdslen(body->ptr));
    funcdef = sdscatlen(funcdef," end",4);

    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script")) {
        addReplyErrorFormat(c,"Error compiling script (new function): %s\n",
            lua_tostring(lua,-1));
        lua_pop(lua,1);
        sdsfree(funcdef);
        return REDIS_ERR;
    }
    sdsfree(funcdef);
    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 REDIS_ERR;
    }

    /* 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),body);
        redisAssertWithInfo(c,NULL,retval == DICT_OK);
        incrRefCount(body);
    }
    return REDIS_OK;
}
Exemplo n.º 2
0
int  processCommand(ugClient *c) {
    if (!strcasecmp(c->argv[0]->ptr,"quit")) {
        addReply(c,shared.ok);
        c->flags |= UG_CLOSE_AFTER_REPLY;
        return UGERR;
    }
    if (c->authenticated  == 0 && strcasecmp(c->argv[0]->ptr, "auth") != 0) {
        addReplyError(c,"NOAUTH Authentication required. ");
        return UGOK;
    }
    c->cmd = lookupCommand((sds)c->argv[0]->ptr)  ;
    if (!c->cmd) {
        addReplyErrorFormat(c,"unknown command '%s'",
                            (char *)c->argv[0]->ptr);
        return UGOK;
    }  else if (c->cmd->params != -1 && c->argc-1 != c->cmd->params) {
        addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
                            c->cmd->name);
        return UGOK;
    }
    call(c, 0);
    /* must has reply */
    ugAssert(strlen(c->buf) || listLength(c->reply));
    //if (addReply(c, ) == UGERR) {
    //    c->flags |= UG_CLOSE_AFTER_REPLY;
    //    return UGERR;
    //}


    return UGOK;
}
Exemplo n.º 3
0
/* Define a lua function with the specified function name and body.
 *
 * 根据给定函数名和代码体(body),创建 Lua 函数。
 *
 * The function name musts be a 2 characters long string, since all the
 * functions we defined in the Lua context are in the form:
 *
 * 所有函数名称的长度都必须大于 2 ,因为我们使用以下格式来创建函数名:
 *
 *   f_<hex sha1 sum>
 *
 * On success REDIS_OK is returned, and nothing is left on the Lua stack.
 * 创建成功返回 REDIS_OK ,并清除 Lua 栈中的所有资料。
 *
 * On error REDIS_ERR is returned and an appropriate error is set in the
 * client context.
 */
int luaCreateFunction(redisClient *c, lua_State *lua, char *funcname, robj *body)
{
    sds funcdef = sdsempty();

    // 函数定义
    funcdef = sdscat(funcdef,"function ");
    funcdef = sdscatlen(funcdef,funcname,42);
    funcdef = sdscatlen(funcdef,"() ",3);
    funcdef = sdscatlen(funcdef,body->ptr,sdslen(body->ptr));
    funcdef = sdscatlen(funcdef," end",4);

    // 将函数定义载入到 Lua 中(并编译),但不执行该定义
    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script"))
    {

        // 如果编译出错,那么返回错误
        addReplyErrorFormat(c,"Error compiling script (new function): %s\n",
                            lua_tostring(lua,-1));
        lua_pop(lua,1);
        sdsfree(funcdef);
        return REDIS_ERR;
    }
    sdsfree(funcdef);

    // 定义函数
    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 REDIS_ERR;
    }

    /* 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.
     *
     * 创建一个从 SHA1 映射到原始脚本的字典,
     * 从而使得脚本可以用于复制,以及写入到 AOF 文件
     */
    {
        int retval = dictAdd(server.lua_scripts,
                             // SHA1 值,不包括前缀 f_
                             sdsnewlen(funcname+2,40),
                             // 代码体
                             body);
        redisAssertWithInfo(c,NULL,retval == DICT_OK);
        incrRefCount(body);
    }
    return REDIS_OK;
}
Exemplo n.º 4
0
/* REPLCONF <option> <value> <option> <value> ...
 * This command is used by a slave in order to configure the replication
 * process before starting it with the SYNC command.
 *
 * Currently the only use of this command is to communicate to the master
 * what is the listening port of the Slave redis instance, so that the
 * master can accurately list slaves and their listening ports in
 * the INFO output.
 *
 * In the future the same command can be used in order to configure
 * the replication to initiate an incremental replication instead of a
 * full resync. */
void replconfCommand(redisClient *c) {
    int j;

    if ((c->argc % 2) == 0) {
        /* Number of arguments must be odd to make sure that every
         * option has a corresponding value. */
        addReply(c,shared.syntaxerr);
        return;
    }

    /* Process every option-value pair. */
    for (j = 1; j < c->argc; j+=2) {
        if (!strcasecmp(c->argv[j]->ptr,"listening-port")) {
            long port;

            if ((getLongFromObjectOrReply(c,c->argv[j+1],
                    &port,NULL) != REDIS_OK))
                return;
            c->slave_listening_port = port;
        } else {
            addReplyErrorFormat(c,"Unrecognized REPLCONF option: %s",
                (char*)c->argv[j]->ptr);
            return;
        }
    }
    addReply(c,shared.ok);
}
Exemplo n.º 5
0
void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
    long long milliseconds = 0; /* initialized to avoid any harmness warning */

    if (expire) {
        if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
            return;
        if (milliseconds <= 0) {
            addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
            return;
        }
        if (unit == UNIT_SECONDS) milliseconds *= 1000;
    }

    if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
        (flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL))
    {
        addReply(c, abort_reply ? abort_reply : shared.nullbulk);
        return;
    }
    setKey(c->db,key,val);
    server.dirty++;
    if (expire) setExpire(c->db,key,mstime()+milliseconds);
    notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id);
    if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
        "expire",key,c->db->id);
    addReply(c, ok_reply ? ok_reply : shared.ok);
}
Exemplo n.º 6
0
int do_delete_event(struct redisClient *c,sds funcname)
{

    redisSrand48(0);
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;
    lua_getglobal(server.lua,(char *)funcname);



    if (lua_isnil(server.lua,1)) {
        addReplyError(c,"no funcname triggle_scipts in lua");
        return 0;
    }
    luaTriggleSetGlobalArray(server.lua,"KEYS",c->argv+1,c->argc-1);

    redisLog(REDIS_NOTICE,"stack: %d",lua_gettop(server.lua));


#ifdef BRIDGE_DEBUG
    for(int i=0;i<c->argc-1;i++){
        redisLog(REDIS_NOTICE,"%s",(c->argv+1)[i]->ptr);
    }
#endif


    selectDb(server.lua_client,c->db->id);


    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;

    if (server.lua_time_limit > 0) {
        lua_sethook(server.lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        server.lua_time_start = ustime()/1000;
    } else {
        lua_sethook(server.lua,luaMaskCountHook,0,0);
    }

    if (lua_pcall(server.lua,0,1,0)) {
        selectDb(c,server.lua_client->db->id); 
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
                (char*)funcname, lua_tostring(server.lua,-1));
        lua_pop(server.lua,1);
        lua_gc(server.lua,LUA_GCCOLLECT,0);
        return -1;
    }
    selectDb(c,server.lua_client->db->id); 
    // luaReplyToRedisReply(c,server.lua);
    server.lua_timedout = 0;
    server.lua_caller = NULL;
    lua_gc(server.lua,LUA_GCSTEP,1);

    //for slaves
    //

    return 0; 
}
Exemplo n.º 7
0
/* *
 * slotsinfo [start] [count]
 * */
void
slotsinfoCommand(redisClient *c) {
    int slots_slot[HASH_SLOTS_SIZE];
    int slots_size[HASH_SLOTS_SIZE];
    int n = 0, beg = 0, end = HASH_SLOTS_SIZE;
    if (c->argc >= 2) {
        if (parse_slot(c, c->argv[1], &beg) != 0) {
            return;
        }
    }
    if (c->argc >= 3) {
        int v;
        if (parse_int(c, c->argv[2], &v) != 0) {
            return;
        }
        if (v < 0) {
            addReplyErrorFormat(c, "invalid slot count = %d", v);
            return;
        }
        if (beg + v < end) {
            end = beg + v;
        }
    }
    if (c->argc >= 4) {
        addReplyErrorFormat(c, "wrong number of arguments for 'slotsinfo' command");
        return;
    }
    int i;
    for (i = beg; i < end; i ++) {
        int s = dictSize(c->db->hash_slots[i]);
        if (s == 0) {
            continue;
        }
        slots_slot[n] = i;
        slots_size[n] = s;
        n ++;
    }
    addReplyMultiBulkLen(c, n);
    for (i = 0; i < n; i ++) {
        addReplyMultiBulkLen(c, 2);
        addReplyLongLong(c, slots_slot[i]);
        addReplyLongLong(c, slots_size[i]);
    }
}
Exemplo n.º 8
0
/* LATENCY command implementations.
 *
 * LATENCY SAMPLES: return time-latency samples for the specified event.
 * LATENCY LATEST: return the latest latency for all the events classes.
 * LATENCY DOCTOR: returns an human readable analysis of instance latency.
 * LATENCY GRAPH: provide an ASCII graph of the latency of the specified event.
 */
void latencyCommand(client *c) {
    struct latencyTimeSeries *ts;

    if (!strcasecmp(c->argv[1]->ptr,"history") && c->argc == 3) {
        /* LATENCY HISTORY <event> */
        ts = dictFetchValue(server.latency_events,c->argv[2]->ptr);
        if (ts == NULL) {
            addReplyMultiBulkLen(c,0);
        } else {
            latencyCommandReplyWithSamples(c,ts);
        }
    } else if (!strcasecmp(c->argv[1]->ptr,"graph") && c->argc == 3) {
        /* LATENCY GRAPH <event> */
        sds graph;
        dictEntry *de;
        char *event;

        de = dictFind(server.latency_events,c->argv[2]->ptr);
        if (de == NULL) goto nodataerr;
        ts = dictGetVal(de);
        event = dictGetKey(de);

        graph = latencyCommandGenSparkeline(event,ts);
        addReplyBulkCString(c,graph);
        sdsfree(graph);
    } else if (!strcasecmp(c->argv[1]->ptr,"latest") && c->argc == 2) {
        /* LATENCY LATEST */
        latencyCommandReplyWithLatestEvents(c);
    } else if (!strcasecmp(c->argv[1]->ptr,"doctor") && c->argc == 2) {
        /* LATENCY DOCTOR */
        sds report = createLatencyReport();

        addReplyBulkCBuffer(c,report,sdslen(report));
        sdsfree(report);
    } else if (!strcasecmp(c->argv[1]->ptr,"reset") && c->argc >= 2) {
        /* LATENCY RESET */
        if (c->argc == 2) {
            addReplyLongLong(c,latencyResetEvent(NULL));
        } else {
            int j, resets = 0;

            for (j = 2; j < c->argc; j++)
                resets += latencyResetEvent(c->argv[j]->ptr);
            addReplyLongLong(c,resets);
        }
    } else {
        addReply(c,shared.syntaxerr);
    }
    return;

nodataerr:
    /* Common error when the user asks for an event we have no latency
     * information about. */
    addReplyErrorFormat(c,
        "No samples available for event '%s'", (char*) c->argv[2]->ptr);
}
Exemplo n.º 9
0
/* PUBSUB command for Pub/Sub introspection. */
void pubsubCommand(redisClient *c) {

	// 处理PUBSUB CHANNELS [pattern]命令
    if (!strcasecmp(c->argv[1]->ptr,"channels") &&
        (c->argc == 2 || c->argc ==3))
    {
        /* PUBSUB CHANNELS [<pattern>] */
        // 获取pattern参数,如果没有则为NULL
        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;
        dictIterator *di = dictGetIterator(server.pubsub_channels);
        dictEntry *de;
        long mblen = 0;
        void *replylen;

        replylen = addDeferredMultiBulkLength(c);
        // 遍历server.pubsub_channels字典
        while((de = dictNext(di)) != NULL) {
        	// 取出当前频道channel
            robj *cobj = dictGetKey(de);
            sds channel = cobj->ptr;

            // 如果没有给定pattern参数,则打印出所有频道
            // 如果给定pattern参数,则打印出与pattern参数相匹配的频道
            if (!pat || stringmatchlen(pat, sdslen(pat),
                                       channel, sdslen(channel),0))
            {
                addReplyBulk(c,cobj);
                mblen++;
            }
        }
        dictReleaseIterator(di);
        setDeferredMultiBulkLength(c,replylen,mblen);
    } 
    // 处理PUBSUB NUMSUB [Channel_1 ... Channel_N]命令
    else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {
        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
        int j;

        addReplyMultiBulkLen(c,(c->argc-2)*2);
        for (j = 2; j < c->argc; j++) {
            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);

            addReplyBulk(c,c->argv[j]);
            addReplyLongLong(c,l ? listLength(l) : 0);
        }
    } 
    // 处理PUBSUB NUMPA命令
    else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
        /* PUBSUB NUMPAT */
        addReplyLongLong(c,listLength(server.pubsub_patterns));
    } else {
        addReplyErrorFormat(c,
            "Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
            (char*)c->argv[1]->ptr);
    }
}
Exemplo n.º 10
0
//TODO webserver_mode,              webserver_index_function,
//     webserver_whitelist_address, webserver_whitelist_netmask,
//     sqlslaveof
int DXDB_configSetCommand(cli *c, robj *o) {
    if        (!strcasecmp(c->argv[2]->ptr,"luacronfunc")) {
        if (server.alc.LuaCronFunc) zfree(server.alc.LuaCronFunc);
        server.alc.LuaCronFunc = zstrdup(o->ptr); return 0;
    } else if (!strcasecmp(c->argv[2]->ptr, "lua_output_start")) {
        if (server.alc.OutputLuaFunc_Start) {
            zfree(server.alc.OutputLuaFunc_Start);
        }
        server.alc.OutputLuaFunc_Start = zstrdup(o->ptr); return 0;
    } else if (!strcasecmp(c->argv[2]->ptr, "lua_output_cnames")) {
        if (server.alc.OutputLuaFunc_Cnames) {
            zfree(server.alc.OutputLuaFunc_Cnames);
        }
        server.alc.OutputLuaFunc_Cnames = zstrdup(o->ptr); return 0;
    } else if (!strcasecmp(c->argv[2]->ptr, "lua_output_row")) {
        if (server.alc.OutputLuaFunc_Row) zfree(server.alc.OutputLuaFunc_Row);
        server.alc.OutputLuaFunc_Row = zstrdup(o->ptr); return 0;
    } else if (!strcasecmp(c->argv[2]->ptr, "rest_api_mode")) {
        int yn = yesnotoi(o->ptr);
        if (yn == -1) goto badfmt;
        server.alc.RestAPIMode = yn ? 1 : -1; return 0;
    } else if (!strcasecmp(c->argv[2]->ptr, "outputmode")) {
        if        (!strcasecmp(o->ptr, "embedded")) {
            server.alc.OutputMode = OUTPUT_EMBEDDED;
        } else if (!strcasecmp(o->ptr, "pure_redis")) {
            server.alc.OutputMode = OUTPUT_PURE_REDIS;
        } else if (!strcasecmp(o->ptr, "normal")) {
            server.alc.OutputMode = OUTPUT_NORMAL;
        } else if (!strcasecmp(o->ptr, "lua")) {
            if (!server.alc.OutputLuaFunc_Start ||
                !server.alc.OutputLuaFunc_Cnames ||
                !server.alc.OutputLuaFunc_Row) {
                sds err = sdsnew("-ERR: OUTPUTMODE lua requires "         \
                                 "[lua_output_start, lua_output_cnames, " \
                                 "lua_output_row]\r\n");
                addReplySds(c, err); decrRefCount(o); return -1;
            }
            server.alc.OutputMode = OUTPUT_LUA;
        } else {
            addReplySds(c,sdscatprintf(sdsempty(),
               "-ERR OUTPUTMODE: [EMBEDDED|PURE_REDIS|LUA|NORMAL] not: %s\r\n",
                (char *)o->ptr));
            decrRefCount(o);
            return -1;
        }
        return 0;
    }
    return 1;

badfmt: /* Bad format errors */
    addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
            (char*)o->ptr,
            (char*)c->argv[2]->ptr);
    return -1;

}
Exemplo n.º 11
0
Arquivo: json.c Projeto: Wouter33/krmt
/* Don't let people use keys starting with a valid container box value */
bool validateKeyFormatAndReply(redisClient *c, sds key) {
    if (isValidBoxKeyPrefix(key[0])) {
        addReplyErrorFormat(
            c, "Invalid first character in key: [%c] (binary [%s])", key[0],
            bb(key[0]));
        return false;
    }

    return true;
}
Exemplo n.º 12
0
void debugCommand(client *c) {
    if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
        *((char*)-1) = 'x';
    } else if (!strcasecmp(c->argv[1]->ptr,"oom")) {
        void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */
        zfree(ptr);
        addReply(c,shared.ok);
    } else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
        if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
        serverAssertWithInfo(c,c->argv[0],1 == 2);
    } else if (!strcasecmp(c->argv[1]->ptr,"flushall")) {
        flushServerData();
        addReply(c,shared.ok);
    } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
        flushServerData();
        if (loadAppendOnlyFile(server.aof_filename) != C_OK) {
            addReply(c,shared.err);
            return;
        }
        serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF");
        addReply(c,shared.ok);
    } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
        double dtime = strtod(c->argv[2]->ptr,NULL);
        long long utime = dtime*1000000;
        struct timespec tv;

        tv.tv_sec = utime / 1000000;
        tv.tv_nsec = (utime % 1000000) * 1000;
        nanosleep(&tv, NULL);
        addReply(c,shared.ok);
    } else if (!strcasecmp(c->argv[1]->ptr,"error") && c->argc == 3) {
        sds errstr = sdsnewlen("-",1);

        errstr = sdscatsds(errstr,c->argv[2]->ptr);
        errstr = sdsmapchars(errstr,"\n\r","  ",2); /* no newlines in errors. */
        errstr = sdscatlen(errstr,"\r\n",2);
        addReplySds(c,errstr);
    } else if (!strcasecmp(c->argv[1]->ptr,"structsize") && c->argc == 2) {
        sds sizes = sdsempty();
        sizes = sdscatprintf(sizes,"bits:%d ",(sizeof(void*) == 8)?64:32);
        sizes = sdscatprintf(sizes,"job:%d ", (int)sizeof(job));
        sizes = sdscatprintf(sizes,"queue:%d ", (int)sizeof(queue));
        sizes = sdscatprintf(sizes,"robj:%d ",(int)sizeof(robj));
        sizes = sdscatprintf(sizes,"dictentry:%d ",(int)sizeof(dictEntry));
        sizes = sdscatprintf(sizes,"sdshdr5:%d ",(int)sizeof(struct sdshdr5));
        sizes = sdscatprintf(sizes,"sdshdr8:%d ",(int)sizeof(struct sdshdr8));
        sizes = sdscatprintf(sizes,"sdshdr16:%d ",(int)sizeof(struct sdshdr16));
        sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32));
        sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
        addReplyBulkSds(c,sizes);
    } else {
        addReplyErrorFormat(c, "Unknown DEBUG subcommand or wrong number of arguments for '%s'",
                            (char*)c->argv[1]->ptr);
    }
}
Exemplo n.º 13
0
static int
parse_slot(redisClient *c, robj *obj, int *p) {
    int v;
    if (parse_int(c, obj, &v) != 0) {
        return -1;
    }
    if (v < 0 || v >= HASH_SLOTS_SIZE) {
        addReplyErrorFormat(c, "invalid slot number = %d", v);
        return -1;
    }
    *p = v;
    return 0;
}
Exemplo n.º 14
0
static int
parse_timeout(redisClient *c, robj *obj, int *p) {
    int v;
    if (parse_int(c, obj, &v) != 0) {
        return -1;
    }
    if (v < 0) {
        addReplyErrorFormat(c, "invalid timeout = %d", v);
        return -1;
    }
    *p = (v == 0) ? 100 : v;
    return 0;
}
Exemplo n.º 15
0
/* *
 * slotsdel slot1 [slot2 ...]
 * */
void
slotsdelCommand(redisClient *c) {
    int slots_slot[HASH_SLOTS_SIZE];
    int n = 0;
    if (c->argc <= 1) {
        addReplyErrorFormat(c, "wrong number of arguments for 'slotsdel' command");
        return;
    }
    int i;
    for (i = 1; i < c->argc; i ++) {
        int slot;
        if (parse_slot(c, c->argv[i], &slot) != 0) {
            return;
        }
        slots_slot[n] = slot;
        n ++;
    }
    for (i = 0; i < n; i ++) {
        dict *d = c->db->hash_slots[slots_slot[i]];
        int s = dictSize(d);
        if (s == 0) {
            continue;
        }
        list *l = listCreate();
        listSetFreeMethod(l, decrRefCountVoid);
        unsigned long cursor = 0;
        do {
            cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l);
        } while (cursor != 0);
        while (1) {
            listNode *head = listFirst(l);
            if (head == NULL) {
                break;
            }
            robj *key = listNodeValue(head);
            robj *keys[] = {key};
            slotsremove(c, keys, 1, 0);
            listDelNode(l, head);
        }
        listRelease(l);
    }
    addReplyMultiBulkLen(c, n);
    for (i = 0; i < n; i ++) {
        int n = slots_slot[i];
        int s = dictSize(c->db->hash_slots[n]);
        addReplyMultiBulkLen(c, 2);
        addReplyLongLong(c, n);
        addReplyLongLong(c, s);
    }
}
Exemplo n.º 16
0
/*
 * PUBSUB 命令, 内省命令
 */
void pubsubCommand(redisClient *c) {
    if (!strcasecmp(c->argv[1]->ptr,"channels") &&
        (c->argc == 2 || c->argc ==3))              //列出当前活跃的频道,每个频道至少有一个订阅者,订阅模式的客户端不计算在内
    {
        /* PUBSUB CHANNELS [<pattern>] */
        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;
        dictIterator *di = dictGetIterator(server.pubsub_channels);
        dictEntry *de;
        long mblen = 0;
        void *replylen;

        replylen = addDeferredMultiBulkLength(c);
        while((de = dictNext(di)) != NULL) {
            robj *cobj = dictGetKey(de);
            sds channel = cobj->ptr;

            if (!pat || stringmatchlen(pat, sdslen(pat),
                                       channel, sdslen(channel),0))
            {
                addReplyBulk(c,cobj);
                mblen++;
            }
        }
        dictReleaseIterator(di);
        setDeferredMultiBulkLength(c,replylen,mblen);
    } else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {     //返回给定频道的订阅者数量,订阅模式的客户端不计算在内
        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
        int j;

        addReplyMultiBulkLen(c,(c->argc-2)*2);
        for (j = 2; j < c->argc; j++) {
            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);

            addReplyBulk(c,c->argv[j]);
            addReplyLongLong(c,l ? listLength(l) : 0);
        }
    } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {     //返回订阅模式的数量
        /* PUBSUB NUMPAT */
        addReplyLongLong(c,listLength(server.pubsub_patterns));
    } else {
        addReplyErrorFormat(c,
            "Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
            (char*)c->argv[1]->ptr);
    }
}
Exemplo n.º 17
0
/* COMMAND <subcommand> <args> */
void commandCommand(client *c) {
    dictIterator *di;
    dictEntry *de;

    if (c->argc == 1) {
        addReplyMultiBulkLen(c, dictSize(server.commands));
        di = dictGetIterator(server.commands);
        while ((de = dictNext(di)) != NULL) {
            addReplyCommand(c, dictGetVal(de));
        }
        dictReleaseIterator(di);
    } else if (!strcasecmp(c->argv[1]->ptr, "info")) {
        int i;
        addReplyMultiBulkLen(c, c->argc-2);
        for (i = 2; i < c->argc; i++) {
            addReplyCommand(c, dictFetchValue(server.commands, c->argv[i]->ptr));
        }
    } else if (!strcasecmp(c->argv[1]->ptr, "count") && c->argc == 2) {
        addReplyLongLong(c, dictSize(server.commands));
    } else if (!strcasecmp(c->argv[1]->ptr,"getkeys") && c->argc >= 3) {
        struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);
        int *keys, numkeys, j;

        if (!cmd) {
            addReplyErrorFormat(c,"Invalid command specified");
            return;
        } else if ((cmd->arity > 0 && cmd->arity != c->argc-2) ||
                   ((c->argc-2) < -cmd->arity))
        {
            addReplyError(c,"Invalid number of arguments specified for command");
            return;
        }

        keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys);
        addReplyMultiBulkLen(c,numkeys);
        for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);
        getKeysFreeResult(keys);
    } else {
        addReplyError(c, "Unknown subcommand or wrong number of arguments.");
        return;
    }
}
Exemplo n.º 18
0
void getCommand(client *c) {
    counter *cntr;

    cntr = counterLookup(c->argv[1]->ptr);
    if (cntr == NULL) {
        if (c->argc == 2) {
            addReplyString(c,OBJ_SHARED_0STR,sizeof(OBJ_SHARED_0STR)-1);
        } else if (!strcasecmp(c->argv[2]->ptr,"state")) {
            addReplyMultiBulkLen(c,2);
            addReplyString(c,OBJ_SHARED_0STR,sizeof(OBJ_SHARED_0STR)-1);
            if (server.cluster->failing_nodes_count > 0) {
                addReplyString(c, OBJ_SHARED_INCONSISTENT, sizeof(OBJ_SHARED_INCONSISTENT)-1);
            } else {
                addReplyString(c, OBJ_SHARED_CONSISTENT, sizeof(OBJ_SHARED_CONSISTENT)-1);
            }
        }
        return;
    }

    /* Do we need to recalculate the cached response? */
    if (cntr->rlen == 0) {
        counterCacheResponse(cntr);
    }

    if (c->argc == 2) {
        addReplyString(c,cntr->rbuf,cntr->rlen);
    } else if (!strcasecmp(c->argv[2]->ptr,"state")) {
        addReplyMultiBulkLen(c,2);
        addReplyString(c,cntr->rbuf,cntr->rlen);
        if (server.cluster->failing_nodes_count > 0) {
            addReplyString(c, OBJ_SHARED_INCONSISTENT, sizeof(OBJ_SHARED_INCONSISTENT)-1);
        } else {
            addReplyString(c, OBJ_SHARED_CONSISTENT, sizeof(OBJ_SHARED_CONSISTENT)-1);
        }
    } else {
        addReplyErrorFormat(c, "Unknown GET option '%s'",
            (char*)c->argv[2]->ptr);
    }
}
Exemplo n.º 19
0
/* The PING command. It works in a different way if the client is in
 * in Pub/Sub mode. */
void pingCommand(client *c) {
    /* The command takes zero or one arguments. */
    if (c->argc > 2) {
        addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
            c->cmd->name);
        return;
    }

    if (c->flags & CLIENT_PUBSUB) {
        addReply(c,shared.mbulkhdr[2]);
        addReplyBulkCBuffer(c,"pong",4);
        if (c->argc == 1)
            addReplyBulkCBuffer(c,"",0);
        else
            addReplyBulk(c,c->argv[1]);
    } else {
        if (c->argc == 1)
            addReply(c,shared.pong);
        else
            addReplyBulk(c,c->argv[1]);
    }
}
Exemplo n.º 20
0
static int
slotsmgrt_get_socket(redisClient *c, sds host, sds port, int timeout) {
    sds name = sdsempty();
    name = sdscatlen(name, host, sdslen(host));
    name = sdscatlen(name, ":", 1);
    name = sdscatlen(name, port, sdslen(port));

    slotsmgrt_sockfd *pfd = dictFetchValue(server.slotsmgrt_cached_sockfds, name);
    if (pfd != NULL) {
        sdsfree(name);
        pfd->lasttime = server.unixtime;
        return pfd->fd;
    }
    
    int fd = anetTcpNonBlockConnect(server.neterr, host, atoi(port));
    if (fd == -1) {
        redisLog(REDIS_WARNING, "slotsmgrt: connect to target %s:%s, error = '%s'",
                host, port, server.neterr);
        sdsfree(name);
        addReplyErrorFormat(c,"Can't connect to target node: %s", server.neterr);
        return -1;
    }
    anetEnableTcpNoDelay(server.neterr, fd);
    if ((aeWait(fd, AE_WRITABLE, timeout) & AE_WRITABLE) == 0) {
        redisLog(REDIS_WARNING, "slotsmgrt: connect to target %s:%s, aewait error = '%s'",
                host, port, server.neterr);
        sdsfree(name);
        close(fd);
        addReplySds(c, sdsnew("-IOERR error or timeout connecting to the client\r\n"));
        return -1;
    }
    redisLog(REDIS_WARNING, "slotsmgrt: connect to target %s:%s", host, port);

    pfd = zmalloc(sizeof(*pfd));
    pfd->fd = fd;
    pfd->lasttime = server.unixtime;
    dictAdd(server.slotsmgrt_cached_sockfds, name, pfd);
    return fd;
}
Exemplo n.º 21
0
int  processLineBuffer(ugClient *c) {
    char *newline = strstr(c->querybuf, "\r\n");
    int argc, j;
    sds *argv;
    size_t querylen;

    /* Nothing to do without a \r\n */
    if (newline == NULL) {
        if (sdslen(c->querybuf) > UG_INLINE_MAX_SIZE) {
            addReplyErrorFormat(c, "Protocol error: too big inline request");
            setProtocolError(c,0);
        }
        return UGERR;
    }

    /* Split the input buffer up to the \r\n */
    querylen = newline-(c->querybuf);
    argv = sdssplitlen(c->querybuf,(int)querylen," ",1,&argc);

    /* Leave data after the first line of the query in the buffer */
    c->querybuf = sdsrange(c->querybuf,(int)(querylen+2),-1);

    /* Setup argv array on client structure */
    if (c->argv) zfree(c->argv);
    c->argv = zmalloc(sizeof(robj *)*argc);

    /* Create redis objects for all arguments. */
    for (c->argc = 0, j = 0; j < argc; j++) {
        if (sdslen(argv[j])) {
            c->argv[c->argc] = createObject(UG_STRING,argv[j]);
            c->argc++;
        } else {
            sdsfree(argv[j]);
        }
    }
    zfree(argv);
    return UGOK;
}
Exemplo n.º 22
0
void lsCommand(redisClient* c)
{
    DIR* pstDir = opendir((const char*)c->argv[1]->ptr);
    if (pstDir == NULL) {
        addReplyErrorFormat(c, "opendir fail, %s", strerror(errno));
        return;
    }

    list* dir = listCreate();
    struct dirent* pstDirent = NULL;
    while ((pstDirent = readdir(pstDir)) != NULL) {

        if(strcmp(pstDirent->d_name, ".") == 0 ||
            strcmp(pstDirent->d_name, "..") == 0)
            continue;

        sds path = sdsdup(c->argv[1]->ptr);
        size_t len = sdslen(path);
        if (path[len - 1] != '/')
            path = sdscat(path, "/");

        path = sdscat(path, pstDirent->d_name);
        listAddNodeTail(dir, path);
    }
    closedir(pstDir);

    addReplyMultiBulkLen(c, listLength(dir));
    listIter* iter = listGetIterator(dir, AL_START_HEAD);
    listNode* node = NULL;
    while ((node = listNext(iter))) {
        addReplyBulkCString(c, node->value);
        sdsfree(node->value);
        listDelNode(dir, node);
    }
    listRelease(dir);
}
Exemplo n.º 23
0
/* If this function gets called we already read a whole
 * command, arguments are in the client argv/argc fields.
 * processCommand() execute the command or prepare the
 * server for a bulk read from the client.
 *
 * If VR_OK is returned the client is still alive and valid and
 * other operations can be performed by the caller. Otherwise
 * if VR_ERROR is returned the client was destroyed (i.e. after QUIT). */
int processCommand(client *c) {
    long long maxmemory;

    /* The QUIT command is handled separately. Normal command procs will
     * go through checking for replication and QUIT will cause trouble
     * when FORCE_REPLICATION is enabled and would be implemented in
     * a regular command proc. */
    if (!strcasecmp(c->argv[0]->ptr,"quit")) {
        addReply(c,shared.ok);
        c->flags |= CLIENT_CLOSE_AFTER_REPLY;
        return VR_ERROR;
    }

    /* Now lookup the command and check ASAP about trivial error conditions
     * such as wrong arity, bad command name and so forth. */
    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
    if (!c->cmd) {
        flagTransaction(c);
        addReplyErrorFormat(c,"unknown command '%s'",
            (char*)c->argv[0]->ptr);
        return VR_OK;
    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
               (c->argc < -c->cmd->arity)) {
        flagTransaction(c);
        addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
            c->cmd->name);
        return VR_OK;
    }

    /* Check if the user is authenticated */
    if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
    {
        flagTransaction(c);
        addReply(c,shared.noautherr);
        return VR_OK;
    }

    /* Handle the maxmemory directive.
     *
     * First we try to free some memory if possible (if there are volatile
     * keys in the dataset). If there are not the only thing we can do
     * is returning an error. */
    conf_server_get(CONFIG_SOPN_MAXMEMORY,&maxmemory);
    if (maxmemory) {
        int retval = freeMemoryIfNeeded(c->vel);
        /* freeMemoryIfNeeded may flush slave output buffers. This may result
         * into a slave, that may be the active client, to be freed. */
        if (c->vel->current_client == NULL) return VR_ERROR;

        /* It was impossible to free enough memory, and the command the client
         * is trying to execute is denied during OOM conditions? Error. */
        if ((c->cmd->flags & CMD_DENYOOM) && retval == VR_ERROR) {
            flagTransaction(c);
            addReply(c, shared.oomerr);
            return VR_OK;
        }
    }

    /* Don't accept write commands if there are problems persisting on disk
     * and if this is a master instance. */
    if (((server.stop_writes_on_bgsave_err &&
          server.saveparamslen > 0 &&
          server.lastbgsave_status == VR_ERROR) ||
          server.aof_last_write_status == VR_ERROR) &&
        repl.masterhost == NULL &&
        (c->cmd->flags & CMD_WRITE ||
         c->cmd->proc == pingCommand))
    {
        flagTransaction(c);
        if (server.aof_last_write_status == VR_OK)
            addReply(c, shared.bgsaveerr);
        else
            addReplySds(c,
                sdscatprintf(sdsempty(),
                "-MISCONF Errors writing to the AOF file: %s\r\n",
                strerror(server.aof_last_write_errno)));
        return VR_OK;
    }

    /* Don't accept write commands if there are not enough good slaves and
     * user configured the min-slaves-to-write option. */
    if (repl.masterhost == NULL &&
        repl.repl_min_slaves_to_write &&
        repl.repl_min_slaves_max_lag &&
        c->cmd->flags & CMD_WRITE &&
        repl.repl_good_slaves_count < repl.repl_min_slaves_to_write)
    {
        flagTransaction(c);
        addReply(c, shared.noreplicaserr);
        return VR_OK;
    }

    /* Don't accept write commands if this is a read only slave. But
     * accept write commands if this is our master. */
    if (repl.masterhost && repl.repl_slave_ro &&
        !(c->flags & CLIENT_MASTER) &&
        c->cmd->flags & CMD_WRITE)
    {
        addReply(c, shared.roslaveerr);
        return VR_OK;
    }

    /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
    if (c->flags & CLIENT_PUBSUB &&
        c->cmd->proc != pingCommand &&
        c->cmd->proc != subscribeCommand &&
        c->cmd->proc != unsubscribeCommand &&
        c->cmd->proc != psubscribeCommand &&
        c->cmd->proc != punsubscribeCommand) {
        addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context");
        return VR_OK;
    }

    /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and
     * we are a slave with a broken link with master. */
    if (repl.masterhost && repl.repl_state != REPL_STATE_CONNECTED &&
        repl.repl_serve_stale_data == 0 &&
        !(c->cmd->flags & CMD_STALE))
    {
        flagTransaction(c);
        addReply(c, shared.masterdownerr);
        return VR_OK;
    }

    /* Loading DB? Return an error if the command has not the
     * CMD_LOADING flag. */
    if (server.loading && !(c->cmd->flags & CMD_LOADING)) {
        addReply(c, shared.loadingerr);
        return VR_OK;
    }

    /* Lua script too slow? Only allow a limited number of commands. */
    if (server.lua_timedout &&
          c->cmd->proc != authCommand &&
          c->cmd->proc != replconfCommand &&
        !(c->cmd->proc == shutdownCommand &&
          c->argc == 2 &&
          tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
        !(c->cmd->proc == scriptCommand &&
          c->argc == 2 &&
          tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))
    {
        flagTransaction(c);
        addReply(c, shared.slowscripterr);
        return VR_OK;
    }

    /* Exec the command */
    if (c->flags & CLIENT_MULTI &&
        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
    {
        queueMultiCommand(c);
        addReply(c,shared.queued);
    } else {
        call(c,CMD_CALL_FULL);
        c->woff = repl.master_repl_offset;
        if (listLength(server.ready_keys))
            handleClientsBlockedOnLists();
    }

    return VR_OK;
}
Exemplo n.º 24
0
void triggleGenericCommand(redisClient *c, int nx, robj *db_id, robj *key_pattern,robj *event_type, robj *script_source) {
    redisLog(REDIS_NOTICE,"dbid: %s keypattern: %s script_source: %s ",db_id->ptr,key_pattern->ptr,script_source->ptr);
    int id = atoi(db_id->ptr);
    int int_event=process_trigglecmd(event_type->ptr);
    if(int_event==-1)
    {
        addReplyError(c,"undefine event in redis triggle");
        return;
    }


    
    //redisLog(REDIS_NOTICE,"get event:%d for: %s",int_event,event_type->ptr);
    if(id<0||id>server.dbnum)
    {
        addReplyError(c,"wrong dbid for triggle");
        return;
    }

//    redisLog(REDIS_NOTICE,"add  into stack: %d",lua_gettop(server.lua));

    /*lua_check*/
    sds funcdef = sdsempty();

    funcdef = sdscat(funcdef,"function ");
    funcdef = sdscatlen(funcdef,key_pattern->ptr,sdslen(key_pattern->ptr));
    funcdef = sdscatlen(funcdef,"() ",3);
    funcdef = sdscatlen(funcdef,script_source->ptr,sdslen(script_source->ptr));
    funcdef = sdscatlen(funcdef," end",4);
    //redisLog(REDIS_NOTICE,"script function:%s",funcdef);

    if (luaL_loadbuffer(server.lua,funcdef,sdslen(funcdef),"@user_script")) {
        addReplyErrorFormat(c,"Error compiling script (new function): %s\n",
                lua_tostring(server.lua,-1));
        lua_pop(server.lua,1);
        sdsfree(funcdef);
        return ;
    }
    sdsfree(funcdef);


    //redisLog(REDIS_NOTICE,"add load buffer stack: %d",lua_gettop(server.lua));
    if (lua_pcall(server.lua,0,0,0)) {
        addReplyErrorFormat(c,"Error running script (new function): %s\n",
                lua_tostring(server.lua,-1));
        lua_pop(server.lua,1);
        return ;
    }

    //redisLog(REDIS_NOTICE,"run buffer stack: %d",lua_gettop(server.lua));
   
    struct bridge_db_triggle_t *tmptrg=zmalloc(sizeof(struct bridge_db_triggle_t));
    tmptrg->dbid=id;
    tmptrg->event=int_event;
    tmptrg->lua_scripts=script_source;
    incrRefCount(script_source);
    sds copy=sdsdup(key_pattern->ptr);
    dictAdd(server.bridge_db.triggle_scipts[id],copy,tmptrg);
    addReply(c, nx ? shared.cone : shared.ok);


}
Exemplo n.º 25
0
/* *
 * slotsrestore key ttl val [key ttl val ...]
 * */
void
slotsrestoreCommand(redisClient *c) {
    if (c->argc < 4 || (c->argc - 1) % 3 != 0) {
        addReplyErrorFormat(c, "wrong number of arguments for 'slotsrestore' command");
        return;
    }
    int n = (c->argc - 1) / 3;

    long long *ttls = zmalloc(sizeof(long long) * n);
    robj **vals = zmalloc(sizeof(robj *) * n);
    for (int i = 0; i < n; i ++) {
        vals[i] = NULL;
    }

    for (int i = 0; i < n; i ++) {
        robj *key = c->argv[i * 3 + 1];
        robj *ttl = c->argv[i * 3 + 2];
        robj *val = c->argv[i * 3 + 3];
        if (lookupKeyWrite(c->db, key) != NULL) {
            redisLog(REDIS_WARNING, "slotsrestore: slot = %d, key = '%s' already exists",
                    slots_num(key->ptr, NULL), (char *)key->ptr);
        }
        if (getLongLongFromObjectOrReply(c, ttl, &ttls[i], NULL) != REDIS_OK) {
            goto cleanup;
        } else if (ttls[i] < 0) {
            addReplyError(c, "invalid ttl value, must be >= 0");
            goto cleanup;
        }
        rio payload;
        int type;
        if (verifyDumpPayload(val->ptr, sdslen(val->ptr)) != REDIS_OK) {
            addReplyError(c, "dump payload version or checksum are wrong");
            goto cleanup;
        }
        rioInitWithBuffer(&payload, val->ptr);
        if (((type = rdbLoadObjectType(&payload)) == -1) ||
                ((vals[i] = rdbLoadObject(type, &payload)) == NULL)) {
            addReplyError(c, "bad data format");
            goto cleanup;
        }
    }

    for (int i = 0; i < n; i ++) {
        robj *key = c->argv[i * 3 + 1];
        long long ttl = ttls[i];
        robj *val = vals[i];
        dbDelete(c->db, key);
        dbAdd(c->db, key, val);
        incrRefCount(val);
        if (ttl) {
            setExpire(c->db, key, mstime() + ttl);
        }
        signalModifiedKey(c->db, key);
        server.dirty ++;
    }
    addReply(c, shared.ok);

cleanup:
    for (int i = 0; i < n; i ++) {
        if (vals[i] != NULL) {
            decrRefCount(vals[i]);
        }
    }
    zfree(vals);
    zfree(ttls);
}
Exemplo n.º 26
0
void evalGenericCommand(redisClient *c, int evalsha) {
    lua_State *lua = server.lua;
    char funcname[43];
    long long numkeys;
    int delhook = 0, err;

    // 随机数的种子,在产生哈希值的时候会用到
    /* We want the same PRNG sequence at every call so that our PRNG is
     * not affected by external state. */
    redisSrand48(0);

    /* 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;
    server.lua_write_dirty = 0;

    // 检查参数的有效性
    /* 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;
    }

    // 函数名以 f_ 开头
    /* We obtain the script SHA1, then check if this function is already
     * defined into the Lua state */
    funcname[0] = 'f';
    funcname[1] = '_';

    // 如果没有哈希值,需要计算 lua 脚本的哈希值
    if (!evalsha) {
        // 计算哈希值,会放入到 SHA1 -> lua_script 哈希表中
        // c->argv[1]->ptr 是用户指定的 lua 脚本
        // sha1hex() 产生的哈希值存在 funcname 中
        /* Hash the code if this is an EVAL call */
        sha1hex(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';
    }

    // 将错误处理函数入栈
    // lua_getglobal() 会将读取指定的全局变量,且将其入栈
    /* Push the pcall error handler function on the stack. */
    lua_getglobal(lua, "__redis__err__handler");

    /* Try to lookup the Lua function */
    // 在 lua 中查找是否注册了此函数。这一句尝试将 funcname 入栈
    lua_getglobal(lua, funcname);
    if (lua_isnil(lua,-1)) { // funcname 在 lua 中不存在

        // 将 nil 出栈
        lua_pop(lua,1); /* remove the nil from the stack */

        // 已经确定 funcname 在 lua 中没有定义,需要创建
        /* Function not defined... let's define it if we have the
         * body of the function. If this is an EVALSHA call we can just
         * return an error. */
        if (evalsha) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            addReply(c, shared.noscripterr);
            return;
        }

        // 创建 lua 函数 funcname
        // c->argv[1] 指向用户指定的 lua 脚本
        if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            /* The error is sent to the client by luaCreateFunction()
             * itself when it returns REDIS_ERR. */
            return;
        }

        // 现在 lua 中已经有 funcname 这个全局变量了,将其读取并入栈,
        // 准备调用
        /* Now the following is guaranteed to return non nil */
        lua_getglobal(lua, funcname);
        redisAssert(!lua_isnil(lua,-1));
    }

    // 设置参数,包括键和值
    /* 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);

    // 选择数据集,lua_client 有专用的数据集
    /* Select the right DB in the context of the Lua client */
    selectDb(server.lua_client,c->db->id);

    // 设置超时回调函数,以在 lua 脚本执行过长时间的时候停止脚本的运行
    /* Set a 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. */
    server.lua_caller = c;
    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;
    if (server.lua_time_limit > 0 && server.masterhost == NULL) {
        // 当 lua 解释器执行了 100000,luaMaskCountHook() 会被调用
        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        delhook = 1;
    }

    // 现在,我们确定函数已经注册成功了.可以直接调用 lua 脚本
    /* At this point whether 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. */
    err = lua_pcall(lua,0,1,-2);

    // 删除超时回调函数
    /* Perform some cleanup that we need to do both on error and success. */
    if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */

    // 如果已经超时了,说明 lua 脚本已在超时后背 SCRPIT KILL 终结了
    // 恢复监听发送 lua 脚本命令的客户端
    if (server.lua_timedout) {
        server.lua_timedout = 0;
        /* Restore the readable handler that was unregistered when the
         * script timeout was detected. */
        aeCreateFileEvent(server.el,c->fd,AE_READABLE,
                          readQueryFromClient,c);
    }
    // lua_caller 置空
    server.lua_caller = NULL;

    // 执行 lua 脚本用的是 lua 脚本执行专用的数据集。现在恢复原有的数据集
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */

    // Garbage collection 垃圾回收
    lua_gc(lua,LUA_GCSTEP,1);

    // 处理执行 lua 脚本的错误
    if (err) {
        // 告知客户端
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
            funcname, lua_tostring(lua,-1));
        lua_pop(lua,2); /* Consume the Lua reply and remove error handler. */

    // 成功了
    } else {
        /* On success convert the Lua return value into Redis protocol, and
         * send it to * the client. */
        luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */
        lua_pop(lua,1); /* Remove the error handler. */
    }

    // 将 lua 脚本发布到主从复制上,并写入 AOF 文件
    /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless
     * we are sure that the script was already in the context of all the
     * attached slaves *and* the current AOF file if enabled.
     *
     * To do so we use a cache of SHA1s of scripts that we already propagated
     * as full EVAL, that's called the Replication Script Cache.
     *
     * For repliation, everytime a new slave attaches to the master, we need to
     * flush our cache of scripts that can be replicated as EVALSHA, while
     * for AOF we need to do so every time we rewrite the AOF file. */
    if (evalsha) {
        if (!replicationScriptCacheExists(c->argv[1]->ptr)) {
            /* This script is not in our script cache, replicate it as
             * EVAL, then add it into the script cache, as from now on
             * slaves and AOF know about it. */

            // 从 server.lua_scripts 获取 lua 脚本
            // c->argv[1]->ptr 是 SHA1
            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);

            // 添加到主从复制专用的脚本缓存中
            replicationScriptCacheAdd(c->argv[1]->ptr);
            redisAssertWithInfo(c,NULL,script != NULL);

            // 重写命令
            // 参数 1 为:EVAL
            // 参数 2 为:lua_script
            // 如此一来在执行 AOF 持久化和主从复制的时候,lua 脚本就能得到传播
            rewriteClientCommandArgument(c,0,
                resetRefCount(createStringObject("EVAL",4)));
            rewriteClientCommandArgument(c,1,script);
        }
    }
}
Exemplo n.º 27
0
Arquivo: json.c Projeto: Wouter33/krmt
/* Returns ID(s) of documents added */
void jsondocsetbyjsonCommand(redisClient *c) {
    /* args 0-N: ["jsondocsetbyjson", id-field-name, json] */
    jsonSyncClients(c);

    sds field_name = c->argv[1]->ptr;
    struct jsonObj *root = yajl_decode(c->argv[2]->ptr, NULL);

    if (!root) {
        addReply(c, g.err_parse);
        return;
    }

    robj *key;
    if (root->type == JSON_TYPE_LIST) {
        robj *keys[root->content.obj.elements];
        D("Procesing list!\n");
        /* First, process all documents for names to make sure they are valid
         * documents.  We don't want to add half the documents then reach a
         * failure scenario. */
        for (int i = 0; i < root->content.obj.elements; i++) {
            struct jsonObj *o = root->content.obj.fields[i];
            key = jsonObjFindId(field_name, o);
            keys[i] = key;
            if (!key) {
                /* Free any allocated keys so far */
                for (int j = 0; i < j; j++)
                    decrRefCount(keys[j]);

                jsonObjFree(root);
                addReplyErrorFormat(
                    c,
                    "field '%s' not found or unusable as key for document %d",
                    field_name, i);
                return;
            }
        }
        /* Now actually add all the documents */
        /* Note how a multi-set gets a multibulk reply while
         * a regular one-document set gets just one bulk result. */
        addReplyMultiBulkLen(c, root->content.obj.elements);
        for (int i = 0; i < root->content.obj.elements; i++) {
            struct jsonObj *o = root->content.obj.fields[i];
            jsonObjAddToDB(keys[i], o);
            addReplyBulkCBuffer(c, keys[i]->ptr, sdslen(keys[i]->ptr));
            decrRefCount(keys[i]);
        }
    } else if (root->type == JSON_TYPE_MAP) {
        key = jsonObjFindId(field_name, root);
        if (key) {
            jsonObjAddToDB(key, root);
            addReplyBulkCBuffer(c, key->ptr, sdslen(key->ptr));
            decrRefCount(key);
        } else {
            addReplyErrorFormat(
                c, "field '%s' not found or unusable as key for document",
                field_name);
        }
    } else {
        addReplyError(c, "JSON isn't map or array of maps.");
    }

    jsonObjFree(root);
}
Exemplo n.º 28
0
Arquivo: json.c Projeto: Wouter33/krmt
void jsondocsetCommand(redisClient *c) {
    /* args 0-N: ["jsondocset", id, json, [id2, json2, ...]] */
    /* If only K:V strings, submit as one hmset command */

    /* This function is O(3*N) in the number of total keys submitted:
     *   - first, we validate the keys
     *   - second, we process all the json
     *   - third, we add all the JSON to the DB.
     *   + we run each loop indepdently because we must be able to abort
     *     the entire add sequence if any prior aggregate attempt fails
     *     (e.g. if you have a bad key or bad json, we don't want to add
     *           _any_ of these documents.) */

    /* If we don't have an even number of Key/Document pairs, exit. */
    if ((c->argc - 1) % 2 != 0) {
        addReplyError(
            c, "Invalid number of arguments.  Must match keys to documents.");
        return;
    }

    /* If any of the keys are invalid, exit. */
    for (int i = 1; i < c->argc; i += 2) {
        if (!validateKeyFormatAndReply(c, c->argv[i]->ptr))
            return;
    }

    int documents = (c->argc - 1) / 2;

    struct jsonObj *additions[documents];
    for (int i = 1; i < c->argc; i += 2) {
        struct jsonObj *root = yajl_decode(c->argv[i + 1]->ptr, NULL);

        additions[i / 2] = root;

        if (!root) {
            /* Free any jsonObj trees already created */
            for (int j = 0; j < i; j++)
                jsonObjFree(additions[j]);

            addReplyErrorFormat(c, "Invalid JSON at document %d.  Use "
                                   "JSONDOCVALIDATE [json] for complete error.",
                                i / 2);
            return;
        }
    }

    jsonSyncClients(c);
    int deleted = 0;
    /* Now jump keys at positions i*2 and documents at i/2 */
    for (int i = 1; i < c->argc; i += 2) {
        struct jsonObj *root = additions[i / 2];
        /* We can add a much simpler "check if key exists" test here first: */
        deleted += jsonObjAddToDB(c->argv[i], root);
        jsonObjFree(root);
        D("Parsed Json %d!\n", i);
    }

    /* Reply += 1 if the document is new; reply += 0 if the document updated
     * (where an update is a full Delete/Create cycle) */
    /* Replies with number of new keys set.  Existing keys also get
     * set, but don't count as new. */
    addReplyLongLong(c, documents - deleted);
}
Exemplo n.º 29
0
void evalGenericCommand(redisClient *c, int evalsha) {
    lua_State *lua = server.lua;
    char funcname[43];
    long long numkeys;
    int delhook = 0, err;

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

    /* 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;
    server.lua_write_dirty = 0;

    /* 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;
    }

    /* We obtain the script SHA1, then check if this function is already
     * defined into the Lua state */
    funcname[0] = 'f';
    funcname[1] = '_';
    if (!evalsha) {
        /* Hash the code if this is an EVAL call */
        sha1hex(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';
    }

    /* Push the pcall error handler function on the stack. */
    lua_getglobal(lua, "__redis__err__handler");

    /* Try to lookup the Lua function */
    lua_getglobal(lua, funcname);
    if (lua_isnil(lua,-1)) {
        lua_pop(lua,1); /* remove the nil from the stack */
        /* Function not defined... let's define it if we have the
         * body of the function. If this is an EVALSHA call we can just
         * return an error. */
        if (evalsha) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            addReply(c, shared.noscripterr);
            return;
        }
        if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            /* The error is sent to the client by luaCreateFunction()
             * itself when it returns REDIS_ERR. */
            return;
        }
        /* Now the following is guaranteed to return non nil */
        lua_getglobal(lua, funcname);
        redisAssert(!lua_isnil(lua,-1));
    }

    /* 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);

    /* Select the right DB in the context of the Lua client */
    selectDb(server.lua_client,c->db->id);
    
    /* Set a 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. */
    server.lua_caller = c;
    server.lua_time_start = mstime();
    server.lua_kill = 0;
    if (server.lua_time_limit > 0 && server.masterhost == NULL) {
        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        delhook = 1;
    }

    /* At this point whether 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. */
    err = lua_pcall(lua,0,1,-2);

    /* Perform some cleanup that we need to do both on error and success. */
    if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
    if (server.lua_timedout) {
        server.lua_timedout = 0;
        /* Restore the readable handler that was unregistered when the
         * script timeout was detected. */
        aeCreateFileEvent(server.el,c->fd,AE_READABLE,
                          readQueryFromClient,c);
    }
    server.lua_caller = NULL;
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
    lua_gc(lua,LUA_GCSTEP,1);

    if (err) {
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
            funcname, lua_tostring(lua,-1));
        lua_pop(lua,2); /* Consume the Lua reply and remove error handler. */
    } else {
        /* On success convert the Lua return value into Redis protocol, and
         * send it to * the client. */
        luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */
        lua_pop(lua,1); /* Remove the error handler. */
    }

    /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless
     * we are sure that the script was already in the context of all the
     * attached slaves *and* the current AOF file if enabled.
     *
     * To do so we use a cache of SHA1s of scripts that we already propagated
     * as full EVAL, that's called the Replication Script Cache.
     *
     * For repliation, everytime a new slave attaches to the master, we need to
     * flush our cache of scripts that can be replicated as EVALSHA, while
     * for AOF we need to do so every time we rewrite the AOF file. */
    if (evalsha) {
        if (!replicationScriptCacheExists(c->argv[1]->ptr)) {
            /* This script is not in our script cache, replicate it as
             * EVAL, then add it into the script cache, as from now on
             * slaves and AOF know about it. */
            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);

            replicationScriptCacheAdd(c->argv[1]->ptr);
            redisAssertWithInfo(c,NULL,script != NULL);
            rewriteClientCommandArgument(c,0,
                resetRefCount(createStringObject("EVAL",4)));
            rewriteClientCommandArgument(c,1,script);
            forceCommandPropagation(c,REDIS_PROPAGATE_REPL|REDIS_PROPAGATE_AOF);
        }
    }
}
/* PUBSUB command for Pub/Sub introspection. */
void pubsubCommand(redisClient *c) {

    // PUBSUB CHANNELS [pattern] 子命令
    if (!strcasecmp(c->argv[1]->ptr,"channels") &&
        (c->argc == 2 || c->argc ==3))
    {
        /* PUBSUB CHANNELS [<pattern>] */
        // 检查命令请求是否给定了 pattern 参数
        // 如果没有给定的话,就设为 NULL
        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;

        // 创建 pubsub_channels 的字典迭代器
        // 该字典的键为频道,值为链表
        // 链表中保存了所有订阅键所对应的频道的客户端
        dictIterator *di = dictGetIterator(server.pubsub_channels);
        dictEntry *de;
        long mblen = 0;
        void *replylen;

        replylen = addDeferredMultiBulkLength(c);
        // 从迭代器中获取一个客户端
        while((de = dictNext(di)) != NULL) {

            // 从字典中取出客户端所订阅的频道
            robj *cobj = dictGetKey(de);
            sds channel = cobj->ptr;

            // 顺带一提
            // 因为 Redis 的字典实现只能遍历字典的值(客户端)
            // 所以这里才会有遍历字典值然后通过字典值取出字典键(频道)的蹩脚用法

            // 如果没有给定 pattern 参数,那么打印所有找到的频道
            // 如果给定了 pattern 参数,那么只打印和 pattern 相匹配的频道
            if (!pat || stringmatchlen(pat, sdslen(pat),
                                       channel, sdslen(channel),0))
            {
                // 向客户端输出频道
                addReplyBulk(c,cobj);
                mblen++;
            }
        }
        // 释放字典迭代器
        dictReleaseIterator(di);
        setDeferredMultiBulkLength(c,replylen,mblen);

    // PUBSUB NUMSUB [channel-1 channel-2 ... channel-N] 子命令
    } else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {
        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
        int j;

        addReplyMultiBulkLen(c,(c->argc-2)*2);
        for (j = 2; j < c->argc; j++) {

            // c->argv[j] 也即是客户端输入的第 N 个频道名字
            // pubsub_channels 的字典为频道名字
            // 而值则是保存了 c->argv[j] 频道所有订阅者的链表
            // 而调用 dictFetchValue 也就是取出所有订阅给定频道的客户端
            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);

            addReplyBulk(c,c->argv[j]);
            // 向客户端返回链表的长度属性
            // 这个属性就是某个频道的订阅者数量
            // 例如:如果一个频道有三个订阅者,那么链表的长度就是 3
            // 而返回给客户端的数字也是三
            addReplyBulkLongLong(c,l ? listLength(l) : 0);
        }

    // PUBSUB NUMPAT 子命令
    } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
        /* PUBSUB NUMPAT */

        // pubsub_patterns 链表保存了服务器中所有被订阅的模式
        // pubsub_patterns 的长度就是服务器中被订阅模式的数量
        addReplyLongLong(c,listLength(server.pubsub_patterns));

    // 错误处理
    } else {
        addReplyErrorFormat(c,
            "Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
            (char*)c->argv[1]->ptr);
    }
}