added seperate input and working on http / sockets
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
--:Minify:--
|
||||
local http = {}
|
||||
|
||||
local syscall = syscall
|
||||
|
||||
local function parseUrl(url)
|
||||
local proto, host, path =
|
||||
url:match(
|
||||
"^(https?://)([^/]+)(/.*)$"
|
||||
)
|
||||
|
||||
if not proto then
|
||||
proto, host =
|
||||
url:match(
|
||||
"^(https?://)([^/]+)$"
|
||||
)
|
||||
|
||||
path = "/"
|
||||
end
|
||||
|
||||
if not proto then
|
||||
return nil, "EINVAL"
|
||||
end
|
||||
|
||||
return host, path
|
||||
end
|
||||
|
||||
local function buildRequest(req)
|
||||
local host, path =
|
||||
parseUrl(req.url)
|
||||
|
||||
if not host then
|
||||
return nil, path
|
||||
end
|
||||
|
||||
local method =
|
||||
req.method or "GET"
|
||||
|
||||
local body =
|
||||
req.body or ""
|
||||
|
||||
local headers =
|
||||
table.deepcopy(
|
||||
req.headers or {}
|
||||
)
|
||||
|
||||
headers.Host =
|
||||
headers.Host or host
|
||||
|
||||
headers.Connection =
|
||||
headers.Connection or "close"
|
||||
|
||||
if body ~= "" then
|
||||
headers["Content-Length"] =
|
||||
tostring(#body)
|
||||
end
|
||||
|
||||
local out = {
|
||||
method ..
|
||||
" " ..
|
||||
path ..
|
||||
" HTTP/1.1"
|
||||
}
|
||||
|
||||
for k, v in pairs(headers) do
|
||||
out[#out + 1] =
|
||||
tostring(k) ..
|
||||
": " ..
|
||||
tostring(v)
|
||||
end
|
||||
|
||||
out[#out + 1] = ""
|
||||
out[#out + 1] = body
|
||||
|
||||
return table.concat(out, "\r\n")
|
||||
end
|
||||
|
||||
local function parseResponse(raw)
|
||||
local headerEnd =
|
||||
raw:find(
|
||||
"\r\n\r\n",
|
||||
1,
|
||||
true
|
||||
)
|
||||
|
||||
if not headerEnd then
|
||||
return nil, "EBADMSG"
|
||||
end
|
||||
|
||||
local headerPart =
|
||||
raw:sub(1, headerEnd - 1)
|
||||
|
||||
local body =
|
||||
raw:sub(headerEnd + 4)
|
||||
|
||||
local lines = {}
|
||||
|
||||
for line in headerPart:gmatch("[^\r\n]+") do
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
|
||||
local _, code, msg =
|
||||
lines[1]:match(
|
||||
"^(HTTP/%S+)%s+(%d+)%s*(.*)$"
|
||||
)
|
||||
|
||||
local headers = {}
|
||||
|
||||
for i = 2, #lines do
|
||||
local k, v =
|
||||
lines[i]:match(
|
||||
"^([^:]+):%s*(.*)$"
|
||||
)
|
||||
|
||||
if k then
|
||||
headers[k:lower()] = v
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
code = tonumber(code),
|
||||
message = msg,
|
||||
headers = headers,
|
||||
body = body
|
||||
}
|
||||
end
|
||||
|
||||
local function readAll(fd)
|
||||
local out = ""
|
||||
|
||||
while true do
|
||||
local chunk =
|
||||
syscall.read(fd, 4096)
|
||||
|
||||
if not chunk or chunk == "" then
|
||||
break
|
||||
end
|
||||
|
||||
out = out .. chunk
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function http.request(req)
|
||||
local raw, err =
|
||||
buildRequest(req)
|
||||
|
||||
if not raw then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local fd =
|
||||
syscall.socket(0, 0)
|
||||
|
||||
if not fd then
|
||||
return nil, "ESOCKET"
|
||||
end
|
||||
|
||||
local ok, err =
|
||||
syscall.connect(fd, req.url)
|
||||
|
||||
if not ok then
|
||||
syscall.close(fd)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local ok2, err2 =
|
||||
syscall.write(fd, raw)
|
||||
|
||||
if not ok2 then
|
||||
syscall.close(fd)
|
||||
return nil, err2
|
||||
end
|
||||
|
||||
local resp =
|
||||
readAll(fd)
|
||||
|
||||
syscall.close(fd)
|
||||
|
||||
return parseResponse(resp)
|
||||
end
|
||||
|
||||
function http.get(url, headers)
|
||||
return http.request({
|
||||
url = url,
|
||||
method = "GET",
|
||||
headers = headers
|
||||
})
|
||||
end
|
||||
|
||||
function http.post(url, body, headers)
|
||||
return http.request({
|
||||
url = url,
|
||||
method = "POST",
|
||||
body = body,
|
||||
headers = headers
|
||||
})
|
||||
end
|
||||
|
||||
return http
|
||||
@@ -1 +1,306 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local handler = {}
|
||||
|
||||
local http = kernel.apis.http
|
||||
|
||||
kernel.cct.httpqueue = kernel.cct.httpqueue or {}
|
||||
kernel.cct.httpresponse = kernel.cct.httpresponse or {}
|
||||
kernel.cct.httperror = kernel.cct.httperror or {}
|
||||
|
||||
local function parseRawRequest(raw)
|
||||
local sepLen = 4
|
||||
|
||||
local headerEnd =
|
||||
raw:find("\r\n\r\n", 1, true)
|
||||
|
||||
if not headerEnd then
|
||||
headerEnd =
|
||||
raw:find("\n\n", 1, true)
|
||||
|
||||
sepLen = 2
|
||||
end
|
||||
|
||||
local headerPart
|
||||
local body = ""
|
||||
|
||||
if headerEnd then
|
||||
headerPart = raw:sub(1, headerEnd - 1)
|
||||
body = raw:sub(headerEnd + sepLen)
|
||||
else
|
||||
headerPart = raw
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
|
||||
for line in headerPart:gmatch("[^\r\n]+") do
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
|
||||
if #lines == 0 then
|
||||
return nil, "EINVAL"
|
||||
end
|
||||
|
||||
local method, path =
|
||||
lines[1]:match("^(%S+)%s+(%S+)")
|
||||
|
||||
if not method or not path then
|
||||
return nil, "EBADMSG"
|
||||
end
|
||||
|
||||
local headers = {}
|
||||
|
||||
for i = 2, #lines do
|
||||
local k, v =
|
||||
lines[i]:match("^([^:]+):%s*(.*)$")
|
||||
|
||||
if k then
|
||||
headers[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local host = headers.Host or headers.host
|
||||
|
||||
if not host and not path:match("^https?://") then
|
||||
return nil, "EHOSTUNREACH"
|
||||
end
|
||||
|
||||
local url
|
||||
|
||||
if path:match("^https?://") then
|
||||
url = path
|
||||
else
|
||||
url = "http://" .. host .. path
|
||||
end
|
||||
|
||||
local req = {
|
||||
url = url,
|
||||
method = method,
|
||||
headers = headers
|
||||
}
|
||||
|
||||
if body ~= "" then
|
||||
req.body = body
|
||||
end
|
||||
|
||||
return req
|
||||
end
|
||||
|
||||
local function buildResponse(resp)
|
||||
if not resp then
|
||||
return nil, "EINVAL"
|
||||
end
|
||||
|
||||
local code, msg = resp.getResponseCode()
|
||||
|
||||
local headers =
|
||||
resp:getResponseHeaders()
|
||||
|
||||
local body =
|
||||
resp:readAll() or ""
|
||||
|
||||
local out = {
|
||||
"HTTP/1.1 " ..
|
||||
tostring(code) ..
|
||||
" " ..
|
||||
tostring(msg)
|
||||
}
|
||||
|
||||
local hasLength = false
|
||||
|
||||
for k, v in pairs(headers or {}) do
|
||||
if k:lower() == "content-length" then
|
||||
hasLength = true
|
||||
end
|
||||
|
||||
out[#out + 1] =
|
||||
tostring(k) ..
|
||||
": " ..
|
||||
tostring(v)
|
||||
end
|
||||
|
||||
if not hasLength then
|
||||
out[#out + 1] =
|
||||
"Content-Length: " ..
|
||||
tostring(#body)
|
||||
end
|
||||
|
||||
out[#out + 1] = ""
|
||||
out[#out + 1] = body
|
||||
|
||||
return table.concat(out, "\r\n")
|
||||
end
|
||||
|
||||
function handler.connect(fd, address)
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
fdo.socket.rbuf = ""
|
||||
fdo.socket.closed = false
|
||||
fdo.socket.httpid = nil
|
||||
|
||||
fdo.handle.write = function(raw)
|
||||
local req, err =
|
||||
parseRawRequest(raw)
|
||||
|
||||
if not req then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local id =
|
||||
tostring(kernel.uuid())
|
||||
|
||||
fdo.socket.httpid = id
|
||||
|
||||
kernel.cct.httpqueue[id] = true
|
||||
|
||||
local ok, err =
|
||||
http.request(req, id)
|
||||
|
||||
if not ok then
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
fdo.handle.read = function(count)
|
||||
count = count or 4096
|
||||
|
||||
local sock = fdo.socket
|
||||
|
||||
if #sock.rbuf > 0 then
|
||||
local out =
|
||||
sock.rbuf:sub(1, count)
|
||||
|
||||
sock.rbuf =
|
||||
sock.rbuf:sub(count + 1)
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
local id = sock.httpid
|
||||
|
||||
if not id then
|
||||
return ""
|
||||
end
|
||||
|
||||
local function finish(resp)
|
||||
sock.rbuf =
|
||||
buildResponse(resp) or ""
|
||||
|
||||
local out =
|
||||
sock.rbuf:sub(1, count)
|
||||
|
||||
sock.rbuf =
|
||||
sock.rbuf:sub(count + 1)
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
if kernel.cct.httpresponse[id] then
|
||||
local resp =
|
||||
kernel.cct.httpresponse[id]
|
||||
|
||||
kernel.cct.httpresponse[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
return finish(resp)
|
||||
end
|
||||
|
||||
if kernel.cct.httperror[id] then
|
||||
local err =
|
||||
kernel.cct.httperror[id]
|
||||
|
||||
kernel.cct.httperror[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
return nil, err
|
||||
end
|
||||
|
||||
kernel.currentTask.status = "D"
|
||||
|
||||
local coro
|
||||
|
||||
coro = function()
|
||||
if kernel.cct.httpresponse[id] then
|
||||
local resp =
|
||||
kernel.cct.httpresponse[id]
|
||||
|
||||
kernel.cct.httpresponse[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
kernel.asyncReturn(
|
||||
finish(resp)
|
||||
)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if kernel.cct.httperror[id] then
|
||||
local err =
|
||||
kernel.cct.httperror[id]
|
||||
|
||||
kernel.cct.httperror[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
kernel.asyncReturn(
|
||||
nil,
|
||||
err
|
||||
)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
kernel.currentTask.ksh =
|
||||
coroutine.create(function()
|
||||
local ok, err =
|
||||
xpcall(
|
||||
coro,
|
||||
debug.traceback
|
||||
)
|
||||
|
||||
if not ok then
|
||||
kernel.asyncReturn(
|
||||
nil,
|
||||
err
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
fdo.handle.close = function()
|
||||
fdo.socket.closed = true
|
||||
|
||||
local id =
|
||||
fdo.socket.httpid
|
||||
|
||||
if id then
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
kernel.cct.httpresponse[id] = nil
|
||||
kernel.cct.httperror[id] = nil
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
kernel.socket.registerProtocal(
|
||||
"http://",
|
||||
handler
|
||||
)
|
||||
|
||||
kernel.socket.registerProtocal(
|
||||
"https://",
|
||||
handler
|
||||
)
|
||||
@@ -223,6 +223,9 @@ local function newtty(obj, id, ev)
|
||||
gctrl=function()
|
||||
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
|
||||
end,
|
||||
isvirt=function()
|
||||
return false
|
||||
end,
|
||||
gplt=function()
|
||||
return plt
|
||||
end
|
||||
@@ -243,8 +246,39 @@ end
|
||||
local fifo = kernel.newFifo()
|
||||
kernel.cct.fifo=fifo
|
||||
|
||||
newtty(kernel.apis.term, "1", fifo.pop)
|
||||
newtty(kernel.apis.term, "1", function() end)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
newtty(v,tostring(i+1),function () end)
|
||||
end
|
||||
|
||||
kernel.devfs.data["input"]["keyboard1"] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "Keyboard"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {fifo.pop()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,7 @@ local keys=kernel.apis.keys
|
||||
|
||||
kernel.processes.cctdeamon = function()
|
||||
local timeout = false
|
||||
kernel.log("CCT deamon started")
|
||||
while true do
|
||||
local event = {kernel.EFI:getMachineEvent()}
|
||||
|
||||
@@ -66,6 +67,12 @@ kernel.processes.cctdeamon = function()
|
||||
|
||||
elseif eventType == "keyTyped" then
|
||||
if charOrKey then kernel.cct.fifo.push(charOrKey) end
|
||||
elseif eventType == "mouse_scroll" then
|
||||
if event[2] == 1 then
|
||||
kernel.cct.fifo.push("[nS")
|
||||
else
|
||||
kernel.cct.fifo.push("[nT")
|
||||
end
|
||||
elseif eventType == "http_success" then
|
||||
kernel.cct.httpqueue[event[2]]=nil
|
||||
kernel.cct.httpresponse[event[2]]=event[3]
|
||||
@@ -84,3 +91,5 @@ kernel.processes.cctdeamon = function()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("CCT deamon queued for execution")
|
||||
@@ -19,26 +19,34 @@ kernel.status = "start"
|
||||
kernel.key = {}
|
||||
kernel.cache = {}
|
||||
kernel.cache.preload = {}
|
||||
kernel.unixSockets={}
|
||||
kernel._G=_G
|
||||
kernel.sleep=sleep
|
||||
|
||||
_G.sleep=nil
|
||||
local windowsExp = true
|
||||
local windowsExp = false
|
||||
|
||||
function kernel.log(msg, level, c)
|
||||
c=c or 0x6D6D6D
|
||||
kernel.LOG_Text = kernel.LOG_Text..tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||
if kernel.status == "start" then
|
||||
screen:setTextColor(c)
|
||||
screen:print(tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||
elseif kernel.status == "term" then
|
||||
kernel.standbyTask=kernel.currentTask
|
||||
kernel.currentTask=kernel.kernelTask
|
||||
local file=kernel.vfs.open("/dev/console", "w")
|
||||
kernel.vfs.devctl(file,"sfgc",c)
|
||||
kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||
kernel.vfs.close(file)
|
||||
kernel.currentTask=kernel.standbyTask
|
||||
function kernel.log(msg, level, c, nopanic)
|
||||
local f=function()
|
||||
c=c or 0x6D6D6D
|
||||
kernel.LOG_Text = kernel.LOG_Text..tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||
if kernel.status == "start" then
|
||||
screen:setTextColor(c)
|
||||
screen:print(tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||
elseif kernel.status == "term" then
|
||||
kernel.standbyTask=kernel.currentTask
|
||||
kernel.currentTask=kernel.kernelTask
|
||||
local file=kernel.vfs.open("/dev/console", "w")
|
||||
kernel.vfs.devctl(file,"sfgc",c)
|
||||
kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||
kernel.vfs.close(file)
|
||||
kernel.currentTask=kernel.standbyTask
|
||||
end
|
||||
end
|
||||
|
||||
local ok,err = xpcall(f,debug.traceback)
|
||||
if not ok and not nopanic then
|
||||
kernel.panic(err)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -246,6 +254,15 @@ function kernel.reboot()
|
||||
kernel.status="reboot"
|
||||
end
|
||||
|
||||
function kernel.halt()
|
||||
kernel.exitMain=true
|
||||
kernel.status="halt"
|
||||
end
|
||||
|
||||
function kernel.asyncReturn(...)
|
||||
kernel.currentTask.syscallReturn = {...}
|
||||
end
|
||||
|
||||
kernel.syscalls["time"]=function() return kernel.EFI:getEpochMs() end
|
||||
kernel.syscalls["date"]=function() return kernel.EFI:date() end
|
||||
kernel.syscalls["log"]=kernel.log
|
||||
@@ -264,8 +281,21 @@ kernel.syscalls["sysdump"]=function()
|
||||
end
|
||||
return rv
|
||||
end
|
||||
kernel.syscalls["reboot"]=kernel.reboot
|
||||
kernel.syscalls["shutdown"]=kernel.shutdown
|
||||
kernel.syscalls["reboot"]=function()
|
||||
if kernel.uid==0 or kernel.groups.wheel then
|
||||
kernel.reboot()
|
||||
end
|
||||
end
|
||||
kernel.syscalls["shutdown"]=function()
|
||||
if kernel.uid==0 or kernel.groups.wheel then
|
||||
kernel.shutdown()
|
||||
end
|
||||
end
|
||||
kernel.syscalls["halt"]=function()
|
||||
if kernel.uid==0 or kernel.groups.wheel then
|
||||
kernel.halt()
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("Running modules")
|
||||
for _,p in ipairs(modules) do
|
||||
@@ -287,11 +317,18 @@ kernel.log("Kernel initialized successfully.")
|
||||
kernel.saveLog()
|
||||
kernel.status="running"
|
||||
screen:disable()
|
||||
kernel.main()
|
||||
local ok,err = xpcall(kernel.main, debug.traceback)
|
||||
if not ok then
|
||||
kernel.panic(err)
|
||||
end
|
||||
if kernel.status=="panic" then
|
||||
kernel.panic(kernel.reason)
|
||||
end
|
||||
if kernel.status=="reboot" then
|
||||
EFI.reboot=true
|
||||
return true
|
||||
elseif kernel.status=="halt" then
|
||||
kernel.log("System halted.")
|
||||
kernel.saveLog()
|
||||
while true do end
|
||||
end
|
||||
@@ -7,5 +7,5 @@ return {
|
||||
initPath = "/sbin/init",
|
||||
maxOpenFiles = 128,
|
||||
maxFilesPerTask = 16,
|
||||
preempt=true
|
||||
preempt = true,
|
||||
}
|
||||
@@ -229,6 +229,23 @@ function toHex(num)
|
||||
return string.format("%X", num)
|
||||
end
|
||||
|
||||
local st={
|
||||
push=function(self,...) table.insert(self.contents,{...}) end,
|
||||
pop=function(self) return table.unpack(table.remove(self.contents)) end,
|
||||
}
|
||||
local q={
|
||||
push=function(self,...) table.insert(self.contents,{...}) end,
|
||||
pop=function(self) return table.unpack(table.remove(self.contents,1)) end,
|
||||
}
|
||||
|
||||
function stack()
|
||||
return setmetatable({contents={}}, {__index=st})
|
||||
end
|
||||
|
||||
function queue()
|
||||
return setmetatable({contents={}}, {__index=q})
|
||||
end
|
||||
|
||||
local function makeSyscallProxy()
|
||||
local backing = {}
|
||||
return setmetatable(backing, {
|
||||
|
||||
@@ -542,6 +542,7 @@ local function allocFD(task)
|
||||
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
|
||||
return fd
|
||||
end
|
||||
|
||||
local function checkSystemLimit()
|
||||
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
|
||||
end
|
||||
@@ -555,27 +556,88 @@ function vfs.newfd(fdobj)
|
||||
return fd
|
||||
end
|
||||
|
||||
function vfs.mount(target, diskOrId)
|
||||
function vfs.mount(target, diskOrId, bind)
|
||||
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
|
||||
if _euid ~= 0 then error("EPERM") end
|
||||
if not target then error("EINVAL") end
|
||||
target = normalizeMountPoint(target)
|
||||
local drive, path = resolvePath(target)
|
||||
if not drive:directoryExists(path) then drive:makeDirectory(path) end
|
||||
if drive:type(target) ~= "directory" then error("EINVAL") end
|
||||
|
||||
if not drive:directoryExists(path) then
|
||||
drive:makeDirectory(path)
|
||||
end
|
||||
if drive:type(path) ~= "directory" then
|
||||
error("EINVAL")
|
||||
end
|
||||
local disk, id
|
||||
if type(diskOrId) == "string" then
|
||||
disk = kernel.disks[diskOrId]
|
||||
if not disk then error("ENODEV") end
|
||||
checkDisk(disk); id = diskOrId
|
||||
elseif type(diskOrId) == "table" then
|
||||
checkDisk(diskOrId); disk = diskOrId
|
||||
id = disk.address; vfs.disks[id] = disk
|
||||
else error("EINVAL") end
|
||||
|
||||
-- bind mount
|
||||
if bind then
|
||||
local src = normalizeMountPoint(diskOrId)
|
||||
local srcDrive = resolvePath(src)
|
||||
if not srcDrive then error("ENOENT") end
|
||||
id = "bind:" .. src
|
||||
disk = {
|
||||
address = id,
|
||||
isBind = true,
|
||||
source = srcDrive,
|
||||
exists = function(_, p)
|
||||
return srcDrive:exists(p)
|
||||
end,
|
||||
type = function(_, p)
|
||||
return srcDrive:type(p)
|
||||
end,
|
||||
list = function(_, p)
|
||||
return srcDrive:list(p)
|
||||
end,
|
||||
open = function(_, ...)
|
||||
return srcDrive:open(...)
|
||||
end,
|
||||
remove = function(_, p)
|
||||
return srcDrive:remove(p)
|
||||
end,
|
||||
makeDirectory = function(_, p)
|
||||
return srcDrive:makeDirectory(p)
|
||||
end,
|
||||
rename = function(_, a, b)
|
||||
return srcDrive:rename(a, b)
|
||||
end,
|
||||
size = function(_, p)
|
||||
return srcDrive:size(p)
|
||||
end,
|
||||
lastModified = function(_, p)
|
||||
return srcDrive:lastModified(p)
|
||||
end,
|
||||
isReadOnly = function()
|
||||
return srcDrive:isReadOnly()
|
||||
end,
|
||||
spaceTotal = function()
|
||||
return srcDrive:spaceTotal()
|
||||
end,
|
||||
spaceUsed = function()
|
||||
return srcDrive:spaceUsed()
|
||||
end
|
||||
}
|
||||
vfs.disks[id] = disk
|
||||
else
|
||||
if type(diskOrId) == "string" then
|
||||
disk = vfs.disks[diskOrId]
|
||||
if not disk then error("ENODEV") end
|
||||
checkDisk(disk)
|
||||
id = diskOrId
|
||||
elseif type(diskOrId) == "table" then
|
||||
checkDisk(diskOrId)
|
||||
disk = diskOrId
|
||||
id = disk.address
|
||||
vfs.disks[id] = disk
|
||||
else
|
||||
error("EINVAL")
|
||||
end
|
||||
end
|
||||
if vfs.mounts[id] then error("EBUSY") end
|
||||
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
|
||||
for _, mp in pairs(vfs.mounts) do
|
||||
if mp == target then
|
||||
error("EBUSY")
|
||||
end
|
||||
end
|
||||
vfs.mounts[id] = target
|
||||
return true
|
||||
end
|
||||
@@ -588,7 +650,11 @@ function vfs.umount(target)
|
||||
for id, mp in pairs(vfs.mounts) do
|
||||
if mp == target then
|
||||
if id == "$" then error("EBUSY") end
|
||||
vfs.mounts[id] = nil; return true
|
||||
vfs.mounts[id] = nil
|
||||
if vfs.disks[id] and vfs.disks[id].isBind then
|
||||
vfs.disks[id] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
error("EINVAL")
|
||||
|
||||
63
Src/Hyperion-kernel/data/lib/modules/hyperion/11_ld.kmod
Normal file
63
Src/Hyperion-kernel/data/lib/modules/hyperion/11_ld.kmod
Normal file
@@ -0,0 +1,63 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
"?", "?.lua", "/lib/?", "/lib/?.lua", "/usr/lib/?", "/usr/lib/?.lua",
|
||||
"/usr/local/lib/?", "/usr/local/lib/?.lua"
|
||||
}
|
||||
kernel.reqcache = cache
|
||||
|
||||
local function require(module, ...)
|
||||
if cache[module] then return cache[module].ret end
|
||||
kernel.currentTask.status = "D"
|
||||
local args = {...}
|
||||
kernel.currentTask.ksh = coroutine.create(function()
|
||||
local coro = function()
|
||||
for _, path in ipairs(kernel.searchpaths) do
|
||||
local filepath = path:gsub("?", module)
|
||||
if kernel.vfs.exists(filepath) then
|
||||
if kernel.vfs.type(filepath) == "directory" then
|
||||
filepath = filepath .. "/init.lua"
|
||||
if kernel.vfs.type(filepath) == "directory" then
|
||||
kernel.asyncReturn(false, "Module not found: "..module)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local fd = kernel.vfs.open(filepath, "r")
|
||||
local chunks = {}
|
||||
while true do
|
||||
local chunk = kernel.vfs.read(fd, 4096)
|
||||
if not chunk or chunk == "" then break end
|
||||
chunks[#chunks + 1] = chunk
|
||||
coroutine.yield()
|
||||
end
|
||||
kernel.vfs.close(fd)
|
||||
local data = table.concat(chunks)
|
||||
local func, err = load(data, "@"..module, "t", kernel._U)
|
||||
if not func then
|
||||
kernel.asyncReturn(false, "Error loading module "..module..": "..tostring(err))
|
||||
return
|
||||
end
|
||||
local ok, ret = xpcall(func, debug.traceback, table.unpack(args))
|
||||
if not ok then
|
||||
kernel.asyncReturn(false, "Error running module "..module..": "..tostring(ret))
|
||||
return
|
||||
end
|
||||
cache[module] = {ret = ret, expires = kernel.EFI:getEpochMs() + 120000}
|
||||
kernel.asyncReturn(true, ret)
|
||||
return
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
kernel.asyncReturn(false, "Module not found: "..module)
|
||||
end
|
||||
local status, err = xpcall(coro, debug.traceback)
|
||||
if not status then
|
||||
kernel.asyncReturn(false, "Error in require coroutine for module "..module..": "..tostring(err))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
kernel.syscalls["require"] = require
|
||||
_G.require = function(...) return syscall.require(...) end
|
||||
@@ -1,40 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
|
||||
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
|
||||
}
|
||||
|
||||
function require(module, ...)
|
||||
if cache[module] then return cache[module] end
|
||||
local modpath = module:gsub("%.", "/")
|
||||
local failed = {}
|
||||
for _, path in ipairs(kernel.searchpaths) do
|
||||
local full_path = string.replace(path, "?", modpath)
|
||||
if full_path:sub(1, 1) ~= "/" then
|
||||
full_path = kernel.currentTask.cwd .. full_path
|
||||
end
|
||||
|
||||
if kernel.vfs.exists(full_path) then
|
||||
if kernel.vfs.type(full_path) == "directory" then
|
||||
full_path = full_path .. "/init"
|
||||
end
|
||||
|
||||
if kernel.vfs.exists(full_path) then
|
||||
local handle = kernel.vfs.open(full_path, "r")
|
||||
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
kernel.vfs.close(handle)
|
||||
|
||||
return
|
||||
assert(load(file_content, full_path, "t", kernel._U))(...)
|
||||
else
|
||||
table.insert(failed, full_path)
|
||||
end
|
||||
else
|
||||
table.insert(failed, full_path)
|
||||
end
|
||||
end
|
||||
|
||||
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
|
||||
end
|
||||
@@ -140,6 +140,83 @@ function data.zero(op, mode)
|
||||
end
|
||||
end
|
||||
|
||||
local function buildMeta(entries, opts)
|
||||
opts = opts or {}
|
||||
local uid = opts.uid or 0
|
||||
local gid = opts.gid or 0
|
||||
local perms = opts.perms or 0x3F -- default read/write for owner/group/world
|
||||
|
||||
local chunks = {}
|
||||
table.insert(chunks, string.char(0x02)) -- version header
|
||||
|
||||
for path, target in pairs(entries) do
|
||||
local name = path
|
||||
local nameLen = #name
|
||||
if nameLen > 255 then
|
||||
error("Filename too long (>255 bytes): "..name)
|
||||
end
|
||||
|
||||
-- Determine entry type: 0x01 = symlink if target ~= nil and target ~= ""
|
||||
local entryType = 0x00
|
||||
local cmeta = ""
|
||||
if target and target ~= "" then
|
||||
entryType = 0x01
|
||||
cmeta = target
|
||||
end
|
||||
local cmetaLen = #cmeta
|
||||
if cmetaLen > 255 then
|
||||
error("cmeta too long (>255 bytes) for "..name)
|
||||
end
|
||||
|
||||
-- Build entry as bytes
|
||||
table.insert(chunks, string.char(nameLen)) -- name length
|
||||
table.insert(chunks, name) -- name
|
||||
table.insert(chunks, string.char(entryType)) -- entry type
|
||||
table.insert(chunks, string.char(uid % 256, math.floor(uid/256) % 256)) -- uid
|
||||
table.insert(chunks, string.char(gid % 256, math.floor(gid/256) % 256)) -- gid
|
||||
table.insert(chunks, string.char(perms % 256, math.floor(perms/256) % 256)) -- perms
|
||||
table.insert(chunks, string.char(cmetaLen)) -- cmeta length
|
||||
if cmetaLen > 0 then
|
||||
table.insert(chunks, cmeta)
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(chunks)
|
||||
end
|
||||
|
||||
local function simpleFile(r,w)
|
||||
return function(op, mode)
|
||||
if op=="type" then
|
||||
return "file"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=r
|
||||
}
|
||||
elseif mode=="w" then
|
||||
return {
|
||||
write=w
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function strFile(str)
|
||||
local dat=tostring(str)
|
||||
local pos=1
|
||||
return simpleFile(function(amount)
|
||||
pos=pos+amount
|
||||
return dat:sub(pos-amount, pos)
|
||||
end,function() error("EACCES") end)
|
||||
end
|
||||
|
||||
data[".meta"]=strFile(buildMeta({
|
||||
stdin="/proc/self/fd/0",
|
||||
stdout="/proc/self/fd/1",
|
||||
log="/var/log/syslog.log"
|
||||
}))
|
||||
|
||||
if kernel.EFI:getEEPROM() then
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
@@ -216,7 +293,8 @@ if kernel.EFI:getNvram() then
|
||||
end
|
||||
end
|
||||
|
||||
data["disk"]={}
|
||||
data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}}
|
||||
data["input"]={}
|
||||
kernel.devfs={}
|
||||
kernel.devfs.data=data
|
||||
kernel.devfs.proxy=proxy
|
||||
|
||||
@@ -114,7 +114,8 @@ local function newtaskproxy(task)
|
||||
},
|
||||
children={
|
||||
[".meta"]=strFile(buildMeta(children))
|
||||
}
|
||||
},
|
||||
pid=strFile(task.pid)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -1,134 +1,141 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
kernel.vfs.remove("/tmp")
|
||||
kernel.vfs.mkdir("/tmp")
|
||||
if not kernel.config.tmpToDisk then
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
|
||||
proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
if not step[steps[i]] then
|
||||
step[steps[i]] = {}
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
if not step[steps[i]] then
|
||||
step[steps[i]] = {}
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
end
|
||||
|
||||
proxy.remove = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
step[steps[#steps]] = nil
|
||||
end
|
||||
|
||||
proxy.setLabel = function(_, label) end
|
||||
proxy.getLabel = function() return "tmpfs" end
|
||||
|
||||
proxy.attributes = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
return {
|
||||
size = type(step) == "string" and #step or 0,
|
||||
modified = 0,
|
||||
created = 0,
|
||||
}
|
||||
end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
if not step[steps[i]] then
|
||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
proxy.remove = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
step = step[steps[i]]
|
||||
step[steps[#steps]] = nil
|
||||
end
|
||||
local filename = steps[#steps]
|
||||
|
||||
if mode == "r" then
|
||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
proxy.setLabel = function(_, label) end
|
||||
proxy.getLabel = function() return "tmpfs" end
|
||||
|
||||
proxy.attributes = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
return {
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
size = type(step) == "string" and #step or 0,
|
||||
modified = 0,
|
||||
created = 0,
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
|
||||
function proxy:type(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then return "directory" end
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then return false end
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
if not step[steps[i]] then
|
||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
local filename = steps[#steps]
|
||||
|
||||
if mode == "r" then
|
||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
return {
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
if type(step) == "table" then return "directory" end
|
||||
if type(step) == "string" then return "file" end
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
function proxy:type(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then return "directory" end
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then return false end
|
||||
end
|
||||
if type(step) == "table" then return "directory" end
|
||||
if type(step) == "string" then return "file" end
|
||||
end
|
||||
if type(step) ~= "table" then error("ENOTDIR") end
|
||||
local keys = {}
|
||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
||||
return keys
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
end
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
if type(step) ~= "table" then error("ENOTDIR") end
|
||||
local keys = {}
|
||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
||||
return keys
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
else
|
||||
kernel.log("tmpToDisk enabled, skipping tmpfs module")
|
||||
kernel.log("tmpfs0000 will passthrough to /tmp on the disk")
|
||||
end
|
||||
@@ -25,10 +25,11 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
||||
else
|
||||
local id = line:sub(3, semicolon_pos - 1)
|
||||
local path = trim(line:sub(semicolon_pos + 1))
|
||||
kernel.log("Mounted "..id.." to "..path)
|
||||
kernel.log("Mounting '"..id.."' to '"..path.."'")
|
||||
if id ~= "$" then
|
||||
kernel.vfs.mount(path, id)
|
||||
end
|
||||
kernel.log("Mounted "..id.." to "..path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,67 +1,194 @@
|
||||
--:Minify:--
|
||||
-- Supports:
|
||||
-- AF_UNIX - local IPC via /var/run/*.sock paths
|
||||
-- AF_INET - network sockets with three backends:
|
||||
-- Implemented by drivers but expect http:// and https://
|
||||
--
|
||||
-- Socket lifecycle:
|
||||
-- fd = syscall.socket(domain, socktype) -- "unix"/"inet", "stream"/"dgram"
|
||||
-- syscall.bind(fd, address) -- server: claim address
|
||||
-- syscall.listen(fd, backlog) -- server: mark as listening
|
||||
-- cfd = syscall.accept(fd) -- server: get connected client fd (blocking poll)
|
||||
-- syscall.connect(fd, address) -- client: connect to server
|
||||
-- syscall.send(fd, data) -- send bytes
|
||||
-- syscall.recv(fd, len) -- receive bytes (blocking poll, returns "" on nothing)
|
||||
-- syscall.sockshutdown(fd) -- half-close send side
|
||||
-- -- normal vfs.close(fd) closes the socket
|
||||
local kernel = ...
|
||||
|
||||
local kernel=...
|
||||
local socket={}
|
||||
socket.handlers={}
|
||||
kernel.socket=socket
|
||||
local socket = {}
|
||||
|
||||
function socket.registerProtocal(protocal, handler)
|
||||
socket.handlers[protocal] = handler
|
||||
socket.handlers = {}
|
||||
|
||||
kernel.socket = socket
|
||||
|
||||
local P = kernel.vfs.P
|
||||
local sys = kernel.syscalls
|
||||
|
||||
function socket.registerProtocal(proto, handler)
|
||||
socket.handlers[proto] = handler
|
||||
end
|
||||
|
||||
function socket.socket()
|
||||
local P=kernel.vfs.P
|
||||
local data=kernel.newFifo()
|
||||
local isClosed=false
|
||||
return kernel.vfs.newfd({
|
||||
handle={
|
||||
read=function() if isClosed then error("ECCON") end return data.pop() end,
|
||||
write=function(data) if isClosed then error("ECCON") end return data.push(data) end,
|
||||
close=function() isClosed = true end
|
||||
local function getHandler(address)
|
||||
for proto, handler in pairs(socket.handlers) do
|
||||
if string.hasPrefix(address, proto) then
|
||||
return handler
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function socket.socket(domain, socktype)
|
||||
local fd = kernel.vfs.newfd({
|
||||
handle = {},
|
||||
|
||||
type = "socket",
|
||||
|
||||
refcount = 1,
|
||||
|
||||
socket = {
|
||||
connected = false,
|
||||
protocol = nil,
|
||||
address = nil
|
||||
},
|
||||
type="socket",
|
||||
refcount=1,
|
||||
socket={},
|
||||
meta={
|
||||
owner=kernel.currentTask.uid,
|
||||
group=kernel.currentTask.uid,
|
||||
etype=2,
|
||||
perms=P.OWNER_R+P.OWNER_W+P.GROUP_R+P.GROUP_W
|
||||
|
||||
meta = {
|
||||
owner = kernel.currentTask.uid,
|
||||
group = kernel.currentTask.uid,
|
||||
etype = 2,
|
||||
|
||||
perms =
|
||||
P.OWNER_R +
|
||||
P.OWNER_W +
|
||||
P.GROUP_R +
|
||||
P.GROUP_W
|
||||
},
|
||||
isvirt=true
|
||||
|
||||
isvirt = true
|
||||
})
|
||||
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
fdo.handle.read = function()
|
||||
return ""
|
||||
end
|
||||
|
||||
fdo.handle.write = function()
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
fdo.handle.close = function()
|
||||
fdo.socket.connected = false
|
||||
return true
|
||||
end
|
||||
|
||||
return fd
|
||||
end
|
||||
|
||||
function socket.connect(fd, address)
|
||||
local handler
|
||||
for k, v in pairs(socket.handlers) do
|
||||
if string.hasPrefix(address, k) then
|
||||
handler=v
|
||||
end
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
handler.connect(kernel.currentTask.fd[fd], address)
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
local handler =
|
||||
getHandler(address)
|
||||
|
||||
if not handler then
|
||||
return nil, "EPROTONOSUPPORT"
|
||||
end
|
||||
|
||||
fdo.socket.protocol = handler
|
||||
fdo.socket.address = address
|
||||
|
||||
local ok, err =
|
||||
handler.connect(fd, address)
|
||||
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
fdo.socket.connected = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function socket.listen(fd, backlog)
|
||||
error("Not Implemented")
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if not fdo.handle.listen then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.listen(backlog)
|
||||
end
|
||||
|
||||
local sys=kernel.syscalls
|
||||
sys["socket"]=socket.socket
|
||||
function socket.send(fd, data)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if not fdo.socket.connected then
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
if not fdo.handle.write then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.write(data)
|
||||
end
|
||||
|
||||
function socket.recv(fd, amount)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if not fdo.socket.connected then
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
if not fdo.handle.read then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.read(amount)
|
||||
end
|
||||
|
||||
function socket.sockshutdown(fd)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if fdo.handle.close then
|
||||
return fdo.handle.close()
|
||||
end
|
||||
|
||||
fdo.socket.connected = false
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
sys.socket = socket.socket
|
||||
sys.connect = socket.connect
|
||||
sys.listen = socket.listen
|
||||
sys.send = socket.send
|
||||
sys.recv = socket.recv
|
||||
sys.sockshutdown = socket.sockshutdown
|
||||
|
||||
kernel.log("Loaded socket module")
|
||||
129
Src/Hyperion-kernel/data/lib/modules/hyperion/30_vterm.kmod
Normal file
129
Src/Hyperion-kernel/data/lib/modules/hyperion/30_vterm.kmod
Normal file
@@ -0,0 +1,129 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local vterms = {}
|
||||
|
||||
local function createVt(id, width, height)
|
||||
local vt = {
|
||||
id = id,
|
||||
width = width,
|
||||
height = height,
|
||||
buffer = {},
|
||||
cursorX = 1,
|
||||
cursorY = 1,
|
||||
fgColor = 0xFFFFFF,
|
||||
bgColor = 0x000000,
|
||||
obj = {}
|
||||
}
|
||||
|
||||
for y = 1, height do
|
||||
vt.buffer[y] = {}
|
||||
for x = 1, width do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
|
||||
local function scroll(lines)
|
||||
for _ = 1, lines do
|
||||
table.remove(vt.buffer, 1)
|
||||
vt.buffer[vt.height] = {}
|
||||
for x = 1, vt.width do
|
||||
vt.buffer[vt.height][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function vt.obj:write(content)
|
||||
local x, y = vt.cursorX, vt.cursorY
|
||||
if x>vt.width then return end
|
||||
if y>vt.height then return end
|
||||
for i = 1, #content do
|
||||
local c = content:sub(i, i)
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
for _ = 1, spaces do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
x = x + 1
|
||||
if x > vt.width then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
else
|
||||
if x <= vt.width and y <= vt.height then
|
||||
vt.buffer[y][x] = {char = c, fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > vt.width then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y > vt.height then
|
||||
scroll(1)
|
||||
y = vt.height
|
||||
end
|
||||
end
|
||||
|
||||
vt.cursorX, vt.cursorY = x, y
|
||||
end
|
||||
|
||||
function vt.obj:spos(x, y)
|
||||
vt.cursorX = tonumber(x)
|
||||
vt.cursorY = tonumber(y)
|
||||
end
|
||||
|
||||
function vt.obj:gpos()
|
||||
return tostring(vt.cursorX)..";"..tostring(vt.cursorY)
|
||||
end
|
||||
|
||||
function vt.obj:sfgc(color)
|
||||
vt.fgColor = tonumber(color)
|
||||
end
|
||||
|
||||
function vt.obj:sbgc(color)
|
||||
vt.bgColor = tonumber(color)
|
||||
end
|
||||
|
||||
function vt.obj:gfgc()
|
||||
return vt.fgColor
|
||||
end
|
||||
|
||||
function vt.obj:gbgc()
|
||||
return vt.bgColor
|
||||
end
|
||||
|
||||
function vt.obj:gplt()
|
||||
return 24
|
||||
end
|
||||
|
||||
function vt.obj:clear()
|
||||
for y = 1, vt.height do
|
||||
for x = 1, vt.width do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
vt.cursorX, vt.cursorY = 1, 1
|
||||
end
|
||||
|
||||
function vt.obj:isvirt()
|
||||
return true
|
||||
end
|
||||
|
||||
function vt.obj:gctrl()
|
||||
return serializeBool(vt.ctrl)..";"..serializeBool(vt.alt)
|
||||
end
|
||||
|
||||
return vt
|
||||
end
|
||||
@@ -21,7 +21,7 @@ local function loadExecutable(path)
|
||||
local env = kernel.freshUserEnv()
|
||||
|
||||
local func, err = load(data, "@" .. path, "t", env)
|
||||
if not func then error("ENOEXEC: " .. tostring(err)) end
|
||||
if not func then kernel.log("Failed to load executable: " .. tostring(err).. " : ".. tostring(path)); error("ENOEXEC: " .. tostring(err)) end
|
||||
|
||||
local meta = kernel.vfs.lstat(path)
|
||||
local suid_set = bit_is_set(meta.perms, 6)
|
||||
@@ -211,6 +211,8 @@ function sys.kill(pid, force)
|
||||
return false, "Task is already dead"
|
||||
elseif task.status == "D" and not force then
|
||||
return false, "Cannot kill task waiting for IO"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
end
|
||||
local caller = kernel.currentTask
|
||||
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
|
||||
@@ -227,6 +229,8 @@ function sys.stop(pid)
|
||||
return false, "Task does not exist"
|
||||
elseif task.status ~= "R" and task.status ~= "S" then
|
||||
return false, "Cannot stop non-running task"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
else
|
||||
if task.status == "S" then
|
||||
task.status = "ST"
|
||||
@@ -243,6 +247,8 @@ function sys.continue(pid)
|
||||
return false, "Task does not exist"
|
||||
elseif task.status ~= "T" and task.status ~= "ST" then
|
||||
return false, "Task is not stopped"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
else
|
||||
if task.status == "ST" then
|
||||
task.status = "S"
|
||||
@@ -299,20 +305,21 @@ function sys.getuid() return kernel.currentTask.uid end
|
||||
local function reapDeadTasks()
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.status == "Z" and not task.reapTime then
|
||||
task.coro = nil
|
||||
task.ivs = nil
|
||||
task.vs = nil
|
||||
task.args = nil
|
||||
task.envars = nil
|
||||
task.cwd = nil
|
||||
task.numRuns = nil
|
||||
task.totalTime = nil
|
||||
task.lastTime = nil
|
||||
task.timeSlice = nil
|
||||
if task.pid == 1 then kernel.panic("Attempted to gc init!") end
|
||||
task.coro = nil
|
||||
task.ivs = nil
|
||||
task.vs = nil
|
||||
task.args = nil
|
||||
task.envars = nil
|
||||
task.cwd = nil
|
||||
task.numRuns = nil
|
||||
task.totalTime = nil
|
||||
task.lastTime = nil
|
||||
task.timeSlice = nil
|
||||
task.syscallReturn = nil
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.EFI:getEpochMs() + 30000
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.EFI:getEpochMs() + 30000
|
||||
|
||||
elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
|
||||
and task.status == "Z" then
|
||||
@@ -344,7 +351,23 @@ local k_max = 0.5
|
||||
local B = 0.01
|
||||
|
||||
function kernel.main()
|
||||
kernel.log("Starting main loop...")
|
||||
kernel.saveLog()
|
||||
local stopLog=5
|
||||
local logTasks=100
|
||||
|
||||
while not kernel.exitMain do
|
||||
if kernel.config.logTasks then
|
||||
if logTasks<0 then
|
||||
kernel.log("Active Tasks:")
|
||||
for i,v in pairs(tasks) do
|
||||
kernel.log(v.name.." : "..v.status.." : "..tostring(v.pid).." : "..tostring(v.exit))
|
||||
end
|
||||
kernel.log("[END BLOCK]")
|
||||
logTasks=100
|
||||
end
|
||||
logTasks=logTasks-1
|
||||
end
|
||||
local N = 0
|
||||
local Tmin_hit = 0
|
||||
local Tmax_hit = 0
|
||||
@@ -352,6 +375,7 @@ function kernel.main()
|
||||
local taskTimes = {}
|
||||
|
||||
for pid, task in pairs(tasks) do
|
||||
if kernel.exitMain then break end
|
||||
kernel.currentTask = task
|
||||
kernel.uid = task.euid or task.uid
|
||||
kernel.process = task.name
|
||||
@@ -366,9 +390,29 @@ function kernel.main()
|
||||
task.sleep = 0
|
||||
end
|
||||
|
||||
if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||
task.status = "D"
|
||||
task.sleep = 0
|
||||
end
|
||||
|
||||
if task.status == "D" then
|
||||
if task.ksh then
|
||||
coroutine.resume(task.ksh)
|
||||
if coroutine.status(task.ksh) == "dead" then
|
||||
task.ksh = nil
|
||||
if task.status == "D" then
|
||||
task.status = "R"
|
||||
end
|
||||
|
||||
if kernel.config.debugSyscalls then
|
||||
kernel.log("Task " .. task.pid .. " IO wait completed", "DBUG", 0x00FFFF)
|
||||
local sysret = task.syscallReturn
|
||||
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", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -405,7 +449,9 @@ function kernel.main()
|
||||
if task.status == "R" then
|
||||
local startTime = kernel.EFI:getEpochMs()
|
||||
local ret
|
||||
|
||||
if stopLog > 0 then
|
||||
kernel.log("Running task " .. tostring(task.pid) .. " (" .. task.name .. ") with time slice " .. string.format("%.3f", task.timeSlice) .. "s", "DBUG", 0x00FFFF)
|
||||
end
|
||||
if kernel.config.preempt then
|
||||
if not task.debugger then
|
||||
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
|
||||
@@ -491,6 +537,11 @@ function kernel.main()
|
||||
end
|
||||
|
||||
reapDeadTasks()
|
||||
if stopLog > 0 then
|
||||
kernel.log("Executed " .. N .. " tasks, avg time: " .. string.format("%.2f", T_prev_avg) .. "ms, var: " .. string.format("%.2f", T_prev_var) .. ", B: " .. string.format("%.5f", B))
|
||||
stopLog = stopLog - 1
|
||||
kernel.saveLog()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
11
Src/Hyperion-kernel/data/lib/modules/hyperion/90_kgc.kmod
Normal file
11
Src/Hyperion-kernel/data/lib/modules/hyperion/90_kgc.kmod
Normal file
@@ -0,0 +1,11 @@
|
||||
local kernel = ...
|
||||
kernel.processes.kgc = function()
|
||||
while true do
|
||||
for i,v in pairs(kernel.reqcache) do
|
||||
if v.expires and kernel.EFI:getEpochMs() > v.expires then
|
||||
kernel.reqcache[i] = nil
|
||||
end
|
||||
end
|
||||
kernel.sleep(5000)
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/1", "r") --stdin (fd 0)
|
||||
syscall.open("/dev/input/keyboard1", "r") --stdin (fd 0)
|
||||
syscall.open("/dev/tty/1", "w") --stdout (fd 1)
|
||||
syscall.open("/dev/null", "w") --stderr (fd 2)
|
||||
|
||||
3
Src/hysh/control/symlinks.json
Normal file
3
Src/hysh/control/symlinks.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"/bin/sh": "/bin/hysh"
|
||||
}
|
||||
@@ -107,6 +107,7 @@ local COMMANDS = {
|
||||
{ name="whoami", usage="whoami", desc="Print the current username.", flags={} },
|
||||
{ name="id", usage="id [username]", desc="Print user identity (uid, gid).", flags={} },
|
||||
{ name="ps", usage="ps", desc="List running tasks with pid, user, name, and status.", flags={} },
|
||||
{ name="hyperctl", usage="hyperctl <command> [service]", desc="Control system services (start, stop, restart, status, list, enable, disable).", flags={} },
|
||||
{ name="hostname", usage="hostname [NAME]", desc="Print or set the system hostname.", flags={} },
|
||||
{ name="uname", usage="uname [-asnrm]", desc="Print system information (OS name, hostname, release, machine).", flags={
|
||||
{"-a","Print all fields"},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/1","r") --stdin (Device 0)
|
||||
local success, errorMsg = xpcall(function()
|
||||
|
||||
syscall.open("/dev/input/keyboard1","r") --stdin (Device 0)
|
||||
syscall.open("/dev/tty/1","w") --stdout (Device 1)
|
||||
syscall.open("/dev/null","w") --stderr (device 2)
|
||||
|
||||
local success, errorMsg = xpcall(function()
|
||||
|
||||
local fs = require("fs")
|
||||
|
||||
syscall.devctl(1,"clear")
|
||||
@@ -833,6 +833,14 @@ builtinCmds.ops = function()
|
||||
end
|
||||
end
|
||||
|
||||
builtinCmds.reboot = function ()
|
||||
syscall.reboot()
|
||||
end
|
||||
|
||||
builtinCmds.shutdown = function ()
|
||||
syscall.shutdown()
|
||||
end
|
||||
|
||||
local function listDir(dir, prefix)
|
||||
local ok, entries = pcall(syscall.listdir, dir)
|
||||
if not ok or not entries then return {} end
|
||||
@@ -1203,9 +1211,9 @@ local function runCommand(command)
|
||||
|
||||
local proc = syscall.spawn(function()
|
||||
-- Open standard fds so programs that don't do it themselves work correctly.
|
||||
syscall.open("/dev/tty/1", "r") -- fd 0 stdin
|
||||
syscall.open("/dev/input/keyboard1", "r") -- fd 0 stdin
|
||||
syscall.open("/dev/tty/1", "w") -- fd 1 stdout
|
||||
syscall.open("/dev/null", "w") -- fd 2 stderr
|
||||
syscall.open("/dev/null", "w") -- fd 2 stderr
|
||||
-- exec replaces this coroutine's code with a fresh isolated environment
|
||||
-- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
|
||||
-- so the child cannot share any upvalue or syscall table state with hysh.
|
||||
|
||||
113
Src/hysh/data/bin/tree
Normal file
113
Src/hysh/data/bin/tree
Normal file
@@ -0,0 +1,113 @@
|
||||
--:Minify:--
|
||||
local cloptions = {
|
||||
a = false,
|
||||
h = false,
|
||||
l = false,
|
||||
help = false,
|
||||
}
|
||||
local inpArgs = { ... }
|
||||
local args = {}
|
||||
local name = syscall.getTask(syscall.getpid()).name
|
||||
|
||||
for _, v in pairs(inpArgs) do
|
||||
if v:sub(1, 2) == "--" then
|
||||
local opt = v:sub(3)
|
||||
if cloptions[opt] == nil then
|
||||
print(name .. ": unrecognized option '" .. v .. "'.")
|
||||
print("try '" .. name .. " --help' for more information.")
|
||||
return
|
||||
end
|
||||
cloptions[opt] = true
|
||||
elseif v:sub(1, 1) == "-" then
|
||||
for i = 2, #v do
|
||||
local opt = v:sub(i, i)
|
||||
if cloptions[opt] == nil then
|
||||
print(name .. ": invalid option '-" .. opt .. "'.")
|
||||
print("try '" .. name .. " --help' for more information.")
|
||||
return
|
||||
end
|
||||
cloptions[opt] = true
|
||||
end
|
||||
else
|
||||
table.insert(args, v)
|
||||
end
|
||||
end
|
||||
|
||||
if cloptions.help then
|
||||
print("Usage: " .. name .. " [OPTION]... [DIR]")
|
||||
print("List all entries in the specified DIRectory, or cwd if not specified.")
|
||||
print("Display directory tree structure.")
|
||||
print("")
|
||||
print("Options:")
|
||||
print(" -a do not ignore entries starting with .")
|
||||
print(" -h with -l, print sizes in human readable format (e.g., 1K 234M 2G)")
|
||||
print(" -l use a long listing format (show file sizes)")
|
||||
print(" --help display this help and exit")
|
||||
return
|
||||
end
|
||||
|
||||
local fs = require("fs")
|
||||
local dir = args[1] or ""
|
||||
if dir:sub(1, 1) ~= "/" then
|
||||
dir = syscall.getcwd() .. "/" .. dir
|
||||
end
|
||||
if dir:sub(-1) ~= "/" then dir = dir .. "/" end
|
||||
|
||||
if not fs.isDir(dir) then
|
||||
print(name .. ": cannot access '" .. (args[1] or dir) .. "': no such directory")
|
||||
return
|
||||
end
|
||||
|
||||
local function format_size(size)
|
||||
if not cloptions.h then return tostring(size) end
|
||||
if size < 1024 then return tostring(size) .. "B"
|
||||
elseif size < 1024*1024 then return string.format("%.1fK", size/1024)
|
||||
elseif size < 1024*1024*1024 then return string.format("%.1fM", size/1024/1024)
|
||||
else return string.format("%.1fG", size/1024/1024/1024) end
|
||||
end
|
||||
|
||||
local function pdir(current_dir, level, prefix)
|
||||
prefix = prefix or ""
|
||||
local list = fs.list(current_dir)
|
||||
if not cloptions.a then
|
||||
for i = #list, 1, -1 do
|
||||
if list[i]:sub(1, 1) == "." then table.remove(list, i) end
|
||||
end
|
||||
end
|
||||
table.sort(list)
|
||||
if current_dir:sub(-1) ~= "/" then current_dir = current_dir .. "/" end
|
||||
|
||||
for i, entry in ipairs(list) do
|
||||
local full_path = current_dir .. entry
|
||||
local is_last = (i == #list)
|
||||
local branch = is_last and "`--" or "|--"
|
||||
local indent = prefix .. branch
|
||||
local suffix = ""
|
||||
local info = ""
|
||||
|
||||
if fs.isDir(full_path) then
|
||||
suffix = "/"
|
||||
end
|
||||
|
||||
if cloptions.l then
|
||||
local stat = fs.stat and fs.stat(full_path)
|
||||
if stat then
|
||||
local size = stat.size or 0
|
||||
info = " " .. format_size(size)
|
||||
end
|
||||
end
|
||||
|
||||
print(indent .. entry .. suffix .. info)
|
||||
|
||||
if fs.isDir(full_path) then
|
||||
local new_prefix = prefix .. (is_last and " " or "| ")
|
||||
pcall(pdir, full_path, level + 1, new_prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Print root directory
|
||||
local root_name = dir:sub(1, -2) -- remove trailing /
|
||||
if root_name == "" then root_name = "/" end
|
||||
print(root_name)
|
||||
pdir(dir, 0, "")
|
||||
0
Src/installer/data/bin/hyperion-build
Normal file
0
Src/installer/data/bin/hyperion-build
Normal file
0
Src/installer/data/bin/install
Normal file
0
Src/installer/data/bin/install
Normal file
29
Src/minify/data/bin/minify
Normal file
29
Src/minify/data/bin/minify
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
local function usageError()
|
||||
print(
|
||||
"\nusage: minify <file> or unminify <file>\n" ..
|
||||
" The modified code will be printed to the stdout, pipe it to a file,\n" ..
|
||||
" or something else as desired EG:\n\n" ..
|
||||
" minify minify input.lua > output.lua\n\n" ..
|
||||
" * minify will minify the code in the file.\n" ..
|
||||
" * unminify will beautify the code and replace the variable names with easily\n" ..
|
||||
" find-replacable ones to aide in reverse engineering minified code.\n")
|
||||
syscall.exit(0)
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
|
||||
if #args ~= 2 then
|
||||
usageError()
|
||||
end
|
||||
|
||||
local minify = require("minify")
|
||||
local fs = require("fs")
|
||||
|
||||
if args[1] == 'minify' then
|
||||
print(minify.minify(fs.readAllText(args[2])))
|
||||
elseif args[1] == 'unminify' then
|
||||
print(minify.beautify(fs.readAllText(args[2])))
|
||||
else
|
||||
usageError()
|
||||
end
|
||||
3106
Src/minify/data/lib/minify.lua
Normal file
3106
Src/minify/data/lib/minify.lua
Normal file
File diff suppressed because it is too large
Load Diff
136
Src/pid/data/lib/pid.lua
Normal file
136
Src/pid/data/lib/pid.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
--:Minify:--
|
||||
|
||||
local pid = {}
|
||||
pid.__index = pid
|
||||
|
||||
local function clamp(value, minimum, maximum)
|
||||
if minimum and value < minimum then
|
||||
return minimum
|
||||
end
|
||||
if maximum and value > maximum then
|
||||
return maximum
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function assertNumber(value, name)
|
||||
if type(value) ~= "number" then
|
||||
error("pid: " .. name .. " must be a number")
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function pid.new(kp, ki, kd, options)
|
||||
assertNumber(kp, "kp")
|
||||
assertNumber(ki, "ki")
|
||||
assertNumber(kd, "kd")
|
||||
|
||||
options = options or {}
|
||||
|
||||
local controller = setmetatable({
|
||||
kp = kp,
|
||||
ki = ki,
|
||||
kd = kd,
|
||||
setpoint = options.setpoint or 0,
|
||||
outputMin = options.outputMin,
|
||||
outputMax = options.outputMax,
|
||||
integralMin = options.integralMin,
|
||||
integralMax = options.integralMax,
|
||||
lastError = 0,
|
||||
integral = 0,
|
||||
lastMeasurement = nil,
|
||||
lastOutput = 0,
|
||||
lastTime = nil,
|
||||
}, pid)
|
||||
|
||||
return controller
|
||||
end
|
||||
|
||||
function pid:reset()
|
||||
self.lastError = 0
|
||||
self.integral = 0
|
||||
self.lastMeasurement = nil
|
||||
self.lastOutput = 0
|
||||
self.lastTime = nil
|
||||
end
|
||||
|
||||
function pid:setSetpoint(setpoint)
|
||||
assertNumber(setpoint, "setpoint")
|
||||
self.setpoint = setpoint
|
||||
end
|
||||
|
||||
function pid:setCoefficients(kp, ki, kd)
|
||||
assertNumber(kp, "kp")
|
||||
assertNumber(ki, "ki")
|
||||
assertNumber(kd, "kd")
|
||||
self.kp = kp
|
||||
self.ki = ki
|
||||
self.kd = kd
|
||||
end
|
||||
|
||||
function pid:setOutputLimits(minimum, maximum)
|
||||
if minimum ~= nil then assertNumber(minimum, "outputMin") end
|
||||
if maximum ~= nil then assertNumber(maximum, "outputMax") end
|
||||
if minimum and maximum and minimum > maximum then
|
||||
error("pid: outputMin must be <= outputMax")
|
||||
end
|
||||
self.outputMin = minimum
|
||||
self.outputMax = maximum
|
||||
end
|
||||
|
||||
function pid:setIntegralLimits(minimum, maximum)
|
||||
if minimum ~= nil then assertNumber(minimum, "integralMin") end
|
||||
if maximum ~= nil then assertNumber(maximum, "integralMax") end
|
||||
if minimum and maximum and minimum > maximum then
|
||||
error("pid: integralMin must be <= integralMax")
|
||||
end
|
||||
self.integralMin = minimum
|
||||
self.integralMax = maximum
|
||||
end
|
||||
|
||||
function pid:update(measurement, dt)
|
||||
assertNumber(measurement, "measurement")
|
||||
assertNumber(dt, "dt")
|
||||
if dt <= 0 then
|
||||
error("pid: dt must be greater than zero")
|
||||
end
|
||||
|
||||
local errorValue = self.setpoint - measurement
|
||||
self.integral = clamp(self.integral + errorValue * dt, self.integralMin, self.integralMax)
|
||||
local derivative = 0
|
||||
if self.lastTime ~= nil then
|
||||
derivative = (errorValue - self.lastError) / dt
|
||||
end
|
||||
|
||||
local output = self.kp * errorValue + self.ki * self.integral + self.kd * derivative
|
||||
output = clamp(output, self.outputMin, self.outputMax)
|
||||
|
||||
self.lastError = errorValue
|
||||
self.lastMeasurement = measurement
|
||||
self.lastOutput = output
|
||||
self.lastTime = (self.lastTime or 0) + dt
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
function pid:compute(measurement, dt)
|
||||
return self:update(measurement, dt)
|
||||
end
|
||||
|
||||
function pid:getState()
|
||||
return {
|
||||
setpoint = self.setpoint,
|
||||
kp = self.kp,
|
||||
ki = self.ki,
|
||||
kd = self.kd,
|
||||
integral = self.integral,
|
||||
lastError = self.lastError,
|
||||
lastOutput = self.lastOutput,
|
||||
}
|
||||
end
|
||||
|
||||
function pid:resetIntegral()
|
||||
self.integral = 0
|
||||
end
|
||||
|
||||
return pid
|
||||
3
Src/sysinit/control/symlinks.json
Normal file
3
Src/sysinit/control/symlinks.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"/sbin/init": "/usr/lib/sysinit/sysinit"
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
local kernel=...
|
||||
local fs=require("fs")
|
||||
kernel.log("Sysinit started...")
|
||||
kernel.saveLog()
|
||||
|
||||
for i,v in pairs(kernel.processes) do
|
||||
kernel.log("Spawning kernel task "..i)
|
||||
@@ -41,6 +42,7 @@ for i,v in ipairs(files) do
|
||||
end
|
||||
end
|
||||
end
|
||||
kernel.saveLog()
|
||||
|
||||
while true do
|
||||
sleep(5)
|
||||
|
||||
3
build.py
3
build.py
@@ -28,6 +28,7 @@ import shutil
|
||||
import argparse
|
||||
import subprocess
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
@@ -71,7 +72,7 @@ def has_minify_header(path: Path) -> bool:
|
||||
|
||||
def minify_file(src: Path) -> str:
|
||||
result = subprocess.run(
|
||||
["luamin.cmd", "-f", str(src)],
|
||||
["npm", "exec", "luamin", "--", "-f", str(src)],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
@@ -29,4 +29,7 @@ logTaskExit<bool>
|
||||
|
||||
showModLoad<bool>
|
||||
log module loads
|
||||
|
||||
tmpToDisk<bool>
|
||||
writes tmp to disk instead of ram
|
||||
```
|
||||
Reference in New Issue
Block a user