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.

299 lines
7.1KB

  1. --[[
  2. packet format:
  3. packetID: random string to differentiate
  4. packetType:
  5. - 0: unreliable
  6. - 1: reliable, requires ack
  7. - 2: ack packet
  8. destination: end destination hostname
  9. sender: original sender of packet
  10. data: the actual packet data, duh.
  11. ]]--
  12. local listeners = {}
  13. local modems = {}
  14. local cfg = {}
  15. cfg.debug = false
  16. cfg.port = 4096
  17. cfg.retry = 10
  18. cfg.retrycount = 64
  19. cfg.route = true
  20. local hostname = computer.address():sub(1,8)
  21. -- packet cache: [packet ID]=uptime
  22. local pcache = {}
  23. cfg.pctime = 30
  24. --[[
  25. LKR format:
  26. address {
  27. local hardware address
  28. remote hardware address
  29. time last received
  30. }
  31. ]]--
  32. cfg.sroutes = {}
  33. local rcache = setmetatable({},{__index=cfg.sroutes})
  34. cfg.rctime = 15
  35. --[[
  36. packet queue format:
  37. {
  38. packetID,
  39. packetType
  40. destination,
  41. data,
  42. timestamp,
  43. attempts
  44. }
  45. ]]--
  46. local pqueue = {}
  47. local function loadconfig()
  48. end
  49. local function saveconfig()
  50. end
  51. -- specific OS support here
  52. if _OSVERSION:sub(1,6) == "OpenOS" then -- OpenOS specific code
  53. local timers = {}
  54. local event = require "event"
  55. local component = require "component"
  56. local computer = require "computer"
  57. local serial = require "serialization"
  58. local listener = false
  59. local function saveconfig()
  60. local f = io.open("/etc/minitel.cfg","wb")
  61. if f then
  62. f:write(serial.serialize(cfg))
  63. f:close()
  64. end
  65. end
  66. local function loadconfig()
  67. local f=io.open("/etc/hostname","rb")
  68. if f then
  69. hostname = f:read()
  70. f:close()
  71. end
  72. local f = io.open("/etc/minitel.cfg","rb")
  73. if f then
  74. local newcfg = serial.unserialize(f:read("*a"))
  75. f:close()
  76. for k,v in pairs(newcfg) do
  77. cfg[k] = v
  78. end
  79. else
  80. saveconfig()
  81. end
  82. end
  83. function stop()
  84. for k,v in pairs(listeners) do
  85. event.ignore(k,v)
  86. print("Stopped listener: "..tostring(v))
  87. end
  88. for k,v in pairs(timers) do
  89. event.cancel(v)
  90. print("Stopped timer: "..tostring(v))
  91. end
  92. end
  93. function set(k,v)
  94. if type(cfg[k]) == "string" then
  95. cfg[k] = v
  96. elseif type(cfg[k]) == "number" then
  97. cfg[k] = tonumber(v)
  98. elseif type(cfg[k]) == "boolean" then
  99. if v:lower():sub(1,1) == "t" then
  100. cfg[k] = true
  101. else
  102. cfg[k] = false
  103. end
  104. end
  105. print("cfg."..k.." = "..tostring(cfg[k]))
  106. saveconfig()
  107. end
  108. function set_route(to,laddr,raddr)
  109. cfg.sroutes[to] = {laddr,raddr,0}
  110. end
  111. function del_route(to)
  112. cfg.sroutes[to] = nil
  113. end
  114. end
  115. local function dprint(...)
  116. if cfg.debug then
  117. print(...)
  118. end
  119. end
  120. function start()
  121. loadconfig()
  122. print("Hostname: "..hostname)
  123. if listener then return end
  124. modems={}
  125. for a,t in component.list("modem") do
  126. modems[#modems+1] = component.proxy(a)
  127. end
  128. for k,v in ipairs(modems) do
  129. v.open(cfg.port)
  130. print("Opened port "..cfg.port.." on "..v.address:sub(1,8))
  131. end
  132. for a,t in component.list("tunnel") do
  133. modems[#modems+1] = component.proxy(a)
  134. end
  135. local function genPacketID()
  136. local npID = ""
  137. for i = 1, 16 do
  138. npID = npID .. string.char(math.random(32,126))
  139. end
  140. return npID
  141. end
  142. local function sendPacket(packetID,packetType,dest,sender,vPort,data)
  143. if rcache[dest] then
  144. dprint("Cached", rcache[dest][1],"send",rcache[dest][2],cfg.port,packetID,packetType,dest,sender,vPort,data)
  145. if component.type(rcache[dest][1]) == "modem" then
  146. component.invoke(rcache[dest][1],"send",rcache[dest][2],cfg.port,packetID,packetType,dest,sender,vPort,data)
  147. elseif component.type(rcache[dest][1]) == "tunnel" then
  148. component.invoke(rcache[dest][1],"send",packetID,packetType,dest,sender,vPort,data)
  149. end
  150. else
  151. dprint("Not cached", cfg.port,packetID,packetType,dest,sender,vPort,data)
  152. for k,v in pairs(modems) do
  153. if v.type == "modem" then
  154. v.broadcast(cfg.port,packetID,packetType,dest,sender,vPort,data)
  155. elseif v.type == "tunnel" then
  156. v.send(packetID,packetType,dest,sender,vPort,data)
  157. end
  158. end
  159. end
  160. end
  161. local function pruneCache()
  162. for k,v in pairs(rcache) do
  163. dprint(k,v[3],computer.uptime())
  164. if v[3] < computer.uptime() then
  165. rcache[k] = nil
  166. dprint("pruned "..k.." from routing cache")
  167. end
  168. end
  169. for k,v in pairs(pcache) do
  170. if v < computer.uptime() then
  171. pcache[k] = nil
  172. dprint("pruned "..k.." from packet cache")
  173. end
  174. end
  175. end
  176. local function checkPCache(packetID)
  177. dprint(packetID)
  178. for k,v in pairs(pcache) do
  179. dprint(k)
  180. if k == packetID then return true end
  181. end
  182. return false
  183. end
  184. local function processPacket(_,localModem,from,pport,_,packetID,packetType,dest,sender,vPort,data)
  185. pruneCache()
  186. if pport == cfg.port or pport == 0 then -- for linked cards
  187. dprint(cfg.port,vPort,packetType,dest)
  188. if checkPCache(packetID) then return end
  189. if dest == hostname then
  190. if packetType == 1 then
  191. sendPacket(genPacketID(),2,sender,hostname,vPort,packetID)
  192. end
  193. if packetType == 2 then
  194. dprint("Dropping "..data.." from queue")
  195. pqueue[data] = nil
  196. computer.pushSignal("net_ack",data)
  197. end
  198. if packetType ~= 2 then
  199. computer.pushSignal("net_msg",sender,vPort,data)
  200. end
  201. elseif dest:sub(1,1) == "~" then -- broadcasts start with ~
  202. computer.pushSignal("net_broadcast",sender,vPort,data)
  203. elseif cfg.route then -- repeat packets if route is enabled
  204. sendPacket(packetID,packetType,dest,sender,vPort,data)
  205. end
  206. if not rcache[sender] then -- add the sender to the rcache
  207. dprint("rcache: "..sender..":", localModem,from,computer.uptime())
  208. rcache[sender] = {localModem,from,computer.uptime()+cfg.rctime}
  209. end
  210. if not pcache[packetID] then -- add the packet ID to the pcache
  211. pcache[packetID] = computer.uptime()+cfg.pctime
  212. end
  213. end
  214. end
  215. local function queuePacket(_,ptype,to,vPort,data,npID)
  216. npID = npID or genPacketID()
  217. if to == hostname or to == "localhost" then
  218. computer.pushSignal("net_msg",to,vPort,data)
  219. computer.pushSignal("net_ack",npID)
  220. return
  221. end
  222. pqueue[npID] = {ptype,to,vPort,data,0,0}
  223. dprint(npID,table.unpack(pqueue[npID]))
  224. end
  225. local function packetPusher()
  226. for k,v in pairs(pqueue) do
  227. if v[5] < computer.uptime() then
  228. dprint(k,v[1],v[2],hostname,v[3],v[4])
  229. sendPacket(k,v[1],v[2],hostname,v[3],v[4])
  230. if v[1] ~= 1 or v[6] == cfg.retrycount then
  231. pqueue[k] = nil
  232. else
  233. pqueue[k][5]=computer.uptime()+cfg.retry
  234. pqueue[k][6]=pqueue[k][6]+1
  235. end
  236. end
  237. end
  238. end
  239. listeners["modem_message"]=processPacket
  240. listeners["net_send"]=queuePacket
  241. if _OSVERSION:sub(1,6) == "OpenOS" then
  242. event.listen("modem_message",processPacket)
  243. print("Started packet listening daemon: "..tostring(processPacket))
  244. event.listen("net_send",queuePacket)
  245. print("Started packet queueing daemon: "..tostring(queuePacket))
  246. timers[#timers+1]=event.timer(0,packetPusher,math.huge)
  247. print("Started packet pusher: "..tostring(timers[#timers]))
  248. end
  249. if _OSVERSION:sub(1,8) == "KittenOS" then
  250. neo.requireAccess("r.svc.minitel","minitel daemon")(function(pkg,pid,sendSig)
  251. processes[pid] = sendSig
  252. return {["sendPacket"]=queuePacket}
  253. end)
  254. end
  255. if _OSVERSION:sub(1,8) == "KittenOS" or _OSVERSION:sub(1,7) == "PsychOS" then
  256. while true do
  257. local ev = {coroutine.yield()}
  258. packetPusher()
  259. pruneCache()
  260. if ev[1] == "k.procdie" then
  261. processes[ev[3]] = nil
  262. end
  263. if listeners[ev[1]] then
  264. pcall(listeners[ev[1]],table.unpack(ev))
  265. end
  266. end
  267. end
  268. end
  269. if _OSVERSION:sub(1,6) ~= "OpenOS" then
  270. start()
  271. end