added seperate input and working on http / sockets

This commit is contained in:
2026-05-27 17:01:26 -04:00
parent 59e09a5995
commit f7b64c11b7
43 changed files with 5035 additions and 533 deletions

View File

@@ -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

View File

@@ -1 +1,306 @@
--:Minify:-- --: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
)

View File

@@ -223,6 +223,9 @@ local function newtty(obj, id, ev)
gctrl=function() gctrl=function()
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt) return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
end, end,
isvirt=function()
return false
end,
gplt=function() gplt=function()
return plt return plt
end end
@@ -243,8 +246,39 @@ end
local fifo = kernel.newFifo() local fifo = kernel.newFifo()
kernel.cct.fifo=fifo 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 for i,v in ipairs({peripheral.find("monitor")}) do
newtty(v,tostring(i+1),function () end) newtty(v,tostring(i+1),function () end)
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

View File

@@ -4,6 +4,7 @@ local keys=kernel.apis.keys
kernel.processes.cctdeamon = function() kernel.processes.cctdeamon = function()
local timeout = false local timeout = false
kernel.log("CCT deamon started")
while true do while true do
local event = {kernel.EFI:getMachineEvent()} local event = {kernel.EFI:getMachineEvent()}
@@ -66,6 +67,12 @@ kernel.processes.cctdeamon = function()
elseif eventType == "keyTyped" then elseif eventType == "keyTyped" then
if charOrKey then kernel.cct.fifo.push(charOrKey) end if charOrKey then kernel.cct.fifo.push(charOrKey) end
elseif eventType == "mouse_scroll" then
if event[2] == 1 then
kernel.cct.fifo.push("S")
else
kernel.cct.fifo.push("T")
end
elseif eventType == "http_success" then elseif eventType == "http_success" then
kernel.cct.httpqueue[event[2]]=nil kernel.cct.httpqueue[event[2]]=nil
kernel.cct.httpresponse[event[2]]=event[3] kernel.cct.httpresponse[event[2]]=event[3]
@@ -84,3 +91,5 @@ kernel.processes.cctdeamon = function()
end end
end end
end end
kernel.log("CCT deamon queued for execution")

View File

@@ -19,26 +19,34 @@ kernel.status = "start"
kernel.key = {} kernel.key = {}
kernel.cache = {} kernel.cache = {}
kernel.cache.preload = {} kernel.cache.preload = {}
kernel.unixSockets={}
kernel._G=_G kernel._G=_G
kernel.sleep=sleep kernel.sleep=sleep
_G.sleep=nil _G.sleep=nil
local windowsExp = true local windowsExp = false
function kernel.log(msg, level, c) function kernel.log(msg, level, c, nopanic)
c=c or 0x6D6D6D local f=function()
kernel.LOG_Text = kernel.LOG_Text..tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n" c=c or 0x6D6D6D
if kernel.status == "start" then kernel.LOG_Text = kernel.LOG_Text..tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
screen:setTextColor(c) if kernel.status == "start" then
screen:print(tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg) screen:setTextColor(c)
elseif kernel.status == "term" then screen:print(tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
kernel.standbyTask=kernel.currentTask elseif kernel.status == "term" then
kernel.currentTask=kernel.kernelTask kernel.standbyTask=kernel.currentTask
local file=kernel.vfs.open("/dev/console", "w") kernel.currentTask=kernel.kernelTask
kernel.vfs.devctl(file,"sfgc",c) local file=kernel.vfs.open("/dev/console", "w")
kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n") kernel.vfs.devctl(file,"sfgc",c)
kernel.vfs.close(file) kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
kernel.currentTask=kernel.standbyTask 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
end end
@@ -246,6 +254,15 @@ function kernel.reboot()
kernel.status="reboot" kernel.status="reboot"
end 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["time"]=function() return kernel.EFI:getEpochMs() end
kernel.syscalls["date"]=function() return kernel.EFI:date() end kernel.syscalls["date"]=function() return kernel.EFI:date() end
kernel.syscalls["log"]=kernel.log kernel.syscalls["log"]=kernel.log
@@ -264,8 +281,21 @@ kernel.syscalls["sysdump"]=function()
end end
return rv return rv
end end
kernel.syscalls["reboot"]=kernel.reboot kernel.syscalls["reboot"]=function()
kernel.syscalls["shutdown"]=kernel.shutdown 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") kernel.log("Running modules")
for _,p in ipairs(modules) do for _,p in ipairs(modules) do
@@ -287,11 +317,18 @@ kernel.log("Kernel initialized successfully.")
kernel.saveLog() kernel.saveLog()
kernel.status="running" kernel.status="running"
screen:disable() 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 if kernel.status=="panic" then
kernel.panic(kernel.reason) kernel.panic(kernel.reason)
end end
if kernel.status=="reboot" then if kernel.status=="reboot" then
EFI.reboot=true EFI.reboot=true
return true return true
elseif kernel.status=="halt" then
kernel.log("System halted.")
kernel.saveLog()
while true do end
end end

View File

@@ -7,5 +7,5 @@ return {
initPath = "/sbin/init", initPath = "/sbin/init",
maxOpenFiles = 128, maxOpenFiles = 128,
maxFilesPerTask = 16, maxFilesPerTask = 16,
preempt=true preempt = true,
} }

View File

@@ -229,6 +229,23 @@ function toHex(num)
return string.format("%X", num) return string.format("%X", num)
end 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 function makeSyscallProxy()
local backing = {} local backing = {}
return setmetatable(backing, { return setmetatable(backing, {

View File

@@ -542,6 +542,7 @@ local function allocFD(task)
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
return fd return fd
end end
local function checkSystemLimit() local function checkSystemLimit()
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
end end
@@ -555,27 +556,88 @@ function vfs.newfd(fdobj)
return fd return fd
end 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 local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if _euid ~= 0 then error("EPERM") end if _euid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end if not target then error("EINVAL") end
target = normalizeMountPoint(target) target = normalizeMountPoint(target)
local drive, path = resolvePath(target) local drive, path = resolvePath(target)
if not drive:directoryExists(path) then drive:makeDirectory(path) end if not drive:directoryExists(path) then
if drive:type(target) ~= "directory" then error("EINVAL") end drive:makeDirectory(path)
end
if drive:type(path) ~= "directory" then
error("EINVAL")
end
local disk, id local disk, id
if type(diskOrId) == "string" then -- bind mount
disk = kernel.disks[diskOrId] if bind then
if not disk then error("ENODEV") end local src = normalizeMountPoint(diskOrId)
checkDisk(disk); id = diskOrId local srcDrive = resolvePath(src)
elseif type(diskOrId) == "table" then if not srcDrive then error("ENOENT") end
checkDisk(diskOrId); disk = diskOrId id = "bind:" .. src
id = disk.address; vfs.disks[id] = disk disk = {
else error("EINVAL") end 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 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 vfs.mounts[id] = target
return true return true
end end
@@ -588,7 +650,11 @@ function vfs.umount(target)
for id, mp in pairs(vfs.mounts) do for id, mp in pairs(vfs.mounts) do
if mp == target then if mp == target then
if id == "$" then error("EBUSY") end 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
end end
error("EINVAL") error("EINVAL")

View 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

View File

@@ -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

View File

@@ -140,6 +140,83 @@ function data.zero(op, mode)
end end
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 if kernel.EFI:getEEPROM() then
function data.eeprom(op, mode) function data.eeprom(op, mode)
if op=="type" then if op=="type" then
@@ -216,7 +293,8 @@ if kernel.EFI:getNvram() then
end end
end end
data["disk"]={} data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}}
data["input"]={}
kernel.devfs={} kernel.devfs={}
kernel.devfs.data=data kernel.devfs.data=data
kernel.devfs.proxy=proxy kernel.devfs.proxy=proxy

View File

@@ -114,7 +114,8 @@ local function newtaskproxy(task)
}, },
children={ children={
[".meta"]=strFile(buildMeta(children)) [".meta"]=strFile(buildMeta(children))
} },
pid=strFile(task.pid)
} }
end end

View File

@@ -1,134 +1,141 @@
--:Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local proxy = {} kernel.vfs.remove("/tmp")
local data = {} kernel.vfs.mkdir("/tmp")
if not kernel.config.tmpToDisk then
local proxy = {}
local data = {}
proxy.address = "tmpfs0000" proxy.address = "tmpfs0000"
proxy.isvirt = true proxy.isvirt = true
proxy.isReadOnly = function() return false end proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function(_, path) proxy.makeDirectory = function(_, path)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
for i=1,#steps do for i=1,#steps do
if not step[steps[i]] then if not step[steps[i]] then
step[steps[i]] = {} step[steps[i]] = {}
elseif type(step[steps[i]]) ~= "table" then elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR") error("ENOTDIR")
end
step = step[steps[i]]
end end
step = step[steps[i]]
end end
end
proxy.remove = function(_, path) proxy.remove = function(_, path)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
for i=1,#steps-1 do for i=1,#steps-1 do
step = step[steps[i]] step = step[steps[i]]
if not step then error("ENOENT") end 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")
end end
step = step[steps[i]] step[steps[#steps]] = nil
end end
local filename = steps[#steps]
if mode == "r" then proxy.setLabel = function(_, label) end
if type(step[filename]) ~= "string" then error("ENOENT") end proxy.getLabel = function() return "tmpfs" end
local content = step[filename]
local pos = 1 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 { return {
read = function(amount) size = type(step) == "string" and #step or 0,
amount = amount or #content modified = 0,
local chunk = content:sub(pos, pos+amount-1) created = 0,
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
end
function proxy:type(path) function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
if #steps == 0 then return "directory" end for i=1,#steps-1 do
for i=1,#steps do if not step[steps[i]] then
step = step[steps[i]] if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
if not step then return false 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 end
if type(step) == "table" then return "directory" end
if type(step) == "string" then return "file" end
end
function proxy:list(path) function proxy:type(path)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
for i=1,#steps do if #steps == 0 then return "directory" end
step = step[steps[i]] for i=1,#steps do
if not step then error("ENOENT") end 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 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) function proxy:list(path)
local t = self:type(path) local steps = kernel.vfs.splitPath(path)
return t == "file" or t == "directory" local step = data
end 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

View File

@@ -25,10 +25,11 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
else else
local id = line:sub(3, semicolon_pos - 1) local id = line:sub(3, semicolon_pos - 1)
local path = trim(line:sub(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 if id ~= "$" then
kernel.vfs.mount(path, id) kernel.vfs.mount(path, id)
end end
kernel.log("Mounted "..id.." to "..path)
end end
end end
end end

View File

@@ -1,67 +1,194 @@
--:Minify:-- --:Minify:--
-- Supports: local kernel = ...
-- 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 socket = {}
local socket={}
socket.handlers={}
kernel.socket=socket
function socket.registerProtocal(protocal, handler) socket.handlers = {}
socket.handlers[protocal] = handler
kernel.socket = socket
local P = kernel.vfs.P
local sys = kernel.syscalls
function socket.registerProtocal(proto, handler)
socket.handlers[proto] = handler
end end
function socket.socket() local function getHandler(address)
local P=kernel.vfs.P for proto, handler in pairs(socket.handlers) do
local data=kernel.newFifo() if string.hasPrefix(address, proto) then
local isClosed=false return handler
return kernel.vfs.newfd({ end
handle={ end
read=function() if isClosed then error("ECCON") end return data.pop() end, end
write=function(data) if isClosed then error("ECCON") end return data.push(data) end,
close=function() isClosed = true 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, meta = {
socket={}, owner = kernel.currentTask.uid,
meta={ group = kernel.currentTask.uid,
owner=kernel.currentTask.uid, etype = 2,
group=kernel.currentTask.uid,
etype=2, perms =
perms=P.OWNER_R+P.OWNER_W+P.GROUP_R+P.GROUP_W 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 end
function socket.connect(fd, address) function socket.connect(fd, address)
local handler local fdo = kernel.currentTask.fd[fd]
for k, v in pairs(socket.handlers) do
if string.hasPrefix(address, k) then if not fdo then
handler=v return nil, "EBADF"
end
end 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 end
function socket.listen(fd, backlog) 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 end
local sys=kernel.syscalls function socket.send(fd, data)
sys["socket"]=socket.socket 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") kernel.log("Loaded socket module")

View 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

View File

@@ -21,7 +21,7 @@ local function loadExecutable(path)
local env = kernel.freshUserEnv() local env = kernel.freshUserEnv()
local func, err = load(data, "@" .. path, "t", env) 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 meta = kernel.vfs.lstat(path)
local suid_set = bit_is_set(meta.perms, 6) local suid_set = bit_is_set(meta.perms, 6)
@@ -211,6 +211,8 @@ function sys.kill(pid, force)
return false, "Task is already dead" return false, "Task is already dead"
elseif task.status == "D" and not force then elseif task.status == "D" and not force then
return false, "Cannot kill task waiting for IO" return false, "Cannot kill task waiting for IO"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
end end
local caller = kernel.currentTask local caller = kernel.currentTask
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid 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" return false, "Task does not exist"
elseif task.status ~= "R" and task.status ~= "S" then elseif task.status ~= "R" and task.status ~= "S" then
return false, "Cannot stop non-running task" return false, "Cannot stop non-running task"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else else
if task.status == "S" then if task.status == "S" then
task.status = "ST" task.status = "ST"
@@ -243,6 +247,8 @@ function sys.continue(pid)
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "T" and task.status ~= "ST" then elseif task.status ~= "T" and task.status ~= "ST" then
return false, "Task is not stopped" return false, "Task is not stopped"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else else
if task.status == "ST" then if task.status == "ST" then
task.status = "S" task.status = "S"
@@ -299,20 +305,21 @@ function sys.getuid() return kernel.currentTask.uid end
local function reapDeadTasks() local function reapDeadTasks()
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then if task.status == "Z" and not task.reapTime then
task.coro = nil if task.pid == 1 then kernel.panic("Attempted to gc init!") end
task.ivs = nil task.coro = nil
task.vs = nil task.ivs = nil
task.args = nil task.vs = nil
task.envars = nil task.args = nil
task.cwd = nil task.envars = nil
task.numRuns = nil task.cwd = nil
task.totalTime = nil task.numRuns = nil
task.lastTime = nil task.totalTime = nil
task.timeSlice = nil task.lastTime = nil
task.timeSlice = nil
task.syscallReturn = nil task.syscallReturn = nil
task.sleep = nil task.sleep = nil
task.fd = nil task.fd = nil
task.reapTime = kernel.EFI:getEpochMs() + 30000 task.reapTime = kernel.EFI:getEpochMs() + 30000
elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
and task.status == "Z" then and task.status == "Z" then
@@ -344,7 +351,23 @@ local k_max = 0.5
local B = 0.01 local B = 0.01
function kernel.main() function kernel.main()
kernel.log("Starting main loop...")
kernel.saveLog()
local stopLog=5
local logTasks=100
while not kernel.exitMain do 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 N = 0
local Tmin_hit = 0 local Tmin_hit = 0
local Tmax_hit = 0 local Tmax_hit = 0
@@ -352,6 +375,7 @@ function kernel.main()
local taskTimes = {} local taskTimes = {}
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if kernel.exitMain then break end
kernel.currentTask = task kernel.currentTask = task
kernel.uid = task.euid or task.uid kernel.uid = task.euid or task.uid
kernel.process = task.name kernel.process = task.name
@@ -366,9 +390,29 @@ function kernel.main()
task.sleep = 0 task.sleep = 0
end 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.status == "D" then
if task.ksh then if task.ksh then
coroutine.resume(task.ksh) 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
end end
@@ -405,7 +449,9 @@ function kernel.main()
if task.status == "R" then if task.status == "R" then
local startTime = kernel.EFI:getEpochMs() local startTime = kernel.EFI:getEpochMs()
local ret 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 kernel.config.preempt then
if not task.debugger then if not task.debugger then
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
@@ -491,6 +537,11 @@ function kernel.main()
end end
reapDeadTasks() 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
end end

View 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

View File

@@ -1,5 +1,5 @@
--:Minify:-- --: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/tty/1", "w") --stdout (fd 1)
syscall.open("/dev/null", "w") --stderr (fd 2) syscall.open("/dev/null", "w") --stderr (fd 2)

View File

@@ -0,0 +1,3 @@
{
"/bin/sh": "/bin/hysh"
}

View File

@@ -107,6 +107,7 @@ local COMMANDS = {
{ name="whoami", usage="whoami", desc="Print the current username.", flags={} }, { name="whoami", usage="whoami", desc="Print the current username.", flags={} },
{ name="id", usage="id [username]", desc="Print user identity (uid, gid).", 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="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="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={ { name="uname", usage="uname [-asnrm]", desc="Print system information (OS name, hostname, release, machine).", flags={
{"-a","Print all fields"}, {"-a","Print all fields"},

View File

@@ -1,10 +1,10 @@
--:Minify:-- --: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/tty/1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2) syscall.open("/dev/null","w") --stderr (device 2)
local success, errorMsg = xpcall(function()
local fs = require("fs") local fs = require("fs")
syscall.devctl(1,"clear") syscall.devctl(1,"clear")
@@ -833,6 +833,14 @@ builtinCmds.ops = function()
end end
end end
builtinCmds.reboot = function ()
syscall.reboot()
end
builtinCmds.shutdown = function ()
syscall.shutdown()
end
local function listDir(dir, prefix) local function listDir(dir, prefix)
local ok, entries = pcall(syscall.listdir, dir) local ok, entries = pcall(syscall.listdir, dir)
if not ok or not entries then return {} end if not ok or not entries then return {} end
@@ -1203,9 +1211,9 @@ local function runCommand(command)
local proc = syscall.spawn(function() local proc = syscall.spawn(function()
-- Open standard fds so programs that don't do it themselves work correctly. -- 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/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 -- exec replaces this coroutine's code with a fresh isolated environment
-- compiled from disk by the kernel (via loadExecutable -> freshUserEnv), -- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
-- so the child cannot share any upvalue or syscall table state with hysh. -- so the child cannot share any upvalue or syscall table state with hysh.

113
Src/hysh/data/bin/tree Normal file
View 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, "")

View File

View File

View 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

File diff suppressed because it is too large Load Diff

136
Src/pid/data/lib/pid.lua Normal file
View 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

View File

@@ -0,0 +1,3 @@
{
"/sbin/init": "/usr/lib/sysinit/sysinit"
}

View File

@@ -2,6 +2,7 @@
local kernel=... local kernel=...
local fs=require("fs") local fs=require("fs")
kernel.log("Sysinit started...") kernel.log("Sysinit started...")
kernel.saveLog()
for i,v in pairs(kernel.processes) do for i,v in pairs(kernel.processes) do
kernel.log("Spawning kernel task "..i) kernel.log("Spawning kernel task "..i)
@@ -41,6 +42,7 @@ for i,v in ipairs(files) do
end end
end end
end end
kernel.saveLog()
while true do while true do
sleep(5) sleep(5)

View File

@@ -28,6 +28,7 @@ import shutil
import argparse import argparse
import subprocess import subprocess
import json import json
import os
from pathlib import Path from pathlib import Path
from typing import Union from typing import Union
@@ -71,7 +72,7 @@ def has_minify_header(path: Path) -> bool:
def minify_file(src: Path) -> str: def minify_file(src: Path) -> str:
result = subprocess.run( result = subprocess.run(
["luamin.cmd", "-f", str(src)], ["npm", "exec", "luamin", "--", "-f", str(src)],
capture_output=True, capture_output=True,
text=True text=True
) )

View File

@@ -29,4 +29,7 @@ logTaskExit<bool>
showModLoad<bool> showModLoad<bool>
log module loads log module loads
tmpToDisk<bool>
writes tmp to disk instead of ram
``` ```