/* 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; }
static bool sendRestAPIReply(cli *c, sds file) { //printf("sendRestAPIReply\n"); int argc; bool ret = 0; sds pb = c->http.post_body; //TODO cat [file,pb] is too much copying sds url = pb ? sdscatprintf(sdsempty(), "%s%s", file, pb) : sdsdup(file); //FREE 156 sds *argv = sdssplitlen(url, sdslen(url), "/", 1, &argc); //FREE 157 rcommand *cmd = lookupCommand(argv[0]); if (!cmd) goto send_rest_end; ret = 1; //printf("sendRestAPIReply: found cmd: %s\n", cmd->name); if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) { addReplyErrorFormat(c,"wrong number of arguments for '%s' command", cmd->name); goto send_rest_end; } //NOTE: rest is copy of redis.c processCommand() if (server.maxmemory) freeMemoryIfNeeded(); if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) && zmalloc_used_memory() > server.maxmemory) { addReplyError(c, "command not allowed when used memory > 'maxmemory'"); goto send_rest_end; } if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0) && cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand && cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) { addReplyError(c, "only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context"); goto send_rest_end; } if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED && server.repl_serve_stale_data == 0 && cmd->proc != infoCommand && cmd->proc != slaveofCommand) { addReplyError(c, "link with MASTER is down and slave-serve-stale-data is set to no"); goto send_rest_end; } if (server.loading && cmd->proc != infoCommand) { addReply(c, shared.loadingerr); goto send_rest_end; } if (c->flags & REDIS_MULTI && cmd->proc != execCommand && cmd->proc != discardCommand && cmd->proc != multiCommand && cmd->proc != watchCommand) { queueMultiCommand(c, cmd); addReply(c, shared.queued); goto send_rest_end; } listNode *ln; listIter *li; cli *restc = server.alc.RestClient; // 1.) call() cmd in RestClient { // REST CLIENT call robj **rargv = zmalloc(sizeof(robj *) * argc); for (int i = 0; i < argc; i++) { rargv[i] = createStringObject(argv[i], sdslen(argv[i])); } restc->argc = argc; restc->argv = rargv; call(restc, cmd); for (int i = 0; i < argc; i++) decrRefCount(rargv[i]); zfree(rargv); } // 2.) calculate Content-Length from RestClient's response ulong brlen = restc->bufpos; ulong trlen = brlen; if (restc->reply->len) { li = listGetIterator(restc->reply, AL_START_HEAD); while((ln = listNext(li))) { robj *r = ln->value; trlen += sdslen(r->ptr); } listReleaseIterator(li); } bool err = brlen && (*restc->buf == '-'); //TODO check for "+OK" and return 201 w/ no body // 3.) create header w/ Content-Length sds s = err ? send_http404_reponse_header(c, trlen) : send_http200_reponse_header(c, trlen); robj *ho = createObject(REDIS_STRING, s); addReply(c, ho); decrRefCount(ho); // 4.) tack on RestClient's response as HTTP Body if (brlen) { addReplyString(c, restc->buf, brlen); } if (restc->reply->len) { li = listGetIterator(restc->reply, AL_START_HEAD); while((ln = listNext(li))) { robj *r = ln->value; addReply(c, r); } listReleaseIterator(li); } // 5.) reset RestClient restc->bufpos = 0; while (restc->reply->len) { listDelNode(restc->reply, listFirst(restc->reply)); } send_rest_end: sdsfree(url); // FREE 156 for (int i = 0; i < argc; i++) sdsfree(argv[i]); zfree(argv); // FREE 157 return ret; }