Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.1KB

  1. local serial = require "serialization"
  2. local minitel = require "minitel"
  3. local event = require "event"
  4. local rpc = {}
  5. _G.rpcf = {}
  6. rpc.port = 111
  7. local function setacl(self, fname, host)
  8. self[fname] = self[fname] or {}
  9. self[fname][host] = true
  10. end
  11. -- function rpc.allow(fn, host) -- string string -- -- Enable the allow list for function *fname* and add *host* to it.
  12. -- function rpc.deny(fn, host) -- string string -- -- Enable the deny list for function *fname* and add *host* to it.
  13. rpc.allow = setmetatable({},{__call=setacl})
  14. rpc.deny = setmetatable({},{__call=setacl})
  15. local function isPermitted(host,fn)
  16. if rpc.allow[fn] then
  17. return rpc.allow[fn][host] or false
  18. end
  19. if rpc.deny[fn] and rpc.deny[fn][host] then
  20. return false
  21. end
  22. return true
  23. end
  24. local function rpcexec(_, from, port, data)
  25. if port ~= rpc.port then return false end
  26. os.spawn(function()
  27. local rpcrq = serial.unserialize(data) or {}
  28. if rpcf[rpcrq[1]] and isPermitted(from,rpcrq[1]) then
  29. os.setenv("RPC_CLIENT",from)
  30. minitel.send(from,port,serial.serialize({rpcrq[2],pcall(rpcf[rpcrq[1]],table.unpack(rpcrq,3))}))
  31. elseif type(rpcrq[2]) == "string" then
  32. minitel.send(from,port,serial.serialize({rpcrq[2],false,"function unavailable"}))
  33. end
  34. end,"rpc worker for "..tostring(from))
  35. end
  36. function rpcf.list()
  37. local rt = {}
  38. for k,v in pairs(rpcf) do
  39. rt[#rt+1] = k
  40. end
  41. return rt
  42. end
  43. function rpc.call(hostname,fn,...) -- string string -- boolean -- Calls exported function *fn* on host *hostname*, with parameters *...*, returning whatever the function returns, or false.
  44. if hostname == "localhost" then
  45. return rpcf[fn](...)
  46. end
  47. local rv = minitel.genPacketID()
  48. minitel.rsend(hostname,rpc.port,serial.serialize({fn,rv,...}),true)
  49. local st = computer.uptime()
  50. local rt = {}
  51. repeat
  52. local _, from, port, data = event.pull(30, "net_msg", hostname, rpc.port)
  53. rt = serial.unserialize(tostring(data)) or {}
  54. until (type(rt) == "table" and rt[1] == rv) or computer.uptime() > st + 30
  55. if rt[1] == rv then
  56. if rt[2] then
  57. return table.unpack(rt,3)
  58. end
  59. error(rt[3])
  60. end
  61. error("timed out")
  62. end
  63. function rpc.proxy(hostname,filter) -- string string -- table -- Returns a component.proxy()-like table from the functions on *hostname* with names matching *filter*.
  64. filter=(filter or "").."(.+)"
  65. local fnames = rpc.call(hostname,"list")
  66. if not fnames then return false end
  67. local rt = {}
  68. for k,v in pairs(fnames) do
  69. fv = v:match(filter)
  70. if fv then
  71. rt[fv] = function(...)
  72. return rpc.call(hostname,v,...)
  73. end
  74. end
  75. end
  76. return rt
  77. end
  78. function rpc.register(name,fn) -- string function -- -- Registers a function to be exported by the RPC library.
  79. local rpcrunning = false
  80. for k,v in pairs(os.tasks()) do
  81. if os.taskInfo(v).name == "rpc daemon" then
  82. rpcrunning = true
  83. end
  84. end
  85. if not rpcrunning then
  86. os.spawn(function()
  87. while true do
  88. pcall(rpcexec,event.pull("net_msg"))
  89. end
  90. end,"rpc daemon")
  91. end
  92. rpcf[name] = fn
  93. end
  94. function rpc.unregister(name) -- string -- -- Removes a function from the RPC function registry, clearing any ACL rules.
  95. rpcf[name] = nil
  96. rpc.allow[name] = nil
  97. rpc.deny[name] = nil
  98. end
  99. return rpc