7 Commits

Author SHA1 Message Date
17453983ad Path traversal fixes, 26_tty removal, ctrl+key fixes 2026-03-01 00:21:02 -06:00
a6550aa069 fixed minify header and build script 2026-02-25 11:41:41 -05:00
02e7b3897c fixed tty naming 2026-02-25 09:03:32 -05:00
34b89a8e34 fixed build script 2026-02-25 08:04:53 -05:00
0eabfebd0f fixed new ghxx exploit 2026-02-25 08:01:28 -05:00
5b2e5eac65 added more libs and fixed build script 2026-02-24 17:54:09 -05:00
415064480a Patch the AsyncSyscall v4 exploit from working 2026-02-24 02:00:37 -06:00
27 changed files with 993 additions and 600 deletions

View File

@@ -1,6 +1,6 @@
--:Minify:--
syscall.open("/dev/tty/tty1","r") --stdin (Device 0)
syscall.open("/dev/tty/tty1","w") --stdout (Device 1)
syscall.open("/dev/tty/1","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()
@@ -810,6 +810,160 @@ builtinCmds.df = function(...)
end
end
local function listDir(dir, prefix)
local ok, entries = pcall(syscall.listdir, dir)
if not ok or not entries then return {} end
local results = {}
for _, e in ipairs(entries) do
if prefix == "" or e:sub(1, #prefix) == prefix then
local fullpath = (dir == "/" and "/" or dir.."/")..e
local t = syscall.type(fullpath)
results[#results+1] = t == "directory" and (e.."/") or e
end
end
table.sort(results)
return results
end
local function listCommands(prefix)
local results = {}
local seen = {}
for name in pairs(builtinCmds) do
if prefix == "" or name:sub(1, #prefix) == prefix then
if not seen[name] then results[#results+1] = name; seen[name] = true end
end
end
local paths = string.split(syscall.getEnviron("PATH") or "/bin/", ":")
for _, p in ipairs(paths) do
local ok, entries = pcall(syscall.listdir, p)
if ok and entries then
for _, e in ipairs(entries) do
local fullpath = (p:sub(-1)=="/" and p or p.."/")..e
local xok = pcall(syscall.access, fullpath, "x")
if xok and (prefix == "" or e:sub(1, #prefix) == prefix) then
if not seen[e] then results[#results+1] = e; seen[e] = true end
end
end
end
end
table.sort(results)
return results
end
local function commonPrefix(list)
if #list == 0 then return "" end
local pre = list[1]
for i = 2, #list do
local s = list[i]
local j = 1
while j <= #pre and j <= #s and pre:sub(j,j) == s:sub(j,j) do j = j+1 end
pre = pre:sub(1, j-1)
if pre == "" then return "" end
end
return pre
end
local function showCompletions(completions)
syscall.write(1, "\n")
local W = 51
local maxlen = 0
for _, c in ipairs(completions) do if #c > maxlen then maxlen = #c end end
local colw = maxlen + 2
local cols = math.max(1, math.floor(W / colw))
local i = 0
for _, c in ipairs(completions) do
local padded = c .. string.rep(" ", colw - #c)
syscall.write(1, padded)
i = i + 1
if i % cols == 0 then syscall.write(1, "\n") end
end
if i % cols ~= 0 then syscall.write(1, "\n") end
end
local function parseForCompletion(input, cursorPos)
local sofar = input:sub(1, cursorPos - 1)
local tokens = {}
for tok in sofar:gmatch("%S+") do tokens[#tokens+1] = tok end
local partial = sofar:match("%S+$") or ""
local isFirst = (#tokens == 0) or (sofar:sub(-1) ~= " " and #tokens == 1)
local partialDir, partialBase
if partial:find("/") then
partialDir = partial:match("^(.*/)") or "/"
partialBase = partial:match("[^/]*$") or ""
else
partialDir = nil
partialBase = partial
end
return tokens, partial, isFirst, partialDir, partialBase
end
local tabState = { last = nil, idx = 0, list = {} }
local function doTabComplete(input, cursorPos)
local tokens, partial, isFirst, partialDir, partialBase = parseForCompletion(input, cursorPos)
local candidates
if isFirst then
if partialDir then
local dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
candidates = listDir(dir, partialBase)
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
else
candidates = listCommands(partialBase)
end
else
local dir, base
if partialDir then
dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
base = partialBase
else
dir = syscall.getcwd()
base = partialBase
end
candidates = listDir(dir, base)
if partialDir then
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
end
end
if #candidates == 0 then
return input, cursorPos, false
end
local context = input.."\0"..tostring(cursorPos)
if tabState.last ~= context then
tabState.last = context
tabState.idx = 0
tabState.list = candidates
end
if #candidates == 1 then
local completed = candidates[1]
local before = input:sub(1, cursorPos - 1 - #partial)
local after = input:sub(cursorPos)
local newInput = before .. completed .. after
local newCursor = #before + #completed + 1
tabState.last = nil
return newInput, newCursor, true
end
local pre = commonPrefix(candidates)
if #pre > #partial then
local before = input:sub(1, cursorPos - 1 - #partial)
local after = input:sub(cursorPos)
local newInput = before .. pre .. after
local newCursor = #before + #pre + 1
tabState.last = newInput.."\0"..tostring(newCursor)
tabState.list = candidates
return newInput, newCursor, true
else
showCompletions(candidates)
return input, cursorPos, true
end
end
local function getUserInput()
syscall.devctl(1,"sfgc",3)
syscall.write(1, userhost)
@@ -829,6 +983,41 @@ local function getUserInput()
local history = 0
local dirty = true
local function getGhostSuffix()
if #input == 0 then return "" end
local _, partial, isFirst, partialDir, partialBase = parseForCompletion(input, cursorPos)
if cursorPos ~= #input + 1 then return "" end
local candidates
if isFirst then
if partialDir then
local dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
candidates = listDir(dir, partialBase)
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
else
candidates = listCommands(partialBase)
end
else
local dir, base
if partialDir then
dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
base = partialBase
else
dir = syscall.getcwd()
base = partialBase
end
candidates = listDir(dir, base)
if partialDir then
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
end
end
if #candidates == 0 then return "" end
local pre = commonPrefix(candidates)
if #pre > #partial then
return pre:sub(#partial + 1)
end
return ""
end
local function redraw()
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, string.sub(input, 1, cursorPos-1))
@@ -841,21 +1030,31 @@ local function getUserInput()
syscall.write(1, string.sub(input, cursorPos, cursorPos))
end
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
syscall.write(1, string.sub(input, cursorPos+1) .. " ")
local after = string.sub(input, cursorPos+1)
syscall.write(1, after)
local ghost = getGhostSuffix()
if #ghost > 0 then
syscall.devctl(1,"sfgc",14)
syscall.write(1, ghost)
syscall.devctl(1,"sfgc",1)
syscall.write(1, " ")
else
syscall.write(1, " ")
end
end
while true do
local key = syscall.read(0)
if key and key ~= "" then
if key=="\19" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end
elseif key=="\20" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end
elseif key=="\17" then
if key=="" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end
elseif key=="" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end
elseif key=="" then
if history<#commandHistory then
history=history+1
input=commandHistory[#commandHistory-history+1]
cursorPos=#input+1;dirty=true
end
elseif key=="\18" then
elseif key=="" then
if history>1 then
history=history-1
input=commandHistory[#commandHistory-history+1]
@@ -863,6 +1062,38 @@ local function getUserInput()
elseif history==1 then
history=0;input="";cursorPos=1;dirty=true
end
elseif key=="" then cursorPos=1;dirty=true
elseif key=="" then cursorPos=#input+1;dirty=true
elseif key=="[3~" then
if cursorPos<=#input then
input=string.sub(input,1,cursorPos-1)..string.sub(input,cursorPos+1)
dirty=true
end
elseif key=="\t" then
local newInput, newCursor, needsRedraw = doTabComplete(input, cursorPos)
if needsRedraw then
input = newInput; cursorPos = newCursor
local posStr = syscall.devctl(1, "gpos")
local sep = posStr:find(";")
local px = tonumber(posStr:sub(1, sep-1))
local py = tonumber(posStr:sub(sep+1))
if px > 1 then
syscall.devctl(1,"spos",1,py)
local tsz = syscall.devctl(1,"size") or "51;19"
local tw = tonumber(tsz:match("^(%d+)")) or 51
syscall.write(1, string.rep(" ", tw))
syscall.devctl(1,"spos",1,py)
end
syscall.devctl(1,"sfgc",3); syscall.write(1, userhost)
syscall.devctl(1,"sfgc",1); syscall.write(1, ":")
syscall.devctl(1,"sfgc",10); syscall.write(1, syscall.getcwd())
syscall.devctl(1,"sfgc",1); syscall.write(1, "$ ")
posStr = syscall.devctl(1, "gpos")
sep = posStr:find(";")
curOffsetX = tonumber(posStr:sub(1, sep-1))
curOffsetY = tonumber(posStr:sub(sep+1))
dirty = true
end
elseif key=="\b" then
if cursorPos>1 then
input=string.sub(input,1,cursorPos-2)..string.sub(input,cursorPos)
@@ -873,7 +1104,7 @@ local function getUserInput()
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, input.." \n")
return input
else
elseif #key == 1 and key:byte(1) >= 32 and key:byte(1) < 127 then
input=string.sub(input,1,cursorPos-1)..key..string.sub(input,cursorPos)
cursorPos=cursorPos+1;dirty=true
end
@@ -946,23 +1177,16 @@ local function runCommand(command)
return
end
local text = fs.readAllText(cmdPath)
local program, err = load(text, progName)
if not program then
syscall.devctl(1,"sfgc",2)
local line, rest = tostring(err):match(":(%d+): (.+)$")
if line then printInline(progName..": load error on line "..line..": "); print(rest)
else print(progName..": load error: "..tostring(err)) end
syscall.devctl(1,"sfgc",1); return
end
local proc = syscall.spawn(function(...)
syscall.open("/dev/tty/tty1","r")
syscall.open("/dev/tty/tty1","w")
syscall.open("/dev/null","w")
local ok2, msg = pcall(program, ...)
if not ok2 then printError(progName, msg) end
end, progName, nil, {table.unpack(args, 2)})
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/tty/1", "w") -- fd 1 stdout
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.
syscall.exec(cmdPath, {table.unpack(args, 2)})
end, progName)
while true do
local exited, code = syscall.collect(proc)

View File

@@ -1,6 +1,6 @@
--:Minify:--
syscall.open("/dev/tty/tty1", "r") --stdin (fd 0)
syscall.open("/dev/tty/tty1", "w") --stdout (fd 1)
syscall.open("/dev/tty/1", "r") --stdin (fd 0)
syscall.open("/dev/tty/1", "w") --stdout (fd 1)
syscall.open("/dev/null", "w") --stderr (fd 2)

View File

@@ -223,7 +223,7 @@ local function prompt(label, default)
tbg(16); tfg(1)
local key = syscall.read(0)
if not key or key == "" then sleep(0.02)
elseif key == "\27" then return nil
elseif key == "" then return nil
elseif key == "\n" then return inp
elseif key == "\b" then if #inp > 0 then inp = inp:sub(1,-2) end
else
@@ -359,31 +359,29 @@ while running do
local key = syscall.read(0)
if key and key ~= "" then
local b = key:byte(1)
if key == "\17" then moveCursorUp(map); dirty=true
elseif key == "\18" then moveCursorDown(map); dirty=true
elseif key == "\19" then
if cx > 1 then cx=cx-1
elseif cy > 1 then cy=cy-1; cx=#lines[cy]+1 end
dirty=true
elseif key == "\20" then
if key == "" then moveCursorUp(map); dirty=true
elseif key == "" then moveCursorDown(map); dirty=true
elseif key == "" then
if cx <= #lines[cy] then cx=cx+1
elseif cy < #lines then cy=cy+1; cx=1 end
dirty=true
elseif key == "" then
if cx > 1 then cx=cx-1
elseif cy > 1 then cy=cy-1; cx=#lines[cy]+1 end
dirty=true
elseif key == "" then cx=1; dirty=true
elseif key == "" then cx=#lines[cy]+1; dirty=true
elseif key == "[5~" then for _=1,ROWS do moveCursorUp(map) end; dirty=true
elseif key == "[6~" then for _=1,ROWS do moveCursorDown(map) end; dirty=true
elseif key == "[3~" then delRight()
elseif key == "\n" then newline()
elseif key == "\b" then delLeft()
elseif key == "\t" then for _=1,4 do insChar(" ") end
elseif b == 1 then cx=1; dirty=true
elseif b == 2 then
for _=1,ROWS do moveCursorUp(map) end; dirty=true
elseif b == 4 then delRight()
elseif b == 5 then cx=#lines[cy]+1; dirty=true
elseif b == 6 then
local p=prompt("Find: ",sPat); dirty=true
if p then sPat=p; sLine=0; findNext() end
elseif b == 7 then goToLine()
elseif b == 11 then cutLine()
elseif b == 12 then
for _=1,ROWS do moveCursorDown(map) end; dirty=true
elseif b == 14 then
if sPat=="" then
local p=prompt("Find: ",""); dirty=true

View File

388
Src/Hyperion-core/lib/json Normal file
View File

@@ -0,0 +1,388 @@
--:Minify:--
-- json.lua
--
-- Copyright (c) 2020 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
local json = { _version = "0.1.2" }
-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------
local encode
local escape_char_map = {
[ "\\" ] = "\\",
[ "\"" ] = "\"",
[ "\b" ] = "b",
[ "\f" ] = "f",
[ "\n" ] = "n",
[ "\r" ] = "r",
[ "\t" ] = "t",
}
local escape_char_map_inv = { [ "/" ] = "/" }
for k, v in pairs(escape_char_map) do
escape_char_map_inv[v] = k
end
local function escape_char(c)
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
end
local function encode_nil(val)
return "null"
end
local function encode_table(val, stack)
local res = {}
stack = stack or {}
-- Circular reference?
if stack[val] then error("circular reference") end
stack[val] = true
if rawget(val, 1) ~= nil or next(val) == nil then
-- Treat as array -- check keys are valid and it is not sparse
local n = 0
for k in pairs(val) do
if type(k) ~= "number" then
error("invalid table: mixed or invalid key types")
end
n = n + 1
end
if n ~= #val then
error("invalid table: sparse array")
end
-- Encode
for i, v in ipairs(val) do
table.insert(res, encode(v, stack))
end
stack[val] = nil
return "[" .. table.concat(res, ",") .. "]"
else
-- Treat as an object
for k, v in pairs(val) do
if type(k) ~= "string" then
error("invalid table: mixed or invalid key types")
end
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
end
stack[val] = nil
return "{" .. table.concat(res, ",") .. "}"
end
end
local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end
local function encode_number(val)
-- Check for NaN, -inf and inf
if val ~= val or val <= -math.huge or val >= math.huge then
error("unexpected number value '" .. tostring(val) .. "'")
end
return string.format("%.14g", val)
end
local type_func_map = {
[ "nil" ] = encode_nil,
[ "table" ] = encode_table,
[ "string" ] = encode_string,
[ "number" ] = encode_number,
[ "boolean" ] = tostring,
}
encode = function(val, stack)
local t = type(val)
local f = type_func_map[t]
if f then
return f(val, stack)
end
error("unexpected type '" .. t .. "'")
end
function json.encode(val)
return ( encode(val) )
end
-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------
local parse
local function create_set(...)
local res = {}
for i = 1, select("#", ...) do
res[ select(i, ...) ] = true
end
return res
end
local space_chars = create_set(" ", "\t", "\r", "\n")
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals = create_set("true", "false", "null")
local literal_map = {
[ "true" ] = true,
[ "false" ] = false,
[ "null" ] = nil,
}
local function next_char(str, idx, set, negate)
for i = idx, #str do
if set[str:sub(i, i)] ~= negate then
return i
end
end
return #str + 1
end
local function decode_error(str, idx, msg)
local line_count = 1
local col_count = 1
for i = 1, idx - 1 do
col_count = col_count + 1
if str:sub(i, i) == "\n" then
line_count = line_count + 1
col_count = 1
end
end
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end
local function codepoint_to_utf8(n)
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
local f = math.floor
if n <= 0x7f then
return string.char(n)
elseif n <= 0x7ff then
return string.char(f(n / 64) + 192, n % 64 + 128)
elseif n <= 0xffff then
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
elseif n <= 0x10ffff then
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
f(n % 4096 / 64) + 128, n % 64 + 128)
end
error( string.format("invalid unicode codepoint '%x'", n) )
end
local function parse_unicode_escape(s)
local n1 = tonumber( s:sub(1, 4), 16 )
local n2 = tonumber( s:sub(7, 10), 16 )
-- Surrogate pair?
if n2 then
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
else
return codepoint_to_utf8(n1)
end
end
local function parse_string(str, i)
local res = ""
local j = i + 1
local k = j
while j <= #str do
local x = str:byte(j)
if x < 32 then
decode_error(str, j, "control character in string")
elseif x == 92 then -- `\`: Escape
res = res .. str:sub(k, j - 1)
j = j + 1
local c = str:sub(j, j)
if c == "u" then
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
or str:match("^%x%x%x%x", j + 1)
or decode_error(str, j - 1, "invalid unicode escape in string")
res = res .. parse_unicode_escape(hex)
j = j + #hex
else
if not escape_chars[c] then
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
end
res = res .. escape_char_map_inv[c]
end
k = j + 1
elseif x == 34 then -- `"`: End of string
res = res .. str:sub(k, j - 1)
return res, j + 1
end
j = j + 1
end
decode_error(str, i, "expected closing quote for string")
end
local function parse_number(str, i)
local x = next_char(str, i, delim_chars)
local s = str:sub(i, x - 1)
local n = tonumber(s)
if not n then
decode_error(str, i, "invalid number '" .. s .. "'")
end
return n, x
end
local function parse_literal(str, i)
local x = next_char(str, i, delim_chars)
local word = str:sub(i, x - 1)
if not literals[word] then
decode_error(str, i, "invalid literal '" .. word .. "'")
end
return literal_map[word], x
end
local function parse_array(str, i)
local res = {}
local n = 1
i = i + 1
while 1 do
local x
i = next_char(str, i, space_chars, true)
-- Empty / end of array?
if str:sub(i, i) == "]" then
i = i + 1
break
end
-- Read token
x, i = parse(str, i)
res[n] = x
n = n + 1
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "]" then break end
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
end
return res, i
end
local function parse_object(str, i)
local res = {}
i = i + 1
while 1 do
local key, val
i = next_char(str, i, space_chars, true)
-- Empty / end of object?
if str:sub(i, i) == "}" then
i = i + 1
break
end
-- Read key
if str:sub(i, i) ~= '"' then
decode_error(str, i, "expected string for key")
end
key, i = parse(str, i)
-- Read ':' delimiter
i = next_char(str, i, space_chars, true)
if str:sub(i, i) ~= ":" then
decode_error(str, i, "expected ':' after key")
end
i = next_char(str, i + 1, space_chars, true)
-- Read value
val, i = parse(str, i)
-- Set
res[key] = val
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "}" then break end
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
end
return res, i
end
local char_func_map = {
[ '"' ] = parse_string,
[ "0" ] = parse_number,
[ "1" ] = parse_number,
[ "2" ] = parse_number,
[ "3" ] = parse_number,
[ "4" ] = parse_number,
[ "5" ] = parse_number,
[ "6" ] = parse_number,
[ "7" ] = parse_number,
[ "8" ] = parse_number,
[ "9" ] = parse_number,
[ "-" ] = parse_number,
[ "t" ] = parse_literal,
[ "f" ] = parse_literal,
[ "n" ] = parse_literal,
[ "[" ] = parse_array,
[ "{" ] = parse_object,
}
parse = function(str, idx)
local chr = str:sub(idx, idx)
local f = char_func_map[chr]
if f then
return f(str, idx)
end
decode_error(str, idx, "unexpected character '" .. chr .. "'")
end
function json.decode(str)
if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str))
end
local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true)
if idx <= #str then
decode_error(str, idx, "trailing garbage")
end
return res
end
return json

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
local apis = kernel.apis
local native = apis.peripheral
@@ -317,7 +317,18 @@ kernel.processes.cctmond = function()
local eventType = event[1]
local charOrKey = event[3]
-- Update modifier keys
local ctrlKeyMap = {
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
[apis.keys.y]=25, [apis.keys.z]=26,
}
if eventType == "keyPressed" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = true
@@ -325,11 +336,31 @@ kernel.processes.cctmond = function()
alt = true
end
-- Handle Ctrl+C
if ctrl and charOrKey == apis.keys.c then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1) -- SIGINT
if ctrl then
local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1)
end
else
fifo.push(string.char(ctrlByte))
end
end
else
local specialKeyMap = {
[apis.keys.up] = "",
[apis.keys.down] = "",
[apis.keys.right] = "",
[apis.keys.left] = "",
[apis.keys.home] = "",
[apis.keys["end"]] = "",
[apis.keys.pageUp] = "[5~",
[apis.keys.pageDown] = "[6~",
[apis.keys.delete] = "[3~",
}
local special = specialKeyMap[charOrKey]
if special then fifo.push(special) end
end
elseif eventType == "keyReleased" then
@@ -354,10 +385,10 @@ kernel.processes.cctmond = function()
end
end
newtty(apis.term, "TTY1", fifo.pop)
newtty(apis.term, "1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5)
v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end)
newtty(v,tostring(i+1),function () end)
end

View File

@@ -8,7 +8,7 @@ local computer = args[6]
local ifs = args[7]
local kernel = {}
kernel.LOG_Text=""
kernel.version="HyperionOS V1.2.0"
kernel.version="HyperionOS V1.2.3"
kernel.process = "Kernel"
kernel.users={[0]="root",[1]="User"}
kernel.hostname = "hyperion"

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
kernel.allowGlobalOverwrites = true
@@ -207,17 +207,30 @@ function toHex(num)
return string.format("%X", num)
end
syscall = setmetatable({}, {
__index = function(self, name)
return function(...)
local res = table.pack(coroutine.yield("syscall", name, ...))
if res[1] then
return table.unpack(res, 2, res.n)
else
error(res[2], 2)
local function makeSyscallProxy()
local backing = {}
return setmetatable(backing, {
__index = function(self, name)
local raw = rawget(self, name)
if raw ~= nil then return raw end
return function(...)
local res = table.pack(coroutine.yield("syscall", name, ...))
if res[1] then
return table.unpack(res, 2, res.n)
else
error(res[2], 2)
end
end
end
end
})
end,
__newindex = function(self, k, v)
rawset(self, k, v)
end,
__metatable=false
})
end
syscall = makeSyscallProxy()
_makeSyscallProxy = makeSyscallProxy
table.serialize = serialize

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
local vfs = {}
kernel.vfs = vfs
@@ -113,57 +113,23 @@ end
local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$"
local function normalizePath(path)
local task = kernel.currentTask
local cwd = task.cwd or "/"
if path:sub(1, 1) ~= "/" then
path = cwd .. "/" .. path
local function tokenizePath(path)
local isAbsolute = (path:sub(1,1) == "/")
local tokens = {}
for comp in (path .. "/"):gmatch("([^/]*)/") do
table.insert(tokens, comp)
end
return isAbsolute, tokens
end
local stack = {}
local i = 1
local len = #path
while i <= len do
local j = path:find("/", i, true)
local comp
if j then
comp = path:sub(i, j - 1)
i = j + 1
else
comp = path:sub(i)
i = len + 1
end
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
if #stack > 0 then
table.remove(stack)
end
else
comp = comp:lower()
if not comp:match(SAFE_COMPONENT_PATTERN) then
error("EINVAL: illegal characters in path component: " .. comp, 2)
end
if comp == ".meta" then
error("EINVAL: reserved path component: " .. comp, 2)
end
table.insert(stack, comp)
end
local function validateComponent(comp)
local lower = comp:lower()
if not lower:match(SAFE_COMPONENT_PATTERN) then
error("EINVAL: illegal characters in path component: " .. comp, 3)
end
local result = "/" .. table.concat(stack, "/")
local root = task and task.root
if root and root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root
end
if lower == ".meta" then
error("EINVAL: reserved path component: .meta", 3)
end
return result
end
function vfs.splitPath(path)
@@ -222,47 +188,205 @@ local function readMetaEntry(disk, parentDiskPath, filename)
end
local MAX_SYMLINK = 16
local function resolveSymlinks(path, noFollow, _depth)
_depth = _depth or 0
if _depth > MAX_SYMLINK then error("ELOOP") end
path = normalizePath(path)
local parts = {}
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
local function namei(path, noFollow, symDepth)
symDepth = symDepth or 0
if symDepth > MAX_SYMLINK then error("ELOOP") end
local resolved = ""
local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid
local groups = (task and task.groups) or kernel.groups or {}
local root = (task and task.root) or "/"
local cwd = (task and task.cwd) or "/"
for i, part in ipairs(parts) do
local candidate = resolved == "" and ("/" .. part) or (resolved .. "/" .. part)
if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
if noFollow and i == #parts then
resolved = candidate
break
end
local disk, parentDisk = resolveMount(resolved == "" and "/" or resolved)
local entry = readMetaEntry(disk, parentDisk, part)
if entry and entry.etype == 0x01 then
local target = entry.cmeta
if target:sub(1,1) ~= "/" then
target = (resolved == "" and "/" or resolved) .. "/" .. target
local function canTraverse(entry)
if euid == 0 then return true end
if not entry then return true end
local bits = entry.perms
if euid == entry.owner and bit_is_set(bits, 9) then return true end
if entry.group then
for _, gid in ipairs(groups) do
if gid == entry.group and bit_is_set(bits, 8) then return true end
end
if i < #parts then
target = target .. "/" .. table.concat(parts, "/", i+1, #parts)
end
return resolveSymlinks(normalizePath(target), noFollow, _depth + 1)
end
resolved = candidate
return bit_is_set(bits, 7)
end
if resolved == "" then resolved = "/" end
return resolved
local isAbsolute, tokens = tokenizePath(path)
local stack = {}
if isAbsolute then
stack = {}
else
for seg in cwd:gmatch("[^/]+") do table.insert(stack, seg) end
end
local i = 1
while i <= #tokens do
local comp = tokens[i]
i = i + 1
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
local currentPath = "/" .. table.concat(stack, "/")
local jailStack = {}
if root ~= "/" then
for seg in root:gmatch("[^/]+") do table.insert(jailStack, seg) end
end
if #stack <= #jailStack then
stack = {}
for _, seg in ipairs(jailStack) do table.insert(stack, seg) end
else
local exitName = stack[#stack]
local parentPath = "/" .. table.concat(stack, "/", 1, #stack - 1)
if parentPath == "/" then parentPath = "/" end
local okM, diskM, dpM = pcall(resolveMount, parentPath == "" and "/" or parentPath)
if okM and diskM then
local entry = readMetaEntry(diskM, dpM, exitName)
if entry then
if entry.etype ~= 0x00 then
error("ENOTDIR: not a directory: " .. currentPath)
end
if not canTraverse(entry) then
error("EACCES: permission denied traversing " .. currentPath)
end
else
local okD, diskD, dpD = pcall(resolveMount, currentPath)
if okD and diskD then
local dtype = diskD:type(dpD)
if dtype ~= nil and dtype ~= "directory" then
error("ENOTDIR: not a directory: " .. currentPath)
end
end
end
end
table.remove(stack)
end
else
validateComponent(comp)
local lname = comp:lower()
local curPath = "/" .. table.concat(stack, "/")
local okM, diskM, dpM = pcall(resolveMount, curPath == "/" and "/" or curPath)
local entry = nil
if okM and diskM then
entry = readMetaEntry(diskM, dpM, lname)
end
local isFinal = (i > #tokens)
if entry and entry.etype == 0x01 then
if isFinal and noFollow then
table.insert(stack, lname)
else
symDepth = symDepth + 1
if symDepth > MAX_SYMLINK then error("ELOOP") end
local target = entry.cmeta
if not target or target == "" then
error("ENOENT: empty symlink target")
end
local symIsAbs, symTokens = tokenizePath(target)
if symIsAbs then
stack = {}
if root ~= "/" then
for seg in root:gmatch("[^/]+") do table.insert(stack, seg) end
end
end
local fresh = {}
for j = 1, i - 2 do table.insert(fresh, tokens[j]) end
local insertAt = #fresh + 1
for _, t in ipairs(symTokens) do table.insert(fresh, t) end
for j = i, #tokens do table.insert(fresh, tokens[j]) end
tokens = fresh
i = insertAt
end
else
table.insert(stack, lname)
if not isFinal then
local newPath = "/" .. table.concat(stack, "/")
local okD, diskD, dpD = pcall(resolveMount, newPath)
if okD and diskD then
local dtype = diskD:type(dpD)
if dtype ~= nil and dtype ~= "directory" then
error("ENOTDIR: not a directory: " .. newPath)
end
end
if not canTraverse(entry) then
error("EACCES: permission denied traversing " .. newPath)
end
end
end
end
end
local result = "/" .. table.concat(stack, "/")
if root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root
end
end
return result
end
local function normalizePath(path)
local task = kernel.currentTask
local cwd = (task and task.cwd) or "/"
local root = (task and task.root) or "/"
if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
local isAbsolute, tokens = tokenizePath(path)
local stack = {}
if not isAbsolute then
for seg in cwd:gmatch("[^/]+") do table.insert(stack, seg) end
end
local jailStack = {}
if root ~= "/" then
for seg in root:gmatch("[^/]+") do table.insert(jailStack, seg) end
end
for _, comp in ipairs(tokens) do
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
if #stack > #jailStack then
table.remove(stack)
end
else
table.insert(stack, comp:lower())
end
end
local result = "/" .. table.concat(stack, "/")
if root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root
end
end
return result
end
local function resolvePath(path, noFollow)
local real = resolveSymlinks(path, noFollow)
local real = namei(path, noFollow)
local disk, diskPath = resolveMount(real)
if kernel.config.logPathResolution then
kernel.log("resolvePath '"..path.."' -> '"..real.."' diskPath '"..diskPath.."'")
@@ -271,24 +395,23 @@ local function resolvePath(path, noFollow)
end
local function getFileMeta(path, noFollow)
local real = resolveSymlinks(path, noFollow)
local real = namei(path, noFollow)
local parts = {}
for p in real:gmatch("[^/]+") do table.insert(parts, p) end
if real == "/" then
return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
end
local default = { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
if #parts == 0 then return default end
local parent, name = real:match("^(.*)/([^/]+)$")
if not parent or parent == "" then parent = "/" end
local parentNorm = "/" .. table.concat(parts, "/", 1, #parts - 1)
if parentNorm == "" then parentNorm = "/" end
local disk, parentDiskPath = resolveMount(parentNorm)
local entry = readMetaEntry(disk, parentDiskPath, parts[#parts])
local disk, parentDiskPath = resolveMount(parent)
local entry = readMetaEntry(disk, parentDiskPath, name)
if entry then return entry end
return default
return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
end
local function writeMetaEntry(path, name, entry, noFollow)
local real = resolveSymlinks(path, noFollow)
local real = namei(path, noFollow)
local disk, diskPath = resolveMount(real)
local mp
@@ -668,7 +791,7 @@ function vfs.mkdir(path)
end
function vfs.remove(path)
local norm = resolveSymlinks(path, true)
local norm = namei(path, true)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
@@ -681,7 +804,7 @@ function vfs.remove(path)
end
if meta.etype == 0x01 then
local norm = resolveSymlinks(path, true)
local norm = namei(path, true)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local name = norm:match("[^/]+$")
@@ -747,7 +870,7 @@ function vfs.access(path, mode)
end
local function updateMeta(path, fn, noFollow)
local real = resolveSymlinks(path, noFollow)
local real = namei(path, noFollow)
local norm = real
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
local cache = {}
kernel.searchpaths = {

View File

@@ -1,3 +1,4 @@
--:Minify:--
local kernel = ...
local proxy = {}

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
-- Loop device driver:
--
-- BIND (directory) - re-routes VFS calls into a host directory subtree.

View File

@@ -1,22 +0,0 @@
---- :Minify:--
--local kernel = ...
--
--local timeout = false
--kernel.processes.keventd = function()
-- while true do
-- local event = {kernel.computer:getMachineEvent()}
-- if event[1] then
-- if event[1] == "keyTyped" then
-- if event[3] == "\x1b^s" then
-- kernel.shutdown()
-- elseif event[3] == "\x1b^r" then
-- kernel.reboot()
-- end
-- end
-- timeout = false
-- else
-- timeout = true
-- end
-- if timeout then sleep(.05) end
-- end
--end

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
-- Supports:
-- AF_UNIX - local IPC via /var/run/*.sock paths
-- AF_INET - network sockets with three backends:

View File

@@ -1,395 +0,0 @@
-- :Minify:--
local kernel = ...
local apis = kernel.apis
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
local peripheral={}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
local icolors = {
[0x1] = 1, -- #000000
[0x2] = 2, -- #FFFFFF
[0x4] = 3, -- #FF0000
[0x8] = 4, -- #00FF00
[0x10] = 5, -- #0000FF
[0x20] = 6, -- #00FFFF
[0x40] = 7, -- #FF00FF
[0x80] = 8, -- #FFFF00
[0x100] = 9, -- #FF6D00
[0x200] = 10, -- #6DFF55
[0x400] = 11, -- #24FFFF
[0x800] = 12, -- #924900
[0x1000] = 13, -- #6D6D55
[0x2000] = 14, -- #DBDBAA
[0x4000] = 15, -- #6D00FF
[0x8000] = 16 -- #B6FF00
}
local colors = {
0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
local function write(text, term)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text: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)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
if x > w then
x = 1
y = y + 1
end
if y - 1 >= h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
kernel.devfs.data.tty={}
local ctrl,alt = false, false
local function serializeBool(bool)
if bool then
return "T"
else
return "F"
end
end
local function newtty(obj, id, ev)
kernel.devfs.data["tty"][id] = function(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
local h = {
read=function(amount)
local rv=""
for i=1, amount or 1 do
local event = {ev()}
if event[1] then
rv=rv..event[1]
end
end
if rv=="" then rv=nil end
return rv
end,
write=function(content)
write(content, obj)
end,
size=function()
local s={obj.getSize()}
return table.concat(s,";")
end,
clear=function()
obj.clear()
obj.setCursorPos(1,1)
end,
gpos=function()
local s={obj.getCursorPos()}
return table.concat(s,";")
end,
spos=function(x,y)
return obj.setCursorPos(x,y)
end,
sfgc=function(c)
return obj.setTextColor(colors[c])
end,
sbgc=function(c)
return obj.setBackgroundColor(colors[c])
end,
gfgc=function()
return icolors[obj.getTextColor()]
end,
gbgc=function()
return icolors[obj.getBackgroundColor()]
end,
gctrl=function()
return serializeBool(ctrl)..";"..serializeBool(alt)
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
end
local fifo = kernel.newFifo()
local ctrlLetterKeys = nil
local specialKeys = nil
local function buildKeyMaps()
if ctrlLetterKeys then return end
local k = apis.keys
ctrlLetterKeys = {}
local letters = {
{k.a,1},{k.b,2},{k.c,3},{k.d,4},{k.e,5},{k.f,6},{k.g,7},
{k.h,8}, {k.j,10},{k.k,11},{k.l,12},{k.m,13},
{k.n,14},{k.o,15},{k.p,16},
{k.u,21},{k.v,22},{k.w,23},{k.x,24},{k.y,25},{k.z,26},
}
for _, pair in ipairs(letters) do
ctrlLetterKeys[pair[1]] = string.char(pair[2])
end
specialKeys = {
[k.home] = "\1",
[k.delete] = "\4",
[k["end"]] = "\5",
[k.pageUp] = "\2",
[k.pageDown]= "\12",
}
end
kernel.processes.cctmond = function()
local timeout = false
while true do
local event = {kernel.computer:getMachineEvent()}
if event[1] then
local eventType = event[1]
local charOrKey = event[3]
buildKeyMaps()
if eventType == "keyPressed" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = true
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
alt = true
end
if ctrl and charOrKey == apis.keys.c then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1)
end
end
if ctrl and ctrlLetterKeys[charOrKey] then
fifo.push(ctrlLetterKeys[charOrKey])
end
if specialKeys[charOrKey] then
fifo.push(specialKeys[charOrKey])
end
elseif eventType == "keyReleased" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = false
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
alt = false
end
elseif eventType == "keyTyped" then
if charOrKey then fifo.push(charOrKey) end
end
timeout = false
else
timeout = true
end
if timeout then
sleep(0.05)
end
end
end
newtty(apis.term, "tty1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5)
v.write("Initializing...")
newtty(v,"tty"..tostring(i+1),function () end)
end

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local args = {...}
local kernel = args[1]
kernel._G = _G
@@ -55,3 +55,19 @@ local origLoad = load
kernel._U = readonly(kernel._G)
kernel._U._G = kernel._U
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
function kernel.freshUserEnv()
local locals = {}
locals.syscall = _makeSyscallProxy()
local env = setmetatable(locals, {
__index = kernel._U,
__newindex = function(_, k, v) rawset(locals, k, v) end,
__metatable=false
})
locals._G = env
locals.load = function(a, b, c, d) return origLoad(a, b, c, d or env) end
return env
end

View File

@@ -11,14 +11,16 @@ local function bit_is_set(num, bit)
return math.floor(num / (2 ^ bit)) % 2 == 1
end
local function loadExecutable(path, env)
local function loadExecutable(path)
kernel.vfs.access(path, "rx")
local fd = kernel.vfs.open(path, "r")
local data = kernel.vfs.read(fd, 1024 * 1024 * 4)
kernel.vfs.close(fd)
local func, err = load(data, "@" .. path, "t", env or kernel._U)
local env = kernel.freshUserEnv()
local func, err = load(data, "@" .. path, "t", env)
if not func then error("ENOEXEC: " .. tostring(err)) end
local meta = kernel.vfs.lstat(path)
@@ -92,7 +94,7 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
end
function sys.spawn(func, name, envars, args, tgid)
local caller = kernel.currentTask
local caller = kernel.currentTask
local real_uid = caller and caller.uid or kernel.uid
local eff_uid = caller and caller.euid or real_uid
return createTask(func, name, envars, args, tgid, real_uid, eff_uid)
@@ -357,10 +359,25 @@ function kernel.main()
if task.sigq and #task.sigq ~= 0 and task.sigh then
local coro = coroutine.create(task.sigh)
if kernel.config.preempt then
resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
else
coroutine.resume(coro, table.remove(task.sigq, 1))
local sigret = { coroutine.resume(coro, table.remove(task.sigq, 1)) }
while coroutine.status(coro) ~= "dead" do
if sigret[1] == false then break end
if sigret[2] == "syscall" then
local scname = sigret[3]
local sysret
if kernel.syscalls[scname] then
sysret = { xpcall(kernel.syscalls[scname], debug.traceback, table.unpack(sigret, 4)) }
else
sysret = { false, "Unknown syscall: " .. tostring(scname) }
end
if not sysret[1] then
sigret = { coroutine.resume(coro, false, sysret[2]) }
else
sigret = { coroutine.resume(coro, true, table.unpack(sysret, 2)) }
end
else
sigret = { coroutine.resume(coro) }
end
end
end

View File

@@ -1,3 +1,4 @@
--:Minify:--
local kernel=...
local sysc=kernel.syscalls
kernel.gpio={}

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
function print(...)
local args = {...}

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
kernel.log("Loading init system...")
kernel.log("InitPath: " .. kernel.config.initPath)

View File

@@ -1,8 +1,8 @@
-- :Minify:--
--:Minify:--
local kernel = ...
kernel.processes.login = function()
local ok, err = pcall(syscall.execspawn, "/bin/login", "login")
local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login")
if not ok then
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
end

View File

@@ -1,4 +1,4 @@
-- :Minify:--
--:Minify:--
local kernel = ...
local P = kernel.vfs.P

View File

@@ -1,2 +1,3 @@
--:Minify:--
local kernel = ...
kernel.allowGlobalOverwrites = false

View File

@@ -0,0 +1,6 @@
local args={...}
local json=require("json")
local http=require("http")
local rootRepo="https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/spm/spm.src"

View File

@@ -72,7 +72,7 @@ def process_root(src_root: Path, out_root: Path, minify: bool):
if minify and has_minify_header(src):
print(" > Minifying")
result = subprocess.run(
["luamin", "-f", str(src)],
["luamin.cmd", "-f", str(src)],
capture_output=True, text=True
)
if result.returncode != 0:
@@ -88,17 +88,8 @@ def process_root(src_root: Path, out_root: Path, minify: bool):
def install_bootloader(arch: str, release: bool):
boot_dir = BUILD_ROOT / "$" / ARCH_BOOT_DIR[arch]
boot_lua = boot_dir / "boot.lua"
eeprom = boot_dir / "eeprom"
for src in (boot_lua, eeprom):
if not src.exists():
print(f" ! Bootloader file not found: {src}", file=sys.stderr)
sys.exit(1)
print(f" Installing: boot.lua -> Build/boot.lua")
shutil.copy2(boot_lua, BUILD_ROOT / "boot.lua")
eeprom_dst_name = "startup.lua" if release else "eeprom"
print(f" Installing: eeprom -> Build/{eeprom_dst_name}")
shutil.copy2(eeprom, BUILD_ROOT / eeprom_dst_name)
@@ -203,7 +194,7 @@ def _make_firstboot_kmod(users):
lines.append("do")
lines.append(" local ok, err = pcall(function()")
lines.append(" kernel.vfs.remove('/lib/modules/hyperion/50_firstboot_users.kmod')")
lines.append(" kernel.vfs.remove('/lib/modules/Hyperion/50_firstboot_users.kmod')")
lines.append(" end)")
lines.append(" if not ok then")
lines.append(" kernel.log('FIRSTBOOT: could not self-delete: ' .. tostring(err), 'WARN')")
@@ -215,7 +206,7 @@ def _make_firstboot_kmod(users):
def inject_makeusers(users, arch):
base = BUILD_ROOT / "$" if arch else BUILD_ROOT
kmod_path = base / "lib" / "modules" / "hyperion" / "50_firstboot_users.kmod"
kmod_path = base / "lib" / "modules" / "Hyperion" / "50_firstboot_users.kmod"
kmod_path.parent.mkdir(parents=True, exist_ok=True)
kmod_path.write_text(_make_firstboot_kmod(users), encoding="utf-8")
print(" Wrote first-boot user setup -> " + str(kmod_path.relative_to(BUILD_ROOT)))