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.

341 lines
8.5KB

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