Exemplo n.º 1
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.º 2
0
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;
}