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.

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