forked from Hyperion/HyperionOS
made unified colors and stuff
This commit is contained in:
@@ -50,7 +50,7 @@ function proxy:type(path, mode)
|
||||
if type(step[steps[#steps]]) == "table" then
|
||||
return "directory"
|
||||
end
|
||||
error("ENOENT")
|
||||
error(type(step[steps[#steps]]))
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
@@ -140,6 +140,68 @@ function data.zero(op, mode)
|
||||
end
|
||||
end
|
||||
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,eeprom=1,kernel.EFI:getEEPROM()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return eeprom:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(data)
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data.nvram(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,nvram=1,kernel.EFI:getNvram()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return nvram:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(data)
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
data["disk"]={}
|
||||
kernel.devfs={}
|
||||
kernel.devfs.data=data
|
||||
|
||||
@@ -258,7 +258,7 @@ function proxy:fileExists(path)
|
||||
return ok
|
||||
end
|
||||
|
||||
data.uptime=simpleFile(function()return tostring(kernel.computer:getUptime())end,function()error("EACCES")end)
|
||||
data.uptime=simpleFile(function()return tostring(kernel.EFI:getUptime())end,function()error("EACCES")end)
|
||||
kernel.procfs={}
|
||||
kernel.procfs.data=data
|
||||
kernel.procfs.proxy=proxy
|
||||
|
||||
@@ -21,7 +21,7 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
||||
end
|
||||
|
||||
if not semicolon_pos or semicolon_pos == 3 then
|
||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8)
|
||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 0xFF8800)
|
||||
else
|
||||
local id = line:sub(3, semicolon_pos - 1)
|
||||
local path = trim(line:sub(semicolon_pos + 1))
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
-- Supports:
|
||||
-- AF_UNIX - local IPC via /var/run/*.sock paths
|
||||
-- AF_INET - network sockets with three backends:
|
||||
-- rednet://0.0.B.C or rednet+PROTO://0.0.B.C -> CC rednet (computer B*256+C)
|
||||
-- modem://0.0.B.C -> raw CC modem frames
|
||||
-- http://host/path or https://... -> HTTP via CC http API
|
||||
-- A.B.C.D (dotted quad, non-zero A) -> HTTP
|
||||
-- Implemented by drivers but expect http:// and https://
|
||||
--
|
||||
-- Socket lifecycle:
|
||||
-- fd = syscall.socket(domain, socktype) -- "unix"/"inet", "stream"/"dgram"
|
||||
@@ -18,539 +15,9 @@
|
||||
-- syscall.sockshutdown(fd) -- half-close send side
|
||||
-- -- normal vfs.close(fd) closes the socket
|
||||
|
||||
local kernel = ...
|
||||
local kernel=...
|
||||
local socket={}
|
||||
kernel.socket=socket
|
||||
|
||||
local sockets = {}
|
||||
local unixSocks = {}
|
||||
local nextSockId = 1
|
||||
|
||||
local function allocSockId()
|
||||
local id = nextSockId
|
||||
nextSockId = nextSockId + 1
|
||||
return id
|
||||
end
|
||||
|
||||
local function parseAddress(addr)
|
||||
if not addr then error("EINVAL") end
|
||||
|
||||
if addr:sub(1,1) == "/" or addr:sub(1,5) == "unix:" then
|
||||
local path = addr:sub(1,5) == "unix:" and addr:sub(6) or addr
|
||||
return { backend="unix", path=path }
|
||||
end
|
||||
|
||||
local rproto, raddr = addr:match("^rednet%+?([^:/]*)://(.+)$")
|
||||
if raddr then
|
||||
local a,b,c,d = raddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if not a then error("EINVAL: bad rednet address " .. raddr) end
|
||||
local compId = tonumber(c)*256 + tonumber(d)
|
||||
return { backend="rednet", compId=compId,
|
||||
protocol=(rproto ~= "" and rproto or "hyperion") }
|
||||
end
|
||||
|
||||
local maddr = addr:match("^modem://(.+)$")
|
||||
if maddr then
|
||||
local a,b,c,d = maddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if not a then error("EINVAL: bad modem address " .. maddr) end
|
||||
local compId = tonumber(c)*256 + tonumber(d)
|
||||
local port = tonumber(maddr:match(":(%d+)$")) or 0
|
||||
return { backend="modem", compId=compId, port=port }
|
||||
end
|
||||
|
||||
local scheme, rest = addr:match("^(https?)://(.+)$")
|
||||
if scheme then
|
||||
return { backend=scheme, url=addr }
|
||||
end
|
||||
|
||||
local a,b,c,d = addr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)")
|
||||
if a and tonumber(a) ~= 0 then
|
||||
return { backend="http", url="http://" .. addr }
|
||||
end
|
||||
|
||||
error("EINVAL: unrecognised address format: " .. tostring(addr))
|
||||
end
|
||||
|
||||
local rednetOpen = false
|
||||
local function ensureRednet()
|
||||
if rednetOpen then return end
|
||||
local rn = kernel.apis and kernel.apis.rednet
|
||||
if not rn then error("ENODEV: no rednet API available") end
|
||||
local peripheral = kernel.apis.peripheral
|
||||
if peripheral then
|
||||
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
|
||||
if peripheral.getType(name) == "modem" then
|
||||
pcall(rn.open, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
rednetOpen = true
|
||||
end
|
||||
|
||||
local function getModem()
|
||||
local peripheral = kernel.apis and kernel.apis.peripheral
|
||||
if not peripheral then error("ENODEV") end
|
||||
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
|
||||
if peripheral.getType(name) == "modem" then
|
||||
local m = peripheral.wrap(name)
|
||||
if m then return m, name end
|
||||
end
|
||||
end
|
||||
error("ENODEV: no modem peripheral found")
|
||||
end
|
||||
|
||||
local function pumpEvents()
|
||||
local ev = kernel.computer:getMachineEvent()
|
||||
while ev do
|
||||
if ev == "rednet_message" then
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "rednet" and sock.bound then
|
||||
if sock.address.protocol == tostring(select(4, table.unpack({ev}))) or
|
||||
sock.address.protocol == "hyperion" then
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ev = kernel.computer:getMachineEvent()
|
||||
end
|
||||
end
|
||||
|
||||
local function pollEvent()
|
||||
local results = table.pack(kernel.computer:getMachineEvent())
|
||||
if results.n == 0 or results[1] == nil then return nil end
|
||||
return results
|
||||
end
|
||||
|
||||
local function dispatchEvent(ev)
|
||||
if not ev then return end
|
||||
local evtype = ev[1]
|
||||
|
||||
if evtype == "rednet_message" then
|
||||
local senderId = ev[2]
|
||||
local message = ev[3]
|
||||
local protocol = ev[4] or "hyperion"
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "rednet" and (sock.listening or sock.connected) then
|
||||
if sock.address and sock.address.protocol == protocol then
|
||||
table.insert(sock.rxbuf, { from=senderId, data=message })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif evtype == "modem_message" then
|
||||
local channel = ev[3]
|
||||
local msg = ev[5]
|
||||
local fromCh = ev[4]
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "modem" and sock.modemChannel == channel then
|
||||
table.insert(sock.rxbuf, { from=fromCh, data=msg })
|
||||
end
|
||||
end
|
||||
|
||||
elseif evtype == "http_success" then
|
||||
local url = ev[2]
|
||||
local handle = ev[3]
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "http" or sock.backend == "https" then
|
||||
if sock.pendingUrl == url then
|
||||
local body = handle.readAll and handle.readAll() or ""
|
||||
handle.close()
|
||||
table.insert(sock.rxbuf, { data=body, done=true })
|
||||
sock.pendingUrl = nil
|
||||
sock.connected = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif evtype == "http_failure" then
|
||||
local url = ev[2]
|
||||
local err = ev[3]
|
||||
for _, sock in pairs(sockets) do
|
||||
if (sock.backend == "http" or sock.backend == "https") and
|
||||
sock.pendingUrl == url then
|
||||
sock.error = err
|
||||
sock.pendingUrl = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pumpAll()
|
||||
local ev = pollEvent()
|
||||
while ev do
|
||||
dispatchEvent(ev)
|
||||
ev = pollEvent()
|
||||
end
|
||||
end
|
||||
|
||||
local function newSocket(domain, socktype)
|
||||
local sock = {
|
||||
id = allocSockId(),
|
||||
domain = domain, -- "unix" | "inet"
|
||||
socktype = socktype, -- "stream" | "dgram"
|
||||
backend = nil,
|
||||
state = "idle", -- idle | bound | listening | connected | closed
|
||||
rxbuf = {},
|
||||
txbuf = {},
|
||||
backlog = {},
|
||||
address = nil,
|
||||
peer = nil,
|
||||
modemChannel = nil,
|
||||
modem = nil,
|
||||
pendingUrl = nil,
|
||||
bound = false,
|
||||
listening = false,
|
||||
connected = false,
|
||||
error = nil,
|
||||
}
|
||||
sockets[sock.id] = sock
|
||||
return sock
|
||||
end
|
||||
|
||||
local sockSend, sockClose
|
||||
|
||||
local function socketToFd(sock)
|
||||
return {
|
||||
isSocket = true,
|
||||
sockId = sock.id,
|
||||
mode = "rw",
|
||||
meta = { etype=0, owner=0, group=0, perms=0x1FF, cmeta="" },
|
||||
type = "socket",
|
||||
refcount = 1,
|
||||
handle = {
|
||||
read = function(count)
|
||||
pumpAll()
|
||||
if #sock.rxbuf == 0 then return "" end
|
||||
local item = table.remove(sock.rxbuf, 1)
|
||||
local data = type(item) == "table" and (item.data or "") or tostring(item)
|
||||
if count and #data > count then
|
||||
table.insert(sock.rxbuf, 1, { data=data:sub(count+1), from=item.from })
|
||||
data = data:sub(1, count)
|
||||
end
|
||||
return data
|
||||
end,
|
||||
write = function(data)
|
||||
if sock.state == "closed" then error("EBADF") end
|
||||
return sockSend(sock, data)
|
||||
end,
|
||||
close = function()
|
||||
sockClose(sock)
|
||||
end,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
sockSend = function(sock, data)
|
||||
if sock.backend == "unix" then
|
||||
local peer = sock.peer
|
||||
if not peer then error("ENOTCONN") end
|
||||
table.insert(peer.rxbuf, { data=data })
|
||||
return #data
|
||||
|
||||
elseif sock.backend == "rednet" then
|
||||
ensureRednet()
|
||||
local rn = kernel.apis.rednet
|
||||
rn.send(sock.address.compId, data, sock.address.protocol)
|
||||
return #data
|
||||
|
||||
elseif sock.backend == "modem" then
|
||||
local modem = sock.modem
|
||||
if not modem then error("ENOTCONN") end
|
||||
modem.transmit(sock.address.port, sock.modemChannel or 0, data)
|
||||
return #data
|
||||
|
||||
elseif sock.backend == "http" or sock.backend == "https" then
|
||||
local http = kernel.apis and kernel.apis.http
|
||||
if not http then error("ENODEV: no http API") end
|
||||
local url = sock.address.url
|
||||
local ok, err = pcall(http.request, url, data, {
|
||||
["Content-Type"] = "application/octet-stream"
|
||||
})
|
||||
if not ok then error("ENETDOWN: " .. tostring(err)) end
|
||||
sock.pendingUrl = url
|
||||
return #data
|
||||
end
|
||||
error("EPROTONOSUPPORT")
|
||||
end
|
||||
|
||||
sockClose = function(sock)
|
||||
if sock.state == "closed" then return end
|
||||
sock.state = "closed"
|
||||
|
||||
if sock.backend == "unix" then
|
||||
if sock.peer then
|
||||
sock.peer.peer = nil
|
||||
sock.peer.state = "closed"
|
||||
end
|
||||
if sock.bound and sock.address and sock.address.path then
|
||||
unixSocks[sock.address.path] = nil
|
||||
end
|
||||
|
||||
elseif sock.backend == "modem" and sock.modem and sock.modemChannel then
|
||||
pcall(sock.modem.close, sock.modemChannel)
|
||||
|
||||
elseif sock.backend == "rednet" then
|
||||
end
|
||||
|
||||
sockets[sock.id] = nil
|
||||
end
|
||||
|
||||
kernel.syscalls["socket"] = function(domain, socktype)
|
||||
domain = domain or "inet"
|
||||
socktype = socktype or "stream"
|
||||
if domain ~= "unix" and domain ~= "inet" then error("EAFNOSUPPORT") end
|
||||
if socktype ~= "stream" and socktype ~= "dgram" then error("EPROTOTYPE") end
|
||||
|
||||
local sock = newSocket(domain, socktype)
|
||||
local fdobj = socketToFd(sock)
|
||||
local fd = kernel.vfs.newfd(fdobj)
|
||||
return fd
|
||||
end
|
||||
|
||||
kernel.syscalls["bind"] = function(fd, address)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if sock.bound then error("EINVAL") end
|
||||
|
||||
local parsed = parseAddress(address)
|
||||
|
||||
if parsed.backend == "unix" then
|
||||
local existing = unixSocks[parsed.path]
|
||||
if existing then
|
||||
if existing.state == "closed" then
|
||||
unixSocks[parsed.path] = nil
|
||||
else
|
||||
error("EADDRINUSE")
|
||||
end
|
||||
end
|
||||
sock.backend = "unix"
|
||||
sock.address = parsed
|
||||
sock.bound = true
|
||||
sock.state = "bound"
|
||||
unixSocks[parsed.path] = sock
|
||||
|
||||
elseif parsed.backend == "rednet" then
|
||||
ensureRednet()
|
||||
sock.backend = "rednet"
|
||||
sock.address = parsed
|
||||
sock.bound = true
|
||||
sock.state = "bound"
|
||||
|
||||
elseif parsed.backend == "modem" then
|
||||
local modem, side = getModem()
|
||||
sock.backend = "modem"
|
||||
sock.address = parsed
|
||||
sock.modem = modem
|
||||
sock.modemChannel = parsed.port
|
||||
sock.bound = true
|
||||
sock.state = "bound"
|
||||
modem.open(parsed.port)
|
||||
|
||||
else
|
||||
error("EOPNOTSUPP: cannot bind to " .. parsed.backend .. " address")
|
||||
end
|
||||
end
|
||||
|
||||
kernel.syscalls["listen"] = function(fd, backlog)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if not sock.bound then error("EDESTADDRREQ") end
|
||||
sock.listening = true
|
||||
sock.state = "listening"
|
||||
sock.maxBacklog = backlog or 5
|
||||
end
|
||||
|
||||
kernel.syscalls["accept"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if not sock.listening then error("EINVAL") end
|
||||
|
||||
local deadline = kernel.computer:time() + 30000
|
||||
while #sock.backlog == 0 do
|
||||
pumpAll()
|
||||
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
local clientSock = table.remove(sock.backlog, 1)
|
||||
local cfdobj = socketToFd(clientSock)
|
||||
local newfd = kernel.vfs.newfd(cfdobj)
|
||||
return newfd
|
||||
end
|
||||
|
||||
kernel.syscalls["connect"] = function(fd, address)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if sock.connected then error("EISCONN") end
|
||||
|
||||
local parsed = parseAddress(address)
|
||||
sock.address = parsed
|
||||
sock.backend = parsed.backend
|
||||
|
||||
if parsed.backend == "unix" then
|
||||
local server = unixSocks[parsed.path]
|
||||
if not server then error("ECONNREFUSED") end
|
||||
if not server.listening then error("ECONNREFUSED") end
|
||||
if #server.backlog >= (server.maxBacklog or 5) then error("ECONNREFUSED") end
|
||||
|
||||
local serverPeer = newSocket("unix", sock.socktype)
|
||||
serverPeer.backend = "unix"
|
||||
serverPeer.connected = true
|
||||
serverPeer.state = "connected"
|
||||
serverPeer.peer = sock
|
||||
|
||||
sock.peer = serverPeer
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
|
||||
table.insert(server.backlog, serverPeer)
|
||||
|
||||
elseif parsed.backend == "rednet" then
|
||||
ensureRednet()
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
|
||||
elseif parsed.backend == "modem" then
|
||||
local modem, side = getModem()
|
||||
local replyChannel = math.random(1024, 65534)
|
||||
sock.modem = modem
|
||||
sock.modemChannel = replyChannel
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
modem.open(replyChannel)
|
||||
|
||||
elseif parsed.backend == "http" or parsed.backend == "https" then
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
|
||||
else
|
||||
error("EAFNOSUPPORT")
|
||||
end
|
||||
end
|
||||
|
||||
kernel.syscalls["send"] = function(fd, data)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
return sockSend(sock, data)
|
||||
end
|
||||
|
||||
kernel.syscalls["recv"] = function(fd, maxlen, timeout_ms)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
|
||||
local deadline = kernel.computer:time() + (timeout_ms or 10000)
|
||||
while #sock.rxbuf == 0 do
|
||||
pumpAll()
|
||||
if #sock.rxbuf > 0 then break end
|
||||
if sock.state == "closed" or sock.error then
|
||||
if sock.error then error("ECONNRESET: " .. tostring(sock.error)) end
|
||||
return ""
|
||||
end
|
||||
if kernel.computer:time() > deadline then return "" end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
local item = table.remove(sock.rxbuf, 1)
|
||||
local data = type(item) == "table" and (item.data or "") or tostring(item)
|
||||
if maxlen and #data > maxlen then
|
||||
table.insert(sock.rxbuf, 1, { data=data:sub(maxlen+1), from=item and item.from })
|
||||
data = data:sub(1, maxlen)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
kernel.syscalls["sockshutdown"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if sock then sockClose(sock) end
|
||||
end
|
||||
|
||||
kernel.syscalls["getpeername"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock or not sock.connected then error("ENOTCONN") end
|
||||
if sock.address then return sock.address end
|
||||
return nil
|
||||
end
|
||||
|
||||
kernel.syscalls["getsockname"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
return sock.address
|
||||
end
|
||||
|
||||
kernel.syscalls["httpget"] = function(url, headers)
|
||||
local http = kernel.apis and kernel.apis.http
|
||||
if not http then error("ENODEV: no http API") end
|
||||
|
||||
local ok, err = pcall(http.request, url, nil, headers)
|
||||
if not ok then error("ENETDOWN: " .. tostring(err)) end
|
||||
|
||||
local deadline = kernel.computer:time() + 15000
|
||||
while true do
|
||||
local ev = pollEvent()
|
||||
if ev then
|
||||
if ev[1] == "http_success" and ev[2] == url then
|
||||
local handle = ev[3]
|
||||
local body = handle.readAll and handle.readAll() or ""
|
||||
handle.close()
|
||||
return body
|
||||
elseif ev[1] == "http_failure" and ev[2] == url then
|
||||
error("ECONNREFUSED: " .. tostring(ev[3]))
|
||||
else
|
||||
dispatchEvent(ev)
|
||||
end
|
||||
end
|
||||
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
kernel.syscalls["resolve"] = function(hostname)
|
||||
if hostname:match("^%d+%.%d+%.%d+%.%d+$") then return hostname end
|
||||
|
||||
local a,b,c,d = hostname:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if a and tonumber(a) == 0 and tonumber(b) == 0 then
|
||||
return hostname
|
||||
end
|
||||
|
||||
local http = kernel.apis and kernel.apis.http
|
||||
if not http then error("ENODEV: no http API for DNS") end
|
||||
|
||||
local url = "https://cloudflare-dns.com/dns-query?name=" .. hostname .. "&type=A"
|
||||
local body = kernel.syscalls["httpget"](url, {
|
||||
["Accept"] = "application/dns-json"
|
||||
})
|
||||
|
||||
local ip = body:match('"type":1[^}]*"data":"([%d%.]+)"')
|
||||
if not ip then error("ENOENT: could not resolve " .. hostname) end
|
||||
return ip
|
||||
end
|
||||
|
||||
kernel.sockets = sockets
|
||||
kernel.unixSockets = unixSocks
|
||||
|
||||
kernel.log("Loaded socket module")
|
||||
|
||||
@@ -139,7 +139,7 @@ end
|
||||
if not blake2s then error("Failed to load blake2s") end
|
||||
|
||||
if not kernel.vfs.exists("/etc/pam.d/secret") then
|
||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT")
|
||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT", "WARN", 0xFF8800)
|
||||
local key = ""
|
||||
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
|
||||
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
|
||||
|
||||
@@ -41,11 +41,11 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
|
||||
|
||||
if kernel.config.logTaskExit then
|
||||
if not ok then
|
||||
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 2)
|
||||
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 0xFF0000)
|
||||
elseif err then
|
||||
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "INFO")
|
||||
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "DBUG", 0x00FFFF)
|
||||
else
|
||||
kernel.log("Task " .. tostring(id) .. " exited without code", "INFO")
|
||||
kernel.log("Task " .. tostring(id) .. " exited without code", "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -110,7 +110,7 @@ function sys.execspawn(path, name, envars, args, tgid)
|
||||
kernel.log(
|
||||
"execspawn: suid exec '" .. path ..
|
||||
"' caller_uid=" .. tostring(real_uid) ..
|
||||
" -> euid=" .. tostring(euid), "INFO"
|
||||
" -> euid=" .. tostring(euid)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -136,9 +136,9 @@ function sys.exec(path, args, envars)
|
||||
local ok, err = xpcall(func, debug.traceback, table.unpack(task.args))
|
||||
if kernel.config.logTaskExit then
|
||||
if not ok then
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 2)
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 0xFF0000)
|
||||
else
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "INFO")
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
if type(err) == "number" then tasks[tostring(task.pid)].exit = err end
|
||||
@@ -155,7 +155,7 @@ end
|
||||
|
||||
function sys.sleep(s)
|
||||
kernel.currentTask.status = "S"
|
||||
kernel.currentTask.sleep = kernel.computer:time() + s * 1000
|
||||
kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
@@ -260,9 +260,9 @@ function sys.exit(code)
|
||||
local task = kernel.currentTask
|
||||
if kernel.config.logTaskExit then
|
||||
if code then
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "INFO")
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "DBUG", 0x00FFFF)
|
||||
else
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "INFO")
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
tasks[tostring(task.pid)].status = "Z"
|
||||
@@ -303,9 +303,9 @@ local function reapDeadTasks()
|
||||
task.syscallReturn = nil
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.computer:time() + 30000
|
||||
task.reapTime = kernel.EFI:getEpochMs() + 30000
|
||||
|
||||
elseif task.reapTime and kernel.computer:time() > task.reapTime
|
||||
elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
|
||||
and task.status == "Z" then
|
||||
for _, child in ipairs(task.children) do
|
||||
child.parent = tasks["1"]
|
||||
@@ -343,7 +343,7 @@ function kernel.main()
|
||||
local taskTimes = {}
|
||||
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.status == "S" and kernel.computer:time() >= task.sleep then
|
||||
if task.status == "S" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||
task.status = "R"
|
||||
task.sleep = 0
|
||||
end
|
||||
@@ -382,7 +382,7 @@ function kernel.main()
|
||||
end
|
||||
|
||||
if task.status == "R" then
|
||||
local startTime = kernel.computer:time()
|
||||
local startTime = kernel.EFI:getEpochMs()
|
||||
local ret
|
||||
|
||||
if kernel.config.preempt then
|
||||
@@ -391,7 +391,7 @@ function kernel.main()
|
||||
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
|
||||
end
|
||||
|
||||
local elapsed = kernel.computer:time() - startTime
|
||||
local elapsed = kernel.EFI:getEpochMs() - startTime
|
||||
task.lastTime = elapsed
|
||||
task.totalTime = (task.totalTime or 0) + elapsed
|
||||
task.numRuns = (task.numRuns or 0) + 1
|
||||
@@ -403,7 +403,7 @@ function kernel.main()
|
||||
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
|
||||
|
||||
if ret[1] == "error" or ret[1] == false then
|
||||
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 2)
|
||||
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 0xFF0000)
|
||||
task.status = "Z"
|
||||
task.exit = "processHandlerException: " .. tostring(ret[2])
|
||||
|
||||
@@ -418,9 +418,9 @@ function kernel.main()
|
||||
local scname = ret[3]
|
||||
if kernel.syscalls[scname] then
|
||||
if kernel.config.debugSyscalls then
|
||||
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 5)
|
||||
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 0x00FFFF)
|
||||
for i = 4, #ret do
|
||||
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 5)
|
||||
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -428,12 +428,12 @@ function kernel.main()
|
||||
|
||||
if kernel.config.debugSyscalls then
|
||||
if not sysret[1] then
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 2)
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 0xFF0000)
|
||||
else
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 5)
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 0x00FFFF)
|
||||
for i = 2, #sysret do
|
||||
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
|
||||
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 5)
|
||||
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,8 +9,20 @@ sysc["gpio_write"]=function(pin, data)
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_writeAnalog"]=function(pin, data)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("wa", data)
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_read"]=function(pin)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("r")
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_readAnalog"]=function(pin)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("ra")
|
||||
end
|
||||
end
|
||||
@@ -9,11 +9,13 @@ if not initOk then
|
||||
end
|
||||
|
||||
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
||||
if not handle then kernel.panic("Failed to open "..kernel.config.initPath) end
|
||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
if not handle then kernel.panic("Failed to read "..kernel.config.initPath) end
|
||||
kernel.vfs.close(handle)
|
||||
|
||||
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
|
||||
if not initFunc then error("Failed to load init system: " .. err) end
|
||||
if not initFunc then kernel.PANIC("Failed to load init system: " .. err) end
|
||||
|
||||
kernel.tasks["1"] = {
|
||||
coro = coroutine.create(function()
|
||||
|
||||
@@ -4,6 +4,6 @@ local kernel = ...
|
||||
kernel.processes.login = function()
|
||||
local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login")
|
||||
if not ok then
|
||||
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
|
||||
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 0xFF0000)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,88 +1,88 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local P = kernel.vfs.P
|
||||
local PERM = kernel.vfs.PERM
|
||||
|
||||
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
|
||||
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
|
||||
+ P.GROUP_R + P.GROUP_X
|
||||
+ P.WORLD_R + P.WORLD_X
|
||||
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
|
||||
local RW____ = P.OWNER_R + P.OWNER_W
|
||||
local RWXRWXRWX = PERM.RWXRWXRWX
|
||||
local SUID_755 = PERM.SUID_755
|
||||
|
||||
local META_VERSION = 0x02
|
||||
local rootDisk = kernel.disks["$"]
|
||||
|
||||
local function makeEntry(name, etype, owner, group, perms, cmeta)
|
||||
cmeta = cmeta or ""
|
||||
local plo = perms % 256
|
||||
local phi = math.floor(perms / 256) % 256
|
||||
local olo = (owner or 0) % 256
|
||||
local ohi = math.floor((owner or 0) / 256) % 256
|
||||
local glo = (group or 0) % 256
|
||||
local ghi = math.floor((group or 0) / 256) % 256
|
||||
return string.char(#name) .. name
|
||||
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
|
||||
.. string.char(#cmeta) .. cmeta
|
||||
end
|
||||
|
||||
local REG = 0x00
|
||||
|
||||
local function mergeMeta(dir, entries)
|
||||
local diskDir = dir
|
||||
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
|
||||
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
|
||||
|
||||
local existing = {}
|
||||
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
|
||||
if rok and rf then
|
||||
local raw = rf.read(65535)
|
||||
if rf.close then rf.close() end
|
||||
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
|
||||
end
|
||||
|
||||
for _, e in ipairs(entries) do
|
||||
local name = e[1]
|
||||
local etype = e[2] or REG
|
||||
local owner = e[3] or 0
|
||||
local group = e[4] or 0
|
||||
local perms = e[5] or RWX_RX_RX
|
||||
local cmeta = e[6] or ""
|
||||
existing[name] = {
|
||||
etype = etype,
|
||||
owner = owner,
|
||||
group = group,
|
||||
perms = perms,
|
||||
cmeta = cmeta,
|
||||
}
|
||||
end
|
||||
|
||||
local data = string.char(META_VERSION)
|
||||
for name, m in pairs(existing) do
|
||||
data = data .. makeEntry(
|
||||
name,
|
||||
m.etype or REG,
|
||||
m.owner or 0,
|
||||
m.group or 0,
|
||||
m.perms or RWX_RX_RX,
|
||||
m.cmeta or ""
|
||||
)
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local f = rootDisk:open(metaPath, "w")
|
||||
f.write(data)
|
||||
f.close()
|
||||
end)
|
||||
if not ok then
|
||||
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
|
||||
end
|
||||
end
|
||||
|
||||
if kernel.firstBoot then
|
||||
local P = kernel.vfs.P
|
||||
local PERM = kernel.vfs.PERM
|
||||
|
||||
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
|
||||
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
|
||||
+ P.GROUP_R + P.GROUP_X
|
||||
+ P.WORLD_R + P.WORLD_X
|
||||
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
|
||||
local RW____ = P.OWNER_R + P.OWNER_W
|
||||
local RWXRWXRWX = PERM.RWXRWXRWX
|
||||
local SUID_755 = PERM.SUID_755
|
||||
|
||||
local META_VERSION = 0x02
|
||||
local rootDisk = kernel.disks["$"]
|
||||
|
||||
local function makeEntry(name, etype, owner, group, perms, cmeta)
|
||||
cmeta = cmeta or ""
|
||||
local plo = perms % 256
|
||||
local phi = math.floor(perms / 256) % 256
|
||||
local olo = (owner or 0) % 256
|
||||
local ohi = math.floor((owner or 0) / 256) % 256
|
||||
local glo = (group or 0) % 256
|
||||
local ghi = math.floor((group or 0) / 256) % 256
|
||||
return string.char(#name) .. name
|
||||
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
|
||||
.. string.char(#cmeta) .. cmeta
|
||||
end
|
||||
|
||||
local REG = 0x00
|
||||
|
||||
local function mergeMeta(dir, entries)
|
||||
local diskDir = dir
|
||||
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
|
||||
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
|
||||
|
||||
local existing = {}
|
||||
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
|
||||
if rok and rf then
|
||||
local raw = rf.read(65535)
|
||||
if rf.close then rf.close() end
|
||||
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
|
||||
end
|
||||
|
||||
for _, e in ipairs(entries) do
|
||||
local name = e[1]
|
||||
local etype = e[2] or REG
|
||||
local owner = e[3] or 0
|
||||
local group = e[4] or 0
|
||||
local perms = e[5] or RWX_RX_RX
|
||||
local cmeta = e[6] or ""
|
||||
existing[name] = {
|
||||
etype = etype,
|
||||
owner = owner,
|
||||
group = group,
|
||||
perms = perms,
|
||||
cmeta = cmeta,
|
||||
}
|
||||
end
|
||||
|
||||
local data = string.char(META_VERSION)
|
||||
for name, m in pairs(existing) do
|
||||
data = data .. makeEntry(
|
||||
name,
|
||||
m.etype or REG,
|
||||
m.owner or 0,
|
||||
m.group or 0,
|
||||
m.perms or RWX_RX_RX,
|
||||
m.cmeta or ""
|
||||
)
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local f = rootDisk:open(metaPath, "w")
|
||||
f.write(data)
|
||||
f.close()
|
||||
end)
|
||||
if not ok then
|
||||
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 0xFF8800)
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("Seeding filesystem permissions...")
|
||||
|
||||
mergeMeta("/", {
|
||||
@@ -117,4 +117,4 @@ if kernel.firstBoot then
|
||||
})
|
||||
|
||||
kernel.log("Filesystem permissions seeded.")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user