2013-11-15 17 views
5

vorrei caricare uno script in Redis che esportare le funzioni che gli script futuri eseguiti dipenderà, ma il tentativo di definire funzione globale non riesce, lo stesso vale per le variabili globali:script ha tentato di creare variabile globale

redis 127.0.0.1:6379> EVAL "function alex() return 3.1415 end" 0 
(error) ERR Error running script (call to f_f24a5a054d91ccc74c2629e113f8f639bbedbfa2): user_script:1: Script attempted to create global variable 'alex' 

Come si definiscono funzioni e variabili globali?

+0

aggiungere lo script lua a meno che non si vogliano risposte come: non definire variabili globali: P –

+0

@Tommaso scusa per la confusione, ma la domanda riguarda le variabili globali. – Alex

risposta

8

Guardando il codice sorgente in file di scripting.c

/* This function installs metamethods in the global table _G that prevent 
* the creation of globals accidentally. 
* 
* It should be the last to be called in the scripting engine initialization 
* sequence, because it may interact with creation of globals. */ 
void scriptingEnableGlobalsProtection(lua_State *lua) { 
    char *s[32]; 
    sds code = sdsempty(); 
    int j = 0; 

    /* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html. 
    * Modified to be adapted to Redis. */ 
    s[j++]="local mt = {}\n"; 
    s[j++]="setmetatable(_G, mt)\n"; 
    s[j++]="mt.__newindex = function (t, n, v)\n"; 
    s[j++]=" if debug.getinfo(2) then\n"; 
    s[j++]=" local w = debug.getinfo(2, \"S\").what\n"; 
    s[j++]=" if w ~= \"main\" and w ~= \"C\" then\n"; 
    s[j++]="  error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n"; 
    s[j++]=" end\n"; 
    s[j++]=" end\n"; 
    s[j++]=" rawset(t, n, v)\n"; 
    s[j++]="end\n"; 
    s[j++]="mt.__index = function (t, n)\n"; 
    s[j++]=" if debug.getinfo(2) and debug.getinfo(2, \"S\").what ~= \"C\" then\n"; 
    s[j++]=" error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n"; 
    s[j++]=" end\n"; 
    s[j++]=" return rawget(t, n)\n"; 
    s[j++]="end\n"; 
    s[j++]=NULL; 

    for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j])); 
    luaL_loadbuffer(lua,code,sdslen(code),"@enable_strict_lua"); 
    lua_pcall(lua,0,0,0); 
    sdsfree(code); 
} 

Il doc-serie di scriptingEnableGlobalsProtection indica che l'intento è quello di informare gli autori di script di errore comune (se non utilizzano local).

Sembra che questo non è caratteristica di sicurezza, quindi abbiamo due soluzioni:

Si può rimuovere questa protezione:

local mt = setmetatable(_G, nil) 
-- define global functions/variables 
function alex() return 3.1415 end 
-- return globals protection mechanizm 
setmetatable(_G, mt) 

o utilizzare rawset:

local function alex() return 3.1415 end 
rawset(_G, "alex", alex) 
+0

Mi piace che tu abbia trovato un modo per farlo. Spirito hacker per la vittoria! Ho intenzione di suggerire che le persone non usano questo metodo, però. Dai documenti Redis sembra chiaro che l'intento è di semplificare la gestione del server forzando gli script a non risiedere sul server (ciò avviene in modo efficiente con la memorizzazione nella cache basata su SHA). L'iniezione di funzioni nell'ambito globale sovverte questo intento. Comunque, meraviglioso hacking, Alex. –

+0

Uno utilizzerà l'ambito globale per memorizzare codice comune. Alternativa è usare il motore di template per compilare tutti gli script in uno script che viene inviato a Redis. Preferisco ex - è più facile connettersi con redis-client e utilizzare tutte le tue utilità. Nota che le operazioni di Redis in pratica non sono mai semplici o chiare. – Alex

+1

Da Redis docs su 'EVAL': "Se l'utente ha problemi con lo stato globale di Lua, la coerenza di AOF e della replica non è garantita: non farlo." –