_provision.lua 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. --SAFETRIM
  2. -- function _provision(self,socket,first_rec)
  3. local self, socket, first_rec = ...
  4. local crypto, file, json, node, table = crypto, file, sjson, node, table
  5. local stripdebug, gc = node.stripdebug, collectgarbage
  6. local buf = {}
  7. gc(); gc()
  8. local function getbuf() -- upval: buf, table
  9. if #buf > 0 then return table.remove(buf, 1) end -- else return nil
  10. end
  11. -- Process a provisioning request record
  12. local function receiveRec(sck, rec) -- upval: self, buf, crypto
  13. -- Note that for 2nd and subsequent responses, we assume that the service has
  14. -- "authenticated" itself, so any protocol errors are fatal and likely to
  15. -- cause a repeating boot, throw any protocol errors are thrown.
  16. local cmdlen = (rec:find('\n',1, true) or 0) - 1
  17. local cmd,hash = rec:sub(1,cmdlen-6), rec:sub(cmdlen-5,cmdlen)
  18. if cmdlen < 16 or
  19. hash ~= crypto.toHex(crypto.hmac("MD5",cmd,self.secret):sub(-3)) then
  20. return error("Invalid command signature")
  21. end
  22. local s
  23. s, cmd = pcall(json.decode, cmd)
  24. if not s then error("JSON decode error") end
  25. local action,resp = cmd.a, {s = "OK"}
  26. local chunk
  27. if action == "ls" then
  28. for name,len in pairs(file.list()) do
  29. resp[name] = len
  30. end
  31. elseif action == "mv" then
  32. if file.exists(cmd.from) then
  33. if file.exists(cmd.to) then file.remove(cmd.to) end
  34. if not file.rename(cmd.from,cmd.to) then
  35. resp.s = "Rename failed"
  36. end
  37. end
  38. else
  39. if action == "pu" or action == "cm" or action == "dl" then
  40. -- These commands have a data buffer appended to the received record
  41. if cmd.data == #rec - cmdlen - 1 then
  42. buf[#buf+1] = rec:sub(cmdlen +2)
  43. else
  44. error(("Record size mismatch, %u expected, %u received"):format(
  45. cmd.data or "nil", #buf - cmdlen - 1))
  46. end
  47. end
  48. if action == "cm" then
  49. stripdebug(2)
  50. local lcf,msg = load(getbuf, cmd.name)
  51. if not msg then
  52. gc(); gc()
  53. local code, name = string.dump(lcf), cmd.name:sub(1,-5) .. ".lc"
  54. local f = file.open(name, "w+")
  55. if f then
  56. for i = 1, #code, 1024 do
  57. f = f and file.write(code:sub(i, ((i+1023)>#code) and i+1023 or #code))
  58. end
  59. file.close()
  60. if not f then file.remove(name) end
  61. end
  62. if f then
  63. resp.lcsize=#code
  64. print("Updated ".. name)
  65. else
  66. msg = "file write failed"
  67. end
  68. end
  69. if msg then
  70. resp.s, resp.err = "compile fail", msg
  71. end
  72. buf = {}
  73. elseif action == "dl" then
  74. local dlFile = file.open(cmd.name, "w+")
  75. if dlFile then
  76. for i = 1, #buf do
  77. dlFile = dlFile and file.write(buf[i])
  78. end
  79. file.close()
  80. end
  81. if dlFile then
  82. print("Updated ".. cmd.name)
  83. else
  84. file.remove(cmd.name)
  85. resp.s = "write failed"
  86. end
  87. buf = {}
  88. elseif action == "ul" then
  89. if file.open(cmd.name, "r") then
  90. file.seek("set", cmd.offset)
  91. chunk = file.read(cmd.len)
  92. file.close()
  93. end
  94. elseif action == "restart" then
  95. cmd.a = nil
  96. cmd.secret = self.secret
  97. file.open(self.prefix.."config.json", "w+")
  98. file.writeline(json.encode(cmd))
  99. file.close()
  100. sck:close()
  101. print("Restarting to load new application")
  102. node.restart() -- reboot just schedules a restart
  103. return
  104. end
  105. end
  106. self.socket_send(sck, resp, chunk)
  107. gc()
  108. end
  109. -- Replace the receive CB by the provisioning version and then tailcall this to
  110. -- process this first record.
  111. socket:on("receive", receiveRec)
  112. return receiveRec(socket, first_rec)