int LUAInterpreter::Assert2(lua_State *lua) { lua_Debug ar; lua_getstack(lua, 1, &ar); lua_getinfo(lua, "nSl", &ar); int lua_line = ar.currentline; int argc = lua_gettop(lua); /* Require at least one argument */ if (argc != 2) { luaPushError(lua, "Please specify 2 arguments for assert2()"); return -1; } if (!lua_isboolean(lua, 1)) { luaPushError(lua, "Lua assert2() command argument[0] must be boolean"); return -1; } int b = lua_toboolean(lua, 1); if (b) { fprintf(stdout, "\e[1;32m%-6s\e[m %s:%d\n", "[PASS]", g_lua_file, lua_line); } else {
int luaLogCommand(lua_State *lua) { int j, argc = lua_gettop(lua); int level; sds log; if (argc < 2) { luaPushError(lua, "redis.log() requires two arguments or more."); return 1; } else if (!lua_isnumber(lua,-argc)) { luaPushError(lua, "First argument must be a number (log level)."); return 1; } level = lua_tonumber(lua,-argc); if (level < REDIS_DEBUG || level > REDIS_WARNING) { luaPushError(lua, "Invalid debug level."); return 1; } /* Glue together all the arguments */ log = sdsempty(); for (j = 1; j < argc; j++) { size_t len; char *s; s = (char*)lua_tolstring(lua,(-argc)+j,&len); if (s) { if (j != 1) log = sdscatlen(log," ",1); log = sdscatlen(log,s,len); } } redisLogRaw(level,log); sdsfree(log); return 0; }
int luaLogCommand(lua_State *lua) { int j, argc = lua_gettop(lua); int level; char *log; char **strs; size_t *strlens; if (argc < 2) { luaPushError(lua, "rlite.log() requires two arguments or more."); return 1; } else if (!lua_isnumber(lua,-argc)) { luaPushError(lua, "First argument must be a number (log level)."); return 1; } level = lua_tonumber(lua,-argc); if (level < RLITE_DEBUG || level > RLITE_WARNING) { luaPushError(lua, "Invalid debug level."); return 1; } size_t totalsize = 0; strs = malloc(sizeof(char *) * (argc - 1)); if (!strs) { return 1; } strlens = malloc(sizeof(size_t) * (argc - 1)); if (!strlens) { free(strs); return 1; } strlens = malloc(sizeof(size_t) * (argc - 1)); for (j = 1; j < argc; j++) { strs[j - 1] = (char*)lua_tolstring(lua,(-argc)+j,&strlens[j - 1]); totalsize += strlens[j - 1]; } log = malloc(sizeof(char) * (totalsize + 1)); if (!log) { free(strs); free(strlens); return 1; } totalsize = 0; for (j = 0; j < argc - 1; j++) { memcpy(&log[totalsize], strs[j], strlens[j]); totalsize += strlens[j]; } log[totalsize] = 0; rliteLogRaw(level,log); return 0; }
int luaSetHttp304Command(lua_State *lua) { int argc = lua_gettop(lua); if (argc != 0) { CLEAR_LUA_STACK luaPushError(lua, "Lua SetHttp304() takes ZERO args"); return 1; } server.alc.CurrClient->http.retcode = 304; return 0; }
int luaSetHttpRedirectCommand(lua_State *lua) { int argc = lua_gettop(lua); if (argc != 1 || !lua_isstring(lua, 1)) { CLEAR_LUA_STACK luaPushError(lua, "Lua SetHttpRedirect() takes 1 string arg"); return 1; } server.alc.CurrClient->http.retcode = 302; server.alc.CurrClient->http.redir = sdsnew(lua_tostring(lua, 1));//FREE079 return 0; }
int LUAInterpreter::IsMergeSupported(lua_State *lua) { int argc = lua_gettop(lua); if (argc != 0) { luaPushError(lua, "wrong number of arguments"); return 1; } lua_pushboolean(lua, g_engine->GetFeatureSet().support_merge); return 1; }
/* Returns a table with a single field 'field' set to the string value * passed as argument. This helper function is handy when returning * a Redis Protocol error or status reply from Lua: * * return redis.error_reply("ERR Some Error") * return redis.status_reply("ERR Some Error") */ int luaRedisReturnSingleFieldTable(lua_State *lua, char *field) { if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) { luaPushError(lua, "wrong number or type of arguments"); return 1; } lua_newtable(lua); lua_pushstring(lua, field); lua_pushvalue(lua, -3); lua_settable(lua, -3); return 1; }
int luaSetHttpResponseHeaderCommand(lua_State *lua) { int argc = lua_gettop(lua); if (argc != 2 || !lua_isstring(lua, 1) || !lua_isstring(lua, 2)) { CLEAR_LUA_STACK luaPushError(lua, "Lua SetHttpResponseHeader() takes 2 string args"); return 1; } sds a = sdsnew(lua_tostring(lua, 1)); sds b = sdsnew(lua_tostring(lua, 2)); addHttpResponseHeader(a, b); return 0; }
/* This adds redis.sha1hex(string) to Lua scripts using the same hashing * function used for sha1ing lua scripts. */ int luaRedisSha1hexCommand(lua_State *lua) { int argc = lua_gettop(lua); char digest[41]; size_t len; char *s; if (argc != 1) { luaPushError(lua, "wrong number of arguments"); return 1; } s = (char*)lua_tolstring(lua,1,&len); sha1hex(digest,s,len); lua_pushstring(lua,digest); 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; }
int LUAInterpreter::CallArdb(lua_State *lua, bool raise_error) { int j, argc = lua_gettop(lua); ArgumentArray cmdargs; /* 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 */ for (j = 0; j < argc; j++) { if (!lua_isstring(lua, j + 1)) break; std::string arg; arg.append(lua_tostring(lua, j + 1), lua_strlen(lua, j + 1)); cmdargs.push_back(arg); } /* 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) { luaPushError(lua, "Lua redis() command arguments must be strings or integers"); return 1; } /* Setup our fake client for command execution */ RedisCommandFrame cmd(cmdargs); Ardb::RedisCommandHandlerSetting* setting = g_db->FindRedisCommandHandlerSetting(cmd); /* Command lookup */ if (NULL == setting) { luaPushError(lua, "Unknown Redis command called from Lua script"); return -1; } /* There are commands that are not allowed inside scripts. */ if (!setting->IsAllowedInScript()) { luaPushError(lua, "This Redis command is not allowed from scripts"); return -1; } LuaExecContext* ctx = g_lua_exec_ctx.GetValue(); /* 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 (setting->IsWriteCommand()) { if (!g_db->GetConf().master_host.empty() && g_db->GetConf().slave_readonly && !g_db->IsLoadingData() && !(ctx->caller->flags.slave)) { luaPushError(lua, "-READONLY You can't write against a read only slave."); return -1; } } Context& lua_ctx = ctx->exec; RedisReply& reply = lua_ctx.GetReply(); reply.Clear(); lua_ctx.ClearFlags(); lua_ctx.flags.lua = 1; g_db->DoCall(lua_ctx, *setting, cmd); if (raise_error && reply.type != REDIS_REPLY_ERROR) { raise_error = 0; } redisProtocolToLuaType(lua, reply); 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; }
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 rliteCommand *cmd; rliteClient *c = lua_client; rliteReply *reply; /* Cached across calls. */ static char **argv = NULL; static size_t *argvlen = NULL; static int argv_size = 0; 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?"; rliteLog(RLITE_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 rlite.call()"); inuse--; return 1; } /* Build the arguments vector */ if (argv_size < argc) { argv = realloc(argv, sizeof(char *) * argc); argvlen = realloc(argvlen, sizeof(size_t) * 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. */ } argv[j] = obj_s; argvlen[j] = 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) { free(argv[j]); j--; } luaPushError(lua, "Lua rlite() command arguments must be strings or integers"); inuse--; return 1; } /* Setup our fake client for command execution */ c->argvlen = argvlen; c->argv = argv; c->argc = argc; /* Command lookup */ cmd = rliteLookupCommand(argv[0], argvlen[0]); 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 & RLITE_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 & RLITE_CMD_WRITE) { if (lua_random_dirty) { luaPushError(lua, "Write commands not allowed after non deterministic commands"); goto cleanup; } } if (cmd->flags & RLITE_CMD_RANDOM) lua_random_dirty = 1; if (cmd->flags & RLITE_CMD_WRITE) lua_write_dirty = 1; /* Run the command */ rliteAppendCommandClient(c); rliteGetReply(c->context, (void **)&reply); if (raise_error && reply->type != RLITE_REPLY_ERROR) raise_error = 0; rliteToLuaType(lua,reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & RLITE_CMD_SORT_FOR_SCRIPT) && (reply->type == RLITE_REPLY_ARRAY && reply->elements > 0)) { luaSortArray(lua); } rliteFreeReplyObject(reply); cleanup: if (c->argv != argv) { free(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; }