/* 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"); } }
/* Output the representation of a Redis command. Used by the COMMAND command. */ static void addReplyCommand(client *c, struct redisCommand *cmd) { if (!cmd) { addReply(c, shared.nullbulk); } else { /* We are adding: command name, arg count, flags, first, last, offset */ addReplyMultiBulkLen(c, 6); addReplyBulkCString(c, cmd->name); addReplyLongLong(c, cmd->arity); int flagcount = 0; void *flaglen = addDeferredMultiBulkLength(c); flagcount += addReplyCommandFlag(c,cmd,CMD_WRITE, "write"); flagcount += addReplyCommandFlag(c,cmd,CMD_READONLY, "readonly"); flagcount += addReplyCommandFlag(c,cmd,CMD_DENYOOM, "denyoom"); flagcount += addReplyCommandFlag(c,cmd,CMD_ADMIN, "admin"); flagcount += addReplyCommandFlag(c,cmd,CMD_PUBSUB, "pubsub"); flagcount += addReplyCommandFlag(c,cmd,CMD_NOSCRIPT, "noscript"); flagcount += addReplyCommandFlag(c,cmd,CMD_RANDOM, "random"); flagcount += addReplyCommandFlag(c,cmd,CMD_SORT_FOR_SCRIPT,"sort_for_script"); flagcount += addReplyCommandFlag(c,cmd,CMD_LOADING, "loading"); flagcount += addReplyCommandFlag(c,cmd,CMD_STALE, "stale"); flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_MONITOR, "skip_monitor"); flagcount += addReplyCommandFlag(c,cmd,CMD_ASKING, "asking"); flagcount += addReplyCommandFlag(c,cmd,CMD_FAST, "fast"); if (cmd->getkeys_proc) { addReplyStatus(c, "movablekeys"); flagcount += 1; } setDeferredMultiBulkLength(c, flaglen, flagcount); addReplyLongLong(c, cmd->firstkey); addReplyLongLong(c, cmd->lastkey); addReplyLongLong(c, cmd->keystep); } }
/* Object command allows to inspect the internals of an Redis Object. * Usage: OBJECT <refcount|encoding|idletime> <key> */ void objectCommand(client *c) { robj *o; if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { fetchInternalDbByKey(c,c->argv[2]); lockDbRead(c->db); if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) { unlockDb(c->db); return; } addReplyBulkCString(c,strEncoding(o->encoding)); unlockDb(c->db); } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { fetchInternalDbByKey(c,c->argv[2]); lockDbRead(c->db); if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) { unlockDb(c->db); return; } addReplyLongLong(c,estimateObjectIdleTime(o)/1000); unlockDb(c->db); } else { addReplyError(c,"Syntax error. Try OBJECT (encoding|idletime)"); } }
/* 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); }
void jsonfieldsetCommand(redisClient *c) { /* args 0-N: ["jsonfieldset", json field components, new-json] */ jsonSyncClients(c); if (!validateKeyFormatAndReply(c, c->argv[1]->ptr)) return; /* sds key = genFieldAccessor(c, 1); */ addReplyBulkCString(c, "Thanks for your interest in the JSONFIELDSET " "command, but it is currently not implemented."); }
/* Add a double as a bulk reply */ void addReplyDouble(client *c, double d) { char dbuf[128], sbuf[128]; int dlen, slen; if (isinf(d)) { /* Libc in odd systems (Hi Solaris!) will format infinite in a * different way, so better to handle it in an explicit way. */ addReplyBulkCString(c, d > 0 ? "inf" : "-inf"); } else { dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d); slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf); addReplyString(c,sbuf,slen); } }
//TODO webserver_mode, webserver_index_function, // webserver_whitelist_address, webserver_whitelist_netmask, // sqlslaveof // lua_output_start lua_output_cnames lua_output_row rest_api_mode void DXDB_configGetCommand(redisClient *c, char *pattern, int *matches) { if (stringmatch(pattern, "luacronfunc", 0)) { addReplyBulkCString(c, "luafronfunc"); addReplyBulkCString(c, server.alc.LuaCronFunc); *matches = *matches + 1; } if (stringmatch(pattern, "outputmode", 0)) { addReplyBulkCString(c, "outputmode"); if (EREDIS) addReplyBulkCString(c, "embedded"); else if (OREDIS) addReplyBulkCString(c, "pure_redis"); else if (LREDIS) addReplyBulkCString(c, "lua"); else addReplyBulkCString(c, "normal"); *matches = *matches + 1; } }
/* Object command allows to inspect the internals of an Redis Object. * Usage: OBJECT <refcount|encoding|idletime> <key> */ void objectCommand(client *c) { robj *o; if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) return; addReplyLongLong(c,o->refcount); } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) return; addReplyBulkCString(c,strEncoding(o->encoding)); } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) return; addReplyLongLong(c,estimateObjectIdleTime(o)/1000); } else { addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); } }
/* latencyCommand() helper to produce the reply for the LATEST subcommand, * listing the last latency sample for every event type registered so far. */ void latencyCommandReplyWithLatestEvents(client *c) { dictIterator *di; dictEntry *de; addReplyMultiBulkLen(c,dictSize(server.latency_events)); di = dictGetIterator(server.latency_events); while((de = dictNext(di)) != NULL) { char *event = dictGetKey(de); struct latencyTimeSeries *ts = dictGetVal(de); int last = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN; addReplyMultiBulkLen(c,4); addReplyBulkCString(c,event); addReplyLongLong(c,ts->samples[last].time); addReplyLongLong(c,ts->samples[last].latency); addReplyLongLong(c,ts->max); } dictReleaseIterator(di); }
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); }