2013-11-15 21 views
5

Tôi muốn tải một đoạn script vào redis rằng sẽ xuất khẩu chức năng mà các kịch bản tương lai thực hiện sẽ phụ thuộc vào, nhưng cố gắng để xác định chức năng toàn cầu thất bại, cũng áp dụng cho các biến toàn cục:Script cố gắng để tạo ra biến toàn cầu

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' 

Làm thế nào tôi có thể xác định các hàm và biến toàn cầu?

+0

thêm đoạn mã lua trừ khi bạn muốn các câu trả lời như: không xác định các biến toàn cầu: P –

+0

@Tommaso xin lỗi vì sự nhầm lẫn, nhưng câu hỏi là về các biến toàn cục. – Alex

Trả lời

8

Nhìn vào mã nguồn trong file 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); 
} 

Các doc-chuỗi scriptingEnableGlobalsProtection chỉ ra rằng mục đích là để thông báo cho tác giả kịch bản của sai lầm phổ biến (không sử dụng local).

Dường như đây không phải là tính năng bảo mật, vì vậy chúng tôi có hai giải pháp:

Người ta có thể loại bỏ sự bảo vệ này:

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

Hoặc sử dụng rawset:

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

Tôi thích rằng bạn đã tìm thấy một cách để làm điều này. Hacker tinh thần cho chiến thắng! Tôi sẽ đề nghị rằng mọi người không sử dụng phương pháp này, mặc dù. Có vẻ như rõ ràng từ các tài liệu Redis rằng mục đích là để đơn giản hóa quản lý máy chủ bằng cách buộc các script không cư trú trên máy chủ (điều này được thực hiện hiệu quả với bộ nhớ đệm dựa trên SHA). Việc tiêm các hàm vào phạm vi toàn cầu sẽ làm giảm ý định này. Mặc dù hack tuyệt vời, Alex. –

+0

Một sẽ sử dụng phạm vi toàn cầu để lưu trữ mã phổ biến. Thay thế là sử dụng công cụ tạo khuôn mẫu để biên dịch tất cả các tập lệnh thành một tập lệnh được gửi tới Redis. Tôi thích trước đây - nó dễ dàng hơn để kết nối với redis-client và sử dụng tất cả các tiện ích của bạn. Lưu ý rằng các thao tác Redis trong thực tế không bao giờ đơn giản hoặc rõ ràng. – Alex

+1

Từ tài liệu Redis trên 'EVAL':" Nếu người dùng lộn xộn với trạng thái toàn cục Lua, tính nhất quán của AOF và sao chép không được đảm bảo: đừng làm điều đó. " –

Các vấn đề liên quan