This commit is contained in:
2026-07-28 20:13:50 -04:00
parent 49c9e5e37c
commit 56646625c0
71 changed files with 638 additions and 103 deletions
-7
View File
@@ -1,7 +0,0 @@
local http=require("http")
local ok,err = xpcall(http.get, debug.traceback, "http://localhost:8000/spm.json")
if not ok then
print(err)
else
print(err.body)
end
+8 -5
View File
@@ -1,11 +1,6 @@
--:Minify:--
local fs={}
-- "open" : open
-- "read" : read
-- "write" : write
-- "close" : close
function fs.open(path, mode)
local fd=syscall.open(path,mode)
local ret={
@@ -140,4 +135,12 @@ function fs.isDir(path)
return syscall.type(path) == "directory"
end
function fs.symlink(target, path)
return syscall.syslink(target, path)
end
function fs.readlink(path)
return syscall.readlink(path)
end
return fs
@@ -463,7 +463,12 @@ local ok, err = xpcall(function()
getTextColor = function()
return fg
end,
enable=function() end,
enable=function()
for i=1, #allscreens do
allscreens[i].clear()
allscreens[i].setCursorPos(1, 1)
end
end,
disable=function() end
},
architecture="cct",
@@ -220,9 +220,6 @@ local function newtty(obj, id, ev)
gbgc=function()
return bg
end,
gctrl=function()
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
end,
isvirt=function()
return false
end,
@@ -269,6 +266,9 @@ kernel.devfs.data["input"]["keyboard1"] = function(op, mode)
return rv
end,
write=function(content)
end,
gctrl=function()
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
end
}
if mode=="rw" then
+3 -3
View File
@@ -7,10 +7,10 @@ local disks=EFI.disks
local arch=EFI.architecture
local kernel = {}
kernel.LOG_Text=""
kernel.version="HyperionOS V1.2.4-dev_5"
kernel.tag="1.2.4-dev_5"
kernel.version="HyperionOS V1.2.4"
kernel.tag="1.2.4"
kernel.process = "Kernel"
kernel.users={[0]="root",[1]="User"}
kernel.users={[0]="root",[1000]="User"}
kernel.hostname = "hyperion"
kernel.groups = {}
kernel.uid = 0
@@ -23,6 +23,7 @@ function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
if not str then return false end
maxResultCountOrNil = (maxResultCountOrNil or 0) - 1
if str == "" then return {} end
local rv = {}
local buf = ""
for i = 1, #str do
@@ -124,7 +124,7 @@ end
local function validateComponent(comp)
local lower = comp:lower()
if not lower:match(SAFE_COMPONENT_PATTERN) then
if not comp:match(SAFE_COMPONENT_PATTERN) then
error("EINVAL: illegal characters in path component: " .. comp, 3)
end
if lower == ".meta" then
@@ -276,7 +276,7 @@ local function namei(path, noFollow, symDepth)
else
validateComponent(comp)
local lname = comp:lower()
local lname = comp
local curPath = "/" .. table.concat(stack, "/")
@@ -374,7 +374,7 @@ local function normalizePath(path)
table.remove(stack)
end
else
table.insert(stack, comp:lower())
table.insert(stack, comp)
end
end
@@ -184,11 +184,16 @@ function socket.sockshutdown(fd)
return true
end
function socket.socketendpoints()
return table.keys(socket.handlers)
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
sys.socketendpoints = socket.socketendpoints
kernel.log("Loaded socket module")
@@ -19,4 +19,25 @@ function printInline(...)
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
output = output:sub(1, -2)
syscall.write(1, output)
end
function userinput(fd, helpstr, replacestr, fdo)
local str = ""
if helpstr then print(helpstr) end
while true do
local inp = syscall.read(fd)
if inp~="" and inp~=nil then
if inp~="\n" then
if replacestr then
syscall.write(fdo, replacestr)
else
syscall.write(fdo, inp)
end
str = inp
else
break
end
end
end
return str
end
+3 -1
View File
@@ -315,7 +315,9 @@ end
-- CRC-16/CRC-32 computation
local band, bnot, xor, lshift, rshift
local bit32
local ok, temp = pcall(require, "bit32")
if ok then bit32=temp end
if bit ~= nil then
band = bit.band
bnot = bit.bnot
+3 -3
View File
@@ -1211,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/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("/proc/self/parent/fd/0", "r") -- fd 0 stdin
syscall.open("/proc/self/parent/fd/1", "w") -- fd 1 stdout
syscall.open("/proc/self/parent/fd/2", "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.
+310 -48
View File
@@ -1,68 +1,330 @@
local args={...}
--:Minify:--
local spm = function(...)
if syscall.geteuid()~=0 then print("Permission denied") return end
local inpArgs = { ... }
local args = {}
local cloptions = {
S = false,
y = false,
u = false,
R = false,
h = false,
r = false,
Q = false,
l = false,
o = false,
i = false,
e = false,
t = false,
sysroot = "",
init = false,
help = false,
source = false
}
-- local test repo
local rootRepo="http://localhost:8000/spm.json"
--local rootRepo="https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/spm.json"
local i = 1
local fs = require("fs")
local http = require("http")
local json = require("json")
local tar = require("tar")
local deflate = require("deflate")
local httpcache = {}
local json = require("json")
local http = require("http")
local deflate = require("deflate")
local function checkRepo(repo)
local resp = http.get(repo)
if resp.code ~= 200 then
return false
local function get(url)
if httpcache[url] then return httpcache[url] end
local ret = http.get(url)
httpcache[url] = ret
return ret
end
local repojson = json.decode(resp.body)
if not repojson then
return false
while i <= #inpArgs do
local v = inpArgs[i]
if v:sub(1, 2) == "--" then
local opt, value = v:match("^%-%-(.-)=(.*)$")
if not opt then
opt = v:sub(3)
end
if cloptions[opt] == nil then
print("spm: unrecognized option '" .. v .. "'.")
print("try 'spm --help' for more information.")
return
end
if type(cloptions[opt]) == "boolean" then
cloptions[opt] = true
else
if value == nil then
i = i + 1
value = inpArgs[i]
end
if value == nil then
print("spm: option '--" .. opt .. "' requires an argument.")
return
end
cloptions[opt] = value
end
elseif v:sub(1, 1) == "-" then
for j = 2, #v do
local opt = v:sub(j, j)
if cloptions[opt] == nil then
print("spm: invalid option '-" .. opt .. "'.")
print("try 'spm --help' for more information.")
return
end
if type(cloptions[opt]) ~= "boolean" then
print("spm: option '-" .. opt .. "' requires the long form '--" .. opt .. "=value'.")
return
end
cloptions[opt] = true
end
else
table.insert(args, v)
end
i = i + 1
end
return true, repojson
end
local function getRepos(root)
local repos = {}
local visited = {}
if cloptions.sysroot=="" then
syscall.chdir("/")
else
syscall.chdir(cloptions.sysroot)
end
local queue = {root}
local head = 1
if cloptions.init then
fs.mkdir("var/spm/cache/")
fs.mkdir("var/spm/db/")
fs.writeAllText("var/spm/db/repos.list","")
fs.mkdir("var/spm/db/installed/")
fs.mkdir("etc/spm/")
fs.writeAllText("etc/spm/sources.list","https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/spm.json\nhttp://localhost:8000/spm.json")
end
while head <= #queue do
local url = queue[head]
head = head + 1
if not fs.exists("var/spm/") or not fs.exists("etc/spm/") then
print("spm not initialized run \"spm --init\"")
return
end
if not visited[url] then
visited[url] = true
local repodb = string.split(fs.readAllText("var/spm/db/repos.list"), "\n")
local sourcelist = string.split(fs.readAllText("etc/spm/sources.list"), "\n")
local pkgdblist = fs.list("var/spm/db/installed/")
local pkgdb = {}
local ok, repo = checkRepo(url)
if ok then
table.insert(repos, {
url = url,
data = repo
})
for _,v in ipairs(pkgdblist) do
local file = fs.readAllText("var/spm/db/installed/"..v)
pkgdb[v] = json.decode(file)
end
if repo.refs then
for _, ref in ipairs(repo.refs) do
if not visited[ref] then
queue[#queue + 1] = ref
local function printpkgs(list)
local colWidth=0
local tmp = syscall.devctl(1, "size")
local sizeX, sizeY = tonumber(tmp:sub(1, tmp:find(";")-1)), tonumber(tmp:sub(tmp:find(";")+1))
for _, v in ipairs(list) do
if #v + 2 > colWidth then colWidth = #v + 2 end
end
local numCols = math.max(1, math.floor(sizeX / colWidth))
for i, v in ipairs(list) do
printInline(v.id)
printInline((" "):rep(colWidth - #v))
if i % numCols == 0 then print("") end
end
if #list % numCols ~= 0 then print("") end
end
local function checkRepo(repo)
local resp = get(repo)
if not resp then return false end
if resp.code ~= 200 then
return false
end
local repojson = json.decode(resp.body)
if not repojson then
return false
end
return true, repojson
end
local function getRepos(root)
local repos = {}
local visited = {}
local queue = table.values(root)
local head = 1
while head <= #queue do
local url = queue[head]
head = head + 1
if not visited[url] then
visited[url] = true
print("GET:"..tostring(head-1).." "..url)
local ok, repo = checkRepo(url)
if ok then
table.insert(repos, url)
if repo.refs then
for _, ref in ipairs(repo.refs) do
if not visited[ref] then
queue[#queue + 1] = ref
end
end
end
end
end
end
return repos
end
return repos
end
local function getPkg(name)
for _, url in ipairs(repodb) do
local ok, repo = checkRepo(url)
if not ok then
print("Failed to contact repository:")
print(url)
print("Consider running 'spm -Sy' to remove broken repositories.")
else
if repo.packages then
if repo.packages[name] then
local pkgurl = repo.packages[name]
local resp = get(pkgurl)
if not resp then
print("Repository: "..url)
print("Has broken package: "..name)
print("Ignoring")
else
if resp.code ~= 200 then
print("Repository: "..url)
print("Has broken package: "..name)
print("Ignoring")
else
local pkgjson = json.decode(resp.body)
if not pkgjson then
print("Repository: "..url)
print("Has broken package: "..name)
print("Ignoring")
else
return {
url=pkgurl,
tar=pkgjson.tarball,
hash=pkgjson.hash,
id=pkgjson.id,
deps=pkgjson.dependencies,
desc=pkgjson.description,
auth=pkgjson.authors,
ver=pkgjson.version
}
end
end
end
end
end
end
end
return false, "target not found: "..name
end
local function getPkg(name)
end
if cloptions.S then
if cloptions.y then
print("Updating repositories...")
repodb={}
for _,v in ipairs(getRepos(sourcelist)) do
repodb[#repodb+1]=v
end
print("Writing new data...")
local str = ""
for _,v in ipairs(repodb) do
str=str..v.."\n"
end
fs.writeAllText("var/spm/db/repos.list", str)
end
if cloptions.i then
-- info remote
end
local needed = {}
local found = {}
for i,v in ipairs(args) do
local pkg, err = getPkg(v)
if not pkg then print(err) syscall.exit(1) end
needed[#needed+1] = pkg
found[v]=true
end
if cloptions.u then
for i,v in ipairs(table.keys(pkgdb)) do
local pkg, err = getPkg(v)
if pkg then
needed[#needed+1] = pkg
found[v]=true
end
end
end
local head = 0
while head<#needed do
head=head+1
for i,v in ipairs(needed[head].deps) do
if not found[v] and not pkgdb[v] then
local pkg, err = getPkg(v)
if pkg then
needed[#needed+1] = pkg
found[v]=true
end
end
end
end
if #needed==0 then
syscall.exit()
end
print("")
print("Packages("..tostring(#needed).."):")
printpkgs(needed)
print("")
while true do
local text = userinput(0, "Proceed with download [Y/n]", nil, 1)
if text=="" then
break
elseif text:lower()=="y" then
break
elseif text:lower()=="n" then
syscall.exit()
end
end
if args[1] == "install" then
local pkg=getPkg(args[2])
elseif args[1] == "sync" then
error("not implemented")
elseif args[1] == "remove" then
error("not implemented")
local files = {}
for _,v in ipairs(needed) do
local file = http.get(v.tar)
end
elseif cloptions.R then
-- remove tree
elseif cloptions.Q then
if cloptions.l then
-- list installed files
elseif cloptions.o then
-- get package file arg[1] is from
elseif cloptions.e then
-- list installed manually
elseif cloptions.i then
-- list installed info
elseif cloptions.t then
-- list orphans
end
end
end
local ok,err = xpcall(spm, debug.traceback, ...)
if not ok then
print("Fatal error in spm:")
print(err)
end
+4
View File
@@ -0,0 +1,4 @@
tar
deflate
json
minify
+2
View File
@@ -0,0 +1,2 @@
Hyperion
Astronand
View File
+201
View File
@@ -0,0 +1,201 @@
local LibDeflate = require("LibDeflate")
local tar = {}
local function pad512(n)
return math.ceil(n / 512) * 512
end
local function octal(n, size)
local s = string.format("%0" .. (size - 1) .. "o", n)
return s .. "\0"
end
local function header(name, size, typeflag, link)
local h = string.rep("\0", 512)
local function put(pos, len, value)
h = h:sub(1, pos - 1) ..
value ..
h:sub(pos + len)
end
put(1, 100, name)
put(101, 8, octal(0, 8))
put(109, 8, octal(0, 8))
put(117, 8, octal(0, 8))
put(125, 12, octal(size, 12))
put(137, 12, octal(os.time(), 12))
put(149, 8, " ")
put(157, 1, typeflag)
if link then
put(158, 100, link)
end
put(257, 6, "ustar\0")
put(263, 2, "00")
local sum = 0
for i = 1, 512 do
sum = sum + h:byte(i)
end
put(149, 8, string.format("%06o\0 ", sum))
return h
end
local function add(out, node, prefix)
local path = prefix and (prefix .. "/" .. node.name)
or node.name
if node.type == "dir" then
table.insert(out, header(path .. "/", 0, "5"))
for _, child in ipairs(node.content) do
add(out, child, path)
end
elseif node.type == "file" then
local data = node.content or ""
table.insert(out, header(path, #data, "0"))
table.insert(out, data)
local padding = pad512(#data) - #data
if padding > 0 then
table.insert(out, string.rep("\0", padding))
end
elseif node.type == "symlink" then
table.insert(out,
header(path, 0, "2", node.content)
)
end
end
function tar.pack(tree, gzip)
local out = {}
add(out, tree)
table.insert(out, string.rep("\0", 1024))
local data = table.concat(out)
if gzip then
return LibDeflate:CompressGzip(data)
end
return data
end
local function read_octal(s)
s = s:gsub("\0.*", "")
return tonumber(s, 8) or 0
end
function tar.unpack(data)
if data:sub(1,2) == "\31\139" then
data = assert(LibDeflate:DecompressGzip(data))
end
local root = {
type = "dir",
name = "",
content = {}
}
local function add_path(parts, node)
local current = root
for i = 1, #parts - 1 do
local name = parts[i]
local found
for _, child in ipairs(current.content) do
if child.name == name and child.type == "dir" then
found = child
break
end
end
if not found then
found = {
type = "dir",
name = name,
content = {}
}
table.insert(current.content, found)
end
current = found
end
table.insert(current.content, node)
end
local pos = 1
while pos + 511 <= #data do
local h = data:sub(pos, pos + 511)
if h == string.rep("\0", 512) then
break
end
local name = h:sub(1,100):gsub("\0.*", "")
local size = read_octal(h:sub(125,136))
local kind = h:sub(157,157)
local skip = false
-- Ignore PAX headers and GNU long filename records
if kind == "x" or kind == "g" or kind == "L" then
skip = true
end
if not skip then
local node
if kind == "5" then
node = {
type = "dir",
name = name:gsub("/$", ""),
content = {}
}
elseif kind == "2" then
node = {
type = "symlink",
name = name,
content =
h:sub(158,257):gsub("\0.*", "")
}
else
node = {
type = "file",
name = name,
content =
data:sub(pos + 512,
pos + 511 + size)
}
end
local parts = {}
for part in node.name:gmatch("[^/]+") do
table.insert(parts, part)
end
add_path(parts, node)
end
pos = pos + 512 + pad512(size)
end
return root
end
return tar
View File
View File