int main(int argc, char **argv) { int firstarg; char **argvcopy; struct redisCommand *rc; config.hostip = "127.0.0.1"; config.hostport = 6379; config.repeat = 1; config.dbnum = 0; config.interactive = 0; config.auth = NULL; firstarg = parseOptions(argc,argv); argc -= firstarg; argv += firstarg; if (argc == 0 || config.interactive == 1) repl(); argvcopy = convertToSds(argc, argv); /* Read the last argument from stdandard input if needed */ if ((rc = lookupCommand(argv[0])) != NULL) { if (rc->arity > 0 && argc == rc->arity-1) { sds lastarg = readArgFromStdin(); argvcopy[argc] = lastarg; argc++; } } return cliSendCommand(argc, argvcopy); }
int processCommand(ugClient *c) { if (!strcasecmp(c->argv[0]->ptr,"quit")) { addReply(c,shared.ok); c->flags |= UG_CLOSE_AFTER_REPLY; return UGERR; } if (c->authenticated == 0 && strcasecmp(c->argv[0]->ptr, "auth") != 0) { addReplyError(c,"NOAUTH Authentication required. "); return UGOK; } c->cmd = lookupCommand((sds)c->argv[0]->ptr) ; if (!c->cmd) { addReplyErrorFormat(c,"unknown command '%s'", (char *)c->argv[0]->ptr); return UGOK; } else if (c->cmd->params != -1 && c->argc-1 != c->cmd->params) { addReplyErrorFormat(c,"wrong number of arguments for '%s' command", c->cmd->name); return UGOK; } call(c, 0); /* must has reply */ ugAssert(strlen(c->buf) || listLength(c->reply)); //if (addReply(c, ) == UGERR) { // c->flags |= UG_CLOSE_AFTER_REPLY; // return UGERR; //} return UGOK; }
void receiveTranslatedWriteRequest(cJSON* nameObject, cJSON* root) { char* name = nameObject->valuestring; cJSON* value = cJSON_GetObjectItem(root, "value"); // Optional, may be NULL cJSON* event = cJSON_GetObjectItem(root, "event"); CanSignal* signal = lookupSignal(name, getSignals(), getSignalCount(), true); if(signal != NULL) { if(value == NULL) { debug("Write request for %s missing value", name); return; } sendCanSignal(signal, value, getSignals(), getSignalCount()); } else { CanCommand* command = lookupCommand(name, getCommands(), getCommandCount()); if(command != NULL) { command->handler(name, value, event, getSignals(), getSignalCount()); } else { debug("Writing not allowed for signal with name %s", name); } } }
int Options::handleFixed( SimpleArg &arg, int & /*argno*/ ) { if ( ST_CMD == m_state ) { if ( !lookupCommand( arg ) ) { fprintf(stderr, "Invalid command '%s'\n", arg.Arg() ); printf("%s", m_usage); m_state = ST_NONE; return -1; } if ( CMD_LIST == m_command ) { m_state = ST_NONE; return 1; } else if ( CMD_DUMP == m_command ) { m_field = lookupField( "all" ); if ( NULL == m_field ) { assert( 0 ); } } m_state = ST_FILE; } else if ( ST_FILE == m_state ) { m_file = arg.Arg(); m_state = ST_FIELD; } else if ( ST_FIELD == m_state ) { m_field = lookupField( arg ); if ( NULL == m_field ) { fprintf(stderr, "Invalid field '%s'\n", arg.Arg() ); printf("%s", m_usage); m_state = ST_NONE; return -1; } if ( 0 == m_field->m_max_values ) { m_state = ST_NONE; return 1; } m_state = ST_VALUES; } else if ( ( ST_VALUES == m_state ) && ( m_field ) && ( m_num_values < m_field->m_max_values ) ) { if ( !parseValue( arg ) ) { fprintf(stderr, "Invalid value for %s: '%s'\n", m_field->m_name, arg.Arg() ); return -1; } m_num_values++; } else { fprintf(stderr, "Unexpected arg '%s'\n", arg.Arg() ); return -1; } return 0; }
/* This gets called when the function "client)" gets called in Lua */ int luaCall_client(lua_State *L) { LuaFlag = PIPE_NONE_FLAG; int argc = lua_gettop(L); const char *arg1 = lua_tostring(L, 1); if (!arg1) { return redisLuaArityErr(L, NULL); } redisCommand *cmd = lookupCommand((char *)arg1); if (!cmd) { char buf[64]; snprintf(buf, 63, "-ERR Unknown command '%s'\r\n", arg1); buf[63] = '\0'; lua_pushstring(L, buf); LuaFlag = PIPE_ONE_LINER_FLAG; return 1; } else if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) { return redisLuaArityErr(L, cmd->name); } if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) && (zmalloc_used_memory() > server.maxmemory)) { LuaFlag = PIPE_ONE_LINER_FLAG; lua_pushstring(L, "-ERR command not allowed when used memory > 'maxmemory'\r\n"); return 1; } if (server.vm_enabled && server.vm_max_threads > 0 && blockClientOnSwappedKeys(LuaClient, cmd)) return 1; long ok = 0; /* must come before first GOTO */ redisClient *rfc = rsql_createFakeClient(); robj **rargv = zmalloc(sizeof(robj *) * argc); for (int i = 0; i < argc; i++) { char *arg = (char *)lua_tostring(L, i + 1); if (!arg) { char *lbuf = "args must be strings"; luaL_argerror (L, i, lbuf); LuaFlag = PIPE_ONE_LINER_FLAG; ok = 1; goto redis_lua_end; } rargv[i] = _createStringObject(arg); } rfc->argv = rargv; rfc->argc = argc; ok = fakeClientPipe(LuaClient, rfc, L, 0, &LuaFlag, luaLine, emptyNoop); redis_lua_end: for (int i = 0; i < argc; i++) decrRefCount(rargv[i]); zfree(rargv); rsql_freeFakeClient(rfc); return (ok > 0) ? 1 : 0; }
/* Send a MULTI command to all the slaves and AOF file. Check the execCommand * implememntation for more information. */ void execCommandReplicateMulti(redisClient *c) { struct redisCommand *cmd; robj *multistring = createStringObject("MULTI",5); cmd = lookupCommand("multi"); if (server.appendonly) feedAppendOnlyFile(cmd,c->db->id,&multistring,1); if (listLength(server.slaves)) replicationFeedSlaves(server.slaves,c->db->id,&multistring,1); decrRefCount(multistring); }
static int cliSendCommand(int argc, char **argv) { struct redisCommand *rc = lookupCommand(argv[0]); int fd, j, retval = 0; sds cmd; if (!rc) { fprintf(stderr,"Unknown command '%s'\n",argv[0]); return 1; } if ((rc->arity > 0 && argc != rc->arity) || (rc->arity < 0 && argc < -rc->arity)) { fprintf(stderr,"Wrong number of arguments for '%s'\n",rc->name); return 1; } if ((fd = cliConnect()) == -1) return 1; while(config.repeat--) { /* Build the command to send */ cmd = sdsempty(); if (rc->flags & REDIS_CMD_MULTIBULK) { cmd = sdscatprintf(cmd,"*%d\r\n",argc); for (j = 0; j < argc; j++) { cmd = sdscatprintf(cmd,"$%d\r\n",sdslen(argv[j])); cmd = sdscatlen(cmd,argv[j],sdslen(argv[j])); cmd = sdscatlen(cmd,"\r\n",2); } } else { for (j = 0; j < argc; j++) { if (j != 0) cmd = sdscat(cmd," "); if (j == argc-1 && rc->flags & REDIS_CMD_BULK) { cmd = sdscatprintf(cmd,"%d",sdslen(argv[j])); } else { cmd = sdscatlen(cmd,argv[j],sdslen(argv[j])); } } cmd = sdscat(cmd,"\r\n"); if (rc->flags & REDIS_CMD_BULK) { cmd = sdscatlen(cmd,argv[argc-1],sdslen(argv[argc-1])); cmd = sdscatlen(cmd,"\r\n",2); } } anetWrite(fd,cmd,sdslen(cmd)); sdsfree(cmd); retval = cliReadReply(fd); if (retval) { close(fd); return retval; } } close(fd); return 0; }
static int cliSendCommand(int argc, char **argv) { struct redisCommand *rc = lookupCommand(argv[0]); int fd, j, retval = 0; sds cmd = sdsempty(); if (!rc) { fprintf(stderr,"Unknown command '%s'\n",argv[0]); return 1; } if ((rc->arity > 0 && argc != rc->arity) || (rc->arity < 0 && argc < -rc->arity)) { fprintf(stderr,"Wrong number of arguments for '%s'\n",rc->name); return 1; } if ((fd = cliConnect()) == -1) return 1; /* Build the command to send */ for (j = 0; j < argc; j++) { if (j != 0) cmd = sdscat(cmd," "); if (j == argc-1 && rc->flags & REDIS_CMD_BULK) { cmd = sdscatprintf(cmd,"%d",sdslen(argv[j])); } else { cmd = sdscatlen(cmd,argv[j],sdslen(argv[j])); } } cmd = sdscat(cmd,"\r\n"); if (rc->flags & REDIS_CMD_BULK) { cmd = sdscatlen(cmd,argv[argc-1],sdslen(argv[argc-1])); cmd = sdscat(cmd,"\r\n"); } anetWrite(fd,cmd,sdslen(cmd)); if (rc->flags & REDIS_CMD_INTREPLY) { retval = cliReadInlineReply(fd,REDIS_CMD_INTREPLY); } else if (rc->flags & REDIS_CMD_RETCODEREPLY) { retval = cliReadInlineReply(fd,REDIS_CMD_RETCODEREPLY); } else if (rc->flags & REDIS_CMD_SINGLELINEREPLY) { retval = cliReadInlineReply(fd,REDIS_CMD_SINGLELINEREPLY); } else if (rc->flags & REDIS_CMD_BULKREPLY) { retval = cliReadBulkReply(fd,0); } else if (rc->flags & REDIS_CMD_MULTIBULKREPLY) { retval = cliReadMultiBulkReply(fd); } if (retval) { close(fd); return retval; } close(fd); return 0; }
static void runCmdInFakeClient(sds s) { //RL4 "runCmdInFakeClient: %s", s); int argc; sds *argv = sdssplitlen(s, sdslen(s), " ", 1, &argc); // FREEME 017 if (!argv) return; if (argc < 1) goto run_cmd_end; redisCommand *cmd = lookupCommand(argv[0]); if (!cmd) goto run_cmd_end; if ((cmd->arity > 0 && cmd->arity > argc) || (argc < -cmd->arity)) goto run_cmd_end; int arity; robj **rargv; if (cmd->arity > 0 || cmd->proc == insertCommand || cmd->proc == sqlSelectCommand || cmd->proc == tscanCommand) { arity = abs(cmd->arity); rargv = zmalloc(sizeof(robj *) * arity); /* ZFREE ME 018 */ for (int j = 0; j < arity - 1; j++) { rargv[j] = createStringObject(argv[j], sdslen(argv[j])); // FREE 019 } sds lastarg = sdsempty(); for (int j = arity - 1; j < argc; j++) { if (j != (arity - 1)) lastarg = sdscatlen(lastarg, " ", 1); lastarg = sdscatlen(lastarg, argv[j], sdslen(argv[j])); } rargv[arity - 1] = createObject(REDIS_STRING, lastarg); // FREE 019 } else { arity = argc; rargv = zmalloc(sizeof(robj *) * arity); /* ZFREE ME 018 */ for (int j = 0; j < arity; j++) { rargv[j] = createStringObject(argv[j], sdslen(argv[j])); // FREE 019 } } redisClient *c = CurrClient; redisClient *fc = rsql_createFakeClient(); /* DESTROY ME 020 */ fc->argv = rargv; fc->argc = arity; fakeClientPipe(c, fc, NULL, 0, &NriFlag, nonRelIndRespHandler, emptyNonRelIndRespHandler); rsql_freeFakeClient(fc); /* DESTROYED 020 */ for (int j = 0; j < arity; j++) decrRefCount(rargv[j]); /* FREED 019 */ zfree(rargv); /* ZFREED 018 */ run_cmd_end: for (int j = 0; j < argc; j++) sdsfree(argv[j]); /* FREED 017 */ zfree(argv); /* FREED 017 */ }
/* Propagate expires into slaves and the AOF file. * When a key expires in the master, a DEL operation for this key is sent * to all the slaves and the AOF file if enabled. * * This way the key expiry is centralized in one place, and since both * AOF and the master->slave link guarantee operation ordering, everything * will be consistent even if we allow write operations against expiring * keys. */ void propagateExpire(redisDb *db, robj *key) { struct redisCommand *cmd; robj *argv[2]; cmd = lookupCommand("del"); argv[0] = createStringObject("DEL",3); argv[1] = key; incrRefCount(key); if (server.appendonly) feedAppendOnlyFile(cmd,db->id,argv,2); if (listLength(server.slaves)) replicationFeedSlaves(server.slaves,db->id,argv,2); decrRefCount(argv[0]); decrRefCount(argv[1]); }
int main(int argc, char **argv) { int firstarg; char **argvcopy; struct redisCommand *rc; config.hostip = "127.0.0.1"; config.hostport = 6379; config.repeat = 1; config.dbnum = 0; config.shutdown = 0; config.interactive = 0; config.monitor_mode = 0; config.pubsub_mode = 0; config.raw_output = 0; config.auth = NULL; firstarg = parseOptions(argc,argv); argc -= firstarg; argv += firstarg; if (config.auth != NULL) { char *authargv[2]; authargv[0] = "AUTH"; authargv[1] = config.auth; cliSendCommand(2, convertToSds(2, authargv), 1); } if (argc == 0 || config.interactive == 1) repl(); argvcopy = convertToSds(argc, argv); /* Read the last argument from stdandard input if needed */ if ((rc = lookupCommand(argv[0])) != NULL) { if (rc->arity > 0 && argc == rc->arity-1) { sds lastarg = readArgFromStdin(); argvcopy[argc] = lastarg; argc++; } } return cliSendCommand(argc, argvcopy, config.repeat); }
/* 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; } }
int main(int argc, char **argv) { int firstarg, j; char **argvcopy; struct redisCommand *rc; config.hostip = "127.0.0.1"; config.hostport = 6379; config.repeat = 1; firstarg = parseOptions(argc,argv); argc -= firstarg; argv += firstarg; /* Turn the plain C strings into Sds strings */ argvcopy = zmalloc(sizeof(char*)*argc+1); for(j = 0; j < argc; j++) argvcopy[j] = sdsnew(argv[j]); if (argc < 1) { fprintf(stderr, "usage: redis-cli [-h host] [-p port] [-r repeat_times] cmd arg1 arg2 arg3 ... argN\n"); fprintf(stderr, "usage: echo \"argN\" | redis-cli [-h host] [-p port] -r [repeat_times] cmd arg1 arg2 ... arg(N-1)\n"); fprintf(stderr, "\nIf a pipe from standard input is detected this data is used as last argument.\n\n"); fprintf(stderr, "example: cat /etc/passwd | redis-cli set my_passwd\n"); fprintf(stderr, "example: redis-cli get my_passwd\n"); fprintf(stderr, "example: redis-cli -r 100 lpush mylist x\n"); exit(1); } /* Read the last argument from stdandard input if needed */ if ((rc = lookupCommand(argv[0])) != NULL) { if (rc->arity > 0 && argc == rc->arity-1) { sds lastarg = readArgFromStdin(); argvcopy[argc] = lastarg; argc++; } } return cliSendCommand(argc, argvcopy); }
/* NOTE: this function implements a fakeClient pipe */ bool fakeClientPipe(redisClient *c, redisClient *rfc, void *wfc, /* can be redisClient or sumthin else */ bool is_ins, bool (* adder) (redisClient *c, void *x, robj *key, long *l, bool b)) { struct redisCommand *cmd = lookupCommand(rfc->argv[0]->ptr); cmd->proc(rfc); long instd = 1; /* ZER0 as pk is sometimes bad */ listNode *ln; listIter li; robj *o; listRewind(rfc->reply,&li); while((ln = listNext(&li))) { o = ln->value; sds s = o->ptr; /* ignore protocol, we just want data */ if (*s == '*' || *s == '\r') continue; if (*s == '-') break; /* error */ if (*s == '$') { /* parse doubles which are w/in this list element */ char *x = strchr(s, '\r'); uint32 line_len = x - s; if (line_len + 2 < sdslen(s)) { /* got a double */ x += 2; /* move past \r\n */ char *y = strchr(x, '\r'); /* kill the final \r\n */ *y = '\0'; robj *r = createStringObject(x, strlen(x)); if (!(*adder)(c, wfc, r, &instd, is_ins)) return 0; } continue; } /* all ranges are single */ if (!(*adder)(c, wfc, o, &instd, is_ins)) return 0; } return 1; }
static int cliSendCommand(int argc, char **argv, int repeat) { struct redisCommand *rc = lookupCommand(argv[0]); int fd, j, retval = 0; sds cmd; if (!rc) { fprintf(stderr,"Unknown command '%s'\n",argv[0]); return 1; } config.raw_output = (rc->flags & CMDFLAG_RAWOUTPUT); if ((rc->arity > 0 && argc != rc->arity) || (rc->arity < 0 && argc < -rc->arity)) { fprintf(stderr,"Wrong number of arguments for '%s'\n",rc->name); return 1; } if (!strcasecmp(rc->name,"shutdown")) config.shutdown = 1; if (!strcasecmp(rc->name,"monitor")) config.monitor_mode = 1; if (!strcasecmp(rc->name,"subscribe") || !strcasecmp(rc->name,"psubscribe")) config.pubsub_mode = 1; if ((fd = cliConnect()) == -1) return 1; /* Select db number */ retval = selectDb(fd); if (retval) { fprintf(stderr,"Error setting DB num\n"); return 1; } while(repeat--) { /* Build the command to send */ cmd = sdscatprintf(sdsempty(),"*%d\r\n",argc); for (j = 0; j < argc; j++) { cmd = sdscatprintf(cmd,"$%lu\r\n", (unsigned long)sdslen(argv[j])); cmd = sdscatlen(cmd,argv[j],sdslen(argv[j])); cmd = sdscatlen(cmd,"\r\n",2); } anetWrite(fd,cmd,sdslen(cmd)); sdsfree(cmd); while (config.monitor_mode) { cliReadSingleLineReply(fd,0); } if (config.pubsub_mode) { printf("Reading messages... (press Ctrl-c to quit)\n"); while (1) { cliReadReply(fd); printf("\n"); } } retval = cliReadReply(fd); if (retval) { return retval; } } return 0; }
/* Replay the append log file. On error REDIS_OK is returned. On non fatal * error (the append only file is zero-length) REDIS_ERR is returned. On * fatal error an error message is logged and the program exists. */ int loadAppendOnlyFile(char *filename) { struct redisClient *fakeClient; FILE *fp = fopen(filename,"r"); struct redis_stat sb; int old_aof_state = server.aof_state; long loops = 0; if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) { server.aof_current_size = 0; fclose(fp); return REDIS_ERR; } if (fp == NULL) { redisLog(REDIS_WARNING,"Fatal error: can't open the append log file for reading: %s",strerror(errno)); exit(1); } /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI * to the same file we're about to read. */ server.aof_state = REDIS_AOF_OFF; fakeClient = createFakeClient(); startLoading(fp); while(1) { int argc, j; unsigned long len; robj **argv; char buf[128]; sds argsds; struct redisCommand *cmd; /* Serve the clients from time to time */ if (!(loops++ % 1000)) { loadingProgress(ftello(fp)); processEventsWhileBlocked(); } if (fgets(buf,sizeof(buf),fp) == NULL) { if (feof(fp)) break; else goto readerr; } if (buf[0] != '*') goto fmterr; if (buf[1] == '\0') goto readerr; argc = atoi(buf+1); if (argc < 1) goto fmterr; argv = zmalloc(sizeof(robj*)*argc); fakeClient->argc = argc; fakeClient->argv = argv; for (j = 0; j < argc; j++) { if (fgets(buf,sizeof(buf),fp) == NULL) { fakeClient->argc = j; /* Free up to j-1. */ freeFakeClientArgv(fakeClient); goto readerr; } if (buf[0] != '$') goto fmterr; len = strtol(buf+1,NULL,10); argsds = sdsnewlen(NULL,len); if (len && fread(argsds,len,1,fp) == 0) { sdsfree(argsds); fakeClient->argc = j; /* Free up to j-1. */ freeFakeClientArgv(fakeClient); goto readerr; } argv[j] = createObject(REDIS_STRING,argsds); if (fread(buf,2,1,fp) == 0) { fakeClient->argc = j+1; /* Free up to j. */ freeFakeClientArgv(fakeClient); goto readerr; /* discard CRLF */ } } /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd) { redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr); exit(1); } /* Run the command in the context of a fake client */ cmd->proc(fakeClient); /* The fake client should not have a reply */ redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0); /* The fake client should never get blocked */ redisAssert((fakeClient->flags & REDIS_BLOCKED) == 0); /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ freeFakeClientArgv(fakeClient); } /* This point can only be reached when EOF is reached without errors. * If the client is in the middle of a MULTI/EXEC, log error and quit. */ if (fakeClient->flags & REDIS_MULTI) goto uxeof; loaded_ok: /* DB loaded, cleanup and return REDIS_OK to the caller. */ fclose(fp); freeFakeClient(fakeClient); server.aof_state = old_aof_state; stopLoading(); aofUpdateCurrentSize(); server.aof_rewrite_base_size = server.aof_current_size; return REDIS_OK; readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */ if (!feof(fp)) { redisLog(REDIS_WARNING,"Unrecoverable error reading the append only file: %s", strerror(errno)); exit(1); } uxeof: /* Unexpected AOF end of file. */ if (server.aof_load_truncated) { redisLog(REDIS_WARNING,"!!! Warning: short read while loading the AOF file !!!"); redisLog(REDIS_WARNING, "AOF loaded anyway because aof-load-truncated is enabled"); goto loaded_ok; } redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server."); exit(1); fmterr: /* Format error. */ redisLog(REDIS_WARNING,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>"); exit(1); }
static int cliSendCommand(int argc, char **argv) { struct redisCommand *rc = lookupCommand(argv[0]); int fd, j, retval = 0; int read_forever = 0; sds cmd; if (!rc) { fprintf(stderr,"Unknown command '%s'\n",argv[0]); return 1; } if ((rc->arity > 0 && argc != rc->arity) || (rc->arity < 0 && argc < -rc->arity)) { fprintf(stderr,"Wrong number of arguments for '%s'\n",rc->name); return 1; } if (!strcasecmp(rc->name,"monitor")) read_forever = 1; if ((fd = cliConnect()) == -1) return 1; /* Select db number */ retval = selectDb(fd); if (retval) { fprintf(stderr,"Error setting DB num\n"); return 1; } while(config.repeat--) { /* Build the command to send */ cmd = sdsempty(); if (rc->flags & REDIS_CMD_MULTIBULK) { cmd = sdscatprintf(cmd,"*%d\r\n",argc); for (j = 0; j < argc; j++) { cmd = sdscatprintf(cmd,"$%lu\r\n", (unsigned long)sdslen(argv[j])); cmd = sdscatlen(cmd,argv[j],sdslen(argv[j])); cmd = sdscatlen(cmd,"\r\n",2); } } else { for (j = 0; j < argc; j++) { if (j != 0) cmd = sdscat(cmd," "); if (j == argc-1 && rc->flags & REDIS_CMD_BULK) { cmd = sdscatprintf(cmd,"%lu", (unsigned long)sdslen(argv[j])); } else { cmd = sdscatlen(cmd,argv[j],sdslen(argv[j])); } } cmd = sdscat(cmd,"\r\n"); if (rc->flags & REDIS_CMD_BULK) { cmd = sdscatlen(cmd,argv[argc-1],sdslen(argv[argc-1])); cmd = sdscatlen(cmd,"\r\n",2); } } anetWrite(fd,cmd,sdslen(cmd)); sdsfree(cmd); while (read_forever) { cliReadSingleLineReply(fd,0); } retval = cliReadReply(fd); if (retval) { return retval; } } return 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; }
/* Replay the append log file. On error REDIS_OK is returned. On non fatal * error (the append only file is zero-length) REDIS_ERR is returned. On * fatal error an error message is logged and the program exists. */ int loadAppendOnlyFile(char *filename) { struct redisClient *fakeClient; FILE *fp = fopen(filename,"r"); struct redis_stat sb; int old_aof_state = server.aof_state; long loops = 0; if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) { server.aof_current_size = 0; fclose(fp); return REDIS_ERR; } if (fp == NULL) { redisLog(REDIS_WARNING,"Fatal error: can't open the append log file for reading: %s",strerror(errno)); exit(1); } /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI * to the same file we're about to read. */ server.aof_state = REDIS_AOF_OFF; fakeClient = createFakeClient(); startLoading(fp); while(1) { int argc, j; unsigned long len; robj **argv; char buf[128]; sds argsds; struct redisCommand *cmd; /* Serve the clients from time to time */ if (!(loops++ % 1000)) { loadingProgress(ftello(fp)); aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT); } if (fgets(buf,sizeof(buf),fp) == NULL) { if (feof(fp)) break; else goto readerr; } if (buf[0] != '*') goto fmterr; argc = atoi(buf+1); if (argc < 1) goto fmterr; argv = zmalloc(sizeof(robj*)*argc); for (j = 0; j < argc; j++) { if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr; if (buf[0] != '$') goto fmterr; len = strtol(buf+1,NULL,10); argsds = sdsnewlen(NULL,len); if (len && fread(argsds,len,1,fp) == 0) goto fmterr; argv[j] = createObject(REDIS_STRING,argsds); if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */ } /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd) { redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr); exit(1); } /* Run the command in the context of a fake client */ fakeClient->argc = argc; fakeClient->argv = argv; cmd->proc(fakeClient); /* The fake client should not have a reply */ redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0); /* The fake client should never get blocked */ redisAssert((fakeClient->flags & REDIS_BLOCKED) == 0); /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ for (j = 0; j < fakeClient->argc; j++) decrRefCount(fakeClient->argv[j]); zfree(fakeClient->argv); } /* This point can only be reached when EOF is reached without errors. * If the client is in the middle of a MULTI/EXEC, log error and quit. */ if (fakeClient->flags & REDIS_MULTI) goto readerr; fclose(fp); freeFakeClient(fakeClient); server.aof_state = old_aof_state; stopLoading(); aofUpdateCurrentSize(); server.aof_rewrite_base_size = server.aof_current_size; return REDIS_OK; readerr: if (feof(fp)) { redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file"); } else { redisLog(REDIS_WARNING,"Unrecoverable error reading the append only file: %s", strerror(errno)); } exit(1); fmterr: redisLog(REDIS_WARNING,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>"); exit(1); }
/* NOTE: this function implements a fakeClient pipe */ long fakeClientPipe(redisClient *c, redisClient *rfc, void *wfc, /* can be redisClient,list,LuaState */ int is_ins, flag *flg, bool (* adder) (redisClient *c, void *x, robj *key, long *l, int b, int n), bool (* emptyer) (redisClient *c)) { redisCommand *cmd = lookupCommand(rfc->argv[0]->ptr); rsql_resetFakeClient(rfc); cmd->proc(rfc); listNode *ln; *flg = PIPE_NONE_FLAG; robj *r = NULL; int nlines = 0; long card = 1; /* ZER0 as pk can cause problems */ bool fline = 1; bool ldef = 0; listIter *li = listGetIterator(rfc->reply, AL_START_HEAD); while((ln = listNext(li)) != NULL) { robj *o = ln->value; sds s = o->ptr; bool o_fl = fline; fline = 0; //RL4 "PIPE: %s", s); if (o_fl) { if (*s == '-') { /* -ERR */ *flg = PIPE_ONE_LINER_FLAG; r = parseUpToR(li, &ln, s); if (!r) goto p_err; if (!(*adder)(c, wfc, r, &card, is_ins, nlines)) goto p_err; break; } else if (*s == '+') { /* +OK */ *flg = PIPE_ONE_LINER_FLAG; r = parseUpToR(li, &ln, s); if (!r) goto p_err; if (!(*adder)(c, wfc, r, &card, is_ins, nlines)) goto p_err; break; } else if (*s == ':') { /* :INT */ *flg = PIPE_ONE_LINER_FLAG; r = parseUpToR(li, &ln, s); if (!r) goto p_err; if (!(*adder)(c, wfc, r, &card, is_ins, nlines)) goto p_err; break; } else if (*s == '*') { nlines = atoi(s + 1); /* some pipes need to know num_lines */ if (nlines == -1) { *flg = PIPE_EMPTY_SET_FLAG; break; /* "*-1" multibulk empty */ } } continue; } /* not first line -> 2+ */ if (*s == '$') { /* parse length [and element] */ if (*(s + 1) == '-') { /* $-1 -> nil */ *flg = PIPE_EMPTY_SET_FLAG; /* NOTE: "-1" must be "adder()"d for Multi-NonRelIndxs */ r = createStringObject("-1", 2); if (!(*adder)(c, wfc, r, &card, is_ins, nlines)) goto p_err; continue; } ldef = 1; char *x = strchr(s, '\r'); if (!x) goto p_err; uint32 llen = x - s; if (llen + 2 < sdslen(s)) { /* more on next line (past "\r\n") */ x += 2; /* move past \r\n */ r = parseUpToR(li, &ln, x); if (!r) goto p_err; if (!(*adder)(c, wfc, r, &card, is_ins, nlines)) goto p_err; ldef = 0; } continue; } /* ignore empty protocol lines */ if (!ldef && sdslen(s) == 2 && *s == '\r' && *(s + 1) == '\n') continue; r = createStringObject(s, sdslen(s)); if (!(*adder)(c, wfc, r, &card, is_ins, nlines)) goto p_err; ldef = 0; } listReleaseIterator(li); if (card == 1) { /* rfc never got called, call empty handler */ if (!(*emptyer)(c)) goto p_err; } return card - 1; /* started at 1 */ p_err: listReleaseIterator(li); if (r) decrRefCount(r); return -1; }
/* 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; }
int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct redisCommand *cmd; redisClient *c = server.lua_client; sds reply; /* Cached across calls. */ static robj **argv = NULL; static int argv_size = 0; static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE]; static size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE]; static int inuse = 0; /* Recursive calls detection. */ /* By using Lua debug hooks it is possible to trigger a recursive call * to luaRedisGenericCommand(), which normally should never happen. * To make this function reentrant is futile and makes it slower, but * we should at least detect such a misuse, and abort. */ if (inuse) { char *recursion_warning = "luaRedisGenericCommand() recursive call detected. " "Are you doing funny stuff with Lua debug hooks?"; redisLog(REDIS_WARNING,"%s",recursion_warning); luaPushError(lua,recursion_warning); return 1; } inuse++; /* Require at least one argument */ if (argc == 0) { luaPushError(lua, "Please specify at least one argument for redis.call()"); inuse--; return 1; } /* Build the arguments vector */ if (argv_size < argc) { argv = zrealloc(argv,sizeof(robj*)*argc); argv_size = argc; } for (j = 0; j < argc; j++) { char *obj_s; size_t obj_len; char dbuf[64]; if (lua_type(lua,j+1) == LUA_TNUMBER) { /* We can't use lua_tolstring() for number -> string conversion * since Lua uses a format specifier that loses precision. */ lua_Number num = lua_tonumber(lua,j+1); obj_len = snprintf(dbuf,sizeof(dbuf),"%.17g",(double)num); obj_s = dbuf; } else { obj_s = (char*)lua_tolstring(lua,j+1,&obj_len); if (obj_s == NULL) break; /* Not a string. */ } /* Try to use a cached object. */ if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] && cached_objects_len[j] >= obj_len) { char *s = cached_objects[j]->ptr; struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); argv[j] = cached_objects[j]; cached_objects[j] = NULL; memcpy(s,obj_s,obj_len+1); sh->free += sh->len - obj_len; sh->len = obj_len; } else { argv[j] = createStringObject(obj_s, obj_len); } } /* Check if one of the arguments passed by the Lua script * is not a string or an integer (lua_isstring() return true for * integers as well). */ if (j != argc) { j--; while (j >= 0) { decrRefCount(argv[j]); j--; } luaPushError(lua, "Lua redis() command arguments must be strings or integers"); inuse--; return 1; } /* Setup our fake client for command execution */ c->argv = argv; c->argc = argc; /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity))) { if (cmd) luaPushError(lua, "Wrong number of args calling Redis command From Lua script"); else luaPushError(lua,"Unknown Redis command called from Lua script"); goto cleanup; } c->cmd = cmd; /* There are commands that are not allowed inside scripts. */ if (cmd->flags & REDIS_CMD_NOSCRIPT) { luaPushError(lua, "This Redis command is not allowed from scripts"); goto cleanup; } /* Write commands are forbidden against read-only slaves, or if a * command marked as non-deterministic was already called in the context * of this script. */ if (cmd->flags & REDIS_CMD_WRITE) { if (server.lua_random_dirty) { luaPushError(lua, "Write commands not allowed after non deterministic commands"); goto cleanup; } else if (server.masterhost && server.repl_slave_ro && !server.loading && !(server.lua_caller->flags & REDIS_MASTER)) { luaPushError(lua, shared.roslaveerr->ptr); goto cleanup; } else if (server.stop_writes_on_bgsave_err && server.saveparamslen > 0 && server.lastbgsave_status == REDIS_ERR) { luaPushError(lua, shared.bgsaveerr->ptr); goto cleanup; } } /* If we reached the memory limit configured via maxmemory, commands that * could enlarge the memory usage are not allowed, but only if this is the * first write in the context of this script, otherwise we can't stop * in the middle. */ if (server.maxmemory && server.lua_write_dirty == 0 && (cmd->flags & REDIS_CMD_DENYOOM)) { if (freeMemoryIfNeeded() == REDIS_ERR) { luaPushError(lua, shared.oomerr->ptr); goto cleanup; } } if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1; if (cmd->flags & REDIS_CMD_WRITE) server.lua_write_dirty = 1; /* If this is a Redis Cluster node, we need to make sure Lua is not * trying to access non-local keys, with the exception of commands * received from our master. */ if (server.cluster_enabled && !(server.lua_caller->flags & REDIS_MASTER)) { /* Duplicate relevant flags in the lua client. */ c->flags &= ~(REDIS_READONLY|REDIS_ASKING); c->flags |= server.lua_caller->flags & (REDIS_READONLY|REDIS_ASKING); if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,NULL) != server.cluster->myself) { luaPushError(lua, "Lua script attempted to access a non local key in a " "cluster node"); goto cleanup; } } /* Run the command */ call(c,REDIS_CALL_SLOWLOG | REDIS_CALL_STATS); /* Convert the result of the Redis command into a suitable Lua type. * The first thing we need is to create a single string from the client * output buffers. */ if (listLength(c->reply) == 0 && c->bufpos < REDIS_REPLY_CHUNK_BYTES) { /* This is a fast path for the common case of a reply inside the * client static buffer. Don't create an SDS string but just use * the client buffer directly. */ c->buf[c->bufpos] = '\0'; reply = c->buf; c->bufpos = 0; } else { reply = sdsnewlen(c->buf,c->bufpos); c->bufpos = 0; while(listLength(c->reply)) { robj *o = listNodeValue(listFirst(c->reply)); reply = sdscatlen(reply,o->ptr,sdslen(o->ptr)); listDelNode(c->reply,listFirst(c->reply)); } } if (raise_error && reply[0] != '-') raise_error = 0; redisProtocolToLuaType(lua,reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & REDIS_CMD_SORT_FOR_SCRIPT) && (reply[0] == '*' && reply[1] != '-')) { luaSortArray(lua); } if (reply != c->buf) sdsfree(reply); c->reply_bytes = 0; cleanup: /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ for (j = 0; j < c->argc; j++) { robj *o = c->argv[j]; /* Try to cache the object in the cached_objects array. * The object must be small, SDS-encoded, and with refcount = 1 * (we must be the only owner) for us to cache it. */ if (j < LUA_CMD_OBJCACHE_SIZE && o->refcount == 1 && (o->encoding == REDIS_ENCODING_RAW || o->encoding == REDIS_ENCODING_EMBSTR) && sdslen(o->ptr) <= LUA_CMD_OBJCACHE_MAX_LEN) { struct sdshdr *sh = (void*)(((char*)(o->ptr))-(sizeof(struct sdshdr))); if (cached_objects[j]) decrRefCount(cached_objects[j]); cached_objects[j] = o; cached_objects_len[j] = sh->free + sh->len; } else { decrRefCount(o); } } if (c->argv != argv) { zfree(c->argv); argv = NULL; argv_size = 0; } if (raise_error) { /* If we are here we should have an error in the stack, in the * form of a table with an "err" field. Extract the string to * return the plain error. */ lua_pushstring(lua,"err"); lua_gettable(lua,-2); inuse--; return lua_error(lua); } inuse--; return 1; }
int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct redisCommand *cmd; robj **argv; redisClient *c = server.lua_client; sds reply; /* Require at least one argument */ if (argc == 0) { luaPushError(lua, "Please specify at least one argument for redis.call()"); return 1; } /* Build the arguments vector */ argv = zmalloc(sizeof(robj*)*argc); for (j = 0; j < argc; j++) { if (!lua_isstring(lua,j+1)) break; argv[j] = createStringObject((char*)lua_tostring(lua,j+1), lua_strlen(lua,j+1)); } /* Check if one of the arguments passed by the Lua script * is not a string or an integer (lua_isstring() return true for * integers as well). */ if (j != argc) { j--; while (j >= 0) { decrRefCount(argv[j]); j--; } zfree(argv); luaPushError(lua, "Lua redis() command arguments must be strings or integers"); return 1; } /* Setup our fake client for command execution */ c->argv = argv; c->argc = argc; /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity))) { if (cmd) luaPushError(lua, "Wrong number of args calling Redis command From Lua script"); else luaPushError(lua,"Unknown Redis command called from Lua script"); goto cleanup; } /* There are commands that are not allowed inside scripts. */ if (cmd->flags & REDIS_CMD_NOSCRIPT) { luaPushError(lua, "This Redis command is not allowed from scripts"); goto cleanup; } /* Write commands are forbidden against read-only slaves, or if a * command marked as non-deterministic was already called in the context * of this script. */ if (cmd->flags & REDIS_CMD_WRITE) { if (server.lua_random_dirty) { luaPushError(lua, "Write commands not allowed after non deterministic commands"); goto cleanup; } else if (server.masterhost && server.repl_slave_ro && !server.loading && !(server.lua_caller->flags & REDIS_MASTER)) { luaPushError(lua, shared.roslaveerr->ptr); goto cleanup; } else if (server.stop_writes_on_bgsave_err && server.saveparamslen > 0 && server.lastbgsave_status == REDIS_ERR) { luaPushError(lua, shared.bgsaveerr->ptr); goto cleanup; } } /* If we reached the memory limit configured via maxmemory, commands that * could enlarge the memory usage are not allowed, but only if this is the * first write in the context of this script, otherwise we can't stop * in the middle. */ if (server.maxmemory && server.lua_write_dirty == 0 && (cmd->flags & REDIS_CMD_DENYOOM)) { if (freeMemoryIfNeeded() == REDIS_ERR) { luaPushError(lua, shared.oomerr->ptr); goto cleanup; } } if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1; if (cmd->flags & REDIS_CMD_WRITE) server.lua_write_dirty = 1; /* Run the command */ c->cmd = cmd; call(c,REDIS_CALL_SLOWLOG | REDIS_CALL_STATS); /* Convert the result of the Redis command into a suitable Lua type. * The first thing we need is to create a single string from the client * output buffers. */ reply = sdsempty(); if (c->bufpos) { reply = sdscatlen(reply,c->buf,c->bufpos); c->bufpos = 0; } while(listLength(c->reply)) { robj *o = listNodeValue(listFirst(c->reply)); reply = sdscatlen(reply,o->ptr,sdslen(o->ptr)); listDelNode(c->reply,listFirst(c->reply)); } if (raise_error && reply[0] != '-') raise_error = 0; redisProtocolToLuaType(lua,reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & REDIS_CMD_SORT_FOR_SCRIPT) && (reply[0] == '*' && reply[1] != '-')) { luaSortArray(lua); } sdsfree(reply); c->reply_bytes = 0; cleanup: /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]); zfree(c->argv); if (raise_error) { /* If we are here we should have an error in the stack, in the * form of a table with an "err" field. Extract the string to * return the plain error. */ lua_pushstring(lua,"err"); lua_gettable(lua,-2); return lua_error(lua); } return 1; }
/* Replay the append log file. On error REDIS_OK is returned. On non fatal * error (the append only file is zero-length) REDIS_ERR is returned. On * fatal error an error message is logged and the program exists. */ int loadAppendOnlyFile(char *filename) { struct redisClient *fakeClient; FILE *fp = fopen(filename,"r"); struct redis_stat sb; unsigned long long loadedkeys = 0; if (redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) return REDIS_ERR; fakeClient = createFakeClient(); while(1) { int argc, j; unsigned long len; robj **argv; char buf[128]; sds argsds; struct redisCommand *cmd; if (fgets(buf,sizeof(buf),fp) == NULL) { if (feof(fp)) break; else goto readerr; } if (buf[0] != '*') goto fmterr; argc = atoi(buf+1); argv = zmalloc(sizeof(robj*)*argc); for (j = 0; j < argc; j++) { if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr; if (buf[0] != '$') goto fmterr; len = strtol(buf+1,NULL,10); argsds = sdsnewlen(NULL,len); if (len && fread(argsds,len,1,fp) == 0) goto fmterr; argv[j] = createObject(REDIS_STRING,argsds); if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */ } /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); /* Try object sharing and encoding */ if (server.shareobjects) { int j; for(j = 1; j < argc; j++) argv[j] = tryObjectSharing(argv[j]); } if (cmd->flags & REDIS_CMD_BULK) tryObjectEncoding(argv[argc-1]); /* Run the command in the context of a fake client */ fakeClient->argc = argc; fakeClient->argv = argv; cmd->proc(fakeClient); /* Discard the reply objects list from the fake client */ while(listLength(fakeClient->reply)) listDelNode(fakeClient->reply,listFirst(fakeClient->reply)); /* Clean up, ready for the next command */ for (j = 0; j < argc; j++) decrRefCount(argv[j]); zfree(argv); /* Handle swapping while loading big datasets when VM is on */ loadedkeys++; if (server.vm_enabled && (loadedkeys % 5000) == 0) { while (zmalloc_used_memory() > server.vm_max_memory) { if (vmSwapOneObjectBlocking() == REDIS_ERR) break; } } } fclose(fp); freeFakeClient(fakeClient); return REDIS_OK; }