This commit is contained in:
2025-12-08 21:42:20 -05:00
parent 67db52a25f
commit 7bc6d87322
70 changed files with 11403 additions and 58 deletions

View File

@@ -0,0 +1,137 @@
function string.hasSuffix(str, suffix)
return string.sub(str, #suffix+1) == suffix
end
function string.hasPrefix(str, prefix)
return string.sub(str, 1, #prefix) == prefix
end
function string.getSuffix(str, prefix)
return string.sub(str, #prefix+1)
end
function string.getPrefix(str, suffix)
return string.sub(str, 1, #suffix)
end
function string.join(str, ...)
return table.concat(table.pack(str, ...))
end
function string.delim(str, ...)
return table.concat(table.pack(...), str)
end
function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str,i,i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf..c
end
end
table.insert(rv, buf)
return rv
end
function string.replace(str, search, replacement)
local rv = ""
local consumedLen = 1
local i = 1
while i<#str do
if string.sub(str, i, i+#search-1) == search then
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
i=i+#search
consumedLen = i
end
i=i+1
end
return rv .. string.sub(str, consumedLen)
end
function table.deepcopy(orig, copies)
copies = copies or {}
if type(orig) ~= 'table' then
return orig
elseif copies[orig] then
return copies[orig]
end
local copy = {}
copies[orig] = copy
for k, v in next, orig, nil do
local copied_key = table.deepcopy(k, copies)
local copied_val = table.deepcopy(v, copies)
copy[copied_key] = copied_val
end
return copy
end
function table.hasKey(tabl, query)
for i,v in pairs(tabl) do
if i==query then
return true
end
end
return false
end
function table.hasVal(tabl, query)
for i,v in pairs(tabl) do
if v==query then
return true
end
end
return false
end
local function serialize(table)
local output = "{"
for i,v in pairs(table) do
local coma=true
if type(i) == "string" then
output=output.."[\""..i.."\"]="
end
if type(v) == "table" then
if v == table then
output=string.sub(output,1,#output-(#i+1))
coma=false
else
output=output..serialize(v)
end
elseif type(v) == "string" then
output=output.."[=["..v.."]=]"
elseif type(v) == "number" then
output=output..tostring(v)
elseif type(v) == "boolean" then
if v == true then
output=output.."true"
else
output=output.."false"
end
elseif type(v) == "function" then
output=output.."function() end"
else
error("serialization of type \""..type(v).."\" is not supported")
end
if coma then
output=output..","
end
end
if #table>0 or string.sub(output,#output,#output) == "," then
output=string.sub(output,1,#output-1)
end
output=output.."}"
return output
end
table.serialize=serialize

View File

@@ -0,0 +1,217 @@
local args = {...}
local kernel = args[1]
local fs = {}
local disks = {}
local mounts = {["/"]="$"}
-- path normaliing
local function normalizePath(path)
if not path or path == "" then return "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
local parts = {}
for part in path:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
parts[#parts+1] = part
end
end
return "/" .. table.concat(parts, "/")
end
-- disk resolution
local function resolvePath(path)
local abs = normalizePath(path)
local best = "/"
for mount, diskAddr in pairs(mounts) do
if abs:sub(1, #mount) == mount and #mount > #best then
best = mount
end
end
local newPath = abs:sub(#best + 1)
if newPath == "" then newPath = "/" end
return disks[mounts[best]], newPath
end
-- Symlinks
-- Format: #!@SYMLINK[target]
local SYMLINK_PREFIX = "#!@SYMLINK["
local SYMLINK_SUFFIX = "]"
local SYMLINK_MAX_DEPTH = 64
local function isSymlink(disk, path)
if not disk:fileExists(path) then return false end
local text = disk:readAllText(path)
if not text then return false end
return text:sub(1, #SYMLINK_PREFIX) == SYMLINK_PREFIX
end
local function readSymlink(disk, path)
local text = disk:readAllText(path)
if not text then return nil end
if text:sub(1, #SYMLINK_PREFIX) ~= SYMLINK_PREFIX then return nil end
local target = text:sub(#SYMLINK_PREFIX + 1)
if target:sub(-1) == SYMLINK_SUFFIX then
target = target:sub(1, -2)
end
return target
end
-- Path resolution
local function splitPath(p)
local parts = {}
for part in p:gmatch("[^/]+") do parts[#parts+1] = part end
return parts
end
local function resolveSymlink(path)
path = normalizePath(path)
local symlinkDepth = 0
local parts = splitPath(path)
local resolved = {}
local i = 1
while i <= #parts do
local comp = parts[i]
if comp == "." then
elseif comp == ".." then
if #resolved > 0 then table.remove(resolved) end
else
local currentPath = "/" .. table.concat(resolved, "/")
local disk, p = resolvePath(currentPath .. "/" .. comp)
if isSymlink(disk, p) then
symlinkDepth = symlinkDepth + 1
if symlinkDepth > SYMLINK_MAX_DEPTH then
error("Too many levels of symbolic links: " .. path)
end
local target = readSymlink(disk, p)
if not target then
resolved[#resolved+1] = comp
else
if target:sub(1,1) == "/" then
resolved = splitPath(normalizePath(target))
else
local base = resolved
resolved = {}
for _, seg in ipairs(base) do resolved[#resolved+1]=seg end
for seg in target:gmatch("[^/]+") do
if seg == ".." then
if #resolved>0 then table.remove(resolved) end
elseif seg ~= "." then
resolved[#resolved+1]=seg
end
end
end
local remaining = {}
for j=i+1,#parts do remaining[#remaining+1]=parts[j] end
parts = remaining
i = 0
end
else
resolved[#resolved+1]=comp
end
end
i=i+1
end
local finalPath="/"..table.concat(resolved,"/")
local disk,diskPath=resolvePath(finalPath)
return disk,diskPath
end
-- PUBLIC API: MOUNTING
function fs.mount(disk, mountPoint)
if kernel.uid ~= 0 then error("Permission Denied") end
mounts[mountPoint]=disk
end
function fs.unmount(mountPoint)
if kernel.uid ~= 0 then error("Permission Denied") end
mounts[mountPoint]=nil
end
function fs.virtDisk(diskObj)
if kernel.uid ~= 0 then error("Permission Denied") end
if disks[diskObj.address] then error("Disk exists") end
disks[diskObj.address]=diskObj
end
function fs.eject(addr)
if kernel.uid ~= 0 then error("Permission Denied") end
disks[addr]=nil
end
-- SYMLINK API
function fs.symlink(target, linkPath)
local disk, p = resolvePath(linkPath)
return disk:writeAllText(p, SYMLINK_PREFIX..target..SYMLINK_SUFFIX)
end
function fs.isLink(path)
local disk, p = resolvePath(path)
return isSymlink(disk,p)
end
function fs.readLink(path)
local disk, p = resolvePath(path)
return readSymlink(disk,p)
end
-- FILE OPERATIONS
function fs.exists(path)
local disk, p = resolveSymlink(path)
return disk:fileExists(p) or disk:directoryExists(p)
end
function fs.isFile(path)
local disk, p = resolveSymlink(path)
if isSymlink(disk, p) then return false end
return disk:fileExists(p)
end
function fs.isDir(path)
local disk, p = resolveSymlink(path)
if isSymlink(disk, p) then return false end
return disk:directoryExists(p)
end
function fs.list(path)
local disk, p = resolveSymlink(path)
return disk:list(p)
end
function fs.makeDir(path)
local disk, p = resolveSymlink(path)
return disk:makeDirectory(p)
end
function fs.remove(path)
local disk, p = resolvePath(path)
if isSymlink(disk, p) then
return disk:remove(p)
end
local d2, p2 = resolveSymlink(path)
return d2:remove(p2)
end
function fs.readAllText(path)
local disk, p = resolveSymlink(path)
return disk:readAllText(p)
end
function fs.writeAllText(path, text)
local disk, p = resolveSymlink(path)
return disk:writeAllText(p, text)
end
function fs.appendAllText(path, text)
local disk, p = resolveSymlink(path)
return disk:appendAllText(p, text)
end
function fs.getSize(path)
local disk, p = resolveSymlink(path)
return disk:getSize(p)
end
-- INIT
for _,v in kernel.initdisks.list() do fs.virtDisk(v) end
kernel.fs=fs
kernel.chache.preload.fs=fs
kernel.chache.preload.filesystem=fs

View File

@@ -0,0 +1,22 @@
local args={...}
local kernel=args[1]
local ifs=kernel.ifs
local initdisks=kernel.initdisks
local fstab=ifs.readAllText("/etc/fstab")
kernel.fs.update(initdisks)
for i,v in ipairs(string.split(fstab,"\n")) do
if v:sub(1,1)=="U" then
local id=""
for i=3,#v do
if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
id=v:sub(3,i-1)
end
end
local path=v:sub(#id+4)
kernel.log("Mounted "..id.." to "..path)
kernel.fs.mount(id,path)
::endline::
end
end

View File

@@ -0,0 +1,182 @@
local args={...}
local kernel=args[1]
function kernel.fs.mkvirtfs(address, readOnly, label, autoRegister)
if kernel.uid ~= 0 then error("Permission Denied") end
local disk = {}
disk.address = address
local isRO = readOnly or false
local backend = {
["/"] = { __dir = true }
}
-----------------------------------------------------------------
-- Helpers
-----------------------------------------------------------------
local function norm(path)
if not path or path == "" then return "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
path = path:gsub("/+", "/")
return path
end
local function split(path)
local parts = {}
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
return parts
end
local function getNode(path)
path = norm(path)
if path == "/" then return backend["/"] end
local node = backend["/"]
for _, part in ipairs(split(path)) do
node = node[part]
if not node then return nil end
end
return node
end
local function getParent(path)
local parts = split(norm(path))
local name = table.remove(parts)
local parentPath = "/" .. table.concat(parts, "/")
return getNode(parentPath), name
end
local function ensureWrite()
if isRO then return false, "read-only" end
return true
end
-----------------------------------------------------------------
-- File IO (supports string or file-object with read/write funcs)
-----------------------------------------------------------------
local function fileRead(node)
if type(node) == "string" then
return node
elseif type(node) == "table" and node.__file and node.read then
return node.read()
end
return nil
end
local function fileWrite(parent, name, text)
local node = parent[name]
if type(node) == "string" then
parent[name] = text
return true
elseif type(node) == "table" and node.__file and node.write then
return node.write(text)
end
parent[name] = text
return true
end
local function fileAppend(parent, name, text)
local node = parent[name]
if type(node) == "string" then
parent[name] = node .. text
return true
elseif type(node) == "table" and node.__file and node.write then
return node.write((node.read() or "") .. text)
end
parent[name] = text
return true
end
-----------------------------------------------------------------
-- API
-----------------------------------------------------------------
function disk:isReadOnly() return isRO end
function disk:spaceUsed() return 0 end
function disk:spaceTotal() return 0 end
function disk:readAllText(path)
local node = getNode(path)
if not node then return nil, "file not found" end
return fileRead(node)
end
function disk:writeAllText(path, text)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid directory" end
return fileWrite(parent, name, tostring(text))
end
function disk:appendAllText(path, text)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid directory" end
return fileAppend(parent, name, tostring(text))
end
function disk:list(path)
local node = getNode(path)
if not node or not node.__dir then return nil, "not a directory" end
local out = {}
for k, _ in pairs(node) do if k ~= "__dir" then table.insert(out, k) end end
return out
end
function disk:fileExists(path)
local node = getNode(path)
return type(node) == "string" or (type(node) == "table" and node.__file)
end
function disk:directoryExists(path)
local node = getNode(path)
return node and type(node) == "table" and node.__dir or false
end
function disk:makeDirectory(path)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid parent directory" end
parent[name] = { __dir = true }
return true
end
function disk:remove(path)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent[name] then return false, "not found" end
parent[name] = nil
return true
end
function disk:setLabel(new)
local ok, err = ensureWrite(); if not ok then return false, err end
label = tostring(new)
end
function disk:getLabel()
return label
end
function disk:size(path)
local node = getNode(path)
if type(node) == "string" then return #node end
if type(node) == "table" and node.__file and node.read then
local v = node.read()
return v and #v or 0
end
return 0
end
-----------------------------------------------------------------
-- Auto-register with kernel and return backend
-----------------------------------------------------------------
if kernel and kernel.fs and kernel.fs.virtDisk and autoRegister then
kernel.fs.virtDisk(disk)
end
return backend, disk
end

View File

@@ -0,0 +1,56 @@
local args = {...}
local kernel = args[1]
-- List of search paths
local paths = {
"/lib/?",
"/usr/lib/?",
"/usr/local/lib/?"
}
-- Custom require implementation
function require(module)
-- Return cached module if it already exists
if kernel.cache.preload[module] then
return kernel.cache.preload[module]
end
local err = {}
for _, path in ipairs(paths) do
-- Replace "?" with module name
local filePath = string.replace(path, "?", module)
-- Try to open file
local file = kernel.fs.isFile(filePath)
if file then
local content = kernel.fs.readAllText(filePath)
-- Load the module as Lua code
local chunk, loadErr = load(content, filePath)
if not chunk then
table.insert(err, "Error loading: " .. filePath .. ": " .. loadErr)
else
-- Execute the module. If the module returns a value, cache it.
local ok, result = xpcall(chunk, debug.traceback)
if not ok then
table.insert(err, "Error executing: "..filePath..": "..tostring(result))
else
if result ~= nil then
kernel.cache.preload[module] = result
return result
end
-- If module doesn't return anything, cache `true` like Lua does
kernel.cache.preload[module] = true
return true
end
end
else
table.insert(err, "Module not found: " .. filePath)
end
end
-- If nothing worked, raise an error with all reasons
error("Unable to require module '" .. module .. "':\n" .. table.concat(err, "\n"))
end

View File

@@ -0,0 +1,17 @@
local args={...}
local kernel = args[1]
local data = kernel.fs.mkvirtfs("devfs0000", true, "devfs", false)
data["/"]["OSVERSION"]="Hyperion 1.0.0"
data["/"]["eeprom"]={
__file=true,
read=function()
return kernel.computer:getEEPROM()
end,
write=function(text)
if kernel.uid ~= 0 then error("Permission Denied") end
kernel.computer:setEEPROM(text)
end
}

View File

@@ -0,0 +1,12 @@
local args={...}
local kernel=args[1]
kernel.drivers.processes[#kernel.drivers.processes+1]=function()
while true do
local event={kernel.computer:getMachineEvent()}
while event[1]~=nil do
kernel.hpv.sendSig("all", table.unpack(event))
event={kernel.computer:getMachineEvent()}
end
coroutine.yield()
end
end

View File

@@ -0,0 +1,19 @@
local args={...}
local kernel=args[2]
for _,subf in ipairs(kernel.fs.list("/lib/modules/")) do
if subf~="Hyperion" then
for _,driver in ipairs(kernel.fs.list("/lib/modules/"..subf)) do
local code = kernel.fs.readAllText("/lib/modules/"..subf.."/"..driver)
local func, err = load(code, "@"..driver)
if not func then
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
else
local ok, err = xpcall(func, debug.traceback)
if not ok then
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
end
end
end
end
end

View File

@@ -0,0 +1,11 @@
local args={...}
local kernel=args[1]
for _,l in ipairs(kernel.drivers.prior) do
for _,d in ipairs(l) do
if d.init then
local ok,err = xpcall(d.init, debug.traceback)
if not ok then kernel.log("DriverInitErr: "..tostring(err)) end
end
end
end

View File

@@ -0,0 +1,167 @@
local args={...}
local kernel = args[1]
local tasks={}
local currentTask={}
local signals={}
local tid=1
local gid=1
local sys={}
function sys.hookSig(sig, func)
if not signals[tostring(currentTask.pid)][sig] then
signals[tostring(currentTask.pid)][sig]={}
end
signals[tostring(currentTask.pid)][sig][#signals[tostring(currentTask.pid)][sig]+1]=func
end
function sys.clearSigHooks(typ)
if not typ or typ == "all" then
signals[tostring(currentTask.pid)]={}
currentTask.signal=signals[tostring(currentTask.pid)]
else
if currentTask.signal[typ] then
currentTask.signal[typ]={}
end
end
end
function sys.sendSig(pid, signal, ...)
if pid=="all" then
for i,v in pairs(tasks) do
v.sigQ[#v.sigQ+1]={signal, ...}
end
return
end
if not tasks[tostring(pid)] then return false end
tasks[tostring(pid)].sigQ[#tasks[tostring(pid)].sigQ+1]={signal, ...}
return true
end
function sys.flushSigs()
local ret = {}
for i=1, #currentTask.sigQ do
if currentTask.signal[currentTask.sigQ[i][1]] then
for _,v in ipairs(currentTask.signal[currentTask.sigQ[1][1]]) do
coroutine.resumeWithTimeout(coroutine.create(function()
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
if not ok then
table.insert(ret, err)
end
end), 10)
end
else
for _,v in ipairs(currentTask.signal["unhandledEvent"]) do
coroutine.resumeWithTimeout(coroutine.create(function()
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
if not ok then
table.insert(ret, err)
end
end), 10)
end
end
end
end
function sys.spawn(func, name, evars, args)
local id=tid
tid=tid+1
name=name or tostring(id)
signals[tostring(id)]={}
tasks[tostring(id)]={
coro=coroutine.create(function()
local ret = {xpcall(func, debug.traceback, table.unpack(args))}
if not ret[1] then
sys.sendSig(currentTask.ppid, "ChildTaskError", id, err)
else
sys.sendSig(currentTask.ppid, "ChildTaskExit", id, table.unpack(ret, 2))
end
currentTask.status="Z"
end),
name=name,
pid=id,
ppid=currentTask.pid,
tgid=currentTask.tgid,
user=kernel.user,
uid=kernel.uid,
evars=evars,
args=args,
vy=0,
ivy=0,
status="R",
signal=signals[tostring(id)],
parent=currentTask,
children={},
sibling=currentTask.children,
sigQ={}
}
end
local function collectZombieProc()
local ret = {}
for _,v in pairs(tasks) do
if v.status=="Z" then
local pid = v.pid
for _,c in ipairs(v.children) do
c.parent=tasks["1"]
c.sibling=tasks["1"].children
c.ppid=1
c.tgid=1
end
signals[tostring(pid)]=nil
tasks[tostring(pid)]=nil
table.insert(ret, pid)
end
end
return ret
end
signals["1"]={}
tasks["1"]={
coro=coroutine.create(function()
local ret = {xpcall(kernel.fs.load(kernel.initPath), debug.traceback)}
if not ok then
kernel.panic(err)
else
kernel.panic("Attempted to kill init!")
end
end),
name="SysInit",
pid=1,
ppid=0,
tgid=1,
user=kernel.user,
uid=kernel.uid,
evars={},
args={kernel},
vy=0,
ivy=0,
status="R",
signal=signals["1"],
parent={name="Hyprkrnl",pid=0},
children={},
sibling={},
sigQ={}
}
kernel.chache.preload.sys=sys
kernel.chache.preload.system=sys
kernel.hpv=sys
kernel.saveLog()
while true do
for _,v in pairs(tasks) do
currentTask=v
kernel.user=currentTask.user
kernel.uid=currentTask.uid
sys.flushSigs()
local status = coroutine.resumeWithTimeout(currentTask.coro, 50)
if status then
currentTask.vy=currentTask.vy+1
else
currentTask.ivy=currentTask.ivy+1
end
end
collectZombieProc()
end
kernel.panic("Exited pid 0")