Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8762b8f022 | |||
| 677b2cccec | |||
| a5e8624368 | |||
| bbda3b3937 | |||
| 585d39bec2 | |||
| b08b14763a | |||
| de6696003b | |||
| 9220281365 | |||
| 813ddabd9d | |||
| 5177639d71 | |||
| 528b4f31bd | |||
| 60162c7c57 | |||
| 18f5c454bb | |||
| 849ecb7dd6 | |||
| e41bd6bee7 | |||
| 359198c1ea | |||
| beebf01223 | |||
| 2d4ea1bbf4 | |||
| ea3a7e99a7 | |||
| be0fe5dc5a | |||
| 12669d9f82 | |||
| f12159bfb9 | |||
| 1590e1f3f7 | |||
| a69f945b91 | |||
| 7da67899db | |||
| 62e032e4c5 | |||
| 6fefa2d9ff | |||
| bb354cc706 | |||
| fabc061731 | |||
| 82c3e2b346 | |||
| e2e1d5b8a5 | |||
| 9342b9b2b3 | |||
| 9a7db6c243 | |||
| c7545e6947 | |||
| b7f52dd17b | |||
| eb5bed0f09 | |||
| 1827a463eb | |||
| b532a63fc6 | |||
| 4e5a4172bf | |||
| 31ce894fda | |||
| 16c900de84 | |||
| 413afd96de | |||
| a0a0ac69d4 | |||
| 17453983ad | |||
| a6550aa069 | |||
| 02e7b3897c | |||
| 34b89a8e34 | |||
| 0eabfebd0f | |||
| 5b2e5eac65 | |||
| 415064480a | |||
| f00453f703 | |||
| ab1e847d1c | |||
| 62a03bfe6b | |||
| e77a8b3636 | |||
| 6bb7f03a3e | |||
| 8798a2f4fe | |||
| a6d2f6dca7 | |||
| b015d5880a |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -5,6 +5,7 @@
|
|||||||
"syscall",
|
"syscall",
|
||||||
"printf",
|
"printf",
|
||||||
"printInline",
|
"printInline",
|
||||||
"toHex"
|
"toHex",
|
||||||
|
"loadcstr"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
[](https://pinestore.cc/projects/225/hyperionos)
|
||||||
# HyperionOS
|
# HyperionOS
|
||||||
|
|
||||||
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
|
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
local args = {...}
|
|
||||||
local name = syscall.getTask(syscall.getpid()).name
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
|
|
||||||
if not args[1] then
|
|
||||||
while true do
|
|
||||||
local content = syscall.read(0, 1024)
|
|
||||||
if not content or content == "" then break end
|
|
||||||
printInline(content)
|
|
||||||
end
|
|
||||||
print("")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, arg in ipairs(args) do
|
|
||||||
local filePath = arg
|
|
||||||
if filePath:sub(1,1) ~= "/" then
|
|
||||||
filePath = syscall.getcwd().."/"..filePath
|
|
||||||
end
|
|
||||||
|
|
||||||
if not fs.exists(filePath) then
|
|
||||||
print(name..": Cannot access '"..arg.."': No such file.")
|
|
||||||
else
|
|
||||||
local fd = syscall.open(filePath, "r")
|
|
||||||
while true do
|
|
||||||
local content = syscall.read(fd, 1024)
|
|
||||||
if not content or content == "" then break end
|
|
||||||
printInline(content)
|
|
||||||
end
|
|
||||||
syscall.close(fd)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print("")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
syscall.devctl(1,"clear")
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
local args = {...}
|
|
||||||
print(table.concat(args, " "))
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
syscall.open("/dev/tty/TTY1","r")
|
|
||||||
syscall.open("/dev/tty/TTY1","w")
|
|
||||||
syscall.open("/dev/null","r")
|
|
||||||
syscall.devctl(1,"clear")
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
syscall.devctl(1,"spos",1,1)
|
|
||||||
print("HyperionOS hysh Shell")
|
|
||||||
local str=""
|
|
||||||
local stopInput=false
|
|
||||||
local proc=0
|
|
||||||
local fs=require("sys.fs")
|
|
||||||
local timeout=false
|
|
||||||
syscall.setEnviron("SHELL","simpleshell")
|
|
||||||
printInline("> ")
|
|
||||||
syscall.sigcatch(function(sig)
|
|
||||||
if sig==1 then
|
|
||||||
syscall.kill(proc)
|
|
||||||
print("Terminated")
|
|
||||||
printInline("> ")
|
|
||||||
stopInput=false
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
while true do
|
|
||||||
if not stopInput then
|
|
||||||
local input=syscall.read(0)
|
|
||||||
if input then
|
|
||||||
if input=="\b" then
|
|
||||||
if #str>0 then
|
|
||||||
str=str:sub(1,#str-1)
|
|
||||||
printInline("\b")
|
|
||||||
end
|
|
||||||
elseif input=="\n" then
|
|
||||||
print("")
|
|
||||||
stopInput=true
|
|
||||||
if str == "" then
|
|
||||||
printInline("> ")
|
|
||||||
stopInput=false
|
|
||||||
else
|
|
||||||
local path=nil
|
|
||||||
local split=string.split(str, " ")
|
|
||||||
if fs.exists("/bin/"..split[1]) then
|
|
||||||
path="/bin/"..split[1]
|
|
||||||
elseif fs.exists("/bin/"..split[1]..".lua") then
|
|
||||||
path="/bin/"..split[1]..".lua"
|
|
||||||
end
|
|
||||||
if not path then
|
|
||||||
print("Program not found")
|
|
||||||
printInline("> ")
|
|
||||||
stopInput=false
|
|
||||||
else
|
|
||||||
local text = fs.readAllText(path)
|
|
||||||
local program, err = load(text, path)
|
|
||||||
if not program then
|
|
||||||
print(err)
|
|
||||||
printInline("> ")
|
|
||||||
end
|
|
||||||
proc = syscall.spawn(function(...)
|
|
||||||
syscall.open("/dev/tty/TTY1","r")
|
|
||||||
syscall.open("/dev/tty/TTY1","w")
|
|
||||||
syscall.open("/dev/null","w")
|
|
||||||
program(...)
|
|
||||||
end, path, nil, {table.unpack(split, 2)})
|
|
||||||
end
|
|
||||||
str=""
|
|
||||||
end
|
|
||||||
else
|
|
||||||
str=str..input
|
|
||||||
printInline(input)
|
|
||||||
end
|
|
||||||
timeout=false
|
|
||||||
else
|
|
||||||
timeout=true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local exited, code = syscall.collect(proc)
|
|
||||||
if exited then
|
|
||||||
if code then
|
|
||||||
print("\nTask exited with code:\n"..tostring(code))
|
|
||||||
end
|
|
||||||
printInline("> ")
|
|
||||||
stopInput=false
|
|
||||||
end
|
|
||||||
timeout=true
|
|
||||||
end
|
|
||||||
if timeout then
|
|
||||||
if stopInput then
|
|
||||||
sleep(.5)
|
|
||||||
else
|
|
||||||
sleep(.05)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
print("HyperionOS lua")
|
|
||||||
local str=""
|
|
||||||
local stopInput=false
|
|
||||||
local timeout=false
|
|
||||||
local luaEnv=setmetatable({},{__index=_ENV})
|
|
||||||
printInline("> ")
|
|
||||||
while true do
|
|
||||||
local input=syscall.read(0)
|
|
||||||
if input then
|
|
||||||
if input=="\b" then
|
|
||||||
if #str>0 then
|
|
||||||
str=str:sub(1,#str-1)
|
|
||||||
printInline("\b")
|
|
||||||
end
|
|
||||||
elseif input=="\n" then
|
|
||||||
print("")
|
|
||||||
stopInput=true
|
|
||||||
if str == "" then
|
|
||||||
printInline("> ")
|
|
||||||
stopInput=false
|
|
||||||
elseif str == "exit()" then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
local func=load(str,"@Lua","t",luaEnv)
|
|
||||||
local ok,err = xpcall(func, debug.traceback)
|
|
||||||
if not ok then
|
|
||||||
print(err)
|
|
||||||
end
|
|
||||||
printInline("\n> ")
|
|
||||||
str=""
|
|
||||||
end
|
|
||||||
str=""
|
|
||||||
else
|
|
||||||
str=str..input
|
|
||||||
printInline(input)
|
|
||||||
end
|
|
||||||
timeout=false
|
|
||||||
else
|
|
||||||
timeout=true
|
|
||||||
end
|
|
||||||
|
|
||||||
if timeout then
|
|
||||||
if stopInput then
|
|
||||||
sleep(.5)
|
|
||||||
else
|
|
||||||
sleep(.05)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
local args = {...}
|
|
||||||
local name = syscall.getTask(syscall.getpid()).name
|
|
||||||
if #args == 0 then
|
|
||||||
print(name..": Missing operand.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
local newDir = args[1]
|
|
||||||
if newDir:sub(1, 1) ~= "/" then
|
|
||||||
newDir = syscall.getcwd().."/"..newDir
|
|
||||||
end
|
|
||||||
|
|
||||||
if newDir:sub(#newDir, #newDir) ~= "/" then
|
|
||||||
newDir = newDir.."/"
|
|
||||||
end
|
|
||||||
|
|
||||||
if fs.isDir(newDir) then
|
|
||||||
print(name..": Cannot create directory '"..args[1].."': Directory already exists.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
fs.mkdir(newDir)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print(syscall.getcwd())
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
local syscalls=syscall.sysdump()
|
|
||||||
for i=1, #syscalls do
|
|
||||||
print(syscalls[i])
|
|
||||||
end
|
|
||||||
print("Total # of syscalls: "..tostring(#syscalls))
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print((syscall.getUsername() or "Unknown"))
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
local io = {}
|
local io = {}
|
||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
|
|
||||||
function io.open(path, mode)
|
function io.open(path, mode)
|
||||||
return fs.open(path, mode)
|
return fs.open(path, mode)
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
local sys = {}
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return sys
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
local sys = {}
|
|
||||||
sys.fs = require("sys.fs")
|
|
||||||
sys.hpv = require("sys.hpv")
|
|
||||||
sys.ipc = require("sys.ipc")
|
|
||||||
return sys
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
local ipc = {}
|
|
||||||
|
|
||||||
return ipc
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
local term = {}
|
|
||||||
|
|
||||||
function term.clear()
|
|
||||||
coroutine.yield("VFS_write", 1, "\27C\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setCursorPos(x, y)
|
|
||||||
coroutine.yield("VFS_write", 1, "\27cs"..tostring(y)..";"..tostring(x).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.size()
|
|
||||||
coroutine.yield("VFS_write", 1, "\27ts\25")
|
|
||||||
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
|
|
||||||
if not ok then error("Failed to get terminal size") end
|
|
||||||
local x, y = string.match(data, "%R(%d+);(%d+)\25")
|
|
||||||
return tonumber(x), tonumber(y)
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.getCursorPos()
|
|
||||||
coroutine.yield("VFS_write", 1, "\27gc\25")
|
|
||||||
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
|
|
||||||
if not ok then error("Failed to get cursor position") end
|
|
||||||
local y, x = string.match(data, "%R(%d+);(%d+)\25")
|
|
||||||
return tonumber(x), tonumber(y)
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.write(data)
|
|
||||||
coroutine.yield("VFS_write", 1, data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setTextColor(color)
|
|
||||||
local ok, err = coroutine.yield("VFS_type", 1)
|
|
||||||
if not ok then error(err) end
|
|
||||||
if ok ~= "tty" then return end
|
|
||||||
coroutine.yield("VFS_write", 1, "\27f"..tostring(color).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setBackgroundColor(color)
|
|
||||||
local ok, err = coroutine.yield("VFS_type", 1)
|
|
||||||
if not ok then error(err) end
|
|
||||||
if ok ~= "tty" then return end
|
|
||||||
coroutine.yield("VFS_write", 1, "\27b"..tostring(color).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.isColor()
|
|
||||||
local ok, err = coroutine.yield("VFS_type", 1)
|
|
||||||
if not ok then error(err) end
|
|
||||||
return ok == "tty"
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.scroll(n)
|
|
||||||
coroutine.yield("VFS_write", 1, "\27S"..tostring(n).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setDefault(color, layer)
|
|
||||||
if layer then
|
|
||||||
coroutine.yield("VFS_write", 1, "\27F"..tostring(color).."\25")
|
|
||||||
else
|
|
||||||
coroutine.yield("VFS_write", 1, "\27B"..tostring(color).."\25")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.showCursor(show)
|
|
||||||
if show then
|
|
||||||
coroutine.yield("VFS_write", 1, "\27sc\25")
|
|
||||||
else
|
|
||||||
coroutine.yield("VFS_write", 1, "\27hc\25")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return term
|
|
||||||
316
Src/Hyperion-firmware-ccpc/boot/cct/boot.lua
Normal file
316
Src/Hyperion-firmware-ccpc/boot/cct/boot.lua
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
-- :Minify:--
|
||||||
|
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||||
|
---@diagnostic disable-next-line: undefined-global
|
||||||
|
local term = term
|
||||||
|
local os = os
|
||||||
|
local function write(text)
|
||||||
|
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
|
||||||
|
|
||||||
|
local function displaySuperBadError(err)
|
||||||
|
term.setBackgroundColor(0x1)
|
||||||
|
term.setTextColor(0x4)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.write("A critical error occurred while loading the system:")
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
write(err)
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
local ok, err = xpcall(function()
|
||||||
|
local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH}
|
||||||
|
|
||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = true,
|
||||||
|
getfenv = true,
|
||||||
|
getmetatable = true,
|
||||||
|
ipairs = true,
|
||||||
|
__inext = true,
|
||||||
|
load = true,
|
||||||
|
math = true,
|
||||||
|
next = true,
|
||||||
|
pairs = true,
|
||||||
|
pcall = true,
|
||||||
|
rawequal = true,
|
||||||
|
rawget = true,
|
||||||
|
rawlen = true,
|
||||||
|
rawset = true,
|
||||||
|
select = true,
|
||||||
|
setfenv = true,
|
||||||
|
setmetatable = true,
|
||||||
|
string = true,
|
||||||
|
table = true,
|
||||||
|
tonumber = true,
|
||||||
|
tostring = true,
|
||||||
|
type = true,
|
||||||
|
xpcall = true,
|
||||||
|
_G = true
|
||||||
|
}
|
||||||
|
|
||||||
|
local debug = debug
|
||||||
|
for i, v in pairs(_G) do
|
||||||
|
if not lua[i] or lua[i] == nil then
|
||||||
|
apis[i] = v
|
||||||
|
_G[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local acekeys={
|
||||||
|
[apis.keys.enter]="\n",
|
||||||
|
[apis.keys.tab]="\t",
|
||||||
|
[apis.keys.backspace]="\b",
|
||||||
|
[apis.keys.up]="\17",
|
||||||
|
[apis.keys.down]="\18",
|
||||||
|
[apis.keys.left]="\19",
|
||||||
|
[apis.keys.right]="\20",
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(time)
|
||||||
|
local stoptime = apis.os.clock() + (time)
|
||||||
|
while stoptime > apis.os.clock() do end
|
||||||
|
end
|
||||||
|
|
||||||
|
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||||
|
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||||
|
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||||
|
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||||
|
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||||
|
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||||
|
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||||
|
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||||
|
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||||
|
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||||
|
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||||
|
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||||
|
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||||
|
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||||
|
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||||
|
apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||||
|
|
||||||
|
local function getFile(path)
|
||||||
|
local file = apis.fs.open(path, "r")
|
||||||
|
if not file then
|
||||||
|
displaySuperBadError("Could not open file: " .. path)
|
||||||
|
end
|
||||||
|
local content = file.readAll()
|
||||||
|
file.close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel")
|
||||||
|
local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis)
|
||||||
|
local fs = load(getFile(BOOT_DRIVE_PATH .. "/boot/initfs"), "@InitFs")()
|
||||||
|
|
||||||
|
if not Kernel then displaySuperBadError("Could not load kernel.") end
|
||||||
|
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||||
|
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||||
|
|
||||||
|
local eventQueue = {}
|
||||||
|
|
||||||
|
local function queueEvent(event, ...)
|
||||||
|
table.insert(eventQueue, {event, ...})
|
||||||
|
end
|
||||||
|
|
||||||
|
local computer = {
|
||||||
|
time = function() return apis.os.epoch("utc") end,
|
||||||
|
clock = function() return apis.os.clock() * 1000 end,
|
||||||
|
shutdown = apis.os.shutdown,
|
||||||
|
reboot = apis.os.reboot,
|
||||||
|
getMachineEvent = function()
|
||||||
|
if #eventQueue > 0 then
|
||||||
|
return table.unpack(table.remove(eventQueue, 1))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
getEEPROM = function() return getFile("/startup.lua") end,
|
||||||
|
setEEPROM = function(_, text)
|
||||||
|
local h = apis.fs.open("/startup.lua", "w")
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
apis.term.setBackgroundColor(0x8000)
|
||||||
|
apis.term.setTextColor(0x1000)
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
|
||||||
|
local kernelCoro = coroutine.create(function()
|
||||||
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||||
|
{
|
||||||
|
print = function(_, text) write(text .. "\n") end,
|
||||||
|
printInline = function(_, text) write(text) end,
|
||||||
|
clear = function()
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
end,
|
||||||
|
setCursorPos = function(_, x, y)
|
||||||
|
apis.term.setCursorPos(x, y)
|
||||||
|
end,
|
||||||
|
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||||
|
getSize = function() return apis.term.getSize() end,
|
||||||
|
setBackgroundColor = function(_, color)
|
||||||
|
apis.term.setBackgroundColor(colors[color])
|
||||||
|
end,
|
||||||
|
setTextColor = function(_, color)
|
||||||
|
apis.term.setTextColor(colors[color])
|
||||||
|
end,
|
||||||
|
getBackgroundColor = function()
|
||||||
|
return icolors[apis.term.getBackgroundColor()]
|
||||||
|
end,
|
||||||
|
getTextColor = function()
|
||||||
|
return icolors[apis.term.getTextColor()]
|
||||||
|
end
|
||||||
|
}, computer, fs, "$")
|
||||||
|
if not ok then displaySuperBadError(err) end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
|
local startTime = computer.time()
|
||||||
|
debug.sethook(co, function()
|
||||||
|
if computer.time() > startTime + timeout then
|
||||||
|
return coroutine.yield("timeout")
|
||||||
|
end
|
||||||
|
end, "", 1000)
|
||||||
|
local ret = {coroutine.resume(co, ...)}
|
||||||
|
if ret[1] and ret[2] == "timeout" then
|
||||||
|
return "timeout"
|
||||||
|
elseif ret[1] == false then
|
||||||
|
return "error", ret[2]
|
||||||
|
else
|
||||||
|
debug.sethook(co)
|
||||||
|
return "success", table.unpack(ret, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||||
|
apis.os.queueEvent("NoSleep")
|
||||||
|
local exit = false
|
||||||
|
while not exit do
|
||||||
|
local event = {coroutine.yield()}
|
||||||
|
if event[1] == "key" then
|
||||||
|
queueEvent("keyPressed", 1, event[2])
|
||||||
|
if acekeys[event[2]] then
|
||||||
|
queueEvent("keyTyped", 1, acekeys[event[2]])
|
||||||
|
end
|
||||||
|
elseif event[1] == "char" then
|
||||||
|
queueEvent("keyTyped", 1, event[2])
|
||||||
|
elseif event[1] == "key_up" then
|
||||||
|
queueEvent("keyReleased", 1, event[2])
|
||||||
|
elseif event[1] == "disk" then
|
||||||
|
queueEvent("componentAdded", "disk")
|
||||||
|
elseif event[1] == "disk_eject" then
|
||||||
|
queueEvent("componentRemoved", "disk")
|
||||||
|
elseif event[1] == "modem_message" then
|
||||||
|
queueEvent("modem_message", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "rednet_message" then
|
||||||
|
queueEvent("rednet_message", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "http_success" then
|
||||||
|
queueEvent("http_success", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "http_failure" then
|
||||||
|
queueEvent("http_failure", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "NoSleep" then
|
||||||
|
exit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||||
|
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||||
|
coroutine.yield("key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, debug.traceback)
|
||||||
|
|
||||||
|
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||||
|
while true do coroutine.yield() end
|
||||||
119
Src/Hyperion-firmware-ccpc/boot/cct/eeprom
Normal file
119
Src/Hyperion-firmware-ccpc/boot/cct/eeprom
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
||||||
|
-- UnBIOS by JackMacWindows
|
||||||
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
|
-- To use, just place a `bios.lua` in the root of the drive, and run this program
|
||||||
|
-- Here's a list of things that are irreversibly changed:
|
||||||
|
-- * both `bit` and `bit32` are kept for compatibility
|
||||||
|
-- * string metatable blocking (on old versions of CC)
|
||||||
|
-- In addition, if `debug` is not available these things are also irreversibly changed:
|
||||||
|
-- * old Lua 5.1 `load` function (for loading from a function)
|
||||||
|
-- * `loadstring` prefixing (before CC:T 1.96.0)
|
||||||
|
-- * `http.request`
|
||||||
|
-- * `os.shutdown` and `os.reboot`
|
||||||
|
-- * `peripheral`
|
||||||
|
-- * `turtle.equip[Left|Right]`
|
||||||
|
-- Licensed under the MIT license
|
||||||
|
local args = {...}
|
||||||
|
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
||||||
|
local t = {}
|
||||||
|
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
||||||
|
for _,k in ipairs(t) do _G[k] = nil end
|
||||||
|
local native = _G.term.native()
|
||||||
|
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
|
||||||
|
_G.term = native
|
||||||
|
if _G.http then
|
||||||
|
_G.http.checkURL = _G.http.checkURLAsync
|
||||||
|
_G.http.websocket = _G.http.websocketAsync
|
||||||
|
end
|
||||||
|
if _G.commands then _G.commands = _G.commands.native end
|
||||||
|
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
||||||
|
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
||||||
|
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
||||||
|
-- Set up TLCO
|
||||||
|
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
||||||
|
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
||||||
|
-- empty placeholder to let it continue and return without throwing. This results
|
||||||
|
-- in the `pcall` returning successfully, preventing the error-displaying code
|
||||||
|
-- from running - essentially making it so that `os.shutdown` is called immediately
|
||||||
|
-- after the new BIOS exits.
|
||||||
|
--
|
||||||
|
-- From there, the setup code is placed in `term.native` since it's the first
|
||||||
|
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
|
||||||
|
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
|
||||||
|
-- allow it to be the last function called in the original BIOS, and returns.
|
||||||
|
-- From there execution continues, calling the `term.redirect` dummy, skipping
|
||||||
|
-- over the error-handling code (since `pcall` returned ok), and calling
|
||||||
|
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
|
||||||
|
-- called, which effectively makes it run as the main chunk.
|
||||||
|
local olderror = error
|
||||||
|
_G.error = function() end
|
||||||
|
_G.term.redirect = function() end
|
||||||
|
function _G.term.native()
|
||||||
|
_G.term.native = nil
|
||||||
|
_G.term.redirect = nil
|
||||||
|
_G.error = olderror
|
||||||
|
term.setBackgroundColor(32768)
|
||||||
|
term.setTextColor(1)
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.setCursorBlink(true)
|
||||||
|
term.clear()
|
||||||
|
local file = fs.open(BOOT_DRIVE_PATH.."/boot/cct/boot.lua", "r")
|
||||||
|
if file == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
local fn, err = loadstring(file.readAll(), "@bootloader")
|
||||||
|
file.close()
|
||||||
|
if fn == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write(err)
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
setfenv(fn, _G)
|
||||||
|
local oldshutdown = os.shutdown
|
||||||
|
os.shutdown = function()
|
||||||
|
os.shutdown = oldshutdown
|
||||||
|
return fn(BOOT_DRIVE_PATH)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if debug then
|
||||||
|
-- Restore functions that were overwritten in the BIOS
|
||||||
|
-- Apparently this has to be done *after* redefining term.native
|
||||||
|
local function restoreValue(tab, idx, name, hint)
|
||||||
|
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
|
||||||
|
while key ~= name and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(tab[idx], i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
tab[idx] = value or tab[idx]
|
||||||
|
end
|
||||||
|
restoreValue(_G, "loadstring", "nativeloadstring", 1)
|
||||||
|
restoreValue(_G, "load", "nativeload", 5)
|
||||||
|
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
|
||||||
|
restoreValue(os, "shutdown", "nativeShutdown", 1)
|
||||||
|
restoreValue(os, "reboot", "nativeReboot", 1)
|
||||||
|
if turtle then
|
||||||
|
restoreValue(turtle, "equipLeft", "v", 1)
|
||||||
|
restoreValue(turtle, "equipRight", "v", 1)
|
||||||
|
end
|
||||||
|
do
|
||||||
|
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
|
||||||
|
while key ~= "native" and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(peripheral.isPresent, i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
_G.peripheral = value or peripheral
|
||||||
|
end
|
||||||
|
end
|
||||||
155
Src/Hyperion-firmware-ccpc/boot/cct/initdisks
Normal file
155
Src/Hyperion-firmware-ccpc/boot/cct/initdisks
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
-- :Minify:--
|
||||||
|
local apis = ({...})[1]
|
||||||
|
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
||||||
|
local fs = apis.fs
|
||||||
|
local native = apis.peripheral
|
||||||
|
local peripheral = {}
|
||||||
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
|
function peripheral.getType(name)
|
||||||
|
if native.isPresent(name) then return native.getType(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, "getTypeRemote", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getNames()
|
||||||
|
local names = {}
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.isPresent(side) then table.insert(names, side) end
|
||||||
|
if native.hasType(side, "peripheral_hub") then
|
||||||
|
local hubSides = native.call(side, "getConnectedSides")
|
||||||
|
for _, hubSide in ipairs(hubSides) do
|
||||||
|
table.insert(names, hubSide)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return names
|
||||||
|
end
|
||||||
|
|
||||||
|
local disks = {}
|
||||||
|
local internal = {}
|
||||||
|
|
||||||
|
local function norm(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
return fs.combine("/", path)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function createDisk(id, basePath, readonly, periph)
|
||||||
|
basePath = norm(basePath)
|
||||||
|
|
||||||
|
local disk = {address = id, isReadOnly = function() return readonly end}
|
||||||
|
|
||||||
|
function disk:spaceUsed()
|
||||||
|
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:spaceTotal() return fs.getCapacity(basePath) end
|
||||||
|
|
||||||
|
function disk:list(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if not fs.exists(p) or not fs.isDir(p) then
|
||||||
|
return nil, "not directory"
|
||||||
|
end
|
||||||
|
return fs.list(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:fileExists(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.exists(p) and not fs.isDir(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:directoryExists(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.exists(p) and fs.isDir(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:type(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if not fs.exists(p) then
|
||||||
|
return nil
|
||||||
|
elseif fs.isDir(p) then
|
||||||
|
return "directory"
|
||||||
|
else
|
||||||
|
return "file"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:makeDirectory(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
fs.makeDir(p)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:remove(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if fs.exists(p) then fs.delete(p) end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:setLabel(label) periph.setLabel(label) end
|
||||||
|
|
||||||
|
function disk:getLabel(label) return periph.getLabel() end
|
||||||
|
|
||||||
|
function disk:attributes(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.attributes(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:open(path, mode)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.open(p, mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
return disk
|
||||||
|
end
|
||||||
|
|
||||||
|
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||||
|
setLabel = function(label)
|
||||||
|
local h = fs.open("/.label", "w")
|
||||||
|
h.write(label)
|
||||||
|
h.close()
|
||||||
|
end,
|
||||||
|
getLabel = function()
|
||||||
|
local h = fs.open("/.label", "r")
|
||||||
|
if not h then return "$" end
|
||||||
|
local label = h.readAll()
|
||||||
|
h.close()
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
local function refresh()
|
||||||
|
for id, _ in pairs(disks) do
|
||||||
|
if not peripheral.getType(id) then disks[id] = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.getType(name) == "disk" then
|
||||||
|
if not disks[name] then
|
||||||
|
local mount = disk.getMountPath(name)
|
||||||
|
if mount then
|
||||||
|
disks[name] = createDisk(name, mount, false, disk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function iter()
|
||||||
|
refresh()
|
||||||
|
local combined = {}
|
||||||
|
|
||||||
|
for id, obj in pairs(internal) do combined[id] = obj end
|
||||||
|
for id, obj in pairs(disks) do combined[id] = obj end
|
||||||
|
|
||||||
|
return pairs(combined)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {refresh = refresh, list = iter}
|
||||||
@@ -308,31 +308,6 @@ end
|
|||||||
|
|
||||||
local fifo = kernel.newFifo()
|
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()
|
kernel.processes.cctmond = function()
|
||||||
local timeout = false
|
local timeout = false
|
||||||
while true do
|
while true do
|
||||||
@@ -342,7 +317,17 @@ kernel.processes.cctmond = function()
|
|||||||
local eventType = event[1]
|
local eventType = event[1]
|
||||||
local charOrKey = event[3]
|
local charOrKey = event[3]
|
||||||
|
|
||||||
buildKeyMaps()
|
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 eventType == "keyPressed" then
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||||
@@ -351,25 +336,40 @@ kernel.processes.cctmond = function()
|
|||||||
alt = true
|
alt = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ctrl and charOrKey == apis.keys.c then
|
if ctrl then
|
||||||
|
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||||
|
if ctrlByte then
|
||||||
|
if ctrlByte == 3 then
|
||||||
for _, task in ipairs(syscall.getTasks()) do
|
for _, task in ipairs(syscall.getTasks()) do
|
||||||
syscall.sigsend(task, 1)
|
syscall.sigsend(task, 1)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
fifo.push(string.char(ctrlByte))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local specialKeyMap = {
|
||||||
|
[apis.keys.up] = "[A",
|
||||||
|
[apis.keys.down] = "[B",
|
||||||
|
[apis.keys.right] = "[C",
|
||||||
|
[apis.keys.left] = "[D",
|
||||||
|
[apis.keys.home] = "[H",
|
||||||
|
[apis.keys["end"]] = "[F",
|
||||||
|
[apis.keys.pageUp] = "[5~",
|
||||||
|
[apis.keys.pageDown] = "[6~",
|
||||||
|
[apis.keys.delete] = "[3~",
|
||||||
|
}
|
||||||
|
local special = specialKeyMap[charOrKey]
|
||||||
|
if special then fifo.push(special) end
|
||||||
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
|
elseif eventType == "keyReleased" then
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||||
ctrl = false
|
ctrl = false
|
||||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||||
alt = false
|
alt = false
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif eventType == "keyTyped" then
|
elseif eventType == "keyTyped" then
|
||||||
if charOrKey then fifo.push(charOrKey) end
|
if charOrKey then fifo.push(charOrKey) end
|
||||||
end
|
end
|
||||||
@@ -385,11 +385,10 @@ kernel.processes.cctmond = function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
newtty(apis.term, "TTY1", fifo.pop)
|
newtty(apis.term, "1", fifo.pop)
|
||||||
|
|
||||||
|
|
||||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||||
v.setTextScale(.5)
|
v.setTextScale(.5)
|
||||||
v.write("Initializing...")
|
v.write("Initializing...")
|
||||||
newtty(v,"TTY"..tostring(i+1),function () end)
|
newtty(v,tostring(i+1),function () end)
|
||||||
end
|
end
|
||||||
@@ -71,7 +71,6 @@ local ok, err = xpcall(function()
|
|||||||
collectgarbage = true,
|
collectgarbage = true,
|
||||||
error = true,
|
error = true,
|
||||||
gcinfo = true,
|
gcinfo = true,
|
||||||
getfenv = true,
|
|
||||||
getmetatable = true,
|
getmetatable = true,
|
||||||
ipairs = true,
|
ipairs = true,
|
||||||
__inext = true,
|
__inext = true,
|
||||||
@@ -85,7 +84,6 @@ local ok, err = xpcall(function()
|
|||||||
rawlen = true,
|
rawlen = true,
|
||||||
rawset = true,
|
rawset = true,
|
||||||
select = true,
|
select = true,
|
||||||
setfenv = true,
|
|
||||||
setmetatable = true,
|
setmetatable = true,
|
||||||
string = true,
|
string = true,
|
||||||
table = true,
|
table = true,
|
||||||
|
|||||||
@@ -6,31 +6,147 @@ local native = apis.peripheral
|
|||||||
local peripheral = {}
|
local peripheral = {}
|
||||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
function peripheral.getType(name)
|
function peripheral.getNames()
|
||||||
if native.isPresent(name) then return native.getType(name) end
|
local results = {}
|
||||||
for n = 1, #sides do
|
for n = 1, #sides do
|
||||||
local side = sides[n]
|
local side = sides[n]
|
||||||
if native.hasType(side, "peripheral_hub") and
|
if native.isPresent(side) then
|
||||||
native.call(side, "isPresentRemote", name) then
|
table.insert(results, side)
|
||||||
return native.call(side, "getTypeRemote", name)
|
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
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.getNames()
|
function peripheral.getName(peripheral)
|
||||||
local names = {}
|
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
|
for n = 1, #sides do
|
||||||
local side = sides[n]
|
local side = sides[n]
|
||||||
if native.isPresent(side) then table.insert(names, side) end
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
if native.hasType(side, "peripheral_hub") then
|
return native.call(side, "callRemote", name, method, ...)
|
||||||
local hubSides = native.call(side, "getConnectedSides")
|
end
|
||||||
for _, hubSide in ipairs(hubSides) do
|
end
|
||||||
table.insert(names, hubSide)
|
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
|
end
|
||||||
end
|
end
|
||||||
return names
|
return table.unpack(results)
|
||||||
end
|
end
|
||||||
|
|
||||||
local disks = {}
|
local disks = {}
|
||||||
@@ -130,14 +246,9 @@ local function refresh()
|
|||||||
if not peripheral.getType(id) then disks[id] = nil end
|
if not peripheral.getType(id) then disks[id] = nil end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||||
if peripheral.getType(name) == "disk" then
|
if disk.isDiskPresent() then
|
||||||
if not disks[name] then
|
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||||
local mount = disk.getMountPath(name)
|
|
||||||
if mount then
|
|
||||||
disks[name] = createDisk(name, mount, false, disk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
|
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
local apis = kernel.apis
|
local apis = kernel.apis
|
||||||
local native = apis.peripheral
|
local native = apis.peripheral
|
||||||
@@ -37,7 +38,7 @@ function peripheral.isPresent(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.getType(peripheral)
|
function peripheral.getType(peripheral)
|
||||||
if type(peripheral) == "string" then -- Peripheral name passed
|
if type(peripheral) == "string" then
|
||||||
if native.isPresent(peripheral) then
|
if native.isPresent(peripheral) then
|
||||||
return native.getType(peripheral)
|
return native.getType(peripheral)
|
||||||
end
|
end
|
||||||
@@ -58,7 +59,7 @@ function peripheral.getType(peripheral)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.hasType(peripheral, peripheral_type)
|
function peripheral.hasType(peripheral, peripheral_type)
|
||||||
if type(peripheral) == "string" then -- Peripheral name passed
|
if type(peripheral) == "string" then
|
||||||
if native.isPresent(peripheral) then
|
if native.isPresent(peripheral) then
|
||||||
return native.hasType(peripheral, peripheral_type)
|
return native.hasType(peripheral, peripheral_type)
|
||||||
end
|
end
|
||||||
@@ -243,6 +244,22 @@ local function serializeBool(bool)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function newtty(obj, id, ev)
|
local function newtty(obj, id, ev)
|
||||||
|
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||||
|
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||||
|
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||||
|
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||||
|
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||||
|
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||||
|
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||||
|
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||||
|
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||||
|
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||||
|
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||||
|
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||||
|
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||||
|
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||||
|
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||||
|
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||||
if op=="type" then
|
if op=="type" then
|
||||||
return "character device"
|
return "character device"
|
||||||
@@ -317,7 +334,18 @@ kernel.processes.cctmond = function()
|
|||||||
local eventType = event[1]
|
local eventType = event[1]
|
||||||
local charOrKey = event[3]
|
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 eventType == "keyPressed" then
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||||
ctrl = true
|
ctrl = true
|
||||||
@@ -325,11 +353,31 @@ kernel.processes.cctmond = function()
|
|||||||
alt = true
|
alt = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Handle Ctrl+C
|
if ctrl then
|
||||||
if ctrl and charOrKey == apis.keys.c then
|
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||||
|
if ctrlByte then
|
||||||
|
if ctrlByte == 3 then
|
||||||
for _, task in ipairs(syscall.getTasks()) do
|
for _, task in ipairs(syscall.getTasks()) do
|
||||||
syscall.sigsend(task, 1) -- SIGINT
|
syscall.sigsend(task, 1)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
fifo.push(string.char(ctrlByte))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local specialKeyMap = {
|
||||||
|
[apis.keys.up] = "[A",
|
||||||
|
[apis.keys.down] = "[B",
|
||||||
|
[apis.keys.right] = "[C",
|
||||||
|
[apis.keys.left] = "[D",
|
||||||
|
[apis.keys.home] = "[H",
|
||||||
|
[apis.keys["end"]] = "[F",
|
||||||
|
[apis.keys.pageUp] = "[5~",
|
||||||
|
[apis.keys.pageDown] = "[6~",
|
||||||
|
[apis.keys.delete] = "[3~",
|
||||||
|
}
|
||||||
|
local special = specialKeyMap[charOrKey]
|
||||||
|
if special then fifo.push(special) end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif eventType == "keyReleased" then
|
elseif eventType == "keyReleased" then
|
||||||
@@ -354,10 +402,10 @@ kernel.processes.cctmond = function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
newtty(apis.term, "TTY1", fifo.pop)
|
newtty(apis.term, "1", fifo.pop)
|
||||||
|
|
||||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||||
v.setTextScale(.5)
|
v.setTextScale(.5)
|
||||||
v.write("Initializing...")
|
v.write("Initializing...")
|
||||||
newtty(v,"TTY"..tostring(i+1),function () end)
|
newtty(v,tostring(i+1),function () end)
|
||||||
end
|
end
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
U $;/
|
U $;/
|
||||||
U devfs0000;/dev/
|
U devfs0000;/dev/
|
||||||
U tmpfs0000;/tmp/
|
U tmpfs0000;/tmp/
|
||||||
|
U procfs0000;/proc/
|
||||||
@@ -8,7 +8,7 @@ local computer = args[6]
|
|||||||
local ifs = args[7]
|
local ifs = args[7]
|
||||||
local kernel = {}
|
local kernel = {}
|
||||||
kernel.LOG_Text=""
|
kernel.LOG_Text=""
|
||||||
kernel.version="HyperionOS V1.2.0"
|
kernel.version="HyperionOS V1.2.3"
|
||||||
kernel.process = "Kernel"
|
kernel.process = "Kernel"
|
||||||
kernel.users={[0]="root",[1]="User"}
|
kernel.users={[0]="root",[1]="User"}
|
||||||
kernel.hostname = "hyperion"
|
kernel.hostname = "hyperion"
|
||||||
@@ -27,15 +27,15 @@ local windowsExp = false
|
|||||||
|
|
||||||
function kernel.log(msg, level, c)
|
function kernel.log(msg, level, c)
|
||||||
c=c or 12
|
c=c or 12
|
||||||
kernel.LOG_Text = kernel.LOG_Text..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
kernel.LOG_Text = kernel.LOG_Text..string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||||
if kernel.status == "start" then
|
if kernel.status == "start" then
|
||||||
screen:setTextColor(c)
|
screen:setTextColor(c)
|
||||||
screen:print(string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
screen:print(tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||||
elseif kernel.status == "term" then
|
elseif kernel.status == "term" then
|
||||||
kernel.standbyTask=kernel.currentTask
|
kernel.standbyTask=kernel.currentTask
|
||||||
kernel.currentTask=kernel.kernelTask
|
kernel.currentTask=kernel.kernelTask
|
||||||
kernel.vfs.devctl(1,"sfgc",c)
|
kernel.vfs.devctl(1,"sfgc",c)
|
||||||
kernel.vfs.write(1,string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
kernel.vfs.write(1,tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||||
kernel.currentTask=kernel.standbyTask
|
kernel.currentTask=kernel.standbyTask
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -113,8 +113,9 @@ local split = function(str, delim, maxResultCountOrNil)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not ifs.isFile("/boot/boot.cfg") then
|
if not ifs.isFile("/boot/boot.cfg") then
|
||||||
kernel.log("boot.cfg missing or corrupted!, Attempting to write recovery boot.cfg", "ERROR", 2)
|
kernel.log("First boot detected writing boot.cfg", "INFO", 3)
|
||||||
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
||||||
|
kernel.firstBoot=true
|
||||||
end
|
end
|
||||||
|
|
||||||
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
||||||
@@ -184,11 +185,18 @@ end
|
|||||||
|
|
||||||
kernel.log("Gathering modules")
|
kernel.log("Gathering modules")
|
||||||
for _, i in ipairs(ifs.list("/lib/modules")) do
|
for _, i in ipairs(ifs.list("/lib/modules")) do
|
||||||
for _,v in ipairs(ifs.list("/lib/modules/"..i)) do
|
local modlist = ifs.list("/lib/modules/"..i)
|
||||||
|
if not modlist then
|
||||||
|
kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8)
|
||||||
|
else
|
||||||
|
for _,v in ipairs(modlist) do
|
||||||
local prior=tonumber(v:sub(1,2))
|
local prior=tonumber(v:sub(1,2))
|
||||||
|
if prior then
|
||||||
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
kernel.ifs=ifs
|
kernel.ifs=ifs
|
||||||
kernel.apis=apis
|
kernel.apis=apis
|
||||||
@@ -244,7 +252,12 @@ kernel.syscalls["sysdump"]=function()
|
|||||||
end
|
end
|
||||||
return rv
|
return rv
|
||||||
end
|
end
|
||||||
kernel.syscalls["test"]=function() return true end
|
kernel.syscalls["reboot"]=function()
|
||||||
|
kernel.computer:reboot()
|
||||||
|
end
|
||||||
|
kernel.syscalls["shutdown"]=function()
|
||||||
|
kernel.computer:reboot()
|
||||||
|
end
|
||||||
|
|
||||||
kernel.log("Running modules")
|
kernel.log("Running modules")
|
||||||
for _,p in ipairs(modules) do
|
for _,p in ipairs(modules) do
|
||||||
@@ -265,6 +278,7 @@ for _,p in ipairs(modules) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("Kernel initialized successfully.")
|
kernel.log("Kernel initialized successfully.")
|
||||||
|
kernel.saveLog()
|
||||||
kernel.status="running"
|
kernel.status="running"
|
||||||
kernel.main()
|
kernel.main()
|
||||||
if kernel.status=="panic" then
|
if kernel.status=="panic" then
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
-- This file is auto-generated during the build process.
|
-- This file is auto-generated during the build process.
|
||||||
-- DEFAULT BOOT CONFIGURATION FILE
|
-- DEFAULT BOOT CONFIGURATION FILE
|
||||||
return {
|
return {
|
||||||
initPath = "/sbin/init.lua",
|
initPath = "/sbin/init",
|
||||||
maxOpenFiles = 128,
|
maxOpenFiles = 128,
|
||||||
maxFilesPerTask = 16,
|
maxFilesPerTask = 16,
|
||||||
preempt=true
|
preempt=true
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local P = kernel.vfs.P
|
|
||||||
local PERM = kernel.vfs.PERM
|
|
||||||
|
|
||||||
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
|
|
||||||
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
|
|
||||||
+ P.GROUP_R + P.GROUP_X
|
|
||||||
+ P.WORLD_R + P.WORLD_X
|
|
||||||
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
|
|
||||||
local RW____ = P.OWNER_R + P.OWNER_W
|
|
||||||
local RWXRWXRWX = PERM.RWXRWXRWX
|
|
||||||
local SUID_755 = PERM.SUID_755
|
|
||||||
|
|
||||||
local META_VERSION = 0x01
|
|
||||||
local rootDisk = kernel.disks["$"]
|
|
||||||
|
|
||||||
local function makeEntry(name, etype, owner, group, perms, cmeta)
|
|
||||||
cmeta = cmeta or ""
|
|
||||||
local plo = perms % 256
|
|
||||||
local phi = math.floor(perms / 256) % 256
|
|
||||||
return string.char(#name) .. name
|
|
||||||
.. string.char(etype, owner, group, plo, phi)
|
|
||||||
.. string.char(#cmeta) .. cmeta
|
|
||||||
end
|
|
||||||
|
|
||||||
local function writeMeta(dir, entries)
|
|
||||||
local diskDir = dir
|
|
||||||
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
|
|
||||||
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
|
|
||||||
|
|
||||||
local data = string.char(META_VERSION)
|
|
||||||
for _, e in ipairs(entries) do
|
|
||||||
data = data .. makeEntry(e[1], e[2] or 0x00, e[3], e[4], e[5], e[6])
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, err = pcall(function()
|
|
||||||
local f = rootDisk:open(metaPath, "w")
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
end)
|
|
||||||
if not ok then
|
|
||||||
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local REG = 0x00
|
|
||||||
|
|
||||||
if rootDisk:fileExists(".meta") then
|
|
||||||
kernel.log("Permissions already seeded, skipping.", "INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Seeding filesystem permissions...", "INFO")
|
|
||||||
|
|
||||||
-- /
|
|
||||||
writeMeta("/", {
|
|
||||||
{"bin", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"boot", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"dev", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"etc", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"home", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"lib", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"root", REG, 0, 0, RW____ },
|
|
||||||
{"sbin", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"tmp", REG, 0, 0, RWXRWXRWX},
|
|
||||||
{"usr", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"var", REG, 0, 0, RWX_RX_RX},
|
|
||||||
})
|
|
||||||
|
|
||||||
-- /bin
|
|
||||||
writeMeta("/bin", {
|
|
||||||
{"cat", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"clear", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"echo", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"hfetch", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"hysh", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"hyshex", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"install", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"login", REG, 0, 0, SUID_755 },
|
|
||||||
{"ls", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"lua", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"luaold", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"mkdir", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"ps", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"pwd", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"spm", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"su", REG, 0, 0, SUID_755 },
|
|
||||||
{"sudo", REG, 0, 0, SUID_755 },
|
|
||||||
{"sysdump", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"whoami", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"yes", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"startup", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"ln", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"readlink", REG, 0, 0, RWX_RX_RX},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/bin/startup", {
|
|
||||||
{"test.lua", REG, 0, 0, RWX_RX_RX},
|
|
||||||
})
|
|
||||||
|
|
||||||
-- /etc
|
|
||||||
writeMeta("/etc", {
|
|
||||||
{"passwd", REG, 0, 0, RW_R_R},
|
|
||||||
{"shadow", REG, 0, 0, RW____},
|
|
||||||
{"pam.d", REG, 0, 0, RWX_RX_RX},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/etc/pam.d", {
|
|
||||||
{"secret", REG, 0, 0, RW____},
|
|
||||||
})
|
|
||||||
|
|
||||||
-- /sbin
|
|
||||||
writeMeta("/sbin", {
|
|
||||||
{"init.lua", REG, 0, 0, RWX_RX_RX},
|
|
||||||
})
|
|
||||||
|
|
||||||
-- /boot
|
|
||||||
writeMeta("/boot", {
|
|
||||||
{"kernel.lua", REG, 0, 0, RW_R_R },
|
|
||||||
{"boot.cfg", REG, 0, 0, RW_R_R },
|
|
||||||
{"safeboot.cfg", REG, 0, 0, RW_R_R },
|
|
||||||
{"fstab", REG, 0, 0, RW_R_R },
|
|
||||||
{"initfs", REG, 0, 0, RW_R_R },
|
|
||||||
{"cct", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"oc", REG, 0, 0, RWX_RX_RX},
|
|
||||||
})
|
|
||||||
|
|
||||||
-- /lib
|
|
||||||
writeMeta("/lib", {
|
|
||||||
{"sys", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"modules", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"crypto", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"store", REG, 0, 0, RWX_RX_RX},
|
|
||||||
{"snip", REG, 0, 0, RW_R_R },
|
|
||||||
{"io", REG, 0, 0, RW_R_R },
|
|
||||||
{"bit32", REG, 0, 0, RW_R_R },
|
|
||||||
})
|
|
||||||
|
|
||||||
kernel.log("Filesystem permissions seeded.", "INFO")
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("Permission module loaded.", "INFO")
|
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
|
--- @diagnostic disable: duplicate-set-field
|
||||||
|
local kernel = ...
|
||||||
|
kernel.allowGlobalOverwrites = true
|
||||||
|
|
||||||
function string.hasSuffix(str, suffix)
|
function string.hasSuffix(str, suffix)
|
||||||
return string.sub(str, #suffix + 1) == suffix
|
return string.sub(str, #suffix + 1) == suffix
|
||||||
end
|
end
|
||||||
@@ -68,30 +72,24 @@ end
|
|||||||
local function serialize(tbl, seen)
|
local function serialize(tbl, seen)
|
||||||
seen = seen or {}
|
seen = seen or {}
|
||||||
|
|
||||||
-- If we've seen this table before, return a placeholder to prevent infinite loops
|
|
||||||
if seen[tbl] then return '"[Circular Reference]"' end
|
if seen[tbl] then return '"[Circular Reference]"' end
|
||||||
|
|
||||||
-- Mark this table as seen
|
|
||||||
seen[tbl] = true
|
seen[tbl] = true
|
||||||
|
|
||||||
local output = "{"
|
local output = "{"
|
||||||
local first = true
|
local first = true
|
||||||
|
|
||||||
for i, v in pairs(tbl) do
|
for i, v in pairs(tbl) do
|
||||||
-- Handle comma placement more cleanly
|
|
||||||
if not first then output = output .. "," end
|
if not first then output = output .. "," end
|
||||||
first = false
|
first = false
|
||||||
|
|
||||||
-- Serialize Key
|
|
||||||
if type(i) == "string" then
|
if type(i) == "string" then
|
||||||
output = output .. "[\"" .. i .. "\"]="
|
output = output .. "[\"" .. i .. "\"]="
|
||||||
elseif type(i) == "number" then
|
elseif type(i) == "number" then
|
||||||
output = output .. "[" .. tostring(i) .. "]="
|
output = output .. "[" .. tostring(i) .. "]="
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Serialize Value
|
|
||||||
if type(v) == "table" then
|
if type(v) == "table" then
|
||||||
-- Pass the 'seen' table down to the recursive call
|
|
||||||
output = output .. serialize(v, seen)
|
output = output .. serialize(v, seen)
|
||||||
elseif type(v) == "string" then
|
elseif type(v) == "string" then
|
||||||
output = output .. "[=[" .. v .. "]=]"
|
output = output .. "[=[" .. v .. "]=]"
|
||||||
@@ -175,6 +173,27 @@ function table.indexOf(t, value)
|
|||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function table.merge(...)
|
||||||
|
local args={...}
|
||||||
|
local out = {}
|
||||||
|
local outi = {}
|
||||||
|
|
||||||
|
for _,t in ipairs(args) do
|
||||||
|
for i,v in pairs(t) do
|
||||||
|
out[i]=v
|
||||||
|
end
|
||||||
|
for i,v in ipairs(t) do
|
||||||
|
outi[#outi+1]=v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(outi) do
|
||||||
|
out[i]=v
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
function string.replace(s, target, repl)
|
function string.replace(s, target, repl)
|
||||||
local result = {}
|
local result = {}
|
||||||
local i = 1
|
local i = 1
|
||||||
@@ -210,8 +229,12 @@ function toHex(num)
|
|||||||
return string.format("%X", num)
|
return string.format("%X", num)
|
||||||
end
|
end
|
||||||
|
|
||||||
syscall = setmetatable({}, {
|
local function makeSyscallProxy()
|
||||||
|
local backing = {}
|
||||||
|
return setmetatable(backing, {
|
||||||
__index = function(self, name)
|
__index = function(self, name)
|
||||||
|
local raw = rawget(self, name)
|
||||||
|
if raw ~= nil then return raw end
|
||||||
return function(...)
|
return function(...)
|
||||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
local res = table.pack(coroutine.yield("syscall", name, ...))
|
||||||
if res[1] then
|
if res[1] then
|
||||||
@@ -220,7 +243,16 @@ syscall = setmetatable({}, {
|
|||||||
error(res[2], 2)
|
error(res[2], 2)
|
||||||
end
|
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
|
table.serialize = serialize
|
||||||
@@ -5,14 +5,14 @@ kernel.vfs = vfs
|
|||||||
vfs.mounts = {["$"] = "/"}
|
vfs.mounts = {["$"] = "/"}
|
||||||
vfs.disks = kernel.disks
|
vfs.disks = kernel.disks
|
||||||
|
|
||||||
-- Metafile format (version 1)
|
-- Metafile format (version 2)
|
||||||
-- File header: 1 byte = version (0x01)
|
-- File header: 1 byte = version (0x02)
|
||||||
-- Per-entry:
|
-- Per-entry:
|
||||||
-- 1 byte = name length
|
-- 1 byte = name length
|
||||||
-- N bytes = name
|
-- N bytes = name
|
||||||
-- 1 byte = entry type (0x00 = regular, 0x01 = symlink)
|
-- 1 byte = entry type (0x00 = regular, 0x01 = symlink)
|
||||||
-- 1 byte = owner uid
|
-- 2 bytes = owner uid (little-endian uint16)
|
||||||
-- 1 byte = group gid
|
-- 2 bytes = group gid (little-endian uint16)
|
||||||
-- 2 bytes = perms (little-endian uint16)
|
-- 2 bytes = perms (little-endian uint16)
|
||||||
-- bit 0 = world-write bit 1 = world-read
|
-- bit 0 = world-write bit 1 = world-read
|
||||||
-- bit 2 = group-write bit 3 = group-read
|
-- bit 2 = group-write bit 3 = group-read
|
||||||
@@ -24,12 +24,16 @@ vfs.disks = kernel.disks
|
|||||||
-- 1 byte = cmeta length
|
-- 1 byte = cmeta length
|
||||||
-- N bytes = cmeta (for symlinks: the link target path)
|
-- N bytes = cmeta (for symlinks: the link target path)
|
||||||
--
|
--
|
||||||
|
-- Version 1:
|
||||||
|
-- 1 byte name len, N bytes name, 1 byte etype, 1 byte owner,
|
||||||
|
-- 1 byte group, 2 bytes perms (little-endian), 1 byte cmeta len, N bytes cmeta
|
||||||
|
--
|
||||||
-- Version 0:
|
-- Version 0:
|
||||||
-- No file header. Per-entry:
|
-- No file header. Per-entry:
|
||||||
-- 1 byte name len, N bytes name, 1 byte owner, 1 byte group,
|
-- 1 byte name len, N bytes name, 1 byte owner, 1 byte group,
|
||||||
-- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta
|
-- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta
|
||||||
|
|
||||||
local META_VERSION = 0x01
|
local META_VERSION = 0x02
|
||||||
|
|
||||||
local function bit_is_set(num, bit)
|
local function bit_is_set(num, bit)
|
||||||
return math.floor(num / (2 ^ bit)) % 2 == 1
|
return math.floor(num / (2 ^ bit)) % 2 == 1
|
||||||
@@ -41,8 +45,9 @@ local function parseMetafile(raw)
|
|||||||
local p = 1
|
local p = 1
|
||||||
|
|
||||||
local version = 0
|
local version = 0
|
||||||
if raw:byte(1) == META_VERSION then
|
local firstByte = raw:byte(1)
|
||||||
version = META_VERSION
|
if firstByte == 0x02 or firstByte == 0x01 then
|
||||||
|
version = firstByte
|
||||||
p = 2
|
p = 2
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -54,7 +59,13 @@ local function parseMetafile(raw)
|
|||||||
|
|
||||||
local etype, owner, group, perms, cmeta
|
local etype, owner, group, perms, cmeta
|
||||||
|
|
||||||
if version == META_VERSION then
|
if version == 0x02 then
|
||||||
|
if p + 6 > #raw then break end
|
||||||
|
etype = raw:byte(p); p = p + 1
|
||||||
|
owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
|
||||||
|
group = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
|
||||||
|
perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
|
||||||
|
elseif version == 0x01 then
|
||||||
if p + 4 > #raw then break end
|
if p + 4 > #raw then break end
|
||||||
etype = raw:byte(p); p = p + 1
|
etype = raw:byte(p); p = p + 1
|
||||||
owner = raw:byte(p); p = p + 1
|
owner = raw:byte(p); p = p + 1
|
||||||
@@ -87,35 +98,38 @@ local function makeMetafile(meta)
|
|||||||
for name, m in pairs(meta) do
|
for name, m in pairs(meta) do
|
||||||
local plo = m.perms % 256
|
local plo = m.perms % 256
|
||||||
local phi = math.floor(m.perms / 256) % 256
|
local phi = math.floor(m.perms / 256) % 256
|
||||||
|
local olo = (m.owner or 0) % 256
|
||||||
|
local ohi = math.floor((m.owner or 0) / 256) % 256
|
||||||
|
local glo = (m.group or 0) % 256
|
||||||
|
local ghi = math.floor((m.group or 0) / 256) % 256
|
||||||
out = out
|
out = out
|
||||||
.. string.char(#name) .. name
|
.. string.char(#name) .. name
|
||||||
.. string.char(m.etype or 0x00)
|
.. string.char(m.etype or 0x00)
|
||||||
.. string.char(m.owner, m.group, plo, phi)
|
.. string.char(olo, ohi, glo, ghi, plo, phi)
|
||||||
.. string.char(#m.cmeta) .. m.cmeta
|
.. string.char(#m.cmeta) .. m.cmeta
|
||||||
end
|
end
|
||||||
return out
|
return out
|
||||||
end
|
end
|
||||||
|
|
||||||
local function normalizePath(path)
|
local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$"
|
||||||
local task = kernel.currentTask
|
|
||||||
local cwd = task.cwd or "/"
|
local function tokenizePath(path)
|
||||||
if path:sub(1,1) ~= "/" then path = cwd .. "/" .. path end
|
local isAbsolute = (path:sub(1,1) == "/")
|
||||||
local parts = {}
|
local tokens = {}
|
||||||
for part in path:gmatch("[^/]+") do
|
for comp in (path .. "/"):gmatch("([^/]*)/") do
|
||||||
if part == ".." then
|
table.insert(tokens, comp)
|
||||||
if #parts > 0 then table.remove(parts) end
|
|
||||||
elseif part ~= "." and part ~= "" then
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
end
|
||||||
|
return isAbsolute, tokens
|
||||||
end
|
end
|
||||||
local result = "/" .. table.concat(parts, "/")
|
|
||||||
local root = task and task.root
|
local function validateComponent(comp)
|
||||||
if root and root ~= "/" then
|
local lower = comp:lower()
|
||||||
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
|
if not lower:match(SAFE_COMPONENT_PATTERN) then
|
||||||
result = root
|
error("EINVAL: illegal characters in path component: " .. comp, 3)
|
||||||
end
|
end
|
||||||
|
if lower == ".meta" then
|
||||||
|
error("EINVAL: reserved path component: .meta", 3)
|
||||||
end
|
end
|
||||||
return result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vfs.splitPath(path)
|
function vfs.splitPath(path)
|
||||||
@@ -146,8 +160,10 @@ local function resolveMount(normalPath)
|
|||||||
return vfs.disks[mountId], diskPath
|
return vfs.disks[mountId], diskPath
|
||||||
end
|
end
|
||||||
|
|
||||||
|
vfs._parseMetafile = parseMetafile
|
||||||
|
|
||||||
local function readMetaEntry(disk, parentDiskPath, filename)
|
local function readMetaEntry(disk, parentDiskPath, filename)
|
||||||
if filename == ".meta" then error("Cannot open metafile") end
|
if filename == ".meta" then error("EACCES: Cannot open metafile") end
|
||||||
local mp
|
local mp
|
||||||
if parentDiskPath == "/" then
|
if parentDiskPath == "/" then
|
||||||
mp = ".meta"
|
mp = ".meta"
|
||||||
@@ -159,52 +175,218 @@ local function readMetaEntry(disk, parentDiskPath, filename)
|
|||||||
if not ok or not f then return nil end
|
if not ok or not f then return nil end
|
||||||
local raw = f.read(65535)
|
local raw = f.read(65535)
|
||||||
if f.close then f.close() end
|
if f.close then f.close() end
|
||||||
|
|
||||||
|
if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then
|
||||||
|
local upgraded = makeMetafile(parseMetafile(raw))
|
||||||
|
local wok, wf = pcall(function() return disk:open(mp, "w") end)
|
||||||
|
if wok and wf then wf.write(upgraded); if wf.close then wf.close() end end
|
||||||
|
raw = upgraded
|
||||||
|
end
|
||||||
|
|
||||||
local parsed = parseMetafile(raw)
|
local parsed = parseMetafile(raw)
|
||||||
return parsed[filename]
|
return parsed[filename]
|
||||||
end
|
end
|
||||||
|
|
||||||
local MAX_SYMLINK = 16
|
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 = {}
|
local function namei(path, noFollow, symDepth)
|
||||||
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
|
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
|
if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
|
||||||
local candidate = resolved == "" and ("/" .. part) or (resolved .. "/" .. part)
|
|
||||||
|
|
||||||
if noFollow and i == #parts then
|
local function canTraverse(entry)
|
||||||
resolved = candidate
|
if euid == 0 then return true end
|
||||||
break
|
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
|
||||||
|
end
|
||||||
|
return bit_is_set(bits, 7)
|
||||||
end
|
end
|
||||||
|
|
||||||
local disk, parentDisk = resolveMount(resolved == "" and "/" or resolved)
|
local isAbsolute, tokens = tokenizePath(path)
|
||||||
local entry = readMetaEntry(disk, parentDisk, part)
|
|
||||||
|
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 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
|
local target = entry.cmeta
|
||||||
if target:sub(1,1) ~= "/" then
|
if not target or target == "" then
|
||||||
target = (resolved == "" and "/" or resolved) .. "/" .. target
|
error("ENOENT: empty symlink target")
|
||||||
end
|
|
||||||
if i < #parts then
|
|
||||||
target = target .. "/" .. table.concat(parts, "/", i+1, #parts)
|
|
||||||
end
|
|
||||||
return resolveSymlinks(normalizePath(target), noFollow, _depth + 1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
resolved = candidate
|
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
|
end
|
||||||
|
|
||||||
if resolved == "" then resolved = "/" end
|
local fresh = {}
|
||||||
return resolved
|
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
|
end
|
||||||
|
|
||||||
local function resolvePath(path, noFollow)
|
local function resolvePath(path, noFollow)
|
||||||
local real = resolveSymlinks(path, noFollow)
|
local real = namei(path, noFollow)
|
||||||
local disk, diskPath = resolveMount(real)
|
local disk, diskPath = resolveMount(real)
|
||||||
if kernel.config.logPathResolution then
|
if kernel.config.logPathResolution then
|
||||||
kernel.log("resolvePath '"..path.."' -> '"..real.."' diskPath '"..diskPath.."'")
|
kernel.log("resolvePath '"..path.."' -> '"..real.."' diskPath '"..diskPath.."'")
|
||||||
@@ -213,24 +395,38 @@ local function resolvePath(path, noFollow)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function getFileMeta(path, noFollow)
|
local function getFileMeta(path, noFollow)
|
||||||
local real = resolveSymlinks(path, noFollow)
|
local real = namei(path, noFollow)
|
||||||
|
|
||||||
local parts = {}
|
if real == "/" then
|
||||||
for p in real:gmatch("[^/]+") do table.insert(parts, p) end
|
return { etype = 0x00, owner = 0, group = 0, perms = 62, cmeta = "" }
|
||||||
|
end
|
||||||
|
|
||||||
local default = { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
|
local cur = real
|
||||||
if #parts == 0 then return default end
|
|
||||||
|
|
||||||
local parentNorm = "/" .. table.concat(parts, "/", 1, #parts - 1)
|
-- FML i hated implementing this - Astronand
|
||||||
if parentNorm == "" then parentNorm = "/" end
|
while true do
|
||||||
local disk, parentDiskPath = resolveMount(parentNorm)
|
local parent, name = cur:match("^(.*)/([^/]+)$")
|
||||||
local entry = readMetaEntry(disk, parentDiskPath, parts[#parts])
|
if not parent or parent == "" then parent = "/" end
|
||||||
if entry then return entry end
|
|
||||||
return default
|
local disk, parentDiskPath = resolveMount(parent)
|
||||||
|
local entry = readMetaEntry(disk, parentDiskPath, name)
|
||||||
|
|
||||||
|
if entry then
|
||||||
|
return entry
|
||||||
|
end
|
||||||
|
|
||||||
|
if parent == "/" or cur == "/" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
cur = parent
|
||||||
|
end
|
||||||
|
|
||||||
|
return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
|
||||||
end
|
end
|
||||||
|
|
||||||
local function writeMetaEntry(path, name, entry, noFollow)
|
local function writeMetaEntry(path, name, entry, noFollow)
|
||||||
local real = resolveSymlinks(path, noFollow)
|
local real = namei(path, noFollow)
|
||||||
local disk, diskPath = resolveMount(real)
|
local disk, diskPath = resolveMount(real)
|
||||||
|
|
||||||
local mp
|
local mp
|
||||||
@@ -400,6 +596,8 @@ function vfs.open(path, mode)
|
|||||||
if not disk then error("NODISK") end
|
if not disk then error("NODISK") end
|
||||||
|
|
||||||
local meta = getFileMeta(path)
|
local meta = getFileMeta(path)
|
||||||
|
local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
|
||||||
|
|
||||||
checkperms(meta, mode == "r" and "r" or "w")
|
checkperms(meta, mode == "r" and "r" or "w")
|
||||||
|
|
||||||
local handle
|
local handle
|
||||||
@@ -408,10 +606,26 @@ function vfs.open(path, mode)
|
|||||||
if type(handle) ~= "table" then error("ENFILE") end
|
if type(handle) ~= "table" then error("ENFILE") end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if isNew then
|
||||||
|
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||||
|
local egid = (task and task.gid) or 0
|
||||||
|
local norm = normalizePath(path)
|
||||||
|
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||||
|
if parent == "" then parent = "/" end
|
||||||
|
local name = norm:match("[^/]+$")
|
||||||
|
if name then
|
||||||
|
local entry = { etype=0x00, owner=euid, group=egid,
|
||||||
|
perms=vfs.PERM.RW_R_R, cmeta="" }
|
||||||
|
pcall(writeMetaEntry, parent, name, entry, false)
|
||||||
|
meta = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath))
|
local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath))
|
||||||
if mode == "r" and bit_is_set(meta.perms, 6) then
|
if mode == "r" and bit_is_set(meta.perms, 6) then
|
||||||
fobj.suid_owner = meta.owner
|
fobj.suid_owner = meta.owner
|
||||||
end
|
end
|
||||||
|
if disk.isvirt then fobj.isvirt=true end
|
||||||
task.fd[fd] = fobj
|
task.fd[fd] = fobj
|
||||||
if not disk.isvirt then total = total + 1 end
|
if not disk.isvirt then total = total + 1 end
|
||||||
return fd
|
return fd
|
||||||
@@ -460,8 +674,10 @@ function vfs.close(fd)
|
|||||||
local task = kernel.currentTask
|
local task = kernel.currentTask
|
||||||
local file = task.fd[fd]
|
local file = task.fd[fd]
|
||||||
if not file then error("EBADF") end
|
if not file then error("EBADF") end
|
||||||
task.fd[fd] = nil
|
if not task.fd[fd].isvirt then
|
||||||
total = total - 1
|
total = total - 1
|
||||||
|
end
|
||||||
|
task.fd[fd] = nil
|
||||||
file.refcount = file.refcount - 1
|
file.refcount = file.refcount - 1
|
||||||
if file.refcount <= 0 and file.handle and file.handle.close then
|
if file.refcount <= 0 and file.handle and file.handle.close then
|
||||||
file.handle.close()
|
file.handle.close()
|
||||||
@@ -574,22 +790,39 @@ function vfs.listdir(path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function vfs.mkdir(path)
|
function vfs.mkdir(path)
|
||||||
|
local norm = normalizePath(path)
|
||||||
|
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||||
|
if parent == "" then parent = "/" end
|
||||||
|
local parentMeta = getFileMeta(parent)
|
||||||
|
checkperms(parentMeta, "w")
|
||||||
local disk, diskPath = resolvePath(path)
|
local disk, diskPath = resolvePath(path)
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "w")
|
|
||||||
disk:makeDirectory(diskPath)
|
disk:makeDirectory(diskPath)
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||||
|
local egid = (task and task.gid) or 0
|
||||||
|
local name = norm:match("[^/]+$")
|
||||||
|
if name then
|
||||||
|
local entry = { etype=0x00, owner=euid, group=egid,
|
||||||
|
perms=vfs.PERM.RWX_RX, cmeta="" }
|
||||||
|
pcall(writeMetaEntry, parent, name, entry, false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function vfs.remove(path)
|
function vfs.remove(path)
|
||||||
|
local norm = namei(path, true)
|
||||||
|
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||||
|
if parent == "" then parent = "/" end
|
||||||
|
local parentMeta = getFileMeta(parent)
|
||||||
|
checkperms(parentMeta, "w")
|
||||||
|
|
||||||
local meta = getFileMeta(path, true)
|
local meta = getFileMeta(path, true)
|
||||||
checkperms(meta, "w")
|
|
||||||
|
|
||||||
if kernel.unixSockets and kernel.unixSockets[path] then
|
if kernel.unixSockets and kernel.unixSockets[path] then
|
||||||
kernel.unixSockets[path] = nil
|
kernel.unixSockets[path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if meta.etype == 0x01 then
|
if meta.etype == 0x01 then
|
||||||
local norm = resolveSymlinks(path, true)
|
local norm = namei(path, true)
|
||||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||||
if parent == "" then parent = "/" end
|
if parent == "" then parent = "/" end
|
||||||
local name = norm:match("[^/]+$")
|
local name = norm:match("[^/]+$")
|
||||||
@@ -655,7 +888,7 @@ function vfs.access(path, mode)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function updateMeta(path, fn, noFollow)
|
local function updateMeta(path, fn, noFollow)
|
||||||
local real = resolveSymlinks(path, noFollow)
|
local real = namei(path, noFollow)
|
||||||
local norm = real
|
local norm = real
|
||||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||||
if parent == "" then parent = "/" end
|
if parent == "" then parent = "/" end
|
||||||
@@ -16,7 +16,7 @@ proxy.getLabel = function() return "devfs" end
|
|||||||
proxy.attributes = function(path) return {
|
proxy.attributes = function(path) return {
|
||||||
size = 0,
|
size = 0,
|
||||||
modified = 0,
|
modified = 0,
|
||||||
created = 0,
|
created = 0
|
||||||
} end
|
} end
|
||||||
|
|
||||||
function proxy:open(path, mode)
|
function proxy:open(path, mode)
|
||||||
265
Src/Hyperion-kernel/lib/modules/hyperion/12_procfs.kmod
Normal file
265
Src/Hyperion-kernel/lib/modules/hyperion/12_procfs.kmod
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
|
||||||
|
local proxy = {}
|
||||||
|
local data = {}
|
||||||
|
|
||||||
|
proxy.address = "procfs0000"
|
||||||
|
proxy.isvirt = true
|
||||||
|
proxy.isReadOnly = function() return false end
|
||||||
|
proxy.spaceUsed = function() return 0 end
|
||||||
|
proxy.spaceTotal = function() return 0 end
|
||||||
|
proxy.makeDirectory = function() error("EACCES") end
|
||||||
|
proxy.remove = function() error("EACCES") end
|
||||||
|
proxy.setLabel = function() error("EACCES") end
|
||||||
|
proxy.getLabel = function() return "procfs" end
|
||||||
|
proxy.attributes = function(path) return {
|
||||||
|
size = 0,
|
||||||
|
modified = 0,
|
||||||
|
created = 0
|
||||||
|
} 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 "character device"
|
||||||
|
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
|
||||||
|
|
||||||
|
local function newtaskproxy(task)
|
||||||
|
local files,siblings,children={},{},{}
|
||||||
|
if task.fd[0] then files["0"]=task.fd[0].path end
|
||||||
|
for i,v in ipairs(task.fd) do
|
||||||
|
files[tostring(i)]=tostring(v.path)
|
||||||
|
end
|
||||||
|
for i,v in ipairs(task.siblings) do
|
||||||
|
siblings[tostring(v.pid)]="/proc/"..tostring(v.pid)
|
||||||
|
end
|
||||||
|
for i,v in ipairs(task.children) do
|
||||||
|
children[tostring(v.pid)]="/proc/"..tostring(v.pid)
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
[".meta"]=strFile(buildMeta({cwd=task.cwd,parent="/proc/"..tostring(task.parent.pid)})),
|
||||||
|
uid=strFile(task.uid),
|
||||||
|
comm=strFile(task.name),
|
||||||
|
fd={
|
||||||
|
[".meta"]=strFile(buildMeta(files))
|
||||||
|
},
|
||||||
|
siblings={
|
||||||
|
[".meta"]=strFile(buildMeta(siblings))
|
||||||
|
},
|
||||||
|
children={
|
||||||
|
[".meta"]=strFile(buildMeta(children))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:open(path, mode)
|
||||||
|
local steps = kernel.vfs.splitPath(path)
|
||||||
|
local step = data
|
||||||
|
if tonumber(steps[1]) then
|
||||||
|
local task=kernel.tasks[tostring(steps[1])]
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("open", mode)
|
||||||
|
end
|
||||||
|
elseif tostring(steps[1])=="self" then
|
||||||
|
local task=kernel.currentTask
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("open", mode)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i=1, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("open", mode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("ENFILE")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:type(path, mode)
|
||||||
|
local steps = kernel.vfs.splitPath(path)
|
||||||
|
local step = data
|
||||||
|
if #steps == 0 then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
if tonumber(steps[1]) then
|
||||||
|
local task=kernel.tasks[steps[1]]
|
||||||
|
if #steps==1 then return "directory" end
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("type", mode)
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
elseif tostring(steps[1])=="self" then
|
||||||
|
local task=kernel.currentTask
|
||||||
|
if #steps==1 then return "directory" end
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("type", mode)
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i=1, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("type", mode)
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("ENOENT")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:list(path)
|
||||||
|
local steps = kernel.vfs.splitPath(path)
|
||||||
|
local step = data
|
||||||
|
if #steps == 0 then
|
||||||
|
return table.merge(table.keys(data),table.keys(kernel.tasks),{"self"})
|
||||||
|
end
|
||||||
|
if tonumber(steps[1]) then
|
||||||
|
local task=kernel.tasks[steps[1]]
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
if #steps==1 then return table.keys(step) end
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENOENT") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return table.keys(step[steps[#steps]])
|
||||||
|
end
|
||||||
|
elseif tostring(steps[1])=="self" then
|
||||||
|
local task=kernel.currentTask
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
if #steps==1 then return table.keys(step) end
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENOENT") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return table.keys(step[steps[#steps]])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i=1, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENOENT") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return table.keys(step[steps[#steps]])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("ENOENT")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:fileExists(path)
|
||||||
|
local ok = pcall(function()
|
||||||
|
return self:type(path)
|
||||||
|
end)
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
data.uptime=simpleFile(function()return tostring(kernel.computer:getUptime())end,function()error("EACCES")end)
|
||||||
|
kernel.procfs={}
|
||||||
|
kernel.procfs.data=data
|
||||||
|
kernel.procfs.proxy=proxy
|
||||||
|
kernel.disks["procfs0000"]=proxy
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--:Minify:--
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
|
|
||||||
local proxy = {}
|
local proxy = {}
|
||||||
@@ -3,6 +3,7 @@ local args = {...}
|
|||||||
local kernel = args[1]
|
local kernel = args[1]
|
||||||
kernel._G = _G
|
kernel._G = _G
|
||||||
|
|
||||||
|
|
||||||
local function readonly(tbl)
|
local function readonly(tbl)
|
||||||
return setmetatable({}, {
|
return setmetatable({}, {
|
||||||
__index = function(_, key)
|
__index = function(_, key)
|
||||||
@@ -20,7 +21,7 @@ local function readonly(tbl)
|
|||||||
error("Attempt to modify global variable '" .. k .. "'", 2)
|
error("Attempt to modify global variable '" .. k .. "'", 2)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__pairs = function()
|
__pairs = function(self)
|
||||||
local function iter(_, key)
|
local function iter(_, key)
|
||||||
local nextKey, value = next(tbl, key)
|
local nextKey, value = next(tbl, key)
|
||||||
if type(value) == "table" then
|
if type(value) == "table" then
|
||||||
@@ -28,7 +29,7 @@ local function readonly(tbl)
|
|||||||
end
|
end
|
||||||
return nextKey, value
|
return nextKey, value
|
||||||
end
|
end
|
||||||
return iter, tbl, nil
|
return iter, self, nil
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__ipairs = function()
|
__ipairs = function()
|
||||||
@@ -49,8 +50,24 @@ local function readonly(tbl)
|
|||||||
__metatable = false
|
__metatable = false
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
local origLoad = load
|
||||||
|
|
||||||
kernel._U = readonly(kernel._G)
|
kernel._U = readonly(kernel._G)
|
||||||
kernel.allowGlobalOverwrites = true
|
|
||||||
kernel._U._G = kernel._U
|
kernel._U._G = kernel._U
|
||||||
kernel.allowGlobalOverwrites = false
|
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
|
||||||
@@ -236,19 +236,18 @@ local function nextUID()
|
|||||||
return max + 1
|
return max + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function auth.login(username, password)
|
function auth.login(uid, password)
|
||||||
if type(username) ~= "string" or type(password) ~= "string" then
|
if type(uid) ~= "number" or type(password) ~= "string" then
|
||||||
return nil, "Authentication failure"
|
return nil, "Authentication failure"
|
||||||
end
|
end
|
||||||
|
|
||||||
local entry = getPasswdByUsername(username)
|
local entry = getPasswdByUID(uid)
|
||||||
if not entry then
|
if not entry then
|
||||||
-- timing attack resistance
|
-- timing attack resistance
|
||||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||||
return nil, "Authentication failure"
|
return nil, "Authentication failure"
|
||||||
end
|
end
|
||||||
|
|
||||||
local uid = tonumber(entry[1])
|
|
||||||
local sEntry = getShadowByUID(uid)
|
local sEntry = getShadowByUID(uid)
|
||||||
if not sEntry then
|
if not sEntry then
|
||||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||||
@@ -273,7 +272,7 @@ function auth.login(username, password)
|
|||||||
_task.egid = tonumber(entry[2]) or uid
|
_task.egid = tonumber(entry[2]) or uid
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
|
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. getPasswdByUID(uid)[3] .. ")")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -394,6 +393,8 @@ function auth.newUser(username, password, gid, homedir, shell)
|
|||||||
|
|
||||||
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
||||||
kernel.vfs.mkdir(homedir)
|
kernel.vfs.mkdir(homedir)
|
||||||
|
-- Homedir must be owned by the new user, not root
|
||||||
|
pcall(kernel.vfs.chown, homedir, uid, uid)
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
||||||
@@ -591,13 +592,13 @@ function auth.elevate(targetUsername, password)
|
|||||||
|
|
||||||
local task = kernel.currentTask
|
local task = kernel.currentTask
|
||||||
local prevUid = task.uid
|
local prevUid = task.uid
|
||||||
task.uid = uid
|
task.uid = 0
|
||||||
task.euid = uid
|
task.euid = 0
|
||||||
task.gid = tonumber(entry[2]) or uid
|
task.gid = 0
|
||||||
task.egid = tonumber(entry[2]) or uid
|
task.egid = 0
|
||||||
kernel.uid = uid
|
kernel.uid = 0
|
||||||
|
|
||||||
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")")
|
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")")
|
||||||
return true, uid
|
return true, uid
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -5,18 +5,22 @@ local sys = {}
|
|||||||
local nextpid = 2
|
local nextpid = 2
|
||||||
kernel.exitMain = false
|
kernel.exitMain = false
|
||||||
|
|
||||||
|
local resumeWithTimeout = coroutine.resumeWithTimeout
|
||||||
|
|
||||||
local function bit_is_set(num, bit)
|
local function bit_is_set(num, bit)
|
||||||
return math.floor(num / (2 ^ bit)) % 2 == 1
|
return math.floor(num / (2 ^ bit)) % 2 == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local function loadExecutable(path, env)
|
local function loadExecutable(path)
|
||||||
kernel.vfs.access(path, "rx")
|
kernel.vfs.access(path, "rx")
|
||||||
|
|
||||||
local fd = kernel.vfs.open(path, "r")
|
local fd = kernel.vfs.open(path, "r")
|
||||||
local data = kernel.vfs.read(fd, 1024 * 1024 * 4)
|
local data = kernel.vfs.read(fd, 1024 * 1024 * 4)
|
||||||
kernel.vfs.close(fd)
|
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
|
if not func then error("ENOEXEC: " .. tostring(err)) end
|
||||||
|
|
||||||
local meta = kernel.vfs.lstat(path)
|
local meta = kernel.vfs.lstat(path)
|
||||||
@@ -206,11 +210,15 @@ function sys.kill(pid)
|
|||||||
return false, "Task does not exist"
|
return false, "Task does not exist"
|
||||||
elseif task.status == "Z" then
|
elseif task.status == "Z" then
|
||||||
return false, "Task is already dead"
|
return false, "Task is already dead"
|
||||||
else
|
end
|
||||||
|
local caller = kernel.currentTask
|
||||||
|
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
|
||||||
|
if ceuid ~= 0 and task.uid ~= (caller and caller.uid or kernel.uid) then
|
||||||
|
return false, "EPERM"
|
||||||
|
end
|
||||||
task.status = "Z"
|
task.status = "Z"
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
function sys.stop(pid)
|
function sys.stop(pid)
|
||||||
local task = tasks[tostring(pid)]
|
local task = tasks[tostring(pid)]
|
||||||
@@ -351,10 +359,25 @@ function kernel.main()
|
|||||||
|
|
||||||
if task.sigq and #task.sigq ~= 0 and task.sigh then
|
if task.sigq and #task.sigq ~= 0 and task.sigh then
|
||||||
local coro = coroutine.create(task.sigh)
|
local coro = coroutine.create(task.sigh)
|
||||||
if kernel.config.preempt then
|
local sigret = { coroutine.resume(coro, table.remove(task.sigq, 1)) }
|
||||||
coroutine.resumeWithTimeout(coro, task.timeSlice, 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
|
else
|
||||||
coroutine.resume(coro, table.remove(task.sigq, 1))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -363,7 +386,7 @@ function kernel.main()
|
|||||||
local ret
|
local ret
|
||||||
|
|
||||||
if kernel.config.preempt then
|
if kernel.config.preempt then
|
||||||
ret = { coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
|
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
|
||||||
else
|
else
|
||||||
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
|
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
|
||||||
end
|
end
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--:Minify:--
|
||||||
local kernel=...
|
local kernel=...
|
||||||
local sysc=kernel.syscalls
|
local sysc=kernel.syscalls
|
||||||
kernel.gpio={}
|
kernel.gpio={}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
local kernel = ...
|
local kernel = ...
|
||||||
|
|
||||||
kernel.processes.login = function()
|
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
|
if not ok then
|
||||||
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
|
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
|
||||||
end
|
end
|
||||||
120
Src/Hyperion-kernel/lib/modules/hyperion/92_setup.kmod
Normal file
120
Src/Hyperion-kernel/lib/modules/hyperion/92_setup.kmod
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
|
||||||
|
local P = kernel.vfs.P
|
||||||
|
local PERM = kernel.vfs.PERM
|
||||||
|
|
||||||
|
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
|
||||||
|
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
|
||||||
|
+ P.GROUP_R + P.GROUP_X
|
||||||
|
+ P.WORLD_R + P.WORLD_X
|
||||||
|
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
|
||||||
|
local RW____ = P.OWNER_R + P.OWNER_W
|
||||||
|
local RWXRWXRWX = PERM.RWXRWXRWX
|
||||||
|
local SUID_755 = PERM.SUID_755
|
||||||
|
|
||||||
|
local META_VERSION = 0x02
|
||||||
|
local rootDisk = kernel.disks["$"]
|
||||||
|
|
||||||
|
local function makeEntry(name, etype, owner, group, perms, cmeta)
|
||||||
|
cmeta = cmeta or ""
|
||||||
|
local plo = perms % 256
|
||||||
|
local phi = math.floor(perms / 256) % 256
|
||||||
|
local olo = (owner or 0) % 256
|
||||||
|
local ohi = math.floor((owner or 0) / 256) % 256
|
||||||
|
local glo = (group or 0) % 256
|
||||||
|
local ghi = math.floor((group or 0) / 256) % 256
|
||||||
|
return string.char(#name) .. name
|
||||||
|
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
|
||||||
|
.. string.char(#cmeta) .. cmeta
|
||||||
|
end
|
||||||
|
|
||||||
|
local REG = 0x00
|
||||||
|
|
||||||
|
local function mergeMeta(dir, entries)
|
||||||
|
local diskDir = dir
|
||||||
|
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
|
||||||
|
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
|
||||||
|
|
||||||
|
local existing = {}
|
||||||
|
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
|
||||||
|
if rok and rf then
|
||||||
|
local raw = rf.read(65535)
|
||||||
|
if rf.close then rf.close() end
|
||||||
|
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, e in ipairs(entries) do
|
||||||
|
local name = e[1]
|
||||||
|
local etype = e[2] or REG
|
||||||
|
local owner = e[3] or 0
|
||||||
|
local group = e[4] or 0
|
||||||
|
local perms = e[5] or RWX_RX_RX
|
||||||
|
local cmeta = e[6] or ""
|
||||||
|
existing[name] = {
|
||||||
|
etype = etype,
|
||||||
|
owner = owner,
|
||||||
|
group = group,
|
||||||
|
perms = perms,
|
||||||
|
cmeta = cmeta,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local data = string.char(META_VERSION)
|
||||||
|
for name, m in pairs(existing) do
|
||||||
|
data = data .. makeEntry(
|
||||||
|
name,
|
||||||
|
m.etype or REG,
|
||||||
|
m.owner or 0,
|
||||||
|
m.group or 0,
|
||||||
|
m.perms or RWX_RX_RX,
|
||||||
|
m.cmeta or ""
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = pcall(function()
|
||||||
|
local f = rootDisk:open(metaPath, "w")
|
||||||
|
f.write(data)
|
||||||
|
f.close()
|
||||||
|
end)
|
||||||
|
if not ok then
|
||||||
|
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if kernel.firstBoot then
|
||||||
|
kernel.log("Seeding filesystem permissions...")
|
||||||
|
|
||||||
|
mergeMeta("/", {
|
||||||
|
{"bin", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"boot", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"dev", REG, 0, 0, RWXRWXRWX},
|
||||||
|
{"etc", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"home", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"lib", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"root", REG, 0, 0, RW____ },
|
||||||
|
{"sbin", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"tmp", REG, 0, 0, RWXRWXRWX},
|
||||||
|
{"usr", REG, 0, 0, RWX_RX_RX},
|
||||||
|
{"var", REG, 0, 0, RWXRWXRWX},
|
||||||
|
{"opt", REG, 0, 0, RWX_RX_RX},
|
||||||
|
})
|
||||||
|
|
||||||
|
mergeMeta("/bin", {
|
||||||
|
{"login", REG, 0, 0, SUID_755 },
|
||||||
|
{"su", REG, 0, 0, SUID_755 },
|
||||||
|
{"sudo", REG, 0, 0, SUID_755 },
|
||||||
|
})
|
||||||
|
|
||||||
|
mergeMeta("/etc", {
|
||||||
|
{"passwd", REG, 0, 0, RW_R_R },
|
||||||
|
{"shadow", REG, 0, 0, RW____ },
|
||||||
|
{"pam.d", REG, 0, 0, RW____ },
|
||||||
|
})
|
||||||
|
|
||||||
|
mergeMeta("/etc/pam.d", {
|
||||||
|
{"secret", REG, 0, 0, RW____},
|
||||||
|
})
|
||||||
|
|
||||||
|
kernel.log("Filesystem permissions seeded.")
|
||||||
|
end
|
||||||
3
Src/Hyperion-kernel/lib/modules/hyperion/99_final.kmod
Normal file
3
Src/Hyperion-kernel/lib/modules/hyperion/99_final.kmod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
kernel.allowGlobalOverwrites = false
|
||||||
@@ -64,12 +64,18 @@ local newGid = resolveGid(groupStr)
|
|||||||
local function chgrpPath(path)
|
local function chgrpPath(path)
|
||||||
local stat = syscall.stat(path)
|
local stat = syscall.stat(path)
|
||||||
if not stat then
|
if not stat then
|
||||||
print(name .. ": cannot stat '" .. path .. "': No such file or directory")
|
print(name .. ": cannot stat '" .. path .. "': no such file or directory")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local ok, err = pcall(syscall.chown, path, stat.owner, newGid)
|
local ok, err = pcall(syscall.chown, path, stat.owner, newGid)
|
||||||
if not ok then
|
if not ok then
|
||||||
print(name .. ": cannot change group of '" .. path .. "': " .. tostring(err))
|
local msg = tostring(err)
|
||||||
|
if msg:find("EPERM") or msg:find("EACCES") then
|
||||||
|
msg = "operation not permitted (must be root)"
|
||||||
|
elseif msg:find("ENOENT") then
|
||||||
|
msg = "no such file or directory"
|
||||||
|
end
|
||||||
|
print(name .. ": cannot change group of '" .. path .. "': " .. msg)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
@@ -220,7 +220,13 @@ local function chmodPath(path)
|
|||||||
|
|
||||||
local ok, cerr = pcall(syscall.chmod, path, newPerms)
|
local ok, cerr = pcall(syscall.chmod, path, newPerms)
|
||||||
if not ok then
|
if not ok then
|
||||||
print(name .. ": cannot change permissions of '" .. path .. "': " .. tostring(cerr))
|
local msg = tostring(cerr)
|
||||||
|
if msg:find("EACCES") or msg:find("EPERM") then
|
||||||
|
msg = "permission denied"
|
||||||
|
elseif msg:find("ENOENT") then
|
||||||
|
msg = "no such file or directory"
|
||||||
|
end
|
||||||
|
print(name .. ": cannot change permissions of '" .. path .. "': " .. msg)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
@@ -95,14 +95,20 @@ end
|
|||||||
local function chownPath(path)
|
local function chownPath(path)
|
||||||
local stat = syscall.stat(path)
|
local stat = syscall.stat(path)
|
||||||
if not stat then
|
if not stat then
|
||||||
print(name .. ": cannot stat '" .. path .. "': No such file or directory")
|
print(name .. ": cannot stat '" .. path .. "': no such file or directory")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local uid = newUid ~= nil and newUid or stat.owner
|
local uid = newUid ~= nil and newUid or stat.owner
|
||||||
local gid = newGid ~= nil and newGid or stat.group
|
local gid = newGid ~= nil and newGid or stat.group
|
||||||
local ok, err = pcall(syscall.chown, path, uid, gid)
|
local ok, err = pcall(syscall.chown, path, uid, gid)
|
||||||
if not ok then
|
if not ok then
|
||||||
print(name .. ": cannot change owner of '" .. path .. "': " .. tostring(err))
|
local msg = tostring(err)
|
||||||
|
if msg:find("EPERM") or msg:find("EACCES") then
|
||||||
|
msg = "operation not permitted (must be root)"
|
||||||
|
elseif msg:find("ENOENT") then
|
||||||
|
msg = "no such file or directory"
|
||||||
|
end
|
||||||
|
print(name .. ": cannot change owner of '" .. path .. "': " .. msg)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
@@ -33,7 +33,6 @@ local host_str = syscall.getHost() or "Unknown"
|
|||||||
local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str
|
local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str
|
||||||
|
|
||||||
local info = {
|
local info = {
|
||||||
-- {label, value} label=nil means print value as-is (userhost / separator)
|
|
||||||
{nil, userhost},
|
{nil, userhost},
|
||||||
{nil, string.rep("-", #userhost)},
|
{nil, string.rep("-", #userhost)},
|
||||||
{"OS", syscall.version() or "Unknown"},
|
{"OS", syscall.version() or "Unknown"},
|
||||||
@@ -42,7 +41,7 @@ local info = {
|
|||||||
{"Uptime", formatUptime(syscall.getUptime() or 0)},
|
{"Uptime", formatUptime(syscall.getUptime() or 0)},
|
||||||
{"Tasks", tostring(#(syscall.getTasks() or {}))},
|
{"Tasks", tostring(#(syscall.getTasks() or {}))},
|
||||||
{"Shell", syscall.getEnviron("SHELL") or "Unknown"},
|
{"Shell", syscall.getEnviron("SHELL") or "Unknown"},
|
||||||
{"Terminal", "TTY1"},
|
{"Terminal", "tty1"},
|
||||||
{"UID", tostring(syscall.getuid())},
|
{"UID", tostring(syscall.getuid())},
|
||||||
{"Packages", "n/a (spm)"},
|
{"Packages", "n/a (spm)"},
|
||||||
}
|
}
|
||||||
@@ -71,15 +70,12 @@ local lines = math.max(#logo, #info)
|
|||||||
for i = 1, lines do
|
for i = 1, lines do
|
||||||
local logo_str = logo[i] or string.rep(" ", 36)
|
local logo_str = logo[i] or string.rep(" ", 36)
|
||||||
|
|
||||||
-- print logo segment in cyan
|
|
||||||
c(C_LOGO)
|
c(C_LOGO)
|
||||||
printInline(logo_str)
|
printInline(logo_str)
|
||||||
|
|
||||||
-- print separator pipe
|
|
||||||
c(C_LABEL)
|
c(C_LABEL)
|
||||||
printInline("| ")
|
printInline("| ")
|
||||||
|
|
||||||
-- print info segment
|
|
||||||
local row = info[i]
|
local row = info[i]
|
||||||
if row then
|
if row then
|
||||||
if row[1] == nil and i == 1 then
|
if row[1] == nil and i == 1 then
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
|
syscall.open("/dev/tty/1","r") --stdin (Device 0)
|
||||||
syscall.open("/dev/tty/TTY1","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 success, errorMsg = xpcall(function()
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
|
|
||||||
syscall.devctl(1,"clear")
|
syscall.devctl(1,"clear")
|
||||||
syscall.devctl(1,"sfgc",1)
|
syscall.devctl(1,"sfgc",1)
|
||||||
@@ -15,9 +15,15 @@ print("HyperionOS hysh Shell")
|
|||||||
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
||||||
local commandHistory = {}
|
local commandHistory = {}
|
||||||
local terminate = false
|
local terminate = false
|
||||||
syscall.setEnviron("SHELL","rtbash")
|
syscall.setEnviron("SHELL","hysh")
|
||||||
syscall.setEnviron("PATH","/bin/")
|
syscall.setEnviron("PATH","/bin/")
|
||||||
|
local _home = syscall.getEnviron("HOME")
|
||||||
|
if _home and _home ~= "" then
|
||||||
|
local ok = pcall(syscall.chdir, _home)
|
||||||
|
if not ok then syscall.chdir("/") end
|
||||||
|
else
|
||||||
syscall.chdir("/")
|
syscall.chdir("/")
|
||||||
|
end
|
||||||
local oldWD = ""
|
local oldWD = ""
|
||||||
|
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
@@ -127,8 +133,6 @@ local function copyfile(src, dst)
|
|||||||
if not data then return false, err end
|
if not data then return false, err end
|
||||||
local ok, err2 = writefile(dst, data)
|
local ok, err2 = writefile(dst, data)
|
||||||
if not ok then return false, err2 end
|
if not ok then return false, err2 end
|
||||||
local ok2, stat = pcall(syscall.stat, src)
|
|
||||||
if ok2 and stat and stat.perms then pcall(syscall.chmod, dst, stat.perms) end
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -804,6 +808,160 @@ builtinCmds.df = function(...)
|
|||||||
end
|
end
|
||||||
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()
|
local function getUserInput()
|
||||||
syscall.devctl(1,"sfgc",3)
|
syscall.devctl(1,"sfgc",3)
|
||||||
syscall.write(1, userhost)
|
syscall.write(1, userhost)
|
||||||
@@ -823,6 +981,41 @@ local function getUserInput()
|
|||||||
local history = 0
|
local history = 0
|
||||||
local dirty = true
|
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()
|
local function redraw()
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||||
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
||||||
@@ -835,21 +1028,31 @@ local function getUserInput()
|
|||||||
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
||||||
end
|
end
|
||||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
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
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local key = syscall.read(0)
|
local key = syscall.read(0)
|
||||||
if key and key ~= "" then
|
if key and key ~= "" then
|
||||||
if key=="\19" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end
|
if key=="[D" 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=="[C" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end
|
||||||
elseif key=="\17" then
|
elseif key=="[A" then
|
||||||
if history<#commandHistory then
|
if history<#commandHistory then
|
||||||
history=history+1
|
history=history+1
|
||||||
input=commandHistory[#commandHistory-history+1]
|
input=commandHistory[#commandHistory-history+1]
|
||||||
cursorPos=#input+1;dirty=true
|
cursorPos=#input+1;dirty=true
|
||||||
end
|
end
|
||||||
elseif key=="\18" then
|
elseif key=="[B" then
|
||||||
if history>1 then
|
if history>1 then
|
||||||
history=history-1
|
history=history-1
|
||||||
input=commandHistory[#commandHistory-history+1]
|
input=commandHistory[#commandHistory-history+1]
|
||||||
@@ -857,6 +1060,38 @@ local function getUserInput()
|
|||||||
elseif history==1 then
|
elseif history==1 then
|
||||||
history=0;input="";cursorPos=1;dirty=true
|
history=0;input="";cursorPos=1;dirty=true
|
||||||
end
|
end
|
||||||
|
elseif key=="[H" then cursorPos=1;dirty=true
|
||||||
|
elseif key=="[F" 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
|
elseif key=="\b" then
|
||||||
if cursorPos>1 then
|
if cursorPos>1 then
|
||||||
input=string.sub(input,1,cursorPos-2)..string.sub(input,cursorPos)
|
input=string.sub(input,1,cursorPos-2)..string.sub(input,cursorPos)
|
||||||
@@ -867,7 +1102,7 @@ local function getUserInput()
|
|||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||||
syscall.write(1, input.." \n")
|
syscall.write(1, input.." \n")
|
||||||
return input
|
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)
|
input=string.sub(input,1,cursorPos-1)..key..string.sub(input,cursorPos)
|
||||||
cursorPos=cursorPos+1;dirty=true
|
cursorPos=cursorPos+1;dirty=true
|
||||||
end
|
end
|
||||||
@@ -940,23 +1175,16 @@ local function runCommand(command)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local text = fs.readAllText(cmdPath)
|
local proc = syscall.spawn(function()
|
||||||
local program, err = load(text, progName)
|
-- Open standard fds so programs that don't do it themselves work correctly.
|
||||||
if not program then
|
syscall.open("/dev/tty/1", "r") -- fd 0 stdin
|
||||||
syscall.devctl(1,"sfgc",2)
|
syscall.open("/dev/tty/1", "w") -- fd 1 stdout
|
||||||
local line, rest = tostring(err):match(":(%d+): (.+)$")
|
syscall.open("/dev/null", "w") -- fd 2 stderr
|
||||||
if line then printInline(progName..": load error on line "..line..": "); print(rest)
|
-- exec replaces this coroutine's code with a fresh isolated environment
|
||||||
else print(progName..": load error: "..tostring(err)) end
|
-- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
|
||||||
syscall.devctl(1,"sfgc",1); return
|
-- so the child cannot share any upvalue or syscall table state with hysh.
|
||||||
end
|
syscall.exec(cmdPath, {table.unpack(args, 2)})
|
||||||
|
end, progName)
|
||||||
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)})
|
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local exited, code = syscall.collect(proc)
|
local exited, code = syscall.collect(proc)
|
||||||
3
Src/hysh/bin/ll
Normal file
3
Src/hysh/bin/ll
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
local args={...}
|
||||||
|
table.insert(args, "-lah")
|
||||||
|
syscall.exec("/bin/ls", args)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
syscall.open("/dev/tty/TTY1", "r") --stdin (fd 0)
|
syscall.open("/dev/tty/1", "r") --stdin (fd 0)
|
||||||
syscall.open("/dev/tty/TTY1", "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)
|
||||||
|
|
||||||
|
|
||||||
@@ -96,6 +96,9 @@ local function spawnShell(username, uid, shell, homedir)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local chdirOk, chdirErr = pcall(syscall.chdir, homedir)
|
local chdirOk, chdirErr = pcall(syscall.chdir, homedir)
|
||||||
|
if not chdirOk then
|
||||||
|
pcall(syscall.chdir, "/")
|
||||||
|
end
|
||||||
|
|
||||||
local ok, err = pcall(syscall.execspawn, shell, username .. ":shell")
|
local ok, err = pcall(syscall.execspawn, shell, username .. ":shell")
|
||||||
if not ok then
|
if not ok then
|
||||||
@@ -127,8 +130,9 @@ local function doLogin()
|
|||||||
|
|
||||||
syscall.write(1, "Password: ")
|
syscall.write(1, "Password: ")
|
||||||
local password = readLine("*")
|
local password = readLine("*")
|
||||||
|
local uid = syscall.getuidbyname(username)
|
||||||
|
|
||||||
local ok, err = syscall.login(username, password)
|
local ok, err = syscall.login(uid, password)
|
||||||
if ok then
|
if ok then
|
||||||
local uid = syscall.getuid()
|
local uid = syscall.getuid()
|
||||||
local pwent = syscall.getpasswd(uid)
|
local pwent = syscall.getpasswd(uid)
|
||||||
@@ -43,7 +43,7 @@ if opts.help then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
|
|
||||||
if opts.x then
|
if opts.x then
|
||||||
if #args < 2 then
|
if #args < 2 then
|
||||||
@@ -45,7 +45,7 @@ if cloptions.help then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
local dir = args[1] or ""
|
local dir = args[1] or ""
|
||||||
if dir:sub(1, 1) ~= "/" then
|
if dir:sub(1, 1) ~= "/" then
|
||||||
dir = syscall.getcwd() .. "/" .. dir
|
dir = syscall.getcwd() .. "/" .. dir
|
||||||
@@ -84,7 +84,7 @@ local function humanSize(size)
|
|||||||
size = size / 1024
|
size = size / 1024
|
||||||
scale = scale + 1
|
scale = scale + 1
|
||||||
end
|
end
|
||||||
if scale == 0 then return tostring(size) end
|
if scale == 0 then return tostring(size).."B" end
|
||||||
if size < 10 then
|
if size < 10 then
|
||||||
return string.format("%.1f%s", size, sizePrefixes[scale])
|
return string.format("%.1f%s", size, sizePrefixes[scale])
|
||||||
end
|
end
|
||||||
@@ -148,7 +148,7 @@ if cloptions.l then
|
|||||||
syscall.devctl(1, "sfgc", 1)
|
syscall.devctl(1, "sfgc", 1)
|
||||||
end
|
end
|
||||||
elseif isDir then
|
elseif isDir then
|
||||||
syscall.devctl(1, "sfgc", 4)
|
syscall.devctl(1, "sfgc", 14)
|
||||||
printInline(v)
|
printInline(v)
|
||||||
syscall.devctl(1, "sfgc", 1)
|
syscall.devctl(1, "sfgc", 1)
|
||||||
else
|
else
|
||||||
@@ -177,7 +177,7 @@ for i, v in ipairs(list) do
|
|||||||
if isSym then
|
if isSym then
|
||||||
syscall.devctl(1, "sfgc", 6)
|
syscall.devctl(1, "sfgc", 6)
|
||||||
elseif isDir then
|
elseif isDir then
|
||||||
syscall.devctl(1, "sfgc", 4)
|
syscall.devctl(1, "sfgc", 14)
|
||||||
else
|
else
|
||||||
local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
|
local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
|
||||||
syscall.devctl(1, "sfgc", isExec and 3 or 1)
|
syscall.devctl(1, "sfgc", isExec and 3 or 1)
|
||||||
@@ -9,7 +9,7 @@ local currentUid = syscall.getuid()
|
|||||||
|
|
||||||
local targetUid
|
local targetUid
|
||||||
if targetName then
|
if targetName then
|
||||||
targetUid = syscall.getuid(targetName)
|
targetUid = syscall.getuid()
|
||||||
if not targetUid then
|
if not targetUid then
|
||||||
print("passwd: user '" .. targetName .. "' does not exist")
|
print("passwd: user '" .. targetName .. "' does not exist")
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
@@ -36,7 +36,7 @@ if currentUid ~= 0 then
|
|||||||
if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end
|
if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
else cur=cur..ch; syscall.write(1,"*") end
|
else cur=cur..ch; syscall.write(1,"*") end
|
||||||
end
|
end
|
||||||
local ok, err = syscall.elevate(targetName, cur)
|
local ok, err = syscall.login(targetUid, cur)
|
||||||
if not ok then
|
if not ok then
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("passwd: authentication failure")
|
print("passwd: authentication failure")
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--:Minify:--
|
||||||
for i,v in ipairs(syscall.getTasks()) do
|
for i,v in ipairs(syscall.getTasks()) do
|
||||||
local task = syscall.getTask(v)
|
local task = syscall.getTask(v)
|
||||||
print(task.pid,task.username,task.name,task.status)
|
print(task.pid,task.username,task.name,task.status)
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local targetUser = ({ ... })[1] or "root"
|
local targetUser = ({ ... })[1]
|
||||||
local currentUid = syscall.getuid()
|
local currentUid = syscall.getuid()
|
||||||
local targetUid = syscall.getuidbyname(targetUser)
|
local targetUid
|
||||||
|
if targetUser then
|
||||||
|
targetUid = syscall.getuidbyname(targetUser)
|
||||||
|
else
|
||||||
|
targetUid = 0
|
||||||
|
end
|
||||||
|
|
||||||
if not targetUid then
|
if not targetUid then
|
||||||
print("su: user '" .. targetUser .. "' does not exist")
|
print("su: user '" .. targetUser .. "' does not exist")
|
||||||
@@ -25,20 +30,21 @@ if currentUid ~= 0 then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = syscall.elevate(targetUser, pw)
|
local ok, err = syscall.login(targetUid, pw)
|
||||||
if not ok then
|
if not ok then
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("su: Authentication failure")
|
print("su: Authentication failure")
|
||||||
syscall.exit(1)
|
syscall.exit(1)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
else
|
||||||
|
|
||||||
syscall.setuid(targetUid)
|
syscall.setuid(targetUid)
|
||||||
|
end
|
||||||
|
|
||||||
local pwent = syscall.getpasswd(targetUid)
|
local pwent = syscall.getpasswd(targetUid)
|
||||||
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||||
local homedir = (pwent and pwent.homedir) or "/"
|
local homedir = (pwent and pwent.homedir) or "/"
|
||||||
|
local username= (pwent and pwent.username)or "Unknown"
|
||||||
|
|
||||||
local ok_cd, err_cd = pcall(syscall.chdir, homedir)
|
local ok_cd, err_cd = pcall(syscall.chdir, homedir)
|
||||||
if not ok_cd then
|
if not ok_cd then
|
||||||
@@ -46,7 +52,7 @@ if not ok_cd then
|
|||||||
syscall.chdir(homedir)
|
syscall.chdir(homedir)
|
||||||
end
|
end
|
||||||
syscall.setEnviron("HOME", homedir)
|
syscall.setEnviron("HOME", homedir)
|
||||||
syscall.setEnviron("USER", targetUser)
|
syscall.setEnviron("USER", username)
|
||||||
syscall.setEnviron("SHELL", shell)
|
syscall.setEnviron("SHELL", shell)
|
||||||
|
|
||||||
local ok, err = pcall(syscall.exec, shell)
|
local ok, err = pcall(syscall.exec, shell)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
|
|
||||||
local cmdArgs = {...}
|
local cmdArgs = {...}
|
||||||
local targetUser = "root"
|
local targetUser = "root"
|
||||||
@@ -31,7 +31,7 @@ for j = i + 1, #cmdArgs do restArgs[#restArgs + 1] = cmdArgs[j] end
|
|||||||
local currentUid = syscall.getuid()
|
local currentUid = syscall.getuid()
|
||||||
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||||
|
|
||||||
local targetUid = syscall.getuid(targetUser)
|
local targetUid = syscall.getuidbyname(targetUser)
|
||||||
if not targetUid then
|
if not targetUid then
|
||||||
print("sudo: user '" .. targetUser .. "' does not exist")
|
print("sudo: user '" .. targetUser .. "' does not exist")
|
||||||
syscall.exit(1)
|
syscall.exit(1)
|
||||||
@@ -39,7 +39,7 @@ if not targetUid then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if currentUid ~= 0 then
|
if currentUid ~= 0 then
|
||||||
printInline("[sudo] password for " .. currentUser .. ": ")
|
printInline("[sudo] password for root: ")
|
||||||
local pw = ""
|
local pw = ""
|
||||||
while true do
|
while true do
|
||||||
local ch = syscall.read(0)
|
local ch = syscall.read(0)
|
||||||
@@ -55,7 +55,7 @@ if currentUid ~= 0 then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = syscall.elevate(currentUser, pw)
|
local ok, err = syscall.login(0, pw)
|
||||||
if not ok then
|
if not ok then
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("sudo: Authentication failure")
|
print("sudo: Authentication failure")
|
||||||
@@ -63,7 +63,7 @@ if currentUid ~= 0 then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if targetUid ~= 0 then
|
if targetUid ~= currentUid then
|
||||||
syscall.setuid(targetUid)
|
syscall.setuid(targetUid)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
10
Src/hysh/bin/sysdump
Normal file
10
Src/hysh/bin/sysdump
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local path=...
|
||||||
|
path=path or "/dev/tty/1"
|
||||||
|
local syscalls=syscall.sysdump()
|
||||||
|
local fd=syscall.open(path,"w")
|
||||||
|
for i=1, #syscalls do
|
||||||
|
syscall.write(fd,syscalls[i].."\n")
|
||||||
|
end
|
||||||
|
syscall.write(fd,"Total # of syscalls: "..tostring(#syscalls))
|
||||||
|
syscall.close(fd)
|
||||||
@@ -29,7 +29,7 @@ if not ok then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if removeHome and pwent and pwent.homedir then
|
if removeHome and pwent and pwent.homedir then
|
||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
local ok2, err2 = pcall(function()
|
local ok2, err2 = pcall(function()
|
||||||
local function rmdir(path)
|
local function rmdir(path)
|
||||||
for _, f in ipairs(fs.list(path) or {}) do
|
for _, f in ipairs(fs.list(path) or {}) do
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--:Minify:--
|
||||||
local args = {...}
|
local args = {...}
|
||||||
while true do
|
while true do
|
||||||
if #args == 0 then
|
if #args == 0 then
|
||||||
38
Src/iniparse/lib/iniparse
Normal file
38
Src/iniparse/lib/iniparse
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
local ini = {}
|
||||||
|
|
||||||
|
function ini.parse(str)
|
||||||
|
local config = {}
|
||||||
|
local section = nil
|
||||||
|
|
||||||
|
for line in str:gmatch("[^\r\n]+") do
|
||||||
|
-- trim whitespace
|
||||||
|
line = line:match("^%s*(.-)%s*$")
|
||||||
|
|
||||||
|
-- skip empty lines and comments
|
||||||
|
if line ~= "" and not line:match("^[;#]") then
|
||||||
|
-- section
|
||||||
|
local sec = line:match("^%[(.-)%]$")
|
||||||
|
if sec then
|
||||||
|
section = sec
|
||||||
|
config[section] = config[section] or {}
|
||||||
|
else
|
||||||
|
-- key=value
|
||||||
|
local key, value = line:match("^(.-)=(.*)$")
|
||||||
|
if key then
|
||||||
|
key = key:match("^%s*(.-)%s*$")
|
||||||
|
value = value:match("^%s*(.-)%s*$")
|
||||||
|
|
||||||
|
if section then
|
||||||
|
config[section][key] = value
|
||||||
|
else
|
||||||
|
config[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return config
|
||||||
|
end
|
||||||
|
|
||||||
|
return ini
|
||||||
388
Src/json/lib/json
Normal file
388
Src/json/lib/json
Normal 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
|
||||||
@@ -260,17 +260,17 @@ local function getUserInput(prompt, history)
|
|||||||
while true do
|
while true do
|
||||||
local key = syscall.read(0)
|
local key = syscall.read(0)
|
||||||
if key and key ~= "" then
|
if key and key ~= "" then
|
||||||
if key == "\19" then
|
if key == "[C" then
|
||||||
if cursor > 1 then cursor = cursor - 1; dirty = true end
|
if cursor > 1 then cursor = cursor - 1; dirty = true end
|
||||||
elseif key == "\20" then
|
elseif key == "[D" then
|
||||||
if cursor <= #input then cursor = cursor + 1; dirty = true end
|
if cursor <= #input then cursor = cursor + 1; dirty = true end
|
||||||
elseif key == "\17" then
|
elseif key == "[A" then
|
||||||
if history and histIdx < #history then
|
if history and histIdx < #history then
|
||||||
histIdx = histIdx + 1
|
histIdx = histIdx + 1
|
||||||
input = history[#history - histIdx + 1]
|
input = history[#history - histIdx + 1]
|
||||||
cursor = #input + 1; dirty = true
|
cursor = #input + 1; dirty = true
|
||||||
end
|
end
|
||||||
elseif key == "\18" then
|
elseif key == "[B" then
|
||||||
if histIdx > 1 then
|
if histIdx > 1 then
|
||||||
histIdx = histIdx - 1
|
histIdx = histIdx - 1
|
||||||
input = history[#history - histIdx + 1]
|
input = history[#history - histIdx + 1]
|
||||||
@@ -288,8 +288,8 @@ local function getUserInput(prompt, history)
|
|||||||
syscall.devctl(1,"spos",ox,oy)
|
syscall.devctl(1,"spos",ox,oy)
|
||||||
w(input .. " \n")
|
w(input .. " \n")
|
||||||
return input
|
return input
|
||||||
else
|
elseif #key == 1 and key:byte(1) >= 32 and key:byte(1) < 127 then
|
||||||
input = input:sub(1, cursor-1) .. key .. input:sub(cursor)
|
input=string.sub(input,1,cursor-1)..key..string.sub(input,cursor)
|
||||||
cursor=cursor+1;dirty=true
|
cursor=cursor+1;dirty=true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -299,8 +299,6 @@ local function getUserInput(prompt, history)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
syscall.devctl(1, "clear")
|
|
||||||
syscall.devctl(1, "spos", 1, 1)
|
|
||||||
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
|
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
|
||||||
c(C_NIL)
|
c(C_NIL)
|
||||||
w("Interactive Lua REPL. exit() to quit.\n\n")
|
w("Interactive Lua REPL. exit() to quit.\n\n")
|
||||||
@@ -223,7 +223,7 @@ local function prompt(label, default)
|
|||||||
tbg(16); tfg(1)
|
tbg(16); tfg(1)
|
||||||
local key = syscall.read(0)
|
local key = syscall.read(0)
|
||||||
if not key or key == "" then sleep(0.02)
|
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 == "\n" then return inp
|
||||||
elseif key == "\b" then if #inp > 0 then inp = inp:sub(1,-2) end
|
elseif key == "\b" then if #inp > 0 then inp = inp:sub(1,-2) end
|
||||||
else
|
else
|
||||||
@@ -359,31 +359,29 @@ while running do
|
|||||||
local key = syscall.read(0)
|
local key = syscall.read(0)
|
||||||
if key and key ~= "" then
|
if key and key ~= "" then
|
||||||
local b = key:byte(1)
|
local b = key:byte(1)
|
||||||
if key == "\17" then moveCursorUp(map); dirty=true
|
if key == "[A" then moveCursorUp(map); dirty=true
|
||||||
elseif key == "\18" then moveCursorDown(map); dirty=true
|
elseif key == "[B" then moveCursorDown(map); dirty=true
|
||||||
elseif key == "\19" then
|
elseif key == "[C" 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 cx <= #lines[cy] then cx=cx+1
|
if cx <= #lines[cy] then cx=cx+1
|
||||||
elseif cy < #lines then cy=cy+1; cx=1 end
|
elseif cy < #lines then cy=cy+1; cx=1 end
|
||||||
dirty=true
|
dirty=true
|
||||||
|
elseif key == "[D" then
|
||||||
|
if cx > 1 then cx=cx-1
|
||||||
|
elseif cy > 1 then cy=cy-1; cx=#lines[cy]+1 end
|
||||||
|
dirty=true
|
||||||
|
elseif key == "[H" then cx=1; dirty=true
|
||||||
|
elseif key == "[F" 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 == "\n" then newline()
|
||||||
elseif key == "\b" then delLeft()
|
elseif key == "\b" then delLeft()
|
||||||
elseif key == "\t" then for _=1,4 do insChar(" ") end
|
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
|
elseif b == 6 then
|
||||||
local p=prompt("Find: ",sPat); dirty=true
|
local p=prompt("Find: ",sPat); dirty=true
|
||||||
if p then sPat=p; sLine=0; findNext() end
|
if p then sPat=p; sLine=0; findNext() end
|
||||||
elseif b == 7 then goToLine()
|
elseif b == 7 then goToLine()
|
||||||
elseif b == 11 then cutLine()
|
elseif b == 11 then cutLine()
|
||||||
elseif b == 12 then
|
|
||||||
for _=1,ROWS do moveCursorDown(map) end; dirty=true
|
|
||||||
elseif b == 14 then
|
elseif b == 14 then
|
||||||
if sPat=="" then
|
if sPat=="" then
|
||||||
local p=prompt("Find: ",""); dirty=true
|
local p=prompt("Find: ",""); dirty=true
|
||||||
@@ -415,8 +413,6 @@ while running do
|
|||||||
redraw()
|
redraw()
|
||||||
dirty = false
|
dirty = false
|
||||||
end
|
end
|
||||||
|
|
||||||
sleep(0.05)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
tclear(); tfg(1); tbg(16); tpos(1,1)
|
tclear(); tfg(1); tbg(16); tpos(1,1)
|
||||||
6
Src/spm/bin/spm
Normal file
6
Src/spm/bin/spm
Normal 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"
|
||||||
|
|
||||||
1
Src/sysinit/$PKGCONFIG.ini
Normal file
1
Src/sysinit/$PKGCONFIG.ini
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[symlinks]
|
||||||
4
Src/sysinit/sbin/init
Normal file
4
Src/sysinit/sbin/init
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
local args={...}
|
||||||
|
syscall.remove("/sbin/init")
|
||||||
|
syscall.symlink("/usr/lib/sysinit/sysinit", "/sbin/init")
|
||||||
|
syscall.exec("/sbin/init", args)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local kernel=...
|
local kernel=...
|
||||||
local fs=require("sys.fs")
|
local fs=require("fs")
|
||||||
|
|
||||||
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)
|
||||||
@@ -14,6 +14,9 @@ for i,v in pairs(kernel.processes) do
|
|||||||
end, i)
|
end, i)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not fs.exists("/bin/startup") then
|
||||||
|
fs.mkdir("/bin/startup")
|
||||||
|
end
|
||||||
local files = fs.list("/bin/startup")
|
local files = fs.list("/bin/startup")
|
||||||
if not files then error("Failed to list /bin/startup") end
|
if not files then error("Failed to list /bin/startup") end
|
||||||
for i,v in ipairs(files) do
|
for i,v in ipairs(files) do
|
||||||
@@ -38,6 +41,6 @@ for i,v in ipairs(files) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
sleep(1)
|
sleep(5)
|
||||||
kernel.saveLog()
|
kernel.saveLog()
|
||||||
end
|
end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
local fs=require("sys.fs")
|
local fs=require("fs")
|
||||||
local units=fs.list("/usr/lib/hunit/")
|
local units=fs.list("/usr/lib/hunit/")
|
||||||
fs.mkdir("/tmp/hunit/")
|
fs.mkdir("/tmp/hunit/")
|
||||||
local errors={}
|
local errors={}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
local fs = require("sys.fs")
|
local fs = require("fs")
|
||||||
assert(fs.mkdir("/tmp/hunit/testdir"), "failed to make directory")
|
assert(fs.mkdir("/tmp/hunit/testdir"), "failed to make directory")
|
||||||
assert(fs.isDir("/tmp/hunit/testdir"), "directory does not exist")
|
assert(fs.isDir("/tmp/hunit/testdir"), "directory does not exist")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user