127 lines
3.6 KiB
Lua
127 lines
3.6 KiB
Lua
|
--SAFETRIM
|
||
|
-- function _provision(self,socket,first_rec)
|
||
|
|
||
|
local self, socket, first_rec = ...
|
||
|
local crypto, file, json, node, table = crypto, file, sjson, node, table
|
||
|
local stripdebug, gc = node.stripdebug, collectgarbage
|
||
|
|
||
|
local buf = {}
|
||
|
gc(); gc()
|
||
|
|
||
|
local function getbuf() -- upval: buf, table
|
||
|
if #buf > 0 then return table.remove(buf, 1) end -- else return nil
|
||
|
end
|
||
|
|
||
|
-- Process a provisioning request record
|
||
|
local function receiveRec(sck, rec) -- upval: self, buf, crypto
|
||
|
-- Note that for 2nd and subsequent responses, we assume that the service has
|
||
|
-- "authenticated" itself, so any protocol errors are fatal and likely to
|
||
|
-- cause a repeating boot, throw any protocol errors are thrown.
|
||
|
local cmdlen = (rec:find('\n',1, true) or 0) - 1
|
||
|
local cmd,hash = rec:sub(1,cmdlen-6), rec:sub(cmdlen-5,cmdlen)
|
||
|
if cmdlen < 16 or
|
||
|
hash ~= crypto.toHex(crypto.hmac("MD5",cmd,self.secret):sub(-3)) then
|
||
|
return error("Invalid command signature")
|
||
|
end
|
||
|
|
||
|
local s
|
||
|
s, cmd = pcall(json.decode, cmd)
|
||
|
if not s then error("JSON decode error") end
|
||
|
local action,resp = cmd.a, {s = "OK"}
|
||
|
local chunk
|
||
|
|
||
|
if action == "ls" then
|
||
|
for name,len in pairs(file.list()) do
|
||
|
resp[name] = len
|
||
|
end
|
||
|
|
||
|
elseif action == "mv" then
|
||
|
if file.exists(cmd.from) then
|
||
|
if file.exists(cmd.to) then file.remove(cmd.to) end
|
||
|
if not file.rename(cmd.from,cmd.to) then
|
||
|
resp.s = "Rename failed"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
else
|
||
|
if action == "pu" or action == "cm" or action == "dl" then
|
||
|
-- These commands have a data buffer appended to the received record
|
||
|
if cmd.data == #rec - cmdlen - 1 then
|
||
|
buf[#buf+1] = rec:sub(cmdlen +2)
|
||
|
else
|
||
|
error(("Record size mismatch, %u expected, %u received"):format(
|
||
|
cmd.data or "nil", #buf - cmdlen - 1))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if action == "cm" then
|
||
|
stripdebug(2)
|
||
|
local lcf,msg = load(getbuf, cmd.name)
|
||
|
if not msg then
|
||
|
gc(); gc()
|
||
|
local code, name = string.dump(lcf), cmd.name:sub(1,-5) .. ".lc"
|
||
|
local f = file.open(name, "w+")
|
||
|
if f then
|
||
|
for i = 1, #code, 1024 do
|
||
|
f = f and file.write(code:sub(i, ((i+1023)>#code) and i+1023 or #code))
|
||
|
end
|
||
|
file.close()
|
||
|
if not f then file.remove(name) end
|
||
|
end
|
||
|
if f then
|
||
|
resp.lcsize=#code
|
||
|
print("Updated ".. name)
|
||
|
else
|
||
|
msg = "file write failed"
|
||
|
end
|
||
|
end
|
||
|
if msg then
|
||
|
resp.s, resp.err = "compile fail", msg
|
||
|
end
|
||
|
buf = {}
|
||
|
|
||
|
elseif action == "dl" then
|
||
|
local dlFile = file.open(cmd.name, "w+")
|
||
|
if dlFile then
|
||
|
for i = 1, #buf do
|
||
|
dlFile = dlFile and file.write(buf[i])
|
||
|
end
|
||
|
file.close()
|
||
|
end
|
||
|
|
||
|
if dlFile then
|
||
|
print("Updated ".. cmd.name)
|
||
|
else
|
||
|
file.remove(cmd.name)
|
||
|
resp.s = "write failed"
|
||
|
end
|
||
|
buf = {}
|
||
|
|
||
|
elseif action == "ul" then
|
||
|
if file.open(cmd.name, "r") then
|
||
|
file.seek("set", cmd.offset)
|
||
|
chunk = file.read(cmd.len)
|
||
|
file.close()
|
||
|
end
|
||
|
|
||
|
elseif action == "restart" then
|
||
|
cmd.a = nil
|
||
|
cmd.secret = self.secret
|
||
|
file.open(self.prefix.."config.json", "w+")
|
||
|
file.writeline(json.encode(cmd))
|
||
|
file.close()
|
||
|
sck:close()
|
||
|
print("Restarting to load new application")
|
||
|
node.restart() -- reboot just schedules a restart
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
self.socket_send(sck, resp, chunk)
|
||
|
gc()
|
||
|
end
|
||
|
|
||
|
-- Replace the receive CB by the provisioning version and then tailcall this to
|
||
|
-- process this first record.
|
||
|
socket:on("receive", receiveRec)
|
||
|
return receiveRec(socket, first_rec)
|