_provision.lua 3.6 KB

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