int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) { long long value; if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR; if (value < LONG_MIN || value > LONG_MAX) { if (msg != NULL) { addReplyError(c,(char*)msg); } else { addReplyError(c,"value is out of range"); } return REDIS_ERR; } *target = value; return REDIS_OK; }
void setrangeCommand(redisClient *c) { robj *o; long offset; sds value = c->argv[3]->ptr; if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK) return; if (offset < 0) { addReplyError(c,"offset is out of range"); return; } o = lookupKeyWrite(c->db,c->argv[1]); if (o == NULL) { /* Return 0 when setting nothing on a non-existing string */ if (sdslen(value) == 0) { addReply(c,shared.czero); return; } /* Return when the resulting string exceeds allowed size */ if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) return; o = createObject(REDIS_STRING,sdsempty()); dbAdd(c->db,c->argv[1],o); } else { size_t olen; /* Key exists, check type */ if (checkType(c,o,REDIS_STRING)) return; /* Return existing string length when setting nothing */ olen = stringObjectLen(o); if (sdslen(value) == 0) { addReplyLongLong(c,olen); return; } /* Return when the resulting string exceeds allowed size */ if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) return; /* Create a copy when the object is shared or encoded. */ o = dbUnshareStringValue(c->db,c->argv[1],o); } if (sdslen(value) > 0) { o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value)); memcpy((char*)o->ptr+offset,value,sdslen(value)); signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(REDIS_NOTIFY_STRING, "setrange",c->argv[1],c->db->id); server.dirty++; } addReplyLongLong(c,sdslen(o->ptr)); }
void discardCommand(client *c) { if (!(c->flags & CLIENT_MULTI)) { addReplyError(c,"DISCARD without MULTI"); return; } discardTransaction(c); addReply(c,shared.ok); }
void selectCommand(redisClient *c) { long id; if (getLongFromObjectOrReply(c, c->argv[1], &id, "invalid DB index") != REDIS_OK) return; if (server.cluster_enabled && id != 0) { addReplyError(c,"SELECT is not allowed in cluster mode"); return; } if (selectDb(c,id) == REDIS_ERR) { addReplyError(c,"invalid DB index"); } else { addReply(c,shared.ok); } }
void multiCommand(redisClient *c) { if (c->flags & REDIS_MULTI) { addReplyError(c,"MULTI calls can not be nested"); return; } c->flags |= REDIS_MULTI; addReply(c,shared.ok); }
// Utility commands void MZHappy(client *c, int argc, sds *argv) { // Set for 1e9 pulses. Should renew this in the status readout. int ret= Pulser(1,0.5,1e9,MappedHappyBaseAddress); if(ret == 0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
int processInlineBuffer(client *c) { char *newline; int argc; sds *argv, aux; size_t querylen; /* Search for end of line */ newline = strchr(c->querybuf,'\n'); /* Nothing to do without a \r\n */ if (newline == NULL) { if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) { addReplyError(c,"Protocol error: too big inline request"); setProtocolError(c,0); } return C_ERR; } /* Handle the \r\n case. */ if (newline && newline != c->querybuf && *(newline-1) == '\r') newline--; /* Split the input buffer up to the \r\n */ querylen = newline-(c->querybuf); aux = sdsnewlen(c->querybuf,querylen); argv = sdssplitargs(aux,&argc); sdsfree(aux); if (argv == NULL) { addReplyError(c,"Protocol error: unbalanced quotes in request"); setProtocolError(c,0); return C_ERR; } /* Leave data after the first line of the query in the buffer */ sdsrange(c->querybuf,querylen+2,-1); /* Setup argv array on client structure */ if (argc) { if (c->argv) free(c->argv); c->argv = argv; } c->argc = argc; return C_OK; }
/* scriptNameCommand() has compound sub-arguments, so it looks slightly more * convoluted than it actually is. Just read each if/else branch as * if it were an individual command. */ void scriptNameCommand(redisClient *c) { char *req = c->argv[1]->ptr; sds script_name = c->argv[2]->ptr; if (c->argc == 4 && !strcasecmp(req, "set")) { sds target_sha = c->argv[3]->ptr; if (sdslen(target_sha) != 40 || dictFind(server.lua_scripts,target_sha) == NULL) { addReply(c, g.err.nosha); return; } /* If name doesn't exist, dictReplace == dictAdd */ dictReplace(g.names, script_name, target_sha); addReplyBulkCBuffer(c, script_name, sdslen(script_name)); } else if (c->argc == 3 && !strcasecmp(req, "get")) { sds found; if ((found = dictFetchValue(g.names, script_name))) { addReplyBulkCBuffer(c, found, sdslen(found)); } else { addReply(c, g.err.noname); } } else if (c->argc == 2 && !strcasecmp(req, "getall")) { dictIterator *di; dictEntry *de; unsigned long sz = dictSize(g.names); if (!sz) { addReply(c, shared.emptymultibulk); return; } /* Multiply by 2 because the size of the dict is the number of keys. * We are returning keys *and* values, so length is dictSize * 2 */ addReplyMultiBulkLen(c, sz * 2); di = dictGetIterator(g.names); while ((de = dictNext(di))) { addReplyBulkCString(c, dictGetKey(de)); addReplyBulkCString(c, dictGetVal(de)); } dictReleaseIterator(di); } else if (c->argc == 3 && !strcasecmp(req, "del")) { sds deleted; if ((deleted = dictFetchValue(g.names, script_name))) { dictDelete(g.names, script_name); addReplyBulkCBuffer(c, deleted, sdslen(deleted)); } else { addReply(c, g.err.noname); } } else { addReplyError(c, "Unknown scriptName subcommand or arg count"); } }
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; }
void selectCommand(redisClient *c) { int id = atoi(c->argv[1]->ptr); if (selectDb(c,id) == REDIS_ERR) { addReplyError(c,"invalid DB index"); } else { addReply(c,shared.ok); } }
int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { long long value; // T = O(N) if (getLongLongFromObject(o, &value) != REDIS_OK) { if (msg != NULL) { addReplyError(c,(char*)msg); } else { addReplyError(c,"value is not an integer or out of range"); } return REDIS_ERR; } *target = value; return REDIS_OK; }
// Clock commands void clockreset(client *c, int argc, sds *argv) { int ret= clockReset(1); usleep(1000); ret= clockReset(0); if(ret != 0) addReplyError(c, tubii_err); else addReplyStatus(c, "+OK"); }
void loadShift(client *c, int argc, sds *argv) { uint32_t lShift; safe_strtoul(argv[1],&lShift); int ret= LoadShift(lShift); if(ret == 0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
//// Shift Register commands // Low level stuff void dataready(client *c, int argc, sds *argv) { uint32_t dReady; safe_strtoul(argv[1],&dReady); int ret= DataReady(dReady); if(ret==0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
void muxer(client *c, int argc, sds *argv) { uint32_t mux; safe_strtoul(argv[1],&mux); int ret= Muxer(mux); if(ret == 0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
void SetComboTrigger(client *c, int argc, sds *argv) { u32 enableMask, logicMask; safe_strtoul(argv[1],&enableMask); safe_strtoul(argv[2],&logicMask); if(comboTrig(enableMask,logicMask) == 0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
void execCommand(redisClient *c) { int j; robj **orig_argv; int orig_argc; struct redisCommand *orig_cmd; if (!(c->flags & REDIS_MULTI)) { addReplyError(c,"EXEC without MULTI"); return; } /* Check if we need to abort the EXEC if some WATCHed key was touched. * A failed EXEC will return a multi bulk nil object. */ if (c->flags & REDIS_DIRTY_CAS) { freeClientMultiState(c); initClientMultiState(c); c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS); unwatchAllKeys(c); addReply(c,shared.nullmultibulk); return; } /* Replicate a MULTI request now that we are sure the block is executed. * This way we'll deliver the MULTI/..../EXEC block as a whole and * both the AOF and the replication link will have the same consistency * and atomicity guarantees. */ execCommandReplicateMulti(c); /* Exec all the queued commands */ unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */ orig_argv = c->argv; orig_argc = c->argc; orig_cmd = c->cmd; addReplyMultiBulkLen(c,c->mstate.count); for (j = 0; j < c->mstate.count; j++) { c->argc = c->mstate.commands[j].argc; c->argv = c->mstate.commands[j].argv; c->cmd = c->mstate.commands[j].cmd; call(c); /* Commands may alter argc/argv, restore mstate. */ c->mstate.commands[j].argc = c->argc; c->mstate.commands[j].argv = c->argv; c->mstate.commands[j].cmd = c->cmd; } c->argv = orig_argv; c->argc = orig_argc; c->cmd = orig_cmd; freeClientMultiState(c); initClientMultiState(c); c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS); /* Make sure the EXEC command is always replicated / AOF, since we * always send the MULTI command (we can't know beforehand if the * next operations will contain at least a modification to the DB). */ server.dirty++; }
/* 事务起始指令multi的执行函数 */ void multiCommand(client *c) { /* 事务不能嵌套 */ if (c->flags & CLIENT_MULTI) { addReplyError(c,"MULTI calls can not be nested"); return; } /* 设置标识 */ c->flags |= CLIENT_MULTI; addReply(c,shared.ok); }
void gtdelay(client *c, int argc, sds *argv) { float length=0; safe_strtof(argv[1],&length); u32 delay = length*ns; int ret= Delay(delay,MappedGTDelayBaseAddress); if(ret == 0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
void SetPrescaleTrigger(client *c, int argc, sds *argv) { float rate; uint32_t bit; safe_strtof(argv[1],&rate); safe_strtoul(argv[2],&bit); if(prescaleTrig(rate,bit) == 0) addReplyStatus(c, "+OK"); else addReplyError(c, tubii_err); }
// 放弃执行事务(命令) void discardCommand(redisClient *c) { // 如果没有调用过 MULTI ,报错 if (!(c->flags & REDIS_MULTI)) { addReplyError(c,"DISCARD without MULTI"); return; } discardTransaction(c); addReply(c,shared.ok); }
void triggleDelCommand(struct redisClient *c) { int id = atoi(c->argv[1]->ptr); if(id<0||id>server.dbnum) { addReplyError(c,"wrong dbid for triggle"); return; } if( dictDelete(server.bridge_db.triggle_scipts[id],c->argv[2]->ptr)==DICT_OK) { addReply(c, shared.ok); } else { addReplyError(c,"delete unknow error"); } }
/* The SLOWLOG command. Implements all the subcommands needed to handle the * Redis slow log. * * SLOWLOG 命令的实现,支持 GET / RESET 和 LEN 参数 */ void slowlogCommand(redisClient *c) { // 重置 if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) { slowlogReset(); addReply(c,shared.ok); // 返回长度 } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) { addReplyLongLong(c,listLength(server.slowlog)); // 获取某条或者全部日志 } else if ((c->argc == 2 || c->argc == 3) && !strcasecmp(c->argv[1]->ptr,"get")) { long count = 10, sent = 0; listIter li; void *totentries; listNode *ln; slowlogEntry *se; if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK) return; // 遍历日志,取出指定数量的日志 listRewind(server.slowlog,&li); totentries = addDeferredMultiBulkLength(c); while(count-- && (ln = listNext(&li))) { int j; se = ln->value; addReplyMultiBulkLen(c,4); addReplyLongLong(c,se->id); addReplyLongLong(c,se->time); addReplyLongLong(c,se->duration); addReplyMultiBulkLen(c,se->argc); for (j = 0; j < se->argc; j++) addReplyBulk(c,se->argv[j]); sent++; } setDeferredMultiBulkLength(c,totentries,sent); } else { addReplyError(c, "Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN."); } }
/* 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; } }
// MULTI命令的实现,标记一个事务的开始 void multiCommand(client *c) { // 客户端已经处于事务状态,回复错误后返回 if (c->flags & CLIENT_MULTI) { addReplyError(c,"MULTI calls can not be nested"); return; } // 打开客户的的事务状态标识 c->flags |= CLIENT_MULTI; // 回复OK addReply(c,shared.ok); }
void bgsaveCommand(redisClient *c) { if (server.bgsavechildpid != -1) { addReplyError(c,"Background save already in progress"); return; } if (rdbSaveBackground(server.dbfilename) == REDIS_OK) { addReplyStatus(c,"Background saving started"); } else { addReply(c,shared.err); } }
// DISCARD取消事务的命令实现 void discardCommand(client *c) { // 客户端当前不处于事务状态,回复错误后返回 if (!(c->flags & CLIENT_MULTI)) { addReplyError(c,"DISCARD without MULTI"); return; } // 取消事务 discardTransaction(c); // 回复OK addReply(c,shared.ok); }
void moveCommand(client *c) { robj *o; redisDb *src, *dst; int srcid; long long dbid, expire; if (server.cluster_enabled) { addReplyError(c,"MOVE is not allowed in cluster mode"); return; } /* Obtain source and target DB pointers */ src = c->db; srcid = c->db->id; if (getLongLongFromObject(c->argv[2],&dbid) == C_ERR || dbid < INT_MIN || dbid > INT_MAX || selectDb(c,dbid) == C_ERR) { addReply(c,shared.outofrangeerr); return; } dst = c->db; selectDb(c,srcid); /* Back to the source DB */ /* If the user is moving using as target the same * DB as the source DB it is probably an error. */ if (src == dst) { addReply(c,shared.sameobjecterr); return; } /* Check if the element exists and get a reference */ o = lookupKeyWrite(c->db,c->argv[1]); if (!o) { addReply(c,shared.czero); return; } expire = getExpire(c->db,c->argv[1]); /* Return zero if the key already exists in the target DB */ if (lookupKeyWrite(dst,c->argv[1]) != NULL) { addReply(c,shared.czero); return; } dbAdd(dst,c->argv[1],o); if (expire != -1) setExpire(dst,c->argv[1],expire); incrRefCount(o); /* OK! key moved, free the entry in the source DB */ dbDelete(src,c->argv[1]); server.dirty++; addReply(c,shared.cone); }
void bgrewriteaofCommand(redisClient *c) { if (server.bgrewritechildpid != -1) { addReplyError(c,"Background append only file rewriting already in progress"); return; } if (rewriteAppendOnlyFileBackground() == REDIS_OK) { addReplyStatus(c,"Background append only file rewriting started"); } else { addReply(c,shared.err); } }
void watchCommand(redisClient *c) { int j; if (c->flags & REDIS_MULTI) { addReplyError(c,"WATCH inside MULTI is not allowed"); return; } for (j = 1; j < c->argc; j++) watchForKey(c,c->argv[j]); addReply(c,shared.ok); }