blob: bd158f51e0b00c7af05b973271514d6ef6caa95f [file] [log] [blame] [raw]
--[[ This is called as the main coroutine by the host. If this returns the
computer crashes. It should never ever return "normally", only when an
error occurred. Shutdown / reboot are signalled via special yields. ]]
--[[ Set up the global environment we make available to userland programs. ]]
local sandbox = {
-- Top level values. The selection of kept methods rougly follows the list
-- as available on the Lua wiki here: http://lua-users.org/wiki/SandBoxes
assert = assert,
error = error,
pcall = pcall,
xpcall = xpcall,
ipairs = ipairs,
next = next,
pairs = pairs,
rawequal = rawequal,
rawget = rawget,
rawlen = rawlen,
rawset = rawset,
select = select,
type = type,
tonumber = tonumber,
tostring = tostring,
getmetatable = getmetatable,
setmetatable = setmetatable,
_VERSION = "Lua 5.2",
bit32 = {
arshift = bit32.arshift,
band = bit32.band,
bnot = bit32.bnot,
bor = bit32.bor,
btest = bit32.btest,
bxor = bit32.bxor,
extract = bit32.extract,
replace = bit32.replace,
lrotate = bit32.lrotate,
lshift = bit32.lshift,
rrotate = bit32.rrotate,
rshift = bit32.rshift
},
coroutine = {
create = coroutine.create,
resume = coroutine.resume,
running = coroutine.running,
status = coroutine.status,
wrap = coroutine.wrap,
yield = coroutine.yield
},
math = {
abs = math.abs,
acos = math.acos,
asin = math.asin,
atan = math.atan,
atan2 = math.atan2,
ceil = math.ceil,
cos = math.cos,
cosh = math.cosh,
deg = math.deg,
exp = math.exp,
floor = math.floor,
fmod = math.fmod,
frexp = math.frexp,
huge = math.huge,
ldexp = math.ldexp,
log = math.log,
max = math.max,
min = math.min,
modf = math.modf,
pi = math.pi,
pow = math.pow,
rad = math.rad,
random = math.random,
randomseed = math.randomseed,
sin = math.sin,
sinh = math.sinh,
sqrt = math.sqrt,
tan = math.tan,
tanh = math.tanh
},
os = {
clock = os.clock,
date = os.date,
difftime = os.difftime,
time = os.time,
uptime = os.uptime,
freeMemory = os.freeMemory,
totalMemory = os.totalMemory,
address = os.address,
romAddress = os.romAddress
},
string = {
breverse = string.breverse,
bsub = string.bsub,
byte = string.byte,
char = string.char,
dump = string.dump,
find = string.find,
format = string.format,
gmatch = string.gmatch,
gsub = string.gsub,
len = string.len,
lower = string.lower,
match = string.match,
rep = string.rep,
reverse = string.reverse,
sub = string.sub,
upper = string.upper
},
table = {
concat = table.concat,
insert = table.insert,
pack = table.pack,
remove = table.remove,
sort = table.sort,
unpack = table.unpack
}
}
sandbox._G = sandbox
function sandbox.load(code, source, env)
return load(code, source, "t", env or sandbox)
end
function sandbox.checkArg(n, have, ...)
have = type(have)
local want = table.pack(...)
for i = 1, want.n do
if have == want[i] then
return
end
end
local msg = "bad argument #" .. n .. " (" .. table.concat({...}, " or ") .. " expected, got " .. have .. ")"
error(debug.traceback(msg, 2), 2)
end
-------------------------------------------------------------------------------
--[[ Install wrappers for coroutine management that reserves the first value
returned by yields for internal stuff. Used for sleeping and message
calls (sendToAddress) that happen synchronized (Server thread).
--]]
local deadline = 0
local function checkDeadline()
if os.realTime() > deadline then
error("too long without yielding", 0)
end
end
local function main(args)
local function init()
sandbox.driver.fs.mount(os.romAddress(), "/boot")
local result, reason = sandbox.loadfile("/boot/init.lua")
if not result then
error(reason, 0)
end
return coroutine.create(result)
end
local co = init()
while true do
deadline = os.realTime() + timeout -- timeout global is set by host
if not debug.gethook(co) then
debug.sethook(co, checkDeadline, "", 10000)
end
local result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n)))
if result[1] then
args = table.pack(coroutine.yield(result[2])) -- system yielded value
else
error(result[2], 0)
end
end
end
function sandbox.coroutine.resume(co, ...)
local args = table.pack(...)
while true do
if not debug.gethook(co) then -- don't reset counter
debug.sethook(co, checkDeadline, "", 10000)
end
local result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n)))
checkDeadline()
if result[1] then
local isSystemYield = coroutine.status(co) ~= "dead" and result[2] ~= nil
if isSystemYield then
args = table.pack(coroutine.yield(result[2]))
else
return true, table.unpack(result, 3, result.n)
end
else -- error: result = (bool, string)
return table.unpack(result, 1, result.n)
end
end
end
function sandbox.coroutine.yield(...)
return coroutine.yield(nil, ...)
end
function sandbox.pcall(...)
local result = table.pack(pcall(...))
checkDeadline()
return table.unpack(result, 1, result.n)
end
function sandbox.xpcall(...)
local result = table.pack(xpcall(...))
checkDeadline()
return table.unpack(result, 1, result.n)
end
-------------------------------------------------------------------------------
function sandbox.os.shutdown()
coroutine.yield(false)
end
function sandbox.os.reboot()
coroutine.yield(true)
end
function sandbox.os.signal(name, timeout)
local waitUntil = os.uptime() + (type(timeout) == "number" and timeout or math.huge)
repeat
local signal = table.pack(coroutine.yield(waitUntil - os.uptime()))
if signal.n > 0 and (name == signal[1] or name == nil) then
return table.unpack(signal, 1, signal.n)
end
until os.uptime() >= waitUntil
end
-------------------------------------------------------------------------------
sandbox.driver = {}
function sandbox.driver.componentType(id)
return nodeName(id)
end
do
local env = setmetatable({ send = sendToAddress },
{ __index = sandbox, __newindex = sandbox })
for name, code in pairs(drivers()) do
local driver, reason = load(code, "=" .. name, "t", env)
if not driver then
print("Failed loading driver '" .. name .. "': " .. reason)
else
local result, reason = pcall(driver)
if not result then
print("Failed initializing driver '" .. name .. "': " .. reason)
end
end
end
end
-------------------------------------------------------------------------------
-- JNLua converts the coroutine to a string immediately, so we can't get the
-- traceback later. Because of that we have to do the error handling here.
-- Also, yield once to allow initializing up to here to get a memory baseline.
return pcall(main, table.pack(coroutine.yield()))