123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- --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)
|