//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; }
void bgsaveCommand(redisClient *c) { if (server.bgsavechildpid != -1) { addReplySds(c,sdsnew("-ERR background save already in progress\r\n")); return; } if (rdbSaveBackground(server.dbfilename) == REDIS_OK) { char *status = "+Background saving started\r\n"; addReplySds(c,sdsnew(status)); } else { addReply(c,shared.err); } }
static void bgrewriteaofCommand(redisClient *c) { if (server.bgrewritechildpid != -1) { addReplySds(c,sdsnew("-ERR background append only file rewriting already in progress\r\n")); return; } if (rewriteAppendOnlyFileBackground() == REDIS_OK) { char *status = "+Background append only file rewriting started\r\n"; addReplySds(c,sdsnew(status)); } else { addReply(c,shared.err); } }
void luaCommand(redisClient *c) { //printf("LUA: %s\n", c->argv[1]->ptr); LuaFlag = PIPE_NONE_FLAG; LuaClient = c; /* used in func redisLua */ int s = luaL_dostring(Lua, c->argv[1]->ptr); if (s) { const char *x = lua_tostring(Lua, -1); lua_pop(Lua, 1); addReplySds(c, sdscatprintf(sdsempty(), "-ERR Lua error: %s \r\n", x)); return; } int lret = lua_gettop(Lua); //printf("LuaFlag: %d lret: %d\n", LuaFlag, lret); if (lua_istable(Lua, -1)) { const int len = lua_objlen(Lua, -1 ); addReplySds(c, sdscatprintf(sdsempty(), "*%d\r\n", len)); for (int i = 1; i <= len; ++i ) { lua_pushinteger(Lua, i); lua_gettable(Lua, -2); char *x = (char *)lua_tostring(Lua, -1); robj *r = _createStringObject(x); addReplyBulk(c, r); decrRefCount(r); lua_pop(Lua, 1); } lua_pop(Lua, 1); } else if (LuaFlag == PIPE_EMPTY_SET_FLAG) { addReply(c, shared.emptymultibulk); lua_pop(Lua, 1); /* pop because Pipe adds "-1" for Multi-NonRelIndxs */ } else if (!lret) { addReply(c, shared.nullbulk); } else { char *x = (char *)lua_tostring(Lua, -1); if (!x) { addReply(c, shared.nullbulk); } else { /* NOTE: if "client() is called in a lua func and the lua func then returns "+OK" it will 'correctly' returned */ if (LuaFlag == PIPE_ONE_LINER_FLAG && (*x == '-' || *x == '+' || *x == ':')) { addReplySds(c, sdscatprintf(sdsempty(), "%s\r\n", x)); } else { robj *r = _createStringObject(x); addReplyBulk(c, r); decrRefCount(r); } } lua_pop(Lua, 1); } lua_gc(Lua, LUA_GCCOLLECT, 0); }
int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { double value; if (getDoubleFromObject(o, &value) != REDIS_OK) { if (msg != NULL) { addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg)); } else { addReplySds(c, sdsnew("-ERR value is not a double\r\n")); } return REDIS_ERR; } *target = value; return REDIS_OK; }
int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { long long value; if (getLongLongFromObject(o, &value) != REDIS_OK) { if (msg != NULL) { addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg)); } else { addReplySds(c, sdsnew("-ERR value is not an integer or out of range\r\n")); } return REDIS_ERR; } *target = value; return REDIS_OK; }
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) { addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg)); } else { addReplySds(c, sdsnew("-ERR value is out of range\r\n")); } return REDIS_ERR; } *target = value; return REDIS_OK; }
void addReplyBulkLen(redisClient *c, robj *obj) { size_t len, intlen; char buf[128]; if (obj->encoding == REDIS_ENCODING_RAW) { len = sdslen(obj->ptr); } else { long n = (long)obj->ptr; /* Compute how many bytes will take this integer as a radix 10 string */ len = 1; if (n < 0) { len++; n = -n; } while((n = n/10) != 0) { len++; } } buf[0] = '$'; intlen = ll2string(buf+1,sizeof(buf)-1,(long long)len); buf[intlen+1] = '\r'; buf[intlen+2] = '\n'; addReplySds(c,sdsnewlen(buf,intlen+3)); }
void msetGenericCommand(redisClient *c, int nx) { int j, busykeys = 0; if ((c->argc % 2) == 0) { addReplySds(c,sdsnew("-ERR wrong number of arguments for MSET\r\n")); return; } /* Handle the NX flag. The MSETNX semantic is to return zero and don't * set nothing at all if at least one already key exists. */ if (nx) { for (j = 1; j < c->argc; j += 2) { if (lookupKeyWrite(c->db,c->argv[j]) != NULL) { busykeys++; } } } if (busykeys) { addReply(c, shared.czero); return; } for (j = 1; j < c->argc; j += 2) { c->argv[j+1] = tryObjectEncoding(c->argv[j+1]); dbReplace(c->db,c->argv[j],c->argv[j+1]); incrRefCount(c->argv[j+1]); removeExpire(c->db,c->argv[j]); } server.dirty += (c->argc-1)/2; addReply(c, nx ? shared.cone : shared.ok); }
void addReplyDouble(redisClient *c, double d) { char buf[128]; snprintf(buf,sizeof(buf),"%.17g",d); addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n%s\r\n", (unsigned long) strlen(buf),buf)); }
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { int retval; long seconds = 0; /* initialized to avoid an harmness warning */ if (expire) { if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK) return; if (seconds <= 0) { addReplySds(c,sdsnew("-ERR invalid expire time in SETEX\r\n")); return; } } touchWatchedKey(c->db,key); if (nx) deleteIfVolatile(c->db,key); retval = dbAdd(c->db,key,val); if (retval == REDIS_ERR) { if (!nx) { dbReplace(c->db,key,val); incrRefCount(val); } else { addReply(c,shared.czero); return; } } else { incrRefCount(val); } server.dirty++; removeExpire(c->db,key); if (expire) setExpire(c->db,key,time(NULL)+seconds); addReply(c, nx ? shared.cone : shared.ok); }
void execCommand(redisClient *c) { int j; robj **orig_argv; int orig_argc; if (!(c->flags & REDIS_MULTI)) { addReplySds(c,sdsnew("-ERR EXEC without MULTI\r\n")); 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; addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",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; call(c,c->mstate.commands[j].cmd); } c->argv = orig_argv; c->argc = orig_argc; 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++; }
void scriptCommand(redisClient *c) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"flush")) { scriptingReset(); addReply(c,shared.ok); replicationScriptCacheFlush(); server.dirty++; /* Propagating this command is a good idea. */ } else if (c->argc >= 2 && !strcasecmp(c->argv[1]->ptr,"exists")) { int j; addReplyMultiBulkLen(c, c->argc-2); for (j = 2; j < c->argc; j++) { if (dictFind(server.lua_scripts,c->argv[j]->ptr)) addReply(c,shared.cone); else addReply(c,shared.czero); } } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,"load")) { char funcname[43]; sds sha; funcname[0] = 'f'; funcname[1] = '_'; sha1hex(funcname+2,c->argv[2]->ptr,sdslen(c->argv[2]->ptr)); sha = sdsnewlen(funcname+2,40); if (dictFind(server.lua_scripts,sha) == NULL) { if (luaCreateFunction(c,server.lua,funcname,c->argv[2]) == REDIS_ERR) { sdsfree(sha); return; } } addReplyBulkCBuffer(c,funcname+2,40); sdsfree(sha); forceCommandPropagation(c,REDIS_PROPAGATE_REPL|REDIS_PROPAGATE_AOF); } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"kill")) { if (server.lua_caller == NULL) { addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n")); } else if (server.lua_write_dirty) { addReplySds(c,sdsnew("-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.\r\n")); } else { server.lua_kill = 1; addReply(c,shared.ok); } } else { addReplyError(c, "Unknown SCRIPT subcommand or wrong # of args."); } }
void multiCommand(redisClient *c) { if (c->flags & REDIS_MULTI) { addReplySds(c,sdsnew("-ERR MULTI calls can not be nested\r\n")); return; } c->flags |= REDIS_MULTI; addReply(c,shared.ok); }
void selectCommand(redisClient *c) { int id = atoi(c->argv[1]->ptr); if (selectDb(c,id) == REDIS_ERR) { addReplySds(c,sdsnew("-ERR invalid DB index\r\n")); } else { addReply(c,shared.ok); } }
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); } }
void replicationCron(void) { /* Bulk transfer I/O timeout? */ if (server.masterhost && server.replstate == REDIS_REPL_TRANSFER && (time(NULL)-server.repl_transfer_lastio) > REDIS_REPL_TIMEOUT) { redisLog(REDIS_WARNING,"Timeout receiving bulk data from MASTER..."); replicationAbortSyncTransfer(); } /* Timed out master when we are an already connected slave? */ if (server.masterhost && server.replstate == REDIS_REPL_CONNECTED && (time(NULL)-server.master->lastinteraction) > REDIS_REPL_TIMEOUT) { redisLog(REDIS_WARNING,"MASTER time out: no data nor PING received..."); freeClient(server.master); } /* Check if we should connect to a MASTER */ if (server.replstate == REDIS_REPL_CONNECT) { redisLog(REDIS_NOTICE,"Connecting to MASTER..."); if (syncWithMaster() == REDIS_OK) { redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started: SYNC sent"); if (server.appendonly) rewriteAppendOnlyFileBackground(); } } /* If we have attached slaves, PING them from time to time. * So slaves can implement an explicit timeout to masters, and will * be able to detect a link disconnection even if the TCP connection * will not actually go down. */ if (!(server.cronloops % (REDIS_REPL_PING_SLAVE_PERIOD*10))) { listIter li; listNode *ln; listRewind(server.slaves,&li); while((ln = listNext(&li))) { redisClient *slave = ln->value; /* Don't ping slaves that are in the middle of a bulk transfer * with the master for first synchronization. */ if (slave->replstate == REDIS_REPL_SEND_BULK) continue; if (slave->replstate == REDIS_REPL_ONLINE) { /* If the slave is online send a normal ping */ addReplySds(slave,sdsnew("PING\r\n")); } else { /* Otherwise we are in the pre-synchronization stage. * Just a newline will do the work of refreshing the * connection last interaction time, and at the same time * we'll be sure that being a single char there are no * short-write problems. */ write(slave->fd, "\n", 1); } } } }
void watchCommand(redisClient *c) { int j; if (c->flags & REDIS_MULTI) { addReplySds(c,sdsnew("-ERR WATCH inside MULTI is not allowed\r\n")); return; } for (j = 1; j < c->argc; j++) watchForKey(c,c->argv[j]); addReply(c,shared.ok); }
void saveCommand(redisClient *c) { if (server.bgsavechildpid != -1) { addReplySds(c,sdsnew("-ERR background save in progress\r\n")); return; } if (rdbSave(server.dbfilename) == REDIS_OK) { addReply(c,shared.ok); } else { addReply(c,shared.err); } }
void discardCommand(redisClient *c) { if (!(c->flags & REDIS_MULTI)) { addReplySds(c,sdsnew("-ERR DISCARD without MULTI\r\n")); return; } freeClientMultiState(c); initClientMultiState(c); c->flags &= (~REDIS_MULTI); unwatchAllKeys(c); addReply(c,shared.ok); }
void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) { redisClient *slave = privdata; REDIS_NOTUSED(el); REDIS_NOTUSED(mask); char buf[REDIS_IOBUF_LEN]; ssize_t nwritten, buflen; if (slave->repldboff == 0) { /* Write the bulk write count before to transfer the DB. In theory here * we don't know how much room there is in the output buffer of the * socket, but in pratice SO_SNDLOWAT (the minimum count for output * operations) will never be smaller than the few bytes we need. */ sds bulkcount; bulkcount = sdscatprintf(sdsempty(),"$%lld\r\n",(unsigned long long) slave->repldbsize); if (write(fd,bulkcount,sdslen(bulkcount)) != (signed)sdslen(bulkcount)) { sdsfree(bulkcount); freeClient(slave); return; } sdsfree(bulkcount); } lseek(slave->repldbfd,slave->repldboff,SEEK_SET); buflen = read(slave->repldbfd,buf,REDIS_IOBUF_LEN); if (buflen <= 0) { redisLog(REDIS_WARNING,"Read error sending DB to slave: %s", (buflen == 0) ? "premature EOF" : strerror(errno)); freeClient(slave); return; } if ((nwritten = write(fd,buf,buflen)) == -1) { redisLog(REDIS_VERBOSE,"Write error sending DB to slave: %s", strerror(errno)); freeClient(slave); return; } slave->repldboff += nwritten; if (slave->repldboff == slave->repldbsize) { close(slave->repldbfd); slave->repldbfd = -1; aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE); slave->replstate = REDIS_REPL_ONLINE; if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE, sendReplyToClient, slave) == AE_ERR) { freeClient(slave); return; } addReplySds(slave,sdsempty()); redisLog(REDIS_NOTICE,"Synchronization with slave succeeded"); } }
bool loadLuaHelperFile(cli *c, char *fname) { sds fwpath = sdscatprintf(sdsempty(), "%s%s", server.alc.Basedir, fname); bool ret = 1; if (luaL_loadfile(server.lua, fwpath) || DXDB_lua_pcall(server.lua, 0, 0, 0)) { const char *lerr = lua_tostring(server.lua, -1); if (c) addReplySds(c, sdscatprintf(sdsempty(), "-ERR luaL_loadfile: %s err: %s\r\n", fwpath, lerr)); else fprintf(stderr, "loadLuaHelperFile: %s err: %s\r\n", fwpath, lerr); ret = 0; } CLEAR_LUA_STACK sdsfree(fwpath); return ret; }
void addReplyUlong(redisClient *c, unsigned long ul) { char buf[128]; size_t len; if (ul == 0) { addReply(c,shared.czero); return; } else if (ul == 1) { addReply(c,shared.cone); return; } len = snprintf(buf,sizeof(buf),":%lu\r\n",ul); addReplySds(c,sdsnewlen(buf,len)); }
/* Blocking RPOP/LPOP */ static void blockingPopGenericCommand(redisClient *c, int where) { robj *o; time_t timeout; int j; for (j = 1; j < c->argc-1; j++) { o = lookupKeyWrite(c->db,c->argv[j]); if (o != NULL) { if (o->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); return; } else { list *list = o->ptr; if (listLength(list) != 0) { /* If the list contains elements fall back to the usual * non-blocking POP operation */ robj *argv[2], **orig_argv; int orig_argc; /* We need to alter the command arguments before to call * popGenericCommand() as the command takes a single key. */ orig_argv = c->argv; orig_argc = c->argc; argv[1] = c->argv[j]; c->argv = argv; c->argc = 2; /* Also the return value is different, we need to output * the multi bulk reply header and the key name. The * "real" command will add the last element (the value) * for us. If this souds like an hack to you it's just * because it is... */ addReplySds(c,sdsnew("*2\r\n")); addReplyBulk(c,argv[1]); popGenericCommand(c,where); /* Fix the client structure with the original stuff */ c->argv = orig_argv; c->argc = orig_argc; return; } } } } /* If the list is empty or the key does not exists we must block */ timeout = strtol(c->argv[c->argc-1]->ptr,NULL,10); if (timeout > 0) timeout += time(NULL); blockForKeys(c,c->argv+1,c->argc-2,timeout); }
void substrCommand(redisClient *c) { robj *o; long start = atoi(c->argv[2]->ptr); long end = atoi(c->argv[3]->ptr); size_t rangelen, strlen; sds range; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,o,REDIS_STRING)) return; o = getDecodedObject(o); strlen = sdslen(o->ptr); /* convert negative indexes */ if (start < 0) start = strlen+start; if (end < 0) end = strlen+end; if (start < 0) start = 0; if (end < 0) end = 0; /* indexes sanity checks */ if (start > end || (size_t)start >= strlen) { /* Out of range start or start > end result in null reply */ addReply(c,shared.nullbulk); decrRefCount(o); return; } if ((size_t)end >= strlen) end = strlen-1; rangelen = (end-start)+1; /* Return the result */ addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen)); range = sdsnewlen((char*)o->ptr+start,rangelen); addReplySds(c,range); addReply(c,shared.crlf); decrRefCount(o); }
void addReplyLongLong(redisClient *c, long long ll) { char buf[128]; size_t len; if (ll == 0) { addReply(c,shared.czero); return; } else if (ll == 1) { addReply(c,shared.cone); return; } buf[0] = ':'; len = ll2string(buf+1,sizeof(buf)-1,ll); buf[len+1] = '\r'; buf[len+2] = '\n'; addReplySds(c,sdsnewlen(buf,len+3)); }
void mgetCommand(redisClient *c) { int j; addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1)); for (j = 1; j < c->argc; j++) { robj *o = lookupKeyRead(c->db,c->argv[j]); if (o == NULL) { addReply(c,shared.nullbulk); } else { if (o->type != REDIS_STRING) { addReply(c,shared.nullbulk); } else { addReplyBulk(c,o); } } } }
/* Mass-unblock clients because something changed in the instance that makes * blocking no longer safe. For example clients blocked in list operations * in an instance which turns from master to slave is unsafe, so this function * is called when a master turns into a slave. * * The semantics is to send an -UNBLOCKED error to the client, disconnecting * it at the same time. */ void disconnectAllBlockedClients(void) { listNode *ln; listIter li; listRewind(server.clients,&li); while((ln = listNext(&li))) { client *c = listNodeValue(ln); if (c->flags & CLIENT_BLOCKED) { addReplySds(c,sdsnew( "-UNBLOCKED force unblock from blocking operation, " "instance state changed (master -> slave?)\r\n")); unblockClient(c); c->flags |= CLIENT_CLOSE_AFTER_REPLY; } } }
/* This should be called from any function PUSHing into lists. * 'c' is the "pushing client", 'key' is the key it is pushing data against, * 'ele' is the element pushed. * * If the function returns 0 there was no client waiting for a list push * against this key. * * If the function returns 1 there was a client waiting for a list push * against this key, the element was passed to this client thus it's not * needed to actually add it to the list and the caller should return asap. */ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) { struct dictEntry *de; redisClient *receiver; list *l; listNode *ln; de = dictFind(c->db->blocking_keys,key); if (de == NULL) return 0; l = dictGetEntryVal(de); ln = listFirst(l); redisAssert(ln != NULL); receiver = ln->value; addReplySds(receiver,sdsnew("*2\r\n")); addReplyBulk(receiver,key); addReplyBulk(receiver,ele); unblockClientWaitingData(receiver); return 1; }
void slaveofCommand(redisClient *c) { if (!strcasecmp(c->argv[1]->ptr,"no") && !strcasecmp(c->argv[2]->ptr,"one")) { if (server.masterhost) { sdsfree(server.masterhost); server.masterhost = NULL; if (server.master) freeClient(server.master); if (server.repl_state == REDIS_REPL_TRANSFER) replicationAbortSyncTransfer(); else if (server.repl_state == REDIS_REPL_CONNECTING || server.repl_state == REDIS_REPL_RECEIVE_PONG) undoConnectWithMaster(); server.repl_state = REDIS_REPL_NONE; redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)"); } } else { long port; if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != REDIS_OK)) return; /* Check if we are already attached to the specified slave */ if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr) && server.masterport == port) { redisLog(REDIS_NOTICE,"SLAVE OF would result into synchronization with the master we are already connected with. No operation performed."); addReplySds(c,sdsnew("+OK Already connected to specified master\r\n")); return; } /* There was no previous master or the user specified a different one, * we can continue. */ sdsfree(server.masterhost); server.masterhost = sdsdup(c->argv[1]->ptr); server.masterport = port; if (server.master) freeClient(server.master); disconnectSlaves(); /* Force our slaves to resync with us as well. */ if (server.repl_state == REDIS_REPL_TRANSFER) replicationAbortSyncTransfer(); server.repl_state = REDIS_REPL_CONNECT; redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)", server.masterhost, server.masterport); } addReply(c,shared.ok); }