Compare commits

..

5 Commits

6 changed files with 180 additions and 157 deletions

View File

@ -1,38 +1,47 @@
local computer = require "computer" local computer = require "computer"
local minitel = require "minitel" local minitel = require "minitel"
local event = require "event"
local rpc = require "rpc" local rpc = require "rpc"
local netutil = {} local netutil = {}
function netutil.importfs(host,rpath,lpath) function netutil.importfs(host,rpath,lpath) -- import filesystem *rpath* from *host* and attach it to *lpath*
local px = rpc.proxy(host,rpath.."_") local px = rpc.proxy(host,rpath.."_")
function px.getLabel() function px.getLabel()
return host..":"..rpath return host..":"..rpath
end end
fs.mount(lpath,px) return fs.mount(lpath,px)
end end
function netutil.exportfs(path) function netutil.exportfs(path) -- export the directory *path* over RPC
local path = "/"..table.concat(fs.segments(path),"/") local path = "/"..table.concat(fs.segments(path),"/")
local px = ufs.create(path) local px = ufs.create(path)
for k,v in pairs(px) do for k,v in pairs(px) do
rpcs.register(path.."_"..k,v) rpcs.register(path.."_"..k,v)
print(path.."_"..k) print(path.."_"..k)
end end
return true
end end
function netutil.ping(addr,times,timeout) function netutil.ping(addr,times,timeout, silent) -- Request acknowledgment from *addr*, waiting *timeout* seconds each try, and try *times* times. If *silent* is true, don't print status. Returns true if there was at least one successful ping, the number of successes, the number of failures, and the average round trip time.
local times, timeout = times or 5, timeout or 30 local times, timeout = times or 5, timeout or 30
local success, fail, time, avg = 0, 0, 0, 0
for i = 1, times do for i = 1, times do
local ipt = computer.uptime() local ipt = computer.uptime()
local pid = minitel.genPacketID() local pid = minitel.genPacketID()
computer.pushSignal("net_send",1,addr,0,"ping",pid) computer.pushSignal("net_send",1,addr,0,"ping",pid)
local t,a = event.pull(timeout,"net_ack") local t,a = event.pull(timeout,"net_ack")
if t == "net_ack" and a == pid then if t == "net_ack" and a == pid then
print("Ping reply: "..tostring(computer.uptime()-ipt).." seconds.") if not silent then print("Ping reply: "..tostring(computer.uptime()-ipt).." seconds.") end
success = success + 1
time = time + computer.uptime()-ipt
avg = time / success
else else
print("Timed out.") if not silent then print("Timed out.") end
fail = fail + 1
end end
end end
if not silent then print(string.format("%d packets transmitted, %d received, %0.0f%% packet loss, %0.1fs",times,success,fail/times*100,time)) end
return success > 0, success, fail, avg
end end
return netutil return netutil

76
lib/rc.lua Normal file
View File

@ -0,0 +1,76 @@
local serial = require "serialization"
local rc = {}
local service = {}
local cfg = {}
cfg.enabled = {}
local function loadConfig()
local f = io.open("/boot/cfg/rc.cfg","rb")
if not f then return false end
cfg = serial.unserialize(f:read("*a")) or {}
f:close()
cfg.enabled = cfg.enabled or {}
return true
end
local function saveConfig()
local f = io.open("/boot/cfg/rc.cfg","wb")
if not f then return false end
f:write(serial.serialize(cfg))
f:close()
return true
end
function rc.load(name,force)
if force then
rc.stop(name)
service[name] = nil
end
if service[name] then return true end
service[name] = setmetatable({},{__index=_G})
local f = io.open("/boot/service/"..name..".lua","rb")
local res = load(f:read("*a"),name,"t",service[name])()
f:close()
return res
end
function rc.stop(name,...)
if not service[name] then return false, "service not found" end
service[name].stop(...)
end
function rc.start(name,...)
rc.load(name)
if not service[name] then return false, "service not found" end
return service[name].start(...)
end
function rc.restart(name)
rc.stop(name)
rc.start(name)
end
function rc.enable(name)
for k,v in pairs(cfg.enabled) do
if v == name then return false end
end
saveConfig()
end
function rc.disable(name)
local disabled = false
for k,v in pairs(cfg.enabled) do
if v == name then table.remove(cfg.enabled,k) disabled = true break end
end
saveConfig()
return disabled
end
loadConfig()
for k,v in pairs(cfg.enabled) do
rc.start(v)
end
_G.service = service
return rc

View File

@ -22,20 +22,16 @@ os.spawn(function()
os.setenv("HOSTNAME",hostname) os.setenv("HOSTNAME",hostname)
syslog(string.format("Hostname set to %s",hostname)) syslog(string.format("Hostname set to %s",hostname))
local pids = {} local pids = {}
local function loadlist() local rc = require "rc"
local f = io.open("/boot/cfg/init.txt","rb") for k,v in pairs(rc.cfg.enabled) do
if not f then return false end pids[v] = -1
for line in f:read("*a"):gmatch("[^\r\n]+") do dprint(v)
pids[line] = -1
end end
f:close()
end
loadlist()
while true do while true do
for k,v in pairs(pids) do for k,v in pairs(pids) do
if not os.taskInfo(v) then if not os.taskInfo(v) then
syslog("Starting service "..k) syslog("Starting service "..k)
pids[k] = os.spawnfile("/boot/service/"..k) pids[k] = rc.start(k)
end end
end end
coroutine.yield() coroutine.yield()

View File

@ -1,12 +1,12 @@
io = {} io = {}
function io.open(path,mode) function io.open(path,mode) -- Open file *path* in *mode*. Returns a buffer object.
local f,e = fs.open(path, mode) local f,e = fs.open(path, mode)
if not f then return false, e end if not f then return false, e end
return buffer.new(mode,f) return buffer.new(mode,f)
end end
function io.input(fd) function io.input(fd) -- Sets the default input stream to *fd* if provided, either as a buffer as a path. Returns the default input stream.
if type(fd) == "string" then if type(fd) == "string" then
fd=io.open(fd,"rb") fd=io.open(fd,"rb")
end end
@ -15,7 +15,7 @@ function io.input(fd)
end end
return os.getenv("STDIN") return os.getenv("STDIN")
end end
function io.output(fd) function io.output(fd) -- Sets the default output stream to *fd* if provided, either as a buffer as a path. Returns the default output stream.
if type(fd) == "string" then if type(fd) == "string" then
fd=io.open(fd,"wb") fd=io.open(fd,"wb")
end end
@ -25,14 +25,14 @@ function io.output(fd)
return os.getenv("STDOUT") return os.getenv("STDOUT")
end end
function io.read(...) function io.read(...) -- Reads from the default input stream.
return io.input():read() return io.input():read(...)
end end
function io.write(...) function io.write(...) -- Writes its arguments to the default output stream.
io.output():write(...) io.output():write(...)
end end
function print(...) function print(...) -- Writes each argument to the default output stream, separated by newlines.
for k,v in ipairs({...}) do for k,v in ipairs({...}) do
io.write(tostring(v).."\n") io.write(tostring(v).."\n")
end end

View File

@ -1,4 +1,5 @@
local gpus,screens,ttyn,pids = {}, {}, 0, {} local gpus,screens,ttyn,pids = {}, {}, 0, {}
local basepid = nil
local shell = require "shell" local shell = require "shell"
local function scan() local function scan()
local w,di = pcall(computer.getDeviceInfo) local w,di = pcall(computer.getDeviceInfo)
@ -53,9 +54,11 @@ local function allocate()
end end
end end
function start()
scan() scan()
allocate() allocate()
dprint("screens ready") dprint("screens ready")
basepid = os.spawn(function()
while true do while true do
coroutine.yield() coroutine.yield()
for k,v in pairs(pids) do for k,v in pairs(pids) do
@ -66,3 +69,10 @@ while true do
end end
end end
end end
end,"getty")
return basepid
end
function stop()
os.kill(basepid)
basepid = nil
end

View File

@ -10,36 +10,27 @@ sender: original sender of packet
data: the actual packet data, duh. data: the actual packet data, duh.
]]-- ]]--
local listeners,timers,processes,modems = {},{},{},{} local listeners = {}
local hostname = os.getenv("HOSTNAME") local timers = {}
local cfg = {} local cfg = {}
local event = require "event"
local component = require "component"
local computer = require "computer"
local serial = require "serialization"
local hostname = computer.address():sub(1,8)
local modems = {}
local pid = nil
cfg.debug = false cfg.debug = false
cfg.port = 4096 cfg.port = 4096
cfg.retry = 10 cfg.retry = 10
cfg.retrycount = 64 cfg.retrycount = 3
cfg.route = true cfg.route = true
local event, component, computer, serial = event, component, computer, serial
local hnpath, cfgpath = "", ""
OPENOS, PSYCHOS, KITTENOS = false, false, false
if _OSVERSION:sub(1,6) == "OpenOS" then
OPENOS = true
hnpath = "/etc/hostname"
cfgpath = "/etc/minitel.cfg"
elseif _OSVERSION:sub(1,7) == "PsychOS" then
PSYCHOS = true
hnpath = "/boot/cfg/hostname"
cfgpath = "/boot/cfg/minitel.cfg"
elseif _OSVERSION:sub(1,8) == "KittenOS" then
KITTENOS = true
end
-- packet cache: [packet ID]=uptime
local pcache = {}
cfg.pctime = 30
--[[ --[[
LKR format: LKR format:
address { address {
@ -48,7 +39,6 @@ address {
time last received time last received
} }
]]-- ]]--
cfg.sroutes = {} cfg.sroutes = {}
local rcache = setmetatable({},{__index=cfg.sroutes}) local rcache = setmetatable({},{__index=cfg.sroutes})
cfg.rctime = 15 cfg.rctime = 15
@ -66,60 +56,9 @@ packet queue format:
]]-- ]]--
local pqueue = {} local pqueue = {}
local function saveconfig() -- packet cache: [packet ID]=uptime
if OPENOS or PSYCHOS then local pcache = {}
local f = io.open(cfgpath,"wb") cfg.pctime = 30
if f then
f:write(serial.serialize(cfg))
f:close()
end
end
end
local function loadconfig()
hostname = os.getenv("HOSTNAME") or computer.address():sub(1,8)
if OPENOS or PSYCHOS then
local f,g=io.open(hnpath,"rb")
if f then
hostname = f:read("*a"):match("(.-)\n")
f:close()
end
local f = io.open(cfgpath,"rb")
if f then
local newcfg = serial.unserialize(f:read("*a")) or {}
f:close()
for k,v in pairs(newcfg) do
cfg[k] = v
end
else
saveconfig()
end
elseif KITTENOS then
local globals = neo.requestAccess("x.neo.pub.globals") -- KittenOS standard hostname stuff
if globals then
hostname = globals.getSetting("hostname") or hostname
globals.setSetting("hostname",hostname)
end
end
end
-- specific OS support here
if PSYCHOS then -- PsychOS specific code
serial = require "serialization"
elseif OPENOS then -- OpenOS specific code
event = require "event"
component = require "component"
computer = require "computer"
serial = require "serialization"
listener = false
elseif KITTENOS then
neo.requireAccess("s.h.modem_message","pulling packets")
computer = {["uptime"]=os.uptime,["address"]=os.address} -- wrap computer so the OpenOS code more or less works
function computer.pushSignal(...)
for k,v in pairs(processes) do
v(...)
end
end
end
local function dprint(...) local function dprint(...)
if cfg.debug then if cfg.debug then
@ -127,35 +66,44 @@ local function dprint(...)
end end
end end
local function saveconfig()
local f = io.open("/boot/cfg/minitel.cfg","wb")
if f then
f:write(serial.serialize(cfg))
f:close()
end
end
local function loadconfig()
local f = io.open("/boot/cfg/minitel.cfg","rb")
if f then
local newcfg = serial.unserialize(f:read("*a"))
f:close()
for k,v in pairs(newcfg) do
cfg[k] = v
end
else
saveconfig()
end
end
function start() function start()
loadconfig() loadconfig()
hostname = os.getenv("HOSTNAME") or computer.address():sub(1,8)
print("Hostname: "..hostname) print("Hostname: "..hostname)
if listener then return end
if OPENOS or PSYCHOS then if pid then return false end
modems={}
for a,t in component.list("modem") do for a,t in component.list("modem") do
modems[#modems+1] = component.proxy(a) modems[#modems+1] = component.proxy(a)
end end
for k,v in ipairs(modems) do for k,v in ipairs(modems) do
v.open(cfg.port) v.open(cfg.port)
print("Opened port "..cfg.port.." on "..v.address:sub(1,8)) print("Opened port "..cfg.port.." on "..v.address)
end end
for a,t in component.list("tunnel") do for a,t in component.list("tunnel") do
modems[#modems+1] = component.proxy(a) modems[#modems+1] = component.proxy(a)
end end
elseif KITTENOS then
for p in neo.requireAccess("c.modem","networking").list() do -- fun stuff for KittenOS
dprint(p.address)
modems[p.address] = p
end
for k,v in pairs(modems) do
v.open(port)
print("Opened port "..port.." on "..v.address)
end
for p in neo.requireAccess("c.tunnel","networking").list() do
dprint(p.address)
modems[p.address] = p
end
end
local function genPacketID() local function genPacketID()
local npID = "" local npID = ""
@ -176,10 +124,11 @@ function start()
else else
dprint("Not cached", cfg.port,packetID,packetType,dest,sender,vPort,data) dprint("Not cached", cfg.port,packetID,packetType,dest,sender,vPort,data)
for k,v in pairs(modems) do for k,v in pairs(modems) do
-- do not send message back to the wired or linked modem it came from
-- the check for tunnels is for short circuiting `v.isWireless()`, which does not exist for tunnels
if v.address ~= repeatingFrom or (v.type ~= "tunnel" and v.isWireless()) then if v.address ~= repeatingFrom or (v.type ~= "tunnel" and v.isWireless()) then
if v.type == "modem" then if v.type == "modem" then
v.broadcast(cfg.port,packetID,packetType,dest,sender,vPort,data) v.broadcast(cfg.port,packetID,packetType,dest,sender,vPort,data)
elseif v.type == "tunnel" then
v.send(packetID,packetType,dest,sender,vPort,data) v.send(packetID,packetType,dest,sender,vPort,data)
end end
end end
@ -255,7 +204,6 @@ function start()
dprint(npID,table.unpack(pqueue[npID])) dprint(npID,table.unpack(pqueue[npID]))
end end
local function packetPusher() local function packetPusher()
for k,v in pairs(pqueue) do for k,v in pairs(pqueue) do
if v[5] < computer.uptime() then if v[5] < computer.uptime() then
@ -273,43 +221,29 @@ function start()
listeners["modem_message"]=processPacket listeners["modem_message"]=processPacket
listeners["net_send"]=queuePacket listeners["net_send"]=queuePacket
if OPENOS then listeners["net_ack"]=dprint
event.listen("modem_message",processPacket)
print("Started packet listening daemon: "..tostring(processPacket))
event.listen("net_send",queuePacket)
print("Started packet queueing daemon: "..tostring(queuePacket))
timers[#timers+1]=event.timer(0,packetPusher,math.huge)
print("Started packet pusher: "..tostring(timers[#timers]))
elseif KITTENOS then
neo.requireAccess("r.svc.minitel","minitel daemon")(function(pkg,pid,sendSig)
processes[pid] = sendSig
return {["sendPacket"]=queuePacket}
end)
end
if KITTENOS or PSYCHOS then pid=os.spawn(function()
while true do while true do
local ev = {coroutine.yield()} local ev = {coroutine.yield()}
packetPusher() packetPusher()
pruneCache() pruneCache()
if ev[1] == "k.procdie" then
processes[ev[3]] = nil
end
if listeners[ev[1]] then if listeners[ev[1]] then
pcall(listeners[ev[1]],table.unpack(ev)) pcall(listeners[ev[1]],table.unpack(ev))
end end
end end
end end,"minitel")
print("Started Minitel daemon: "..tostring(pid))
return pid
end end
function stop() function stop()
for k,v in pairs(listeners) do if pid then
event.ignore(k,v) os.kill(pid)
print("Stopped listener: "..tostring(v)) pid = nil
end return true
for k,v in pairs(timers) do else
event.cancel(v) return false
print("Stopped timer: "..tostring(v))
end end
end end
@ -331,11 +265,9 @@ end
function set_route(to,laddr,raddr) function set_route(to,laddr,raddr)
cfg.sroutes[to] = {laddr,raddr,0} cfg.sroutes[to] = {laddr,raddr,0}
saveconfig()
end end
function del_route(to) function del_route(to)
cfg.sroutes[to] = nil cfg.sroutes[to] = nil
end saveconfig()
if not OPENOS then
start()
end end