forked from Hyperion/HyperionOS
Compare commits
98 Commits
5b8043b6ec
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c18d89489 | |||
| 22b5021e9d | |||
| 462c74686a | |||
| 0072547beb | |||
| 041663e1e8 | |||
| 7deefc83ca | |||
| fcac0ff8d8 | |||
| df62414229 | |||
| d98555328b | |||
| 055dd4e606 | |||
| 5755dd9cbe | |||
| 61cbb91db2 | |||
| b79492dbb8 | |||
| bc87c53427 | |||
| 8e11faf9ec | |||
| 0ea42f9454 | |||
| d0f26a937f | |||
| 9b338328f0 | |||
| 030e5bfd96 | |||
| 099638c735 | |||
| 4f9eebade2 | |||
| 5172da1b5b | |||
| d08935b68a | |||
| 45b46cf3c4 | |||
| aeea68bc9b | |||
| f983b13d56 | |||
| 03c5b106c4 | |||
| 08323e00ff | |||
| 5e3cdbe40c | |||
| 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 | |||
| 6694711423 | |||
| 40c97ca000 | |||
| dd2437d4af | |||
| d026cfbb03 | |||
| aad7efd055 | |||
| 93c3bab263 | |||
| 5bd16b8fd4 | |||
| 3cc1459769 | |||
| 0655f2a39e | |||
| 57b1d46837 | |||
| 875759022a |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -5,6 +5,7 @@
|
|||||||
"syscall",
|
"syscall",
|
||||||
"printf",
|
"printf",
|
||||||
"printInline",
|
"printInline",
|
||||||
"toHex"
|
"toHex",
|
||||||
|
"loadcstr"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
20
Makefile
Normal file
20
Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
PYTHON := python3
|
||||||
|
ARCH_FLAG := $(if $(ARCH),--arch $(ARCH),)
|
||||||
|
MODE_FLAG := $(if $(DEV),--dev,--release)
|
||||||
|
|
||||||
|
.PHONY: build build-mini build-test build-mini-test clean
|
||||||
|
|
||||||
|
build:
|
||||||
|
$(PYTHON) build.py build $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
build-mini:
|
||||||
|
$(PYTHON) build.py build-mini $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
build-test:
|
||||||
|
$(PYTHON) build.py build-test $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
build-mini-test:
|
||||||
|
$(PYTHON) build.py build-mini-test $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(PYTHON) build.py clean
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
|
[](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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
See `building.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Functionality is split into kernel modules (`.kmod`)
|
- Functionality is split into kernel modules (`.kmod`)
|
||||||
|
|||||||
Binary file not shown.
@@ -1,295 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
|
|
||||||
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1)
|
|
||||||
syscall.open("/dev/null","w") --stderr (device 2)
|
|
||||||
|
|
||||||
local success, errorMsg = xpcall(function()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
|
|
||||||
syscall.devctl(1,"clear")
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
syscall.devctl(1,"spos",1,1)
|
|
||||||
print("HyperionOS Bash Shell")
|
|
||||||
|
|
||||||
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
|
||||||
local commandHistory = {}
|
|
||||||
local terminate = false
|
|
||||||
syscall.setEnviron("SHELL","rtbash")
|
|
||||||
syscall.setEnviron("PATH","/bin/")
|
|
||||||
syscall.chdir("/")
|
|
||||||
local oldWD = ""
|
|
||||||
|
|
||||||
for i = 1, 16 do
|
|
||||||
syscall.devctl(1,"sbgc",i)
|
|
||||||
printInline(" ");
|
|
||||||
end
|
|
||||||
print("\n")
|
|
||||||
|
|
||||||
syscall.sigcatch(function(sig)
|
|
||||||
if sig == 1 then
|
|
||||||
terminate = true
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local builtinCmds = {}
|
|
||||||
|
|
||||||
builtinCmds.cd = function(path)
|
|
||||||
local cwd = syscall.getcwd()
|
|
||||||
local dirIn = (path or "")
|
|
||||||
if dirIn == "-" then
|
|
||||||
if oldWD == "" then
|
|
||||||
print("bash-cd: No previous working directory set.")
|
|
||||||
else
|
|
||||||
print(oldWD)
|
|
||||||
syscall.chdir(oldWD)
|
|
||||||
oldWD = cwd
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local dirInMod = dirIn
|
|
||||||
if dirIn:sub(1, 1) ~= "/" then dirInMod = cwd .. "/" .. dirIn end
|
|
||||||
local parts = {}
|
|
||||||
for part in dirInMod:gmatch("[^/]+") do
|
|
||||||
if part == ".." then
|
|
||||||
if #parts > 0 then table.remove(parts) end
|
|
||||||
elseif part ~= "." and part ~= "" then
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local normDir = "/" .. table.concat(parts, "/")
|
|
||||||
if normDir:sub(#normDir, #normDir) ~= "/" then normDir = normDir .. "/" end
|
|
||||||
|
|
||||||
if not fs.isDir(normDir) then
|
|
||||||
print("bash-cd: "..dirIn..": No such directory.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
oldWD = cwd
|
|
||||||
syscall.chdir(normDir)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function getUserInput()
|
|
||||||
syscall.devctl(1,"sfgc",3)
|
|
||||||
printInline(userhost)
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
printInline(":")
|
|
||||||
syscall.devctl(1,"sfgc",10)
|
|
||||||
printInline(syscall.getcwd())
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
printInline("$ ")
|
|
||||||
local curOffsetStr = syscall.devctl(1, "gpos")
|
|
||||||
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
|
|
||||||
local curOffsetY = tonumber(curOffsetStr:sub(curOffsetStr:find(";")+1))
|
|
||||||
|
|
||||||
local input = ""
|
|
||||||
local blinkState = false
|
|
||||||
local cursorPos = 1
|
|
||||||
local history = 0
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local key=syscall.read(0)
|
|
||||||
if key then
|
|
||||||
if key == "\19" then --TODO: REPLACE WITH LEFT ARROW
|
|
||||||
if cursorPos > 1 then
|
|
||||||
cursorPos = cursorPos - 1
|
|
||||||
end
|
|
||||||
elseif key == "\20" then --TODO: REPLACE WITH RIGHT ARROW
|
|
||||||
if cursorPos <= #input then
|
|
||||||
cursorPos = cursorPos + 1
|
|
||||||
end
|
|
||||||
elseif key == "\17" then --TODO: REPLACE WITH UP ARROW
|
|
||||||
if history < #commandHistory then
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
printInline((" "):rep(#input + 1))
|
|
||||||
history = history + 1
|
|
||||||
input = commandHistory[#commandHistory - history + 1]
|
|
||||||
cursorPos = #input + 1
|
|
||||||
end
|
|
||||||
elseif key == "\18" then --TODO: REPLACE WITH DOWN ARROW
|
|
||||||
if history > 1 then
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
printInline((" "):rep(#input + 1))
|
|
||||||
history = history - 1
|
|
||||||
input = commandHistory[#commandHistory - history + 1]
|
|
||||||
cursorPos = #input + 1
|
|
||||||
elseif history == 1 then
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
printInline((" "):rep(#input + 1))
|
|
||||||
history = 0
|
|
||||||
input = ""
|
|
||||||
cursorPos = 1
|
|
||||||
end
|
|
||||||
elseif key == "\b" then
|
|
||||||
if cursorPos > 1 then
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
printInline((" "):rep(#input + 1))
|
|
||||||
input = string.sub(input, 1, cursorPos-2)..string.sub(input, cursorPos)
|
|
||||||
cursorPos = cursorPos - 1
|
|
||||||
end
|
|
||||||
elseif key == "\n" then
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
print(input.." ")
|
|
||||||
return input
|
|
||||||
else
|
|
||||||
input = string.sub(input, 1, cursorPos-1)..key..string.sub(input, cursorPos)
|
|
||||||
cursorPos = cursorPos + 1
|
|
||||||
end
|
|
||||||
local screenSizeStr = syscall.devctl(1, "size")
|
|
||||||
local sizeX = tonumber(screenSizeStr:sub(1, screenSizeStr:find(";")-1))
|
|
||||||
local sizeY = tonumber(screenSizeStr:sub(screenSizeStr:find(";")+1))
|
|
||||||
local totalChars = sizeX * sizeY
|
|
||||||
local eocCharNum = ((curOffsetY - 1) * sizeX) + curOffsetX + #input
|
|
||||||
if eocCharNum >= totalChars then
|
|
||||||
syscall.devctl(1,"spos",sizeX,sizeY)
|
|
||||||
printInline(" ")
|
|
||||||
curOffsetY = curOffsetY - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
printInline(string.sub(input, 1, cursorPos-1))
|
|
||||||
if blinkState then
|
|
||||||
syscall.devctl(1,"sfgc",16)
|
|
||||||
syscall.devctl(1,"sbgc",1)
|
|
||||||
end
|
|
||||||
if cursorPos > #input then
|
|
||||||
printInline(" ")
|
|
||||||
else
|
|
||||||
printInline(string.sub(input, cursorPos, cursorPos))
|
|
||||||
end
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
printInline(string.sub(input, cursorPos+1))
|
|
||||||
if cursorPos <= #input then
|
|
||||||
printInline(" ")
|
|
||||||
end
|
|
||||||
local curBlink = ((math.floor(syscall.getUptime() / 500) % 2) == 0)
|
|
||||||
if curBlink ~= blinkState then
|
|
||||||
blinkState = curBlink
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function runCommand(command)
|
|
||||||
do
|
|
||||||
local func = load("return " .. command, "@equation", "t", {})
|
|
||||||
if func then
|
|
||||||
local success, result = pcall(func)
|
|
||||||
if success and type(result) == "number" then
|
|
||||||
print(result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
terminate = false
|
|
||||||
local args = string.split(command, " ")
|
|
||||||
if builtinCmds[args[1]] then
|
|
||||||
local success, msg = pcall(builtinCmds[args[1]], table.unpack(args, 2))
|
|
||||||
if not success then
|
|
||||||
local errSL = string.sub(msg, string.find(msg, "]") + 2)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
printInline(args[1]..": Program runtime error on line ")
|
|
||||||
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cmdPath = ""
|
|
||||||
if string.find(args[1], "/") then
|
|
||||||
if fs.exists(args[1]) then
|
|
||||||
cmdPath = args[1]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local paths = string.split(syscall.getEnviron("PATH"), ":")
|
|
||||||
for _, path in pairs(paths) do
|
|
||||||
if fs.exists(path..args[1]) then
|
|
||||||
cmdPath = path..args[1]
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if cmdPath == "" then
|
|
||||||
print(args[1]..": Command not found")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local progName = string.sub(cmdPath, #cmdPath - string.find(string.reverse(cmdPath), "/") + 2)
|
|
||||||
|
|
||||||
local text = fs.readAllText(cmdPath)
|
|
||||||
local program, err = load(text, progName)
|
|
||||||
if not program then
|
|
||||||
local errSL = string.sub(err, string.find(err, ":") + 1)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
printInline(progName..": Program load error on line ")
|
|
||||||
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local proc = syscall.spawn(function(...)
|
|
||||||
syscall.open("/dev/tty/TTY1","r")
|
|
||||||
syscall.open("/dev/tty/TTY1","w")
|
|
||||||
syscall.open("/dev/null","w")
|
|
||||||
local success, msg = pcall(program, ...)
|
|
||||||
if not success then
|
|
||||||
local errSL = string.sub(msg, string.find(msg, ":") + 1)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
printInline(progName..": Program runtime error on line ")
|
|
||||||
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
||||||
end
|
|
||||||
end, progName, nil, {table.unpack(args, 2)})
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local exited, code = syscall.collect(proc)
|
|
||||||
if exited then
|
|
||||||
if code then
|
|
||||||
print("\nTask exited with code:\n"..tostring(code))
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if terminate then
|
|
||||||
local success, err = syscall.kill(proc)
|
|
||||||
if success then
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
print("\nProgram Terminated.")
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
end
|
|
||||||
terminate = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
sleep(0.05)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local command = getUserInput()
|
|
||||||
if command ~= "" then
|
|
||||||
if command ~= commandHistory[#commandHistory] then
|
|
||||||
table.insert(commandHistory, command)
|
|
||||||
end
|
|
||||||
runCommand(command)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--ERROR HANDLING
|
|
||||||
end, debug.traceback)
|
|
||||||
|
|
||||||
if not success then
|
|
||||||
syscall.log("Error running shell: "..errorMsg, "ERROR")
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
print()
|
|
||||||
print("Error running shell: ")
|
|
||||||
print(errorMsg)
|
|
||||||
end
|
|
||||||
@@ -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 Bash 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,24 +0,0 @@
|
|||||||
local args = {...}
|
|
||||||
local name = syscall.getTask(syscall.getpid()).name
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
local filePath = (args[1] or "")
|
|
||||||
if filePath:sub(1, 1) ~= "/" then
|
|
||||||
filePath = syscall.getcwd().."/"..filePath
|
|
||||||
end
|
|
||||||
|
|
||||||
if not fs.exists(filePath) and args[1] then
|
|
||||||
print(name..": Cannot access '"..args[1].."': No such file.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local file = 0
|
|
||||||
if args[1] then
|
|
||||||
file = syscall.open(filePath, "r")
|
|
||||||
end
|
|
||||||
local content=""
|
|
||||||
while content~=nil or file == 0 do
|
|
||||||
content=syscall.read(file, 1024)
|
|
||||||
printInline(content)
|
|
||||||
end
|
|
||||||
syscall.close(file)
|
|
||||||
print("")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
syscall.devctl(1,"clear")
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
local args = {...}
|
|
||||||
print(table.concat(args, " "))
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
-- help: display command reference with paged scrolling
|
|
||||||
|
|
||||||
local COMMANDS = {
|
|
||||||
-- {name, usage, description, {flags...}}
|
|
||||||
-- flags: {flag, desc}
|
|
||||||
{
|
|
||||||
name = "cat",
|
|
||||||
usage = "cat [file]",
|
|
||||||
desc = "Print file contents to stdout. Reads from stdin if no file given.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "cd",
|
|
||||||
usage = "cd [dir]",
|
|
||||||
desc = "Change working directory. Use '-' to return to previous directory.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "clear",
|
|
||||||
usage = "clear",
|
|
||||||
desc = "Clear the terminal screen.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "echo",
|
|
||||||
usage = "echo [text...]",
|
|
||||||
desc = "Print arguments to stdout.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "hfetch",
|
|
||||||
usage = "hfetch",
|
|
||||||
desc = "Display system information in a neofetch-style layout.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "id",
|
|
||||||
usage = "id [username]",
|
|
||||||
desc = "Print user identity (uid, gid). Defaults to current user.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "login",
|
|
||||||
usage = "login",
|
|
||||||
desc = "System login prompt. Launched automatically at boot.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "ls",
|
|
||||||
usage = "ls [-alh] [dir]",
|
|
||||||
desc = "List directory contents.",
|
|
||||||
flags = {
|
|
||||||
{"-a", "Show hidden files (starting with .)"},
|
|
||||||
{"-l", "Long format: permissions, owner, size"},
|
|
||||||
{"-h", "Human-readable file sizes"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "lsusers",
|
|
||||||
usage = "lsusers",
|
|
||||||
desc = "List all user accounts with uid, gid, home, and shell.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "lua",
|
|
||||||
usage = "lua",
|
|
||||||
desc = "Interactive Lua REPL prompt.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "mkdir",
|
|
||||||
usage = "mkdir <dir>",
|
|
||||||
desc = "Create a directory.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "passwd",
|
|
||||||
usage = "passwd [username]",
|
|
||||||
desc = "Change a user password. Non-root must verify current password first.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "ps",
|
|
||||||
usage = "ps",
|
|
||||||
desc = "List running tasks with pid, user, name, and status.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "pwd",
|
|
||||||
usage = "pwd",
|
|
||||||
desc = "Print current working directory.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "su",
|
|
||||||
usage = "su [username]",
|
|
||||||
desc = "Switch user. Defaults to root. Root can switch without a password.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "sudo",
|
|
||||||
usage = "sudo [-u user] <command> [args...]",
|
|
||||||
desc = "Run a command as another user (default root). Authenticates as current user.",
|
|
||||||
flags = {
|
|
||||||
{"-u user", "Run as the specified user (name or uid)"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "sysdump",
|
|
||||||
usage = "sysdump",
|
|
||||||
desc = "List all registered kernel syscalls.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "useradd",
|
|
||||||
usage = "useradd [-p pw] [-g gid] [-d home] [-s shell] [-M] <username>",
|
|
||||||
desc = "Create a new user account.",
|
|
||||||
flags = {
|
|
||||||
{"-p pw", "Set password (prompted interactively if omitted)"},
|
|
||||||
{"-g gid", "Set primary group id"},
|
|
||||||
{"-d home", "Set home directory (default: /home/username)"},
|
|
||||||
{"-s shell", "Set login shell (default: /bin/hysh)"},
|
|
||||||
{"-M", "Do not create home directory"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "userdel",
|
|
||||||
usage = "userdel [-r] <username>",
|
|
||||||
desc = "Delete a user account.",
|
|
||||||
flags = {
|
|
||||||
{"-r", "Also recursively remove the user's home directory"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "usermod",
|
|
||||||
usage = "usermod [-l name] [-p pw] [-g gid] [-d home] [-s shell] [-L] [-U] <username>",
|
|
||||||
desc = "Modify an existing user account.",
|
|
||||||
flags = {
|
|
||||||
{"-l name", "Rename the user"},
|
|
||||||
{"-p pw", "Set new password"},
|
|
||||||
{"-g gid", "Change primary group id"},
|
|
||||||
{"-d home", "Change home directory"},
|
|
||||||
{"-s shell", "Change login shell"},
|
|
||||||
{"-L", "Lock the account (disable login)"},
|
|
||||||
{"-U", "Unlock the account"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "whoami",
|
|
||||||
usage = "whoami",
|
|
||||||
desc = "Print the current username.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "yes",
|
|
||||||
usage = "yes [text]",
|
|
||||||
desc = "Repeatedly print 'y' (or given text) until interrupted with Ctrl+C.",
|
|
||||||
flags = {}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Build lines to display
|
|
||||||
local C_HEAD = 7 -- yellow (section headers)
|
|
||||||
local C_CMD = 5 -- cyan (command name)
|
|
||||||
local C_USAGE = 1 -- white (usage line)
|
|
||||||
local C_DESC = 13 -- grey (description)
|
|
||||||
local C_FLAG = 3 -- green (flag name)
|
|
||||||
local C_DIM = 12 -- dark grey
|
|
||||||
|
|
||||||
-- Each entry is {text, color}
|
|
||||||
local lines = {}
|
|
||||||
local function push(text, col) lines[#lines+1] = {text, col or 1} end
|
|
||||||
|
|
||||||
push("HyperionOS Command Reference", C_HEAD)
|
|
||||||
push(string.rep("=", 50), C_DIM)
|
|
||||||
push("", 1)
|
|
||||||
|
|
||||||
local args = {...}
|
|
||||||
local filter = args[1] -- optional: help <command>
|
|
||||||
|
|
||||||
local function addCmd(cmd)
|
|
||||||
push(cmd.name, C_CMD)
|
|
||||||
push(" Usage: " .. cmd.usage, C_USAGE)
|
|
||||||
push(" " .. cmd.desc, C_DESC)
|
|
||||||
if #cmd.flags > 0 then
|
|
||||||
for _, f in ipairs(cmd.flags) do
|
|
||||||
push(" " .. f[1], C_FLAG)
|
|
||||||
push(" " .. f[2], C_DESC)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
push("", 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if filter then
|
|
||||||
local found = false
|
|
||||||
for _, cmd in ipairs(COMMANDS) do
|
|
||||||
if cmd.name == filter then addCmd(cmd); found = true; break end
|
|
||||||
end
|
|
||||||
if not found then
|
|
||||||
push("help: unknown command '" .. filter .. "'", 2)
|
|
||||||
push("Run 'help' with no arguments for the full list.", C_DESC)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
push("Run 'help <command>' for details on a specific command.", C_DESC)
|
|
||||||
push("", 1)
|
|
||||||
for _, cmd in ipairs(COMMANDS) do addCmd(cmd) end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pager
|
|
||||||
local sizeStr = syscall.devctl(1, "size")
|
|
||||||
local screenW = tonumber(sizeStr:sub(1, sizeStr:find(";")-1)) or 51
|
|
||||||
local screenH = tonumber(sizeStr:sub(sizeStr:find(";")+1)) or 19
|
|
||||||
local pageSize = screenH - 2 -- reserve bottom row for status bar
|
|
||||||
|
|
||||||
local scroll = 0
|
|
||||||
local totalLines = #lines
|
|
||||||
local dirty = true
|
|
||||||
|
|
||||||
local function render()
|
|
||||||
syscall.devctl(1, "clear")
|
|
||||||
syscall.devctl(1, "spos", 1, 1)
|
|
||||||
for row = 1, pageSize do
|
|
||||||
local li = scroll + row
|
|
||||||
if li <= totalLines then
|
|
||||||
local text, col = lines[li][1], lines[li][2]
|
|
||||||
syscall.devctl(1, "sfgc", col)
|
|
||||||
-- truncate to screen width
|
|
||||||
if #text > screenW then text = text:sub(1, screenW) end
|
|
||||||
syscall.write(1, text .. "\n")
|
|
||||||
else
|
|
||||||
syscall.write(1, "\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- status bar
|
|
||||||
syscall.devctl(1, "sfgc", 16)
|
|
||||||
syscall.devctl(1, "sbgc", 13)
|
|
||||||
local pct = math.floor(math.min(100, (scroll + pageSize) / totalLines * 100))
|
|
||||||
local status = string.format(" help -- line %d/%d (%d%%) [up/down: scroll q: quit] ",
|
|
||||||
scroll + 1, totalLines, pct)
|
|
||||||
if #status > screenW then status = status:sub(1, screenW) end
|
|
||||||
syscall.devctl(1, "spos", 1, screenH)
|
|
||||||
syscall.write(1, status .. string.rep(" ", screenW - #status))
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
syscall.devctl(1, "sbgc", 16)
|
|
||||||
dirty = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If output fits on one screen, just print without pager
|
|
||||||
if totalLines <= pageSize then
|
|
||||||
syscall.devctl(1, "clear")
|
|
||||||
syscall.devctl(1, "spos", 1, 1)
|
|
||||||
for _, line in ipairs(lines) do
|
|
||||||
syscall.devctl(1, "sfgc", line[2])
|
|
||||||
syscall.write(1, line[1] .. "\n")
|
|
||||||
end
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
render()
|
|
||||||
while true do
|
|
||||||
local ch = syscall.read(0)
|
|
||||||
if not ch or ch == "" then
|
|
||||||
-- idle
|
|
||||||
elseif ch == "q" or ch == "Q" then
|
|
||||||
break
|
|
||||||
elseif ch == "\17" then -- up arrow
|
|
||||||
if scroll > 0 then
|
|
||||||
scroll = scroll - 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif ch == "\18" then -- down arrow
|
|
||||||
if scroll + pageSize < totalLines then
|
|
||||||
scroll = scroll + 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif ch == "\19" then -- left / page up (reuse left arrow)
|
|
||||||
scroll = math.max(0, scroll - pageSize)
|
|
||||||
dirty = true
|
|
||||||
elseif ch == "\20" then -- right / page down
|
|
||||||
scroll = math.min(totalLines - pageSize, scroll + pageSize)
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
if dirty then render() end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Restore screen
|
|
||||||
syscall.devctl(1, "clear")
|
|
||||||
syscall.devctl(1, "spos", 1, 1)
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
syscall.devctl(1, "sbgc", 16)
|
|
||||||
@@ -1,299 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
|
|
||||||
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1)
|
|
||||||
syscall.open("/dev/null","w") --stderr (device 2)
|
|
||||||
|
|
||||||
local success, errorMsg = xpcall(function()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
|
|
||||||
syscall.devctl(1,"clear")
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
syscall.devctl(1,"spos",1,1)
|
|
||||||
print("HyperionOS hysh Shell")
|
|
||||||
|
|
||||||
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
|
||||||
local commandHistory = {}
|
|
||||||
local terminate = false
|
|
||||||
syscall.setEnviron("SHELL","rtbash")
|
|
||||||
syscall.setEnviron("PATH","/bin/")
|
|
||||||
syscall.chdir("/")
|
|
||||||
local oldWD = ""
|
|
||||||
|
|
||||||
for i = 1, 16 do
|
|
||||||
syscall.devctl(1,"sbgc",i)
|
|
||||||
printInline(" ");
|
|
||||||
end
|
|
||||||
print("\n")
|
|
||||||
|
|
||||||
syscall.sigcatch(function(sig)
|
|
||||||
if sig == 1 then
|
|
||||||
terminate = true
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local builtinCmds = {}
|
|
||||||
|
|
||||||
builtinCmds.cd = function(path)
|
|
||||||
local cwd = syscall.getcwd()
|
|
||||||
local dirIn = (path or "")
|
|
||||||
if dirIn == "-" then
|
|
||||||
if oldWD == "" then
|
|
||||||
print("hysh-cd: No previous working directory set.")
|
|
||||||
else
|
|
||||||
print(oldWD)
|
|
||||||
syscall.chdir(oldWD)
|
|
||||||
oldWD = cwd
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local dirInMod = dirIn
|
|
||||||
if dirIn:sub(1, 1) ~= "/" then dirInMod = cwd .. "/" .. dirIn end
|
|
||||||
local parts = {}
|
|
||||||
for part in dirInMod:gmatch("[^/]+") do
|
|
||||||
if part == ".." then
|
|
||||||
if #parts > 0 then table.remove(parts) end
|
|
||||||
elseif part ~= "." and part ~= "" then
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local normDir = "/" .. table.concat(parts, "/")
|
|
||||||
if normDir:sub(#normDir, #normDir) ~= "/" then normDir = normDir .. "/" end
|
|
||||||
|
|
||||||
if not fs.isDir(normDir) then
|
|
||||||
print("hysh-cd: "..dirIn..": No such directory.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
oldWD = cwd
|
|
||||||
syscall.chdir(normDir)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function getUserInput()
|
|
||||||
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, "$ ")
|
|
||||||
local curOffsetStr = syscall.devctl(1, "gpos")
|
|
||||||
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
|
|
||||||
local curOffsetY = tonumber(curOffsetStr:sub(curOffsetStr:find(";")+1))
|
|
||||||
|
|
||||||
local input = ""
|
|
||||||
local blinkState = false
|
|
||||||
local cursorPos = 1
|
|
||||||
local history = 0
|
|
||||||
local dirty = true -- redraw on first iteration
|
|
||||||
|
|
||||||
local function redraw()
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
-- text before cursor
|
|
||||||
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
|
||||||
-- cursor character (inverted if blinking on)
|
|
||||||
if blinkState then
|
|
||||||
syscall.devctl(1,"sfgc",16)
|
|
||||||
syscall.devctl(1,"sbgc",1)
|
|
||||||
end
|
|
||||||
if cursorPos > #input then
|
|
||||||
syscall.write(1, " ")
|
|
||||||
else
|
|
||||||
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
|
||||||
end
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
-- text after cursor + trailing space to erase old chars
|
|
||||||
syscall.write(1, string.sub(input, cursorPos+1) .. " ")
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local key = syscall.read(0)
|
|
||||||
if key and key ~= "" then
|
|
||||||
if key == "\19" then -- left arrow
|
|
||||||
if cursorPos > 1 then
|
|
||||||
cursorPos = cursorPos - 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif key == "\20" then -- right arrow
|
|
||||||
if cursorPos <= #input then
|
|
||||||
cursorPos = cursorPos + 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif key == "\17" then -- up arrow
|
|
||||||
if history < #commandHistory then
|
|
||||||
history = history + 1
|
|
||||||
input = commandHistory[#commandHistory - history + 1]
|
|
||||||
cursorPos = #input + 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif key == "\18" then -- down arrow
|
|
||||||
if history > 1 then
|
|
||||||
history = history - 1
|
|
||||||
input = commandHistory[#commandHistory - history + 1]
|
|
||||||
cursorPos = #input + 1
|
|
||||||
dirty = true
|
|
||||||
elseif history == 1 then
|
|
||||||
history = 0
|
|
||||||
input = ""
|
|
||||||
cursorPos = 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif key == "\b" then
|
|
||||||
if cursorPos > 1 then
|
|
||||||
input = string.sub(input, 1, cursorPos-2) .. string.sub(input, cursorPos)
|
|
||||||
cursorPos = cursorPos - 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
elseif key == "\n" then
|
|
||||||
-- redraw cleanly with no cursor highlight before committing
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
||||||
syscall.write(1, input .. " \n")
|
|
||||||
return input
|
|
||||||
else
|
|
||||||
input = string.sub(input, 1, cursorPos-1) .. key .. string.sub(input, cursorPos)
|
|
||||||
cursorPos = cursorPos + 1
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- cursor blink
|
|
||||||
local curBlink = ((math.floor(syscall.getUptime() / 500) % 2) == 0)
|
|
||||||
if curBlink ~= blinkState then
|
|
||||||
blinkState = curBlink
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if dirty then
|
|
||||||
redraw()
|
|
||||||
dirty = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function runCommand(command)
|
|
||||||
do
|
|
||||||
local func = load("return " .. command, "@equation", "t", {})
|
|
||||||
if func then
|
|
||||||
local success, result = pcall(func)
|
|
||||||
if success and type(result) == "number" then
|
|
||||||
print(result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
terminate = false
|
|
||||||
local args = string.split(command, " ")
|
|
||||||
if builtinCmds[args[1]] then
|
|
||||||
local success, msg = pcall(builtinCmds[args[1]], table.unpack(args, 2))
|
|
||||||
if not success then
|
|
||||||
local errSL = string.sub(msg, string.find(msg, "]") + 2)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
printInline(args[1]..": Program runtime error on line ")
|
|
||||||
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cmdPath = ""
|
|
||||||
if string.find(args[1], "/") then
|
|
||||||
if fs.exists(args[1]) then
|
|
||||||
cmdPath = args[1]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local paths = string.split(syscall.getEnviron("PATH"), ":")
|
|
||||||
for _, path in pairs(paths) do
|
|
||||||
if fs.exists(path..args[1]) then
|
|
||||||
cmdPath = path..args[1]
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if cmdPath == "" then
|
|
||||||
print(args[1]..": Command not found")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local progName = string.sub(cmdPath, #cmdPath - string.find(string.reverse(cmdPath), "/") + 2)
|
|
||||||
|
|
||||||
local text = fs.readAllText(cmdPath)
|
|
||||||
local program, err = load(text, progName)
|
|
||||||
if not program then
|
|
||||||
local errSL = string.sub(err, string.find(err, ":") + 1)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
printInline(progName..": Program load error on line ")
|
|
||||||
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local proc = syscall.spawn(function(...)
|
|
||||||
syscall.open("/dev/tty/TTY1","r")
|
|
||||||
syscall.open("/dev/tty/TTY1","w")
|
|
||||||
syscall.open("/dev/null","w")
|
|
||||||
local success, msg = pcall(program, ...)
|
|
||||||
if not success then
|
|
||||||
local errSL = string.sub(msg, string.find(msg, ":") + 1)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
printInline(progName..": Program runtime error on line ")
|
|
||||||
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
||||||
end
|
|
||||||
end, progName, nil, {table.unpack(args, 2)})
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local exited, code = syscall.collect(proc)
|
|
||||||
if exited then
|
|
||||||
if code then
|
|
||||||
print("\nTask exited with code:\n"..tostring(code))
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if terminate then
|
|
||||||
local success, err = syscall.kill(proc)
|
|
||||||
if success then
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
print("\nProgram Terminated.")
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
end
|
|
||||||
terminate = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
sleep(0.05)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local command = getUserInput()
|
|
||||||
if command ~= "" then
|
|
||||||
if command ~= commandHistory[#commandHistory] then
|
|
||||||
table.insert(commandHistory, command)
|
|
||||||
end
|
|
||||||
runCommand(command)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--ERROR HANDLING
|
|
||||||
end, debug.traceback)
|
|
||||||
|
|
||||||
if not success then
|
|
||||||
syscall.log("Error running shell: "..errorMsg, "ERROR")
|
|
||||||
syscall.devctl(1,"sfgc",2)
|
|
||||||
syscall.devctl(1,"sbgc",16)
|
|
||||||
print()
|
|
||||||
print("Error running shell: ")
|
|
||||||
print(errorMsg)
|
|
||||||
end
|
|
||||||
@@ -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,178 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
syscall.open("/dev/tty/TTY1","r") --stdin (fd 0)
|
|
||||||
syscall.open("/dev/tty/TTY1","w") --stdout (fd 1)
|
|
||||||
syscall.open("/dev/null","w") --stderr (fd 2)
|
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
|
|
||||||
local MAX_ATTEMPTS = 3
|
|
||||||
|
|
||||||
local function readLine(mask)
|
|
||||||
local input = ""
|
|
||||||
while true do
|
|
||||||
local ch = syscall.read(0)
|
|
||||||
if not ch or ch == "" then
|
|
||||||
-- buffer empty, spin
|
|
||||||
elseif ch == "\n" then
|
|
||||||
syscall.write(1, "\n")
|
|
||||||
return input
|
|
||||||
elseif ch == "\b" then
|
|
||||||
if #input > 0 then
|
|
||||||
input = input:sub(1, -2)
|
|
||||||
syscall.write(1, "\b \b")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
input = input .. ch
|
|
||||||
syscall.write(1, mask or ch)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function firstBoot()
|
|
||||||
local shadow = fs.readAllText("/etc/shadow") or ""
|
|
||||||
if shadow:match("%S") then return end
|
|
||||||
|
|
||||||
syscall.devctl(1, "clear")
|
|
||||||
syscall.devctl(1, "spos", 1, 1)
|
|
||||||
syscall.devctl(1, "sfgc", 3)
|
|
||||||
syscall.write(1, "HyperionOS First Boot Setup\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
syscall.write(1, "No root password is set. Please create one now.\n\n")
|
|
||||||
|
|
||||||
while true do
|
|
||||||
syscall.write(1, "New root password: ")
|
|
||||||
local pw1 = readLine("*")
|
|
||||||
syscall.write(1, "Confirm password: ")
|
|
||||||
local pw2 = readLine("*")
|
|
||||||
|
|
||||||
if pw1 ~= pw2 then
|
|
||||||
syscall.devctl(1, "sfgc", 2)
|
|
||||||
syscall.write(1, "Passwords do not match. Try again.\n\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
elseif #pw1 < 6 then
|
|
||||||
syscall.devctl(1, "sfgc", 2)
|
|
||||||
syscall.write(1, "Password too short (minimum 6 characters).\n\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
else
|
|
||||||
local ok, err = syscall.auth_setpassword(0, pw1)
|
|
||||||
if ok then
|
|
||||||
syscall.devctl(1, "sfgc", 3)
|
|
||||||
syscall.write(1, "Root password set.\n\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
sleep(0.5)
|
|
||||||
break
|
|
||||||
else
|
|
||||||
syscall.devctl(1, "sfgc", 2)
|
|
||||||
syscall.write(1, "Error: " .. tostring(err) .. "\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function spawnShell(username, uid, shell, homedir)
|
|
||||||
local shellText = fs.readAllText(shell)
|
|
||||||
if not shellText then
|
|
||||||
syscall.write(1, "login: shell not found: " .. shell .. "\n")
|
|
||||||
sleep(2)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Spawn a wrapper that loads and runs the shell, reporting any error back
|
|
||||||
-- via exit code channel so we can display it
|
|
||||||
local errFifo = {}
|
|
||||||
|
|
||||||
local proc = syscall.spawn(function()
|
|
||||||
syscall.setuid(uid)
|
|
||||||
syscall.chdir(homedir)
|
|
||||||
syscall.setEnviron("HOME", homedir)
|
|
||||||
syscall.setEnviron("USER", username)
|
|
||||||
syscall.setEnviron("SHELL", shell)
|
|
||||||
syscall.setEnviron("PATH", "/bin/")
|
|
||||||
|
|
||||||
local shellFn, loadErr = load(shellText, "@" .. shell)
|
|
||||||
if not shellFn then
|
|
||||||
-- Report load error via log and a recognizable exit code
|
|
||||||
syscall.log("login: shell load error: " .. tostring(loadErr), "ERROR")
|
|
||||||
syscall.exit(-1)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, runErr = xpcall(shellFn, debug.traceback)
|
|
||||||
if not ok then
|
|
||||||
syscall.log("login: shell runtime error: " .. tostring(runErr), "ERROR")
|
|
||||||
end
|
|
||||||
end, username .. ":shell")
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local exited, code = syscall.collect(proc)
|
|
||||||
if exited then
|
|
||||||
if code then
|
|
||||||
syscall.devctl(1, "sfgc", 2)
|
|
||||||
syscall.write(1, "\nShell exited with code: " .. tostring(code) .. "\n")
|
|
||||||
syscall.write(1, "(Check /var/log/syslog.log for details)\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
sleep(2)
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
sleep(0.1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function doLogin()
|
|
||||||
syscall.devctl(1, "clear")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
syscall.devctl(1, "sbgc", 16)
|
|
||||||
syscall.devctl(1, "spos", 1, 1)
|
|
||||||
|
|
||||||
local hostname = syscall.getHostname() or "hyperion"
|
|
||||||
syscall.write(1, "HyperionOS\n")
|
|
||||||
syscall.write(1, hostname .. " login\n\n")
|
|
||||||
|
|
||||||
local attempts = 0
|
|
||||||
while attempts < MAX_ATTEMPTS do
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
syscall.write(1, "Username: ")
|
|
||||||
local username = readLine(nil)
|
|
||||||
|
|
||||||
if username == "" then goto continue end
|
|
||||||
|
|
||||||
syscall.write(1, "Password: ")
|
|
||||||
local password = readLine("*")
|
|
||||||
|
|
||||||
local ok, err = syscall.auth_login(username, password)
|
|
||||||
if ok then
|
|
||||||
local uid = syscall.auth_getuid(username)
|
|
||||||
local pwent = uid and syscall.auth_getpasswd(uid)
|
|
||||||
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
|
||||||
local homedir = (pwent and pwent.homedir) or "/"
|
|
||||||
|
|
||||||
syscall.devctl(1, "sfgc", 3)
|
|
||||||
syscall.write(1, "\nWelcome, " .. username .. "!\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
sleep(0.3)
|
|
||||||
|
|
||||||
spawnShell(username, uid, shell, homedir)
|
|
||||||
return -- back to login prompt
|
|
||||||
else
|
|
||||||
attempts = attempts + 1
|
|
||||||
sleep(1)
|
|
||||||
syscall.devctl(1, "sfgc", 2)
|
|
||||||
syscall.write(1, "Login incorrect.\n\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
|
|
||||||
syscall.devctl(1, "sfgc", 2)
|
|
||||||
syscall.write(1, "Maximum login attempts exceeded.\n")
|
|
||||||
syscall.devctl(1, "sfgc", 1)
|
|
||||||
sleep(5)
|
|
||||||
end
|
|
||||||
|
|
||||||
firstBoot()
|
|
||||||
while true do
|
|
||||||
doLogin()
|
|
||||||
end
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
local cloptions = {
|
|
||||||
a = false,
|
|
||||||
h = false,
|
|
||||||
l = false,
|
|
||||||
help = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
local inpArgs = {...}
|
|
||||||
local args = {}
|
|
||||||
local name = syscall.getTask(syscall.getpid()).name
|
|
||||||
|
|
||||||
local optToSet = false
|
|
||||||
for _, v in pairs(inpArgs) do
|
|
||||||
if optToSet then
|
|
||||||
cloptions[optToSet] = v
|
|
||||||
optToSet = false
|
|
||||||
elseif v:sub(1, 2) == "--" then
|
|
||||||
local opt = v:sub(3)
|
|
||||||
if cloptions[opt] == nil then
|
|
||||||
print(name..": unrecognized option '"..v.."'.")
|
|
||||||
if cloptions.help ~= nil then
|
|
||||||
print("try '"..name.." --help' for more information.")
|
|
||||||
end
|
|
||||||
return
|
|
||||||
elseif cloptions[opt] == false then
|
|
||||||
cloptions[opt] = true
|
|
||||||
else
|
|
||||||
optToSet = opt
|
|
||||||
end
|
|
||||||
elseif v:sub(1, 1) == "-" then
|
|
||||||
for i = 2, #v do
|
|
||||||
local opt = v:sub(i, i)
|
|
||||||
if cloptions[opt] == nil then
|
|
||||||
print(name..": invalid option '-"..opt.."'.")
|
|
||||||
if cloptions.help ~= nil then
|
|
||||||
print("try '"..name.." --help' for more information.")
|
|
||||||
end
|
|
||||||
return
|
|
||||||
else
|
|
||||||
cloptions[opt] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(args, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if cloptions.help then
|
|
||||||
print("Usage: "..name.." [OPTION]... [DIR]")
|
|
||||||
print("List all entries in the specified DIRectory, or cwd if not specified")
|
|
||||||
print("\nOptions:")
|
|
||||||
print(" -a Do not ignore entries starting with .")
|
|
||||||
print(" -h with -l, print sizes in a human readble format")
|
|
||||||
print(" -l Use a long listing format")
|
|
||||||
print(" --help Display this help and exit")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
local dir = (args[1] or "")
|
|
||||||
if dir:sub(1, 1) ~= "/" then
|
|
||||||
dir = syscall.getcwd().."/"..dir
|
|
||||||
end
|
|
||||||
|
|
||||||
if dir:sub(#dir, #dir) ~= "/" then
|
|
||||||
dir = dir.."/"
|
|
||||||
end
|
|
||||||
|
|
||||||
if not fs.isDir(dir) then
|
|
||||||
print(name..": Cannot access '"..args[1].."': No such directory.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local screenSizeStr = syscall.devctl(1, "size")
|
|
||||||
local sizeX = tonumber(screenSizeStr:sub(1, screenSizeStr:find(";")-1))
|
|
||||||
local sizeY = tonumber(screenSizeStr:sub(screenSizeStr:find(";")+1))
|
|
||||||
|
|
||||||
local list = fs.list(dir)
|
|
||||||
if not cloptions.a then
|
|
||||||
for i = #list, 1, -1 do
|
|
||||||
if list[i]:sub(1, 1) == "." then
|
|
||||||
table.remove(list, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local colWidth = 0
|
|
||||||
local numCols = 1
|
|
||||||
if not cloptions.l then
|
|
||||||
for _, item in pairs(list) do
|
|
||||||
if #item + 2 > colWidth then
|
|
||||||
colWidth = #item + 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
numCols = math.floor(sizeX / colWidth)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sizePrefixes = {"K", "M", "G"}
|
|
||||||
|
|
||||||
for i,v in ipairs(list) do
|
|
||||||
local fileStats = syscall.stat(dir..v)
|
|
||||||
local isDir = fs.isDir(dir..v)
|
|
||||||
if cloptions.l then
|
|
||||||
if isDir then
|
|
||||||
printInline("d")
|
|
||||||
else
|
|
||||||
printInline("-")
|
|
||||||
end
|
|
||||||
printInline("------ ")
|
|
||||||
printInline(fileStats.owner.." ")
|
|
||||||
printInline(fileStats.group.." ")
|
|
||||||
local size = fileStats.size
|
|
||||||
if cloptions.h then
|
|
||||||
local scale = 0
|
|
||||||
while size > 1024 do
|
|
||||||
size = size / 1024
|
|
||||||
scale = scale + 1
|
|
||||||
end
|
|
||||||
if scale > 0 then
|
|
||||||
if size < 10 then
|
|
||||||
size = math.floor(size).."."..math.floor((size * 10) % 10)..sizePrefixes[scale]
|
|
||||||
else
|
|
||||||
size = math.floor(size)..sizePrefixes[scale]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
printInline(size.." ")
|
|
||||||
printInline(math.floor(fileStats.modified / 1000).." ")
|
|
||||||
end
|
|
||||||
if isDir then
|
|
||||||
syscall.devctl(1,"sfgc",4)
|
|
||||||
else
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
end
|
|
||||||
printInline(v)
|
|
||||||
printInline((" "):rep(colWidth - #v))
|
|
||||||
syscall.devctl(1,"sfgc",1)
|
|
||||||
if i % numCols == 0 then
|
|
||||||
print("")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #list % numCols ~= 0 then
|
|
||||||
print("")
|
|
||||||
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,18 +0,0 @@
|
|||||||
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
|
||||||
print(".. *. .. | "..userhost)
|
|
||||||
print(" *= +@* +* | "..string.rep("-",#userhost))
|
|
||||||
print(" .@#. -@@@= :#@. | OS: "..(syscall.version() or "Unknown"))
|
|
||||||
print(" =@@+ *@@@# +@@= | Host: "..(syscall.getHost() or "Unknown"))
|
|
||||||
print(" %@@%: *@@@# -%@@% | Uptime: "..(syscall.getUptime() or "Unknown"))
|
|
||||||
print(" :@@@@+ *@@@# .*@@@@: | Tasks: "..tostring((#syscall.getTasks() or "Unknown")))
|
|
||||||
print(" :*@@@%- *@@@# -@@@@*: | Packages: ".."Unknown")
|
|
||||||
print(" =%@@#. *@@@# .#@@%= | Shell: "..(syscall.getEnviron("SHELL") or "Unknown"))
|
|
||||||
print(" :=. :*@@= *@@@# =@@+: .=: | ")
|
|
||||||
print(" %@#=..*# +@@@# #*..=#@# | ")
|
|
||||||
print(" .@@@@+=# .%@%: #=+@@@@. | ")
|
|
||||||
print(" .....=# -@= *+...:. | ")
|
|
||||||
print(" -*%*-@= - =@-*%*- | ")
|
|
||||||
print(" -@*. -@%. :%@- :*@- | ")
|
|
||||||
print(" .#@#@* | ")
|
|
||||||
print(" -#- | ")
|
|
||||||
print(" | ")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print(syscall.getcwd())
|
|
||||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
local fs = require("sys.fs")
|
|
||||||
local bashStr = fs.readAllText("/bin/bash")
|
|
||||||
local bashFun = load(bashStr)
|
|
||||||
syscall.spawn(bashFun, "bash")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
syscall.chown("/bin", 0, 0)
|
|
||||||
@@ -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.auth_whoami() or "Unknown"))
|
|
||||||
Binary file not shown.
18
Src/Hyperion-core/lib/colors
Normal file
18
Src/Hyperion-core/lib/colors
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
--:Minify:--
|
||||||
|
return {
|
||||||
|
white=0xFFFFFF,
|
||||||
|
red=0xFF0000,
|
||||||
|
green=0x00FF00,
|
||||||
|
blue=0x0000FF,
|
||||||
|
cyan=0x00FFFF,
|
||||||
|
yellow=0xFFFF00,
|
||||||
|
purple=0xFF00FF,
|
||||||
|
black=0x000000,
|
||||||
|
gray=0x888888,
|
||||||
|
lightgrey=0xBBBBBB,
|
||||||
|
darkgrey=0x444444,
|
||||||
|
orange=0xFF8800,
|
||||||
|
mint=0x00FF88,
|
||||||
|
brown=0xa52a2a,
|
||||||
|
chocolate=0xd2691e
|
||||||
|
}
|
||||||
@@ -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,26 +0,0 @@
|
|||||||
local args={...}
|
|
||||||
local kernel=args[1]
|
|
||||||
local driver={}
|
|
||||||
|
|
||||||
driver.name="CCT Term Module"
|
|
||||||
driver.version="0.1.0"
|
|
||||||
driver.type="gpio"
|
|
||||||
driver.description="CCT redstone Module Kernel Module"
|
|
||||||
driver.arch="cct"
|
|
||||||
driver.author="HyperionOS Dev Team"
|
|
||||||
driver.license="MIT"
|
|
||||||
driver.api={}
|
|
||||||
|
|
||||||
function driver.load()
|
|
||||||
-- will
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.unload()
|
|
||||||
-- Nothing to unload
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.main()
|
|
||||||
-- Nothing to run
|
|
||||||
end
|
|
||||||
|
|
||||||
-- kernel.drivers.register(driver)
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
function string.hasSuffix(str, suffix)
|
|
||||||
return string.sub(str, #suffix + 1) == suffix
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.hasPrefix(str, prefix)
|
|
||||||
return string.sub(str, 1, #prefix) == prefix
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.getSuffix(str, prefix) return string.sub(str, #prefix + 1) end
|
|
||||||
|
|
||||||
function string.getPrefix(str, suffix) return string.sub(str, 1, #suffix) end
|
|
||||||
|
|
||||||
function string.join(str, ...) return table.concat(table.pack(str, ...)) end
|
|
||||||
|
|
||||||
function string.delim(str, ...) return table.concat(table.pack(...), str) end
|
|
||||||
|
|
||||||
function string.split(str, delim, maxResultCountOrNil)
|
|
||||||
assert(#delim == 1, "only delim len 1 supported for now")
|
|
||||||
if not str then return false end
|
|
||||||
maxResultCountOrNil = (maxResultCountOrNil or 0) - 1
|
|
||||||
local rv = {}
|
|
||||||
local buf = ""
|
|
||||||
for i = 1, #str do
|
|
||||||
local c = string.sub(str, i, i)
|
|
||||||
if #rv ~= maxResultCountOrNil and c == delim then
|
|
||||||
table.insert(rv, buf)
|
|
||||||
buf = ""
|
|
||||||
else
|
|
||||||
buf = buf .. c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(rv, buf)
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.deepcopy(orig, copies)
|
|
||||||
copies = copies or {}
|
|
||||||
|
|
||||||
if type(orig) ~= 'table' then
|
|
||||||
return orig
|
|
||||||
elseif copies[orig] then
|
|
||||||
return copies[orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
local copy = {}
|
|
||||||
copies[orig] = copy
|
|
||||||
|
|
||||||
for k, v in next, orig, nil do
|
|
||||||
local copied_key = table.deepcopy(k, copies)
|
|
||||||
local copied_val = table.deepcopy(v, copies)
|
|
||||||
copy[copied_key] = copied_val
|
|
||||||
end
|
|
||||||
|
|
||||||
return copy
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.hasKey(tabl, query)
|
|
||||||
for i, v in pairs(tabl) do if i == query then return v end end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.hasVal(tabl, query)
|
|
||||||
for i, v in pairs(tabl) do if v == query then return i end end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function serialize(tbl, seen)
|
|
||||||
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
|
|
||||||
|
|
||||||
-- Mark this table as seen
|
|
||||||
seen[tbl] = true
|
|
||||||
|
|
||||||
local output = "{"
|
|
||||||
local first = true
|
|
||||||
|
|
||||||
for i, v in pairs(tbl) do
|
|
||||||
-- Handle comma placement more cleanly
|
|
||||||
if not first then output = output .. "," end
|
|
||||||
first = false
|
|
||||||
|
|
||||||
-- Serialize Key
|
|
||||||
if type(i) == "string" then
|
|
||||||
output = output .. "[\"" .. i .. "\"]="
|
|
||||||
elseif type(i) == "number" then
|
|
||||||
output = output .. "[" .. tostring(i) .. "]="
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Serialize Value
|
|
||||||
if type(v) == "table" then
|
|
||||||
-- Pass the 'seen' table down to the recursive call
|
|
||||||
output = output .. serialize(v, seen)
|
|
||||||
elseif type(v) == "string" then
|
|
||||||
output = output .. "[=[" .. v .. "]=]"
|
|
||||||
elseif type(v) == "number" or type(v) == "boolean" then
|
|
||||||
output = output .. tostring(v)
|
|
||||||
elseif type(v) == "function" then
|
|
||||||
output = output .. "\"" .. tostring(v) .. "\""
|
|
||||||
elseif type(v) == "thread" then
|
|
||||||
output = output .. "\"" .. tostring(v) .. "\""
|
|
||||||
else
|
|
||||||
error("serialization of type \"" .. type(v) .. "\" is not supported")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
seen[tbl] = nil
|
|
||||||
|
|
||||||
output = output .. "}"
|
|
||||||
return output
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldtype = type
|
|
||||||
local oldgetmetatable = getmetatable
|
|
||||||
function type(object, trueType)
|
|
||||||
if trueType then return oldtype(object) end
|
|
||||||
if oldtype(object) ~= "table" then
|
|
||||||
return oldtype(object)
|
|
||||||
else
|
|
||||||
if oldtype(oldgetmetatable(object)) == "table" then
|
|
||||||
local metatable = oldgetmetatable(object)
|
|
||||||
---@diagnostic disable-next-line: need-check-nil
|
|
||||||
if metatable.__type then return metatable.__type end
|
|
||||||
else
|
|
||||||
return "table"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function getmetatable(object)
|
|
||||||
if oldtype(object) ~= "table" then return end
|
|
||||||
if oldtype(oldgetmetatable(object)) == "table" then
|
|
||||||
if oldgetmetatable(object).__isuserdata then
|
|
||||||
if oldtype(oldgetmetatable(object).__usermeta) == "function" then
|
|
||||||
return oldgetmetatable(object).__usermeta()
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object).__usermeta
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function isEqualToAny(a, ...)
|
|
||||||
local args = {...}
|
|
||||||
for i = 0, #args do if a == args[i] then return true end end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function isEqualToAll(a, ...)
|
|
||||||
local args = {...}
|
|
||||||
for i = 0, #args do if a ~= args[i] then return false end end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.keys(t)
|
|
||||||
local a = {}
|
|
||||||
for n in pairs(t) do table.insert(a, n) end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.values(t)
|
|
||||||
local a = {}
|
|
||||||
for _, n in pairs(t) do table.insert(a, n) end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.indexOf(t, value)
|
|
||||||
for i, v in ipairs(t) do if v == value then return i end end
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.replace(s, target, repl)
|
|
||||||
local result = {}
|
|
||||||
local i = 1
|
|
||||||
local n = #s
|
|
||||||
local t_len = #target
|
|
||||||
|
|
||||||
while i <= n do
|
|
||||||
local match = true
|
|
||||||
if i + t_len - 1 <= n then
|
|
||||||
for j = 1, t_len do
|
|
||||||
if s:sub(i + j - 1, i + j - 1) ~= target:sub(j, j) then
|
|
||||||
match = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
match = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if match then
|
|
||||||
table.insert(result, repl)
|
|
||||||
i = i + t_len
|
|
||||||
else
|
|
||||||
table.insert(result, s:sub(i, i))
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
function toHex(num)
|
|
||||||
return string.format("%X", num)
|
|
||||||
end
|
|
||||||
|
|
||||||
syscall = setmetatable({}, {
|
|
||||||
__index = function(self, name)
|
|
||||||
return function(...)
|
|
||||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
|
||||||
if res[1] then
|
|
||||||
return table.unpack(res, 2, res.n)
|
|
||||||
else
|
|
||||||
error(res[2], 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
table.serialize = serialize
|
|
||||||
@@ -1,629 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local vfs = {}
|
|
||||||
kernel.vfs = vfs
|
|
||||||
vfs.mounts = {["$"] = "/"}
|
|
||||||
vfs.disks = kernel.disks
|
|
||||||
|
|
||||||
-- Path normalization
|
|
||||||
local function normalizePath(path)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local cwd = task.cwd or "/"
|
|
||||||
if path:sub(1, 1) ~= "/" then path = cwd .. "/" .. path end
|
|
||||||
local parts = {}
|
|
||||||
for part in path:gmatch("[^/]+") do
|
|
||||||
if part == ".." then
|
|
||||||
if #parts > 0 then table.remove(parts) end
|
|
||||||
elseif part ~= "." and part ~= "" then
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "/" .. table.concat(parts, "/")
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.splitPath(path)
|
|
||||||
local rv=string.split(path,"/")
|
|
||||||
while table.indexOf(rv, "") ~= -1 do
|
|
||||||
table.remove(rv, table.indexOf(rv, ""))
|
|
||||||
end
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Resolve mount and disk path
|
|
||||||
local function resolvePath(path)
|
|
||||||
path = normalizePath(path)
|
|
||||||
|
|
||||||
local mountPoint = nil
|
|
||||||
local mountId = nil
|
|
||||||
|
|
||||||
for id, mp in pairs(vfs.mounts) do
|
|
||||||
if path == mp or (mp == "/" and path:sub(1, 1) == "/") or path:sub(1, #mp + 1) == mp .. "/" then
|
|
||||||
if not mountPoint or #mp > #mountPoint then
|
|
||||||
mountPoint = mp
|
|
||||||
mountId = id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not mountId then
|
|
||||||
error("ENODEV")
|
|
||||||
end
|
|
||||||
|
|
||||||
local diskPath = path:sub(#mountPoint + 1)
|
|
||||||
if diskPath == "" then
|
|
||||||
diskPath = "/"
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.config.logPathResolution then
|
|
||||||
kernel.log("Path '"..path.."' resolved to disk '"..mountId.."' and path '"..diskPath.."'")
|
|
||||||
end
|
|
||||||
|
|
||||||
return vfs.disks[mountId], diskPath
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Allocate file descriptor for current task
|
|
||||||
local function allocFD(task)
|
|
||||||
local fd = 0
|
|
||||||
while task.fd[fd] do fd = fd + 1 end
|
|
||||||
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
-- System-wide open file limit
|
|
||||||
local total = 0
|
|
||||||
local function checkSystemLimit()
|
|
||||||
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- File object constructor
|
|
||||||
local function newFileObj(handle, mode, path, meta, type)
|
|
||||||
return {
|
|
||||||
handle = handle,
|
|
||||||
mode = mode,
|
|
||||||
path = path,
|
|
||||||
meta = meta,
|
|
||||||
type = type,
|
|
||||||
refcount = 1
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.newfd(fdobj)
|
|
||||||
checkSystemLimit()
|
|
||||||
total=total+1
|
|
||||||
local fd = allocFD(kernel.currentTask)
|
|
||||||
kernel.currentTask.fd[fd]=fdobj
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse metafile
|
|
||||||
local function parseMetafile(file)
|
|
||||||
if not file or file == "" then return {} end
|
|
||||||
|
|
||||||
local ret = {}
|
|
||||||
local pointer = 1
|
|
||||||
|
|
||||||
while pointer <= #file do
|
|
||||||
local namelen = file:byte(pointer)
|
|
||||||
pointer = pointer + 1
|
|
||||||
|
|
||||||
local name = file:sub(pointer, pointer + namelen - 1)
|
|
||||||
pointer = pointer + namelen
|
|
||||||
|
|
||||||
local owner = file:byte(pointer)
|
|
||||||
local group = file:byte(pointer + 1)
|
|
||||||
local perms = file:byte(pointer + 2)
|
|
||||||
pointer = pointer + 3
|
|
||||||
|
|
||||||
local cmetalen = file:byte(pointer)
|
|
||||||
pointer = pointer + 1
|
|
||||||
|
|
||||||
local cmeta = ""
|
|
||||||
if cmetalen > 0 then
|
|
||||||
cmeta = file:sub(pointer, pointer + cmetalen - 1)
|
|
||||||
pointer = pointer + cmetalen
|
|
||||||
end
|
|
||||||
|
|
||||||
ret[name] = {owner = owner, group = group, perms = perms, cmeta = cmeta}
|
|
||||||
end
|
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Build metafile
|
|
||||||
local function makeMetafile(meta)
|
|
||||||
local file = ""
|
|
||||||
for name, m in pairs(meta) do
|
|
||||||
local entry = ""
|
|
||||||
entry = entry .. string.char(#name) .. name
|
|
||||||
entry = entry .. string.char(m.owner, m.group, m.perms)
|
|
||||||
entry = entry .. string.char(#m.cmeta) .. m.cmeta
|
|
||||||
file = file .. entry
|
|
||||||
end
|
|
||||||
return file
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get file metadata object
|
|
||||||
local function getFileMeta(path)
|
|
||||||
local disk, fullPath = resolvePath(path)
|
|
||||||
fullPath = normalizePath(fullPath)
|
|
||||||
|
|
||||||
local parts = {}
|
|
||||||
for p in fullPath:gmatch("[^/]+") do table.insert(parts, p) end
|
|
||||||
|
|
||||||
-- default fallback
|
|
||||||
local default = {owner = 0, group = 0, perms = 63, cmeta = ""}
|
|
||||||
|
|
||||||
-- walk from deepest parent upward
|
|
||||||
for i = #parts, 1, -1 do
|
|
||||||
local parent = "/" .. table.concat(parts, "/", 1, i - 1)
|
|
||||||
if parent ~= "/" then parent = parent .. "/" end
|
|
||||||
|
|
||||||
local target = parts[i]
|
|
||||||
if target == ".meta" then error("Cannot open metafile") end
|
|
||||||
local metaPath = parent .. ".meta"
|
|
||||||
|
|
||||||
if disk:fileExists(metaPath) then
|
|
||||||
local f = disk:open(metaPath, "r")
|
|
||||||
local text = f.read(65535)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
local parsed = parseMetafile(text)
|
|
||||||
if parsed[target] then return parsed[target] end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
|
|
||||||
local function ensureParentMeta(path)
|
|
||||||
local disk, fullPath = resolvePath(path)
|
|
||||||
fullPath = normalizePath(fullPath)
|
|
||||||
|
|
||||||
-- split parent + name
|
|
||||||
local parent, name = fullPath:match("^(.*)/([^/]+)$")
|
|
||||||
if not parent then
|
|
||||||
parent = "/"
|
|
||||||
name = fullPath:gsub("^/", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
if name == ".meta" then error("Cannot open metafile") end
|
|
||||||
|
|
||||||
if parent ~= "/" and parent:sub(-1) ~= "/" then parent = parent .. "/" end
|
|
||||||
|
|
||||||
local metaPath = parent .. ".meta"
|
|
||||||
|
|
||||||
if not disk:fileExists(metaPath) then
|
|
||||||
local f = disk:open(metaPath, "w")
|
|
||||||
f.write("")
|
|
||||||
f.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
return metaPath, name
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Permission checking
|
|
||||||
local function checkperms(meta, mode)
|
|
||||||
local modes = {
|
|
||||||
r = {owner = 5, group = 3, everyone = 1},
|
|
||||||
w = {owner = 4, group = 2, everyone = 0},
|
|
||||||
a = {owner = 4, group = 2, everyone = 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
local bits = meta.perms
|
|
||||||
local function bit_is_set(num, bit)
|
|
||||||
return math.floor(num / (2 ^ bit)) % 2 == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.uid == 0 then return true end
|
|
||||||
if kernel.uid == meta.owner and bit_is_set(bits, modes[mode].owner) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
if meta.group and kernel.groups then
|
|
||||||
for _, gid in ipairs(kernel.groups) do
|
|
||||||
if gid == meta.group and bit_is_set(bits, modes[mode].group) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if bit_is_set(bits, modes[mode].everyone) then return true end
|
|
||||||
error("EACCES")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- mounts
|
|
||||||
local function normalizeMountPoint(path)
|
|
||||||
path = normalizePath(path)
|
|
||||||
if path ~= "/" and path:sub(-1) == "/" then path = path:sub(1, -2) end
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
|
|
||||||
local required = {
|
|
||||||
"open",
|
|
||||||
"type",
|
|
||||||
"list",
|
|
||||||
"attributes",
|
|
||||||
"fileExists",
|
|
||||||
"makeDirectory",
|
|
||||||
"remove"
|
|
||||||
}
|
|
||||||
|
|
||||||
local function check(disk)
|
|
||||||
for _, name in ipairs(required) do
|
|
||||||
if type(disk[name]) ~= "function" then
|
|
||||||
error("Invalid disk: missing method '" .. name .. "'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.mount(target, diskOrId)
|
|
||||||
if kernel.uid ~= 0 then error("EPERM") end
|
|
||||||
if not target then error("EINVAL") end
|
|
||||||
|
|
||||||
target = normalizeMountPoint(target)
|
|
||||||
if not vfs.exists(target) then vfs.mkdir(target) end
|
|
||||||
if vfs.type(target) ~= "directory" then error("EINVAL") end
|
|
||||||
|
|
||||||
local disk
|
|
||||||
local id
|
|
||||||
|
|
||||||
if type(diskOrId) == "string" then
|
|
||||||
disk = kernel.disks[diskOrId]
|
|
||||||
if not disk then error("ENODEV") end
|
|
||||||
check(disk)
|
|
||||||
id = diskOrId
|
|
||||||
elseif type(diskOrId) == "table" then
|
|
||||||
check(disk)
|
|
||||||
disk = diskOrId
|
|
||||||
id = disk.address
|
|
||||||
vfs.disks[id] = disk
|
|
||||||
else
|
|
||||||
error("EINVAL")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Prevent shadowing an existing mount
|
|
||||||
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
|
|
||||||
|
|
||||||
vfs.mounts[id] = target
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.umount(target)
|
|
||||||
if kernel.uid ~= 0 then error("EPERM") end
|
|
||||||
if not target then error("EINVAL") end
|
|
||||||
|
|
||||||
target = normalizeMountPoint(target)
|
|
||||||
|
|
||||||
for id, mp in pairs(vfs.mounts) do
|
|
||||||
if mp == target then
|
|
||||||
if id == "$" then error("EBUSY") end -- root fs
|
|
||||||
vfs.mounts[id] = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
error("EINVAL")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open file
|
|
||||||
function vfs.open(path, mode)
|
|
||||||
checkSystemLimit()
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local fd = allocFD(task)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("NODISK") end
|
|
||||||
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, mode)
|
|
||||||
|
|
||||||
local handle
|
|
||||||
if disk:type(diskPath)~="directory" then
|
|
||||||
handle = disk:open(diskPath, mode)
|
|
||||||
if type(handle)~="table" then error("ENFILE") end
|
|
||||||
end
|
|
||||||
|
|
||||||
task.fd[fd] = newFileObj(handle, mode, path, meta, disk:type(diskPath))
|
|
||||||
if not disk.isvirt then
|
|
||||||
total = total + 1
|
|
||||||
end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Read
|
|
||||||
function vfs.read(fd, count)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.read then error("EBADF") end
|
|
||||||
return file.handle.read(count or 1) or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Write
|
|
||||||
function vfs.write(fd, content)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.write then error("EBADF") end
|
|
||||||
return file.handle.write(content)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pread / Pwrite
|
|
||||||
function vfs.pread(fd, count, offset)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.read then error("EBADF") end
|
|
||||||
if not file.handle.seek then error("EBADF") end
|
|
||||||
file.handle.seek("set", offset)
|
|
||||||
return file.handle.read(count or 1) or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.pwrite(fd, content, offset)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.write then error("EBADF") end
|
|
||||||
if not file.handle.seek then error("EBADF") end
|
|
||||||
file.handle.seek("set", offset)
|
|
||||||
return file.handle.write(content)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Seek
|
|
||||||
function vfs.lseek(fd, offset, whence)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.seek then error("EBADF") end
|
|
||||||
return file.handle.seek(whence or "set", offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fsync
|
|
||||||
function vfs.fsync(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.flush then error("EBADF") end
|
|
||||||
if file.mode ~= "w" and file.mode ~= "a" then error("EBADF") end
|
|
||||||
file.handle.flush()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Close
|
|
||||||
function vfs.close(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
task.fd[fd] = nil
|
|
||||||
total = total - 1
|
|
||||||
|
|
||||||
file.refcount = file.refcount - 1
|
|
||||||
if file.refcount <= 0 then
|
|
||||||
if file.handle.close then
|
|
||||||
file.handle.close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sendfile
|
|
||||||
function vfs.sendfile(outfd, infd, count)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local inFile = task.fd[infd]
|
|
||||||
local outFile = task.fd[outfd]
|
|
||||||
if not inFile or not outFile then error("EBADF") end
|
|
||||||
if not inFile.handle.read then error("EBADF") end
|
|
||||||
if not outFile.handle.write then error("EBADF") end
|
|
||||||
local data = inFile.handle.read(count or 1024)
|
|
||||||
if not data or data == "" then return end
|
|
||||||
return outFile.handle.write(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Stat / Fstat
|
|
||||||
function vfs.stat(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
local attrs = disk:attributes(diskPath)
|
|
||||||
return {
|
|
||||||
size = attrs.size,
|
|
||||||
modified = attrs.modified,
|
|
||||||
created = attrs.created,
|
|
||||||
owner = meta.owner,
|
|
||||||
group = meta.group,
|
|
||||||
xattr = meta.cmeta
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.fstat(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
local disk, path = resolvePath(file.path)
|
|
||||||
local attrs = disk:attributes(path)
|
|
||||||
return {
|
|
||||||
size = attrs.size,
|
|
||||||
modified = attrs.modified,
|
|
||||||
created = attrs.created,
|
|
||||||
owner = file.meta.owner,
|
|
||||||
group = file.meta.group,
|
|
||||||
xattr = file.meta.cmeta
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Directory operations
|
|
||||||
function vfs.listdir(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if disk:type(diskPath) ~= "directory" then error("ENOENT") end
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "r")
|
|
||||||
local list = disk:list(diskPath)
|
|
||||||
if table.indexOf(list, ".meta") ~= -1 then
|
|
||||||
table.remove(list, table.indexOf(list, ".meta"))
|
|
||||||
end
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.mkdir(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "w")
|
|
||||||
disk:makeDirectory(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.remove(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "w")
|
|
||||||
disk:remove(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Permission functions
|
|
||||||
function vfs.chmod(path, perms)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
|
|
||||||
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
|
|
||||||
meta.perms = perms
|
|
||||||
|
|
||||||
local mpath, target = ensureParentMeta(path)
|
|
||||||
|
|
||||||
local mf = disk:open(mpath, "r")
|
|
||||||
local text = mf.read(65535)
|
|
||||||
mf.close()
|
|
||||||
|
|
||||||
local parsed = parseMetafile(text)
|
|
||||||
parsed[target] = meta
|
|
||||||
|
|
||||||
local f = disk:open(mpath, "w")
|
|
||||||
f.write(makeMetafile(parsed))
|
|
||||||
f.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.fchmod(fd, perms)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
vfs.chmod(file.path, perms)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.chown(path, uid, gid)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
|
|
||||||
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
|
|
||||||
meta.owner = uid
|
|
||||||
meta.group = gid
|
|
||||||
|
|
||||||
local mpath, target = ensureParentMeta(path)
|
|
||||||
|
|
||||||
local mf = disk:open(mpath, "r")
|
|
||||||
local text = mf.read(65535)
|
|
||||||
mf.close()
|
|
||||||
|
|
||||||
local parsed = parseMetafile(text)
|
|
||||||
parsed[target] = meta
|
|
||||||
|
|
||||||
local f = disk:open(mpath, "w")
|
|
||||||
f.write(makeMetafile(parsed))
|
|
||||||
f.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.fchown(fd, uid, gid)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
vfs.chown(file.path, uid, gid)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.exists(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "r")
|
|
||||||
return disk:fileExists(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.type(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "r")
|
|
||||||
return disk:type(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.getcwd() return kernel.currentTask.cwd end
|
|
||||||
|
|
||||||
function vfs.chdir(path) kernel.currentTask.cwd = path end
|
|
||||||
|
|
||||||
function vfs.dup(oldfd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[oldfd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
checkSystemLimit()
|
|
||||||
|
|
||||||
local newfd = allocFD(task)
|
|
||||||
file.refcount = file.refcount + 1
|
|
||||||
task.fd[newfd] = file
|
|
||||||
total = total + 1
|
|
||||||
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.dup2(oldfd, newfd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[oldfd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if newfd < 0 or newfd >= kernel.config.maxFilesPerTask then
|
|
||||||
error("EBADF")
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldfd == newfd then
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
if task.fd[newfd] then
|
|
||||||
vfs.close(newfd)
|
|
||||||
end
|
|
||||||
|
|
||||||
checkSystemLimit()
|
|
||||||
|
|
||||||
file.refcount = file.refcount + 1
|
|
||||||
task.fd[newfd] = file
|
|
||||||
total = total + 1
|
|
||||||
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.devctl(fd, method, ...)
|
|
||||||
if not kernel.currentTask.fd[fd] then error("EBADF") end
|
|
||||||
if not kernel.currentTask.fd[fd].handle[method] then error("EINVAL") end
|
|
||||||
return kernel.currentTask.fd[fd].handle[method](...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Export syscalls
|
|
||||||
local sys = kernel.syscalls
|
|
||||||
sys["open"] = vfs.open
|
|
||||||
sys["close"] = vfs.close
|
|
||||||
sys["read"] = vfs.read
|
|
||||||
sys["write"] = vfs.write
|
|
||||||
sys["pread"] = vfs.pread
|
|
||||||
sys["pwrite"] = vfs.pwrite
|
|
||||||
sys["lseek"] = vfs.lseek
|
|
||||||
sys["fsync"] = vfs.fsync
|
|
||||||
sys["sendfile"] = vfs.sendfile
|
|
||||||
sys["stat"] = vfs.stat
|
|
||||||
sys["fstat"] = vfs.fstat
|
|
||||||
sys["mkdir"] = vfs.mkdir
|
|
||||||
sys["remove"] = vfs.remove
|
|
||||||
sys["listdir"] = vfs.listdir
|
|
||||||
sys["chmod"] = vfs.chmod
|
|
||||||
sys["fchmod"] = vfs.fchmod
|
|
||||||
sys["chown"] = vfs.chown
|
|
||||||
sys["fchown"] = vfs.fchown
|
|
||||||
sys["exists"] = vfs.exists
|
|
||||||
sys["type"] = vfs.type
|
|
||||||
sys["mount"] = vfs.mount
|
|
||||||
sys["umount"] = vfs.umount
|
|
||||||
sys["getcwd"] = vfs.getcwd
|
|
||||||
sys["chdir"] = vfs.chdir
|
|
||||||
sys["dup"] = vfs.dup
|
|
||||||
sys["dup2"] = vfs.dup2
|
|
||||||
sys["devctl"] = vfs.devctl
|
|
||||||
|
|
||||||
kernel.log("VFS module loaded")
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local cache = {}
|
|
||||||
kernel.searchpaths = {
|
|
||||||
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
|
|
||||||
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
|
|
||||||
}
|
|
||||||
|
|
||||||
function require(module, ...)
|
|
||||||
if cache[module] then return cache[module] end
|
|
||||||
local modpath = module:gsub("%.", "/")
|
|
||||||
local failed = {}
|
|
||||||
for _, path in ipairs(kernel.searchpaths) do
|
|
||||||
local full_path = string.replace(path, "?", modpath)
|
|
||||||
if full_path:sub(1, 1) ~= "/" then
|
|
||||||
full_path = kernel.currentTask.cwd .. full_path
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.vfs.exists(full_path) then
|
|
||||||
if kernel.vfs.type(full_path) == "directory" then
|
|
||||||
full_path = full_path .. "/init"
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.vfs.exists(full_path) then
|
|
||||||
local handle = kernel.vfs.open(full_path, "r")
|
|
||||||
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
return
|
|
||||||
assert(load(file_content, full_path, "t", kernel._U))(...)
|
|
||||||
else
|
|
||||||
table.insert(failed, full_path)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(failed, full_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
|
|
||||||
end
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local proxy = {}
|
|
||||||
local data = {}
|
|
||||||
|
|
||||||
proxy.address = "devfs0000"
|
|
||||||
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 "devfs" end
|
|
||||||
proxy.attributes = function(path) return {
|
|
||||||
size = 0,
|
|
||||||
modified = 0,
|
|
||||||
created = 0,
|
|
||||||
} end
|
|
||||||
|
|
||||||
function proxy:open(path, mode)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1, #steps-1 do
|
|
||||||
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
|
|
||||||
error("ENFILE")
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:type(path, mode)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
if #steps == 0 then
|
|
||||||
return "directory"
|
|
||||||
end
|
|
||||||
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
|
|
||||||
error("ENOENT")
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:list(path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
if #steps == 0 then
|
|
||||||
return table.keys(data)
|
|
||||||
end
|
|
||||||
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
|
|
||||||
error("ENOENT")
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:fileExists(path)
|
|
||||||
local ok = pcall(function()
|
|
||||||
return self:type(path)
|
|
||||||
end)
|
|
||||||
return ok
|
|
||||||
end
|
|
||||||
|
|
||||||
function data.random(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
if mode=="r" then
|
|
||||||
return {
|
|
||||||
read=function(amount)
|
|
||||||
local str = ""
|
|
||||||
for i=1, amount or 1 do
|
|
||||||
str=str..string.char(math.random(0, 255))
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
}
|
|
||||||
elseif mode=="w" or mode=="a" then
|
|
||||||
return {
|
|
||||||
write=function() end
|
|
||||||
}
|
|
||||||
else error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function data.null(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
if mode=="r" then
|
|
||||||
return {
|
|
||||||
read=function(amount) end
|
|
||||||
}
|
|
||||||
elseif mode=="w" or mode=="a" then
|
|
||||||
return {
|
|
||||||
write=function() end
|
|
||||||
}
|
|
||||||
else error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function data.zero(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
if mode=="r" then
|
|
||||||
return {
|
|
||||||
read=function(amount)
|
|
||||||
local str = ""
|
|
||||||
for i=1, amount or 1 do
|
|
||||||
str=str..string.char(0)
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
}
|
|
||||||
elseif mode=="w" or mode=="a" then
|
|
||||||
return {
|
|
||||||
write=function() end
|
|
||||||
}
|
|
||||||
else error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
data["disk"]={}
|
|
||||||
kernel.devfs={}
|
|
||||||
kernel.devfs.data=data
|
|
||||||
kernel.devfs.proxy=proxy
|
|
||||||
kernel.disks["devfs0000"]=proxy
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
local kernel = ...
|
|
||||||
|
|
||||||
local proxy = {}
|
|
||||||
local data = {}
|
|
||||||
|
|
||||||
proxy.address = "tmpfs0000"
|
|
||||||
proxy.isvirt = true
|
|
||||||
proxy.isReadOnly = function() return false end
|
|
||||||
|
|
||||||
-- Space functions (just placeholders)
|
|
||||||
proxy.spaceUsed = function() return 0 end
|
|
||||||
proxy.spaceTotal = function() return 0 end
|
|
||||||
|
|
||||||
-- Writable operations
|
|
||||||
proxy.makeDirectory = function(_, path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1,#steps do
|
|
||||||
if not step[steps[i]] then
|
|
||||||
step[steps[i]] = {}
|
|
||||||
elseif type(step[steps[i]]) ~= "table" then
|
|
||||||
error("ENOTDIR")
|
|
||||||
end
|
|
||||||
step = step[steps[i]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
proxy.remove = function(_, path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1,#steps-1 do
|
|
||||||
step = step[steps[i]]
|
|
||||||
if not step then error("ENOENT") end
|
|
||||||
end
|
|
||||||
step[steps[#steps]] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
proxy.setLabel = function(_, label) end
|
|
||||||
proxy.getLabel = function() return "tmpfs" end
|
|
||||||
|
|
||||||
proxy.attributes = function(_, path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1,#steps do
|
|
||||||
step = step[steps[i]]
|
|
||||||
if not step then error("ENOENT") end
|
|
||||||
end
|
|
||||||
return {
|
|
||||||
size = type(step) == "string" and #step or 0,
|
|
||||||
modified = 0,
|
|
||||||
created = 0,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open files
|
|
||||||
function proxy:open(path, mode)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1,#steps-1 do
|
|
||||||
if not step[steps[i]] then
|
|
||||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
|
||||||
elseif type(step[steps[i]]) ~= "table" then
|
|
||||||
error("ENOTDIR")
|
|
||||||
end
|
|
||||||
step = step[steps[i]]
|
|
||||||
end
|
|
||||||
local filename = steps[#steps]
|
|
||||||
|
|
||||||
if mode == "r" then
|
|
||||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
|
||||||
local content = step[filename]
|
|
||||||
local pos = 1
|
|
||||||
return {
|
|
||||||
read = function(amount)
|
|
||||||
amount = amount or #content
|
|
||||||
local chunk = content:sub(pos, pos+amount-1)
|
|
||||||
pos = pos + #chunk
|
|
||||||
return chunk
|
|
||||||
end
|
|
||||||
}
|
|
||||||
elseif mode == "w" then
|
|
||||||
step[filename] = ""
|
|
||||||
return {
|
|
||||||
write = function(str)
|
|
||||||
step[filename] = str
|
|
||||||
end
|
|
||||||
}
|
|
||||||
elseif mode == "a" then
|
|
||||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
|
||||||
return {
|
|
||||||
write = function(str)
|
|
||||||
step[filename] = step[filename] .. str
|
|
||||||
end
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:type(path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
if #steps == 0 then return "directory" end
|
|
||||||
for i=1,#steps do
|
|
||||||
step = step[steps[i]]
|
|
||||||
if not step then return false end
|
|
||||||
end
|
|
||||||
if type(step) == "table" then return "directory" end
|
|
||||||
if type(step) == "string" then return "file" end
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:list(path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1,#steps do
|
|
||||||
step = step[steps[i]]
|
|
||||||
if not step then error("ENOENT") end
|
|
||||||
end
|
|
||||||
if type(step) ~= "table" then error("ENOTDIR") end
|
|
||||||
local keys = {}
|
|
||||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
|
||||||
return keys
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:fileExists(path)
|
|
||||||
return pcall(function() return self:type(path) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.disks["tmpfs0000"] = proxy
|
|
||||||
@@ -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,34 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local function trim(str)
|
|
||||||
local s, e = 1, #str
|
|
||||||
while s <= e and (str:sub(s,s) == " " or str:sub(s,s) == "\t") do s = s + 1 end
|
|
||||||
while e >= s and (str:sub(e,e) == " " or str:sub(e,e) == "\t" or str:sub(e,e) == "\n" or str:sub(e,e) == "\r") do e = e - 1 end
|
|
||||||
if s > e then return "" end
|
|
||||||
return str:sub(s,e)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
|
||||||
line = trim(line)
|
|
||||||
if line ~= "" and line:sub(1,1) == "U" then
|
|
||||||
local semicolon_pos
|
|
||||||
for i = 3, #line do
|
|
||||||
if line:sub(i,i) == ";" then
|
|
||||||
semicolon_pos = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not semicolon_pos or semicolon_pos == 3 then
|
|
||||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8)
|
|
||||||
else
|
|
||||||
local id = line:sub(3, semicolon_pos - 1)
|
|
||||||
local path = trim(line:sub(semicolon_pos + 1))
|
|
||||||
kernel.log("Mounted "..id.." to "..path)
|
|
||||||
if id ~= "$" then
|
|
||||||
kernel.vfs.mount(path, id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local socket = {}
|
|
||||||
|
|
||||||
function socket.socket()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function socket.bind()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.socket=socket
|
|
||||||
kernel.log("Loaded socket module")
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
kernel.vfs.open("/dev/null", "r")
|
|
||||||
kernel.vfs.open("/dev/tty/TTY1", "w")
|
|
||||||
kernel.vfs.open("/dev/null", "w")
|
|
||||||
kernel.status="term"
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local args = {...}
|
|
||||||
local kernel = args[1]
|
|
||||||
kernel._G = _G
|
|
||||||
|
|
||||||
local function readonly(tbl)
|
|
||||||
return setmetatable({}, {
|
|
||||||
__index = function(_, key)
|
|
||||||
local value = tbl[key]
|
|
||||||
if type(value) == "table" then return readonly(value) end
|
|
||||||
return value
|
|
||||||
end,
|
|
||||||
|
|
||||||
__newindex = function(t, k, v)
|
|
||||||
if kernel.config.allowGlobalOverwrites or
|
|
||||||
kernel.allowGlobalOverwrites then
|
|
||||||
rawset(tbl, k, v)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
error("Attempt to modify global variable '" .. k .. "'", 2)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__pairs = function()
|
|
||||||
local function iter(_, key)
|
|
||||||
local nextKey, value = next(tbl, key)
|
|
||||||
if type(value) == "table" then
|
|
||||||
value = readonly(value)
|
|
||||||
end
|
|
||||||
return nextKey, value
|
|
||||||
end
|
|
||||||
return iter, tbl, nil
|
|
||||||
end,
|
|
||||||
|
|
||||||
__ipairs = function()
|
|
||||||
local i = 0
|
|
||||||
return function()
|
|
||||||
i = i + 1
|
|
||||||
local value = tbl[i]
|
|
||||||
if value == nil then return end
|
|
||||||
if type(value) == "table" then
|
|
||||||
value = readonly(value)
|
|
||||||
end
|
|
||||||
return i, value
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
__len = function() return #tbl end,
|
|
||||||
|
|
||||||
__metatable = false
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel._U = readonly(kernel._G)
|
|
||||||
kernel.allowGlobalOverwrites = true
|
|
||||||
kernel._U._G = kernel._U
|
|
||||||
kernel.allowGlobalOverwrites = false
|
|
||||||
@@ -1,447 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local tasks = {}
|
|
||||||
local sys = {}
|
|
||||||
local nextpid = 2
|
|
||||||
kernel.exitMain = false
|
|
||||||
|
|
||||||
function sys.spawn(func, name, envars, args, tgid)
|
|
||||||
local id = nextpid
|
|
||||||
nextpid = nextpid + 1
|
|
||||||
|
|
||||||
tasks[tostring(id)] = {
|
|
||||||
coro = coroutine.create(function()
|
|
||||||
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
|
|
||||||
if not ok then
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
kernel.log(
|
|
||||||
"Task " .. tostring(id) .. " exited with err: " ..
|
|
||||||
tostring(err), "ERROR", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(err) == "number" then
|
|
||||||
tasks[tostring(id)].exit = err
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
if err then
|
|
||||||
kernel.log("Task " .. tostring(id) ..
|
|
||||||
" exited with code: " .. tostring(err),
|
|
||||||
"INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Task " .. tostring(id) ..
|
|
||||||
" exited without code", "INFO")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(err) == "number" then
|
|
||||||
tasks[tostring(id)].exit = err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for v, _ in ipairs(tasks[tostring(id)].fd) do pcall(kernel.vfs.close,v) end
|
|
||||||
tasks[tostring(id)].status = "Z"
|
|
||||||
|
|
||||||
end),
|
|
||||||
name = name or ("task" .. tostring(id)),
|
|
||||||
envars = envars or kernel.currentTask.envars,
|
|
||||||
args = args or {},
|
|
||||||
status = "R",
|
|
||||||
pid = id,
|
|
||||||
tgid = tgid or kernel.currentTask.tgid,
|
|
||||||
uid = kernel.uid,
|
|
||||||
fd = {},
|
|
||||||
sleep = 0,
|
|
||||||
ivs = 0,
|
|
||||||
vs = 0,
|
|
||||||
children = {},
|
|
||||||
parent = kernel.currentTask,
|
|
||||||
siblings = kernel.currentTask.children,
|
|
||||||
syscallReturn = {},
|
|
||||||
cwd = kernel.currentTask.cwd,
|
|
||||||
timeSlice = 0,
|
|
||||||
lastTime = 0,
|
|
||||||
totalTime = 0,
|
|
||||||
numRuns = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(kernel.currentTask.children, tasks[tostring(id)])
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.sleep(s)
|
|
||||||
kernel.currentTask.status = "S"
|
|
||||||
kernel.currentTask.sleep = kernel.computer:time() + s * 1000
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getTask(pid)
|
|
||||||
if tasks[tostring(pid)] then
|
|
||||||
local task = tasks[tostring(pid)]
|
|
||||||
local children = {}
|
|
||||||
local siblings = {}
|
|
||||||
|
|
||||||
for i, v in ipairs(task.children) do children[i] = v.pid end
|
|
||||||
for i, v in ipairs(task.siblings) do siblings[i] = v.pid end
|
|
||||||
|
|
||||||
return {
|
|
||||||
name = task.name,
|
|
||||||
status = task.status,
|
|
||||||
pid = task.pid,
|
|
||||||
tgid = task.tgid,
|
|
||||||
username = kernel.users[task.uid],
|
|
||||||
uid = task.uid,
|
|
||||||
exit = task.exit,
|
|
||||||
sleep = task.sleep,
|
|
||||||
ivs = task.ivs,
|
|
||||||
vs = task.vs,
|
|
||||||
children = children,
|
|
||||||
siblings = siblings,
|
|
||||||
parent = task.parent.pid,
|
|
||||||
cwd = task.cwd,
|
|
||||||
term = task.term
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.collect(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status ~= "Z" then
|
|
||||||
return false, "Task must exit to collect status"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].reapTime = 0
|
|
||||||
return true, tasks[tostring(pid)].exit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.kill(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status == "Z" then
|
|
||||||
return false, "Task is already dead"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status = "Z"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.stop(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status ~= "R" then
|
|
||||||
return false, "Cannot stop non running task"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status = "T"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.continue(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status ~= "T" then
|
|
||||||
return false, "Task is not stopped"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status = "R"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getpid() return kernel.currentTask.pid end
|
|
||||||
|
|
||||||
function sys.getppid() return kernel.currentTask.parent.pid end
|
|
||||||
|
|
||||||
function sys.getTasks()
|
|
||||||
local ret = {}
|
|
||||||
for i, v in pairs(tasks) do ret[#ret + 1] = v.pid end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getEnviron(key) return kernel.currentTask.envars[key] end
|
|
||||||
|
|
||||||
function sys.setEnviron(key, value) kernel.currentTask.envars[key] = value end
|
|
||||||
|
|
||||||
function sys.exit(code)
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
if code then
|
|
||||||
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited with code: " .. tostring(code), "INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited without code", "INFO")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tasks[tostring(kernel.currentTask.pid)].status = "Z"
|
|
||||||
if type(code) == "number" then
|
|
||||||
tasks[tostring(kernel.currentTask.pid)].exit = code
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.setuid(uid)
|
|
||||||
if kernel.uid ~= 0 then error("EACCES") end
|
|
||||||
kernel.currentTask.uid = uid
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getuid() return kernel.currentTask.uid end
|
|
||||||
|
|
||||||
local sysc = kernel.syscalls
|
|
||||||
sysc["spawn"] = sys.spawn
|
|
||||||
sysc["sleep"] = sys.sleep
|
|
||||||
sysc["getTask"] = sys.getTask
|
|
||||||
sysc["collect"] = sys.collect
|
|
||||||
sysc["kill"] = sys.kill
|
|
||||||
sysc["stop"] = sys.stop
|
|
||||||
sysc["continue"] = sys.continue
|
|
||||||
sysc["getpid"] = sys.getpid
|
|
||||||
sysc["getppid"] = sys.getppid
|
|
||||||
sysc["getTasks"] = sys.getTasks
|
|
||||||
sysc["setEnviron"] = sys.setEnviron
|
|
||||||
sysc["getEnviron"] = sys.getEnviron
|
|
||||||
sysc["exit"] = sys.exit
|
|
||||||
sysc["setuid"] = sys.setuid
|
|
||||||
sysc["getuid"] = sys.getuid
|
|
||||||
kernel._G.sleep = function(...) coroutine.yield("syscall", "sleep", ...) end
|
|
||||||
|
|
||||||
local function reapDeadTasks()
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.status == "Z" and not task.reapTime then
|
|
||||||
kernel.currentTask = task
|
|
||||||
kernel.uid = task.uid
|
|
||||||
kernel.process = task.name
|
|
||||||
task.coro = nil
|
|
||||||
task.ivs = nil
|
|
||||||
task.vs = nil
|
|
||||||
task.args = nil
|
|
||||||
task.envars = nil
|
|
||||||
task.cwd = nil
|
|
||||||
task.numRuns = nil
|
|
||||||
task.totalTime = nil
|
|
||||||
task.lastTime = nil
|
|
||||||
task.timeSlice = nil
|
|
||||||
task.syscallReturn = nil
|
|
||||||
task.sleep = nil
|
|
||||||
task.fd = nil
|
|
||||||
task.reapTime = kernel.computer:time() + 30000
|
|
||||||
|
|
||||||
elseif task.reapTime and kernel.computer:time() > task.reapTime and
|
|
||||||
task.status == "Z" then
|
|
||||||
for _, child in ipairs(task.children) do
|
|
||||||
child.parent = tasks["1"]
|
|
||||||
child.siblings = tasks["1"].children
|
|
||||||
table.insert(tasks["1"].children, child)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i, sibling in ipairs(task.siblings) do
|
|
||||||
if sibling.pid == task.pid then
|
|
||||||
table.remove(task.siblings, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tasks[pid] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local alpha = 0.85
|
|
||||||
local C_target = 0.01
|
|
||||||
local Tmin = 0.0005
|
|
||||||
local Tmax = 0.5
|
|
||||||
local lambda_budget = 0.08
|
|
||||||
local lambda_clamp = 0.03
|
|
||||||
local lambda_var = 0.02
|
|
||||||
local k_min = 0.5
|
|
||||||
local k_max = 0.5
|
|
||||||
local B = 0.01
|
|
||||||
|
|
||||||
function kernel.main()
|
|
||||||
while not kernel.exitMain do
|
|
||||||
local N = 0
|
|
||||||
local Tmin_hit = 0
|
|
||||||
local Tmax_hit = 0
|
|
||||||
local totalTaskTime = 0
|
|
||||||
local taskTimes = {}
|
|
||||||
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.status == "S" then
|
|
||||||
if kernel.computer:time() >= task.sleep then
|
|
||||||
task.status = "R"
|
|
||||||
task.sleep = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if task.status == "R" then
|
|
||||||
kernel.currentTask = task
|
|
||||||
kernel.uid = task.uid
|
|
||||||
kernel.process = task.name
|
|
||||||
N = N + 1
|
|
||||||
|
|
||||||
-- assign adaptive time slice
|
|
||||||
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
|
|
||||||
|
|
||||||
if task.sigq and #task.sigq~=0 and task.sigh then
|
|
||||||
local coro = coroutine.create(task.sigh)
|
|
||||||
if kernel.config.preempt then
|
|
||||||
coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
|
|
||||||
else
|
|
||||||
coroutine.resume(coro, table.remove(task.sigq, 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check for exit/stop
|
|
||||||
if task.status=="R" then
|
|
||||||
-- measure execution time
|
|
||||||
local startTime = kernel.computer:time()
|
|
||||||
local ret
|
|
||||||
if kernel.config.preempt then
|
|
||||||
ret = {
|
|
||||||
coroutine.resumeWithTimeout(
|
|
||||||
task.coro,
|
|
||||||
task.timeSlice,
|
|
||||||
table.unpack(task.syscallReturn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ret = {
|
|
||||||
coroutine.resume(
|
|
||||||
task.coro,
|
|
||||||
table.unpack(task.syscallReturn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local elapsed = kernel.computer:time() - startTime
|
|
||||||
task.lastTime = elapsed
|
|
||||||
task.totalTime = (task.totalTime or 0) + elapsed
|
|
||||||
task.numRuns = (task.numRuns or 0) + 1
|
|
||||||
|
|
||||||
taskTimes[#taskTimes + 1] = elapsed
|
|
||||||
totalTaskTime = totalTaskTime + elapsed
|
|
||||||
|
|
||||||
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
|
|
||||||
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
|
|
||||||
|
|
||||||
-- handle task results
|
|
||||||
if ret[1] == "error" or ret[1] == false then
|
|
||||||
kernel.log("processHandlerException: " .. ret[2], "ERROR", 2)
|
|
||||||
task.status = "Z"
|
|
||||||
task.exit = "processHandlerException: " .. ret[2]
|
|
||||||
|
|
||||||
elseif ret[1] == "timeout" then
|
|
||||||
task.ivs = task.ivs + 1
|
|
||||||
task.syscallReturn = {}
|
|
||||||
|
|
||||||
elseif ret[1] == "success" or ret[1] == true then
|
|
||||||
task.vs = task.vs + 1
|
|
||||||
|
|
||||||
if ret[2] == "syscall" then
|
|
||||||
if kernel.syscalls[ret[3]] then
|
|
||||||
if kernel.config.debugSyscalls then
|
|
||||||
kernel.log("Task " .. task.pid .. " invoking syscall: " .. ret[3], "DBUG", 5)
|
|
||||||
|
|
||||||
for i = 4, #ret do
|
|
||||||
kernel.log(" inval[" .. tostring(i - 3) .. "] = " .. tostring(ret[i]), "DBUG", 5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local sysret = {
|
|
||||||
xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))
|
|
||||||
}
|
|
||||||
|
|
||||||
if kernel.config.debugSyscalls then
|
|
||||||
if not sysret[1] then
|
|
||||||
kernel.log(
|
|
||||||
"Task " .. task.pid .. " syscall " .. ret[3] .. " failed: " .. tostring(sysret[2]), "ERROR", 2
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
|
||||||
kernel.log(
|
|
||||||
"Task " .. task.pid .. " syscall " .. ret[3] .. " completed returning " .. tostring(#sysret - 1) .. " values", "DBUG", 5
|
|
||||||
)
|
|
||||||
|
|
||||||
for i = 2, #sysret do
|
|
||||||
if type(sysret[i]) == "table" then
|
|
||||||
kernel.log(
|
|
||||||
" retval[" .. tostring(i - 1) .. "] = " .. table.serialize(sysret[i]),"DBUG", 5
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
|
||||||
kernel.log(
|
|
||||||
" retval[" .. tostring(i - 1) .. "] = " .. tostring(sysret[i]), "DBUG", 5
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not sysret[1] then
|
|
||||||
task.syscallReturn = {false, sysret[2]}
|
|
||||||
|
|
||||||
else
|
|
||||||
task.syscallReturn = {
|
|
||||||
true, table.unpack(sysret, 2)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
task.syscallReturn = {
|
|
||||||
false, "Unknown syscall: " .. tostring(ret[3])
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
|
|
||||||
local T_prev_var = 0
|
|
||||||
|
|
||||||
for _, t in ipairs(taskTimes) do
|
|
||||||
T_prev_var = T_prev_var + (t - T_prev_avg) ^ 2
|
|
||||||
end
|
|
||||||
if N > 0 then T_prev_var = T_prev_var / N end
|
|
||||||
|
|
||||||
if N > 0 then
|
|
||||||
local f_clamp = k_min * (Tmin_hit / N) - k_max * (Tmax_hit / N)
|
|
||||||
local B_budget = (C_target * (N ^ (alpha - 1))) /
|
|
||||||
math.max(T_prev_avg, 1e-8)
|
|
||||||
B = B + lambda_budget * (B_budget - B) + lambda_clamp * f_clamp -
|
|
||||||
lambda_var * T_prev_var
|
|
||||||
end
|
|
||||||
|
|
||||||
-- clean up dead tasks
|
|
||||||
reapDeadTasks()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.tasks = tasks
|
|
||||||
kernel.hpv = sys
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
function print(...)
|
|
||||||
local args = {...}
|
|
||||||
local output = ""
|
|
||||||
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
|
||||||
output = output:sub(1, -2)
|
|
||||||
syscall.write(1, output.."\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
function printf(fmt, ...)
|
|
||||||
coroutine.yield()
|
|
||||||
local output = string.format(fmt, ...)
|
|
||||||
syscall.write(1, output.."\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
function printInline(...)
|
|
||||||
coroutine.yield()
|
|
||||||
local args = {...}
|
|
||||||
local output = ""
|
|
||||||
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
|
||||||
output = output:sub(1, -2)
|
|
||||||
syscall.write(1, output)
|
|
||||||
end
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
kernel.log("Loading init system...")
|
|
||||||
kernel.log("InitPath: " .. kernel.config.initPath)
|
|
||||||
|
|
||||||
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
|
||||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
|
|
||||||
if not initFunc then error("Failed to load init system: " .. err) end
|
|
||||||
|
|
||||||
kernel.tasks["1"] = {
|
|
||||||
coro = coroutine.create(function()
|
|
||||||
local ok, err = xpcall(initFunc, debug.traceback, kernel)
|
|
||||||
if not ok then
|
|
||||||
kernel.panic("Init system crashed: " .. tostring(err))
|
|
||||||
else
|
|
||||||
kernel.panic("Init system exited: " .. tostring(err))
|
|
||||||
end
|
|
||||||
end),
|
|
||||||
|
|
||||||
name = "sysinit",
|
|
||||||
status = "R",
|
|
||||||
pid = 1,
|
|
||||||
tgid = 1,
|
|
||||||
uid = 0,
|
|
||||||
fd = {},
|
|
||||||
envars = {},
|
|
||||||
args = {},
|
|
||||||
exit = "",
|
|
||||||
sleep = 0,
|
|
||||||
ivs = 0,
|
|
||||||
vs = 0,
|
|
||||||
parent = kernel.kernelTask,
|
|
||||||
siblings = kernel.kernelTask.children,
|
|
||||||
children = {},
|
|
||||||
syscallReturn = {},
|
|
||||||
cwd = "/",
|
|
||||||
timeSlice = 0,
|
|
||||||
lastTime = 0,
|
|
||||||
totalTime = 0,
|
|
||||||
numRuns = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel.log("created init task with PID 1")
|
|
||||||
kernel.log("Initializing init system...")
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
-- It runs at uid 0 so it can call setuid() to drop privileges to the logged in user
|
|
||||||
kernel.processes.login = function()
|
|
||||||
local handle = kernel.vfs.open("/bin/login", "r")
|
|
||||||
local text = kernel.vfs.read(handle, 1024 * 1024)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
local fn, err = load(text, "@/bin/login", "t", kernel._U)
|
|
||||||
if not fn then
|
|
||||||
kernel.log("Failed to load /bin/login: " .. tostring(err), "ERROR", 2)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
fn()
|
|
||||||
end
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local bit32 = require("bit32")
|
|
||||||
local bor = bit32.bor
|
|
||||||
local lshift = bit32.lshift
|
|
||||||
|
|
||||||
-- bit 0 = everyone-write, bit 1 = everyone-read
|
|
||||||
-- bit 2 = group-write, bit 3 = group-read
|
|
||||||
-- bit 4 = owner-write, bit 5 = owner-read
|
|
||||||
-- bit 6 = suid
|
|
||||||
local P_OWNER_R = lshift(1, 5)
|
|
||||||
local P_OWNER_W = lshift(1, 4)
|
|
||||||
local P_GROUP_R = lshift(1, 3)
|
|
||||||
local P_GROUP_W = lshift(1, 2)
|
|
||||||
local P_WORLD_R = lshift(1, 1)
|
|
||||||
local P_WORLD_W = lshift(1, 0)
|
|
||||||
local P_SUID = lshift(1, 6)
|
|
||||||
|
|
||||||
local RW_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 644 / rw-r--r--
|
|
||||||
local RWX_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 755 / rwxr--r--
|
|
||||||
local RW_R__ = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R) -- 640 / rw-r-----
|
|
||||||
local RW____ = bor(P_OWNER_R, P_OWNER_W) -- 600 / rw-------
|
|
||||||
local SUID_755 = bor(P_SUID, P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 4755
|
|
||||||
|
|
||||||
local function metaEntry(name, owner, group, perms)
|
|
||||||
return string.char(#name) .. name
|
|
||||||
.. string.char(owner, group, perms)
|
|
||||||
.. string.char(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
local rootDisk = kernel.disks["$"]
|
|
||||||
|
|
||||||
local function writeMeta(dir, entries)
|
|
||||||
local diskDir = dir == "/" and "/" or dir
|
|
||||||
local path = (diskDir:sub(-1) == "/" and diskDir or diskDir .. "/") .. ".meta"
|
|
||||||
if path:sub(1,1) == "/" then path = path:sub(2) end
|
|
||||||
if path == "" then path = ".meta" end
|
|
||||||
|
|
||||||
local data = ""
|
|
||||||
for _, e in ipairs(entries) do
|
|
||||||
data = data .. metaEntry(e[1], e[2], e[3], e[4])
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, err = pcall(function()
|
|
||||||
local f = rootDisk:open(path, "w")
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
end)
|
|
||||||
if not ok then
|
|
||||||
kernel.log("permissions: failed to write /" .. path .. ": " .. tostring(err), "WARN", 8)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if rootDisk:fileExists(".meta") then
|
|
||||||
kernel.log("Permissions already seeded, skipping.", "INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Seeding filesystem permissions...", "INFO")
|
|
||||||
|
|
||||||
writeMeta("/", {
|
|
||||||
{"bin", 0, 0, RWX_R_R},
|
|
||||||
{"boot", 0, 0, RWX_R_R},
|
|
||||||
{"dev", 0, 0, RWX_R_R},
|
|
||||||
{"etc", 0, 0, RWX_R_R},
|
|
||||||
{"home", 0, 0, RWX_R_R},
|
|
||||||
{"lib", 0, 0, RWX_R_R},
|
|
||||||
{"root", 0, 0, RW____ },
|
|
||||||
{"sbin", 0, 0, RWX_R_R},
|
|
||||||
{"tmp", 0, 0, bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_GROUP_W, P_WORLD_R, P_WORLD_W)},
|
|
||||||
{"usr", 0, 0, RWX_R_R},
|
|
||||||
{"var", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/bin", {
|
|
||||||
{"cat", 0, 0, RWX_R_R},
|
|
||||||
{"clear", 0, 0, RWX_R_R},
|
|
||||||
{"echo", 0, 0, RWX_R_R},
|
|
||||||
{"hfetch", 0, 0, RWX_R_R},
|
|
||||||
{"hysh", 0, 0, RWX_R_R},
|
|
||||||
{"hyshex", 0, 0, RWX_R_R},
|
|
||||||
{"install", 0, 0, RWX_R_R},
|
|
||||||
{"login", 0, 0, SUID_755},
|
|
||||||
{"ls", 0, 0, RWX_R_R},
|
|
||||||
{"lua", 0, 0, RWX_R_R},
|
|
||||||
{"luaold", 0, 0, RWX_R_R},
|
|
||||||
{"mkdir", 0, 0, RWX_R_R},
|
|
||||||
{"ps", 0, 0, RWX_R_R},
|
|
||||||
{"pwd", 0, 0, RWX_R_R},
|
|
||||||
{"spm", 0, 0, RWX_R_R},
|
|
||||||
{"su", 0, 0, SUID_755},
|
|
||||||
{"sudo", 0, 0, SUID_755},
|
|
||||||
{"sysdump", 0, 0, RWX_R_R},
|
|
||||||
{"whoami", 0, 0, RWX_R_R},
|
|
||||||
{"yes", 0, 0, RWX_R_R},
|
|
||||||
{"startup", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/bin/startup", {
|
|
||||||
{"test.lua", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/etc", {
|
|
||||||
{"passwd", 0, 0, RW_R_R},
|
|
||||||
{"shadow", 0, 0, RW____ },
|
|
||||||
{"pam.d", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/etc/pam.d", {
|
|
||||||
{"secret", 0, 0, RW____},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/sbin", {
|
|
||||||
{"init.lua", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/boot", {
|
|
||||||
{"kernel.lua", 0, 0, RW_R_R},
|
|
||||||
{"boot.cfg", 0, 0, RW_R_R},
|
|
||||||
{"safeboot.cfg", 0, 0, RW_R_R},
|
|
||||||
{"fstab", 0, 0, RW_R_R},
|
|
||||||
{"initfs", 0, 0, RW_R_R},
|
|
||||||
{"cct", 0, 0, RWX_R_R},
|
|
||||||
{"oc", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/lib", {
|
|
||||||
{"sys", 0, 0, RWX_R_R},
|
|
||||||
{"modules", 0, 0, RWX_R_R},
|
|
||||||
{"crypto", 0, 0, RWX_R_R},
|
|
||||||
{"store", 0, 0, RWX_R_R},
|
|
||||||
{"snip", 0, 0, RW_R_R},
|
|
||||||
{"io", 0, 0, RW_R_R},
|
|
||||||
{"bit32", 0, 0, RW_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
kernel.log("Filesystem permissions seeded.", "INFO")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: move this to vfs.kmod
|
|
||||||
local _orig_open = kernel.vfs.open
|
|
||||||
kernel.vfs.open = function(path, mode)
|
|
||||||
local fd = _orig_open(path, mode)
|
|
||||||
if mode == "r" then
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local fobj = task.fd[fd]
|
|
||||||
if fobj and fobj.meta then
|
|
||||||
local suid_set = bit32.extract(fobj.meta.perms, 6) == 1
|
|
||||||
if suid_set then
|
|
||||||
fobj.suid_owner = fobj.meta.owner
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls["fget_suid"] = function(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local fobj = task and task.fd[fd]
|
|
||||||
if fobj and fobj.suid_owner then
|
|
||||||
return fobj.suid_owner
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("Permission module loaded.", "INFO")
|
|
||||||
@@ -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
|
|
||||||
Binary file not shown.
344
Src/Hyperion-firmware-ac/boot/ac/boot.lua
Normal file
344
Src/Hyperion-firmware-ac/boot/ac/boot.lua
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local args={...}
|
||||||
|
local bootdrive=args[1]
|
||||||
|
local gpu=components:getFirst("gpu")
|
||||||
|
local screenTextBuffer=nil
|
||||||
|
local cursorX,cursorY=0,0
|
||||||
|
local screenSizeX,screenSizeY=128,25
|
||||||
|
|
||||||
|
|
||||||
|
if gpu then
|
||||||
|
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||||
|
for t,v in components:list() do
|
||||||
|
if t == "screen" then
|
||||||
|
gpu:assignBuffer(screenTextBuffer, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write(text)
|
||||||
|
cursorX, cursorY = screenTextBuffer:pasteText(cursorX, cursorY, "SCROLL_SPILL_CLEAR", text)
|
||||||
|
cursorY = cursorY + 1
|
||||||
|
cursorX = 0
|
||||||
|
if cursorY >= screenSizeY then
|
||||||
|
cursorY = screenSizeY-1
|
||||||
|
screenTextBuffer:newline()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function displaySuperBadError(err)
|
||||||
|
gpu:freeAllBuffers()
|
||||||
|
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||||
|
for t,v in components:list() do
|
||||||
|
if t == "screen" then
|
||||||
|
gpu:assignBuffer(screenTextBuffer, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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
|
||||||
|
|
||||||
|
local ok, err = xpcall(function()
|
||||||
|
local apis = {}
|
||||||
|
|
||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = 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,
|
||||||
|
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 function string_file(file)
|
||||||
|
local str = file:read()
|
||||||
|
local buf = {str or ""}
|
||||||
|
local pos = 1
|
||||||
|
local closed = false
|
||||||
|
|
||||||
|
local function content()
|
||||||
|
return table.concat(buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_content(s)
|
||||||
|
buf = {s}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function flush()
|
||||||
|
file.write(content())
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = {}
|
||||||
|
|
||||||
|
function file:read(n)
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
|
||||||
|
local s = content()
|
||||||
|
local len = #s
|
||||||
|
|
||||||
|
if not n then
|
||||||
|
local out = s:sub(pos)
|
||||||
|
pos = len + 1
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
local out = s:sub(pos, pos + n - 1)
|
||||||
|
pos = pos + #out
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function file:write(data)
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
|
||||||
|
local s = content()
|
||||||
|
local before = s:sub(1, pos - 1)
|
||||||
|
local after = s:sub(pos + #data)
|
||||||
|
|
||||||
|
set_content(before .. data .. after)
|
||||||
|
pos = pos + #data
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function file:seek(whence, offset)
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
|
||||||
|
local s = content()
|
||||||
|
local len = #s
|
||||||
|
|
||||||
|
whence = whence or "cur"
|
||||||
|
offset = offset or 0
|
||||||
|
|
||||||
|
if whence == "set" then
|
||||||
|
pos = offset + 1
|
||||||
|
elseif whence == "cur" then
|
||||||
|
pos = pos + offset
|
||||||
|
elseif whence == "end" then
|
||||||
|
pos = len + offset + 1
|
||||||
|
else
|
||||||
|
error("invalid whence")
|
||||||
|
end
|
||||||
|
|
||||||
|
if pos < 1 then pos = 1 end
|
||||||
|
if pos > len + 1 then pos = len + 1 end
|
||||||
|
|
||||||
|
return pos - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function file:close()
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
flush()
|
||||||
|
closed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function file:flush()
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
return file
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getFile(path)
|
||||||
|
local file = bootdrive:open(path, "r")
|
||||||
|
if not file then
|
||||||
|
displaySuperBadError("Could not open file: " .. path)
|
||||||
|
end
|
||||||
|
local content = file:read()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
local Kernel = load(getFile("/boot/kernel.lua"),"@Kernel")
|
||||||
|
local initFs = load(getFile("/boot/ac/initdisks"),"@Init_disks")(apis)
|
||||||
|
local fs = load(getFile("/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 computercomp=apis.components:getFirst("computer")
|
||||||
|
local uefi=apis.components:getFirst("uefi")
|
||||||
|
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()
|
||||||
|
return computercomp:getMachineEvent()
|
||||||
|
end,
|
||||||
|
getEEPROM = function() return uefi.data end,
|
||||||
|
setEEPROM = function(_, text)
|
||||||
|
uefi.data=text
|
||||||
|
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
|
||||||
@@ -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,
|
||||||
@@ -154,17 +152,106 @@ local ok, err = xpcall(function()
|
|||||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||||
|
|
||||||
|
if not apis.fs.exists("/nvram.dat") then
|
||||||
|
local file = apis.fs.open("/nvram.dat", "w")
|
||||||
|
file.write("Hello, World!")
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local eeprom
|
||||||
|
if apis.fs.exists("/startup.lua") then
|
||||||
|
eeprom="/startup.lua"
|
||||||
|
elseif apis.fs.exists("/eeprom") then
|
||||||
|
eeprom="/eeprom"
|
||||||
|
end
|
||||||
|
|
||||||
local eventQueue = {}
|
local eventQueue = {}
|
||||||
|
|
||||||
local function queueEvent(event, ...)
|
local function queueEvent(event, ...)
|
||||||
table.insert(eventQueue, {event, ...})
|
table.insert(eventQueue, {event, ...})
|
||||||
end
|
end
|
||||||
|
|
||||||
local computer = {
|
local colors = {
|
||||||
time = function() return apis.os.epoch("utc") end,
|
[0x000000]=0x0001,
|
||||||
clock = function() return apis.os.clock() * 1000 end,
|
[0xFFFFFF]=0x0002,
|
||||||
shutdown = apis.os.shutdown,
|
[0xFF0000]=0x0004,
|
||||||
reboot = apis.os.reboot,
|
[0x00FF00]=0x0008,
|
||||||
|
[0x0000FF]=0x0010,
|
||||||
|
[0x00FFFF]=0x0020,
|
||||||
|
[0xFF00FF]=0x0040,
|
||||||
|
[0xFFFF00]=0x0080,
|
||||||
|
[0xFF6D00]=0x0100,
|
||||||
|
[0x6DFF55]=0x0200,
|
||||||
|
[0x24FFFF]=0x0400,
|
||||||
|
[0x924900]=0x0800,
|
||||||
|
[0x6D6D55]=0x1000,
|
||||||
|
[0xDBDBAA]=0x2000,
|
||||||
|
[0x6D00FF]=0x4000,
|
||||||
|
[0xB6FF00]=0x8000
|
||||||
|
}
|
||||||
|
|
||||||
|
local fg,bg=0x6D6D55,0x000000
|
||||||
|
local l1f,l1d,l2,ops={},{},{},0
|
||||||
|
|
||||||
|
local function findClosest(tbl, target)
|
||||||
|
local closest = nil
|
||||||
|
local smallestDiff = math.huge
|
||||||
|
|
||||||
|
for k, _ in pairs(tbl) do
|
||||||
|
local diff = math.abs(k - target)
|
||||||
|
if diff < smallestDiff then
|
||||||
|
smallestDiff = diff
|
||||||
|
closest = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return closest
|
||||||
|
end
|
||||||
|
|
||||||
|
local function aprox(c24)
|
||||||
|
ops = ops + 1
|
||||||
|
|
||||||
|
if ops % 1024 == 0 then
|
||||||
|
l1d = {}
|
||||||
|
l1f = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if ops % 8192 == 0 then
|
||||||
|
l2 = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if l2[c24] ~= nil then
|
||||||
|
return l2[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
if l1d[c24] ~= nil then
|
||||||
|
l1f[c24] = l1f[c24] + 1
|
||||||
|
|
||||||
|
if l1f[c24] >= 16 then
|
||||||
|
l2[c24] = l1d[c24]
|
||||||
|
l1d[c24] = nil
|
||||||
|
l1f[c24] = nil
|
||||||
|
return l2[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
return l1d[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
local closestKey = findClosest(colors, c24)
|
||||||
|
if not closestKey then return nil end
|
||||||
|
|
||||||
|
local value = colors[closestKey]
|
||||||
|
|
||||||
|
l1d[c24] = value
|
||||||
|
l1f[c24] = 1
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
local EFI = {
|
||||||
|
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||||
|
getUptime = function() return apis.os.clock() * 1000 end,
|
||||||
|
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||||
getMachineEvent = function()
|
getMachineEvent = function()
|
||||||
if #eventQueue > 0 then
|
if #eventQueue > 0 then
|
||||||
return table.unpack(table.remove(eventQueue, 1))
|
return table.unpack(table.remove(eventQueue, 1))
|
||||||
@@ -172,61 +259,15 @@ local ok, err = xpcall(function()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
getEEPROM = function() return getFile("/startup.lua") end,
|
getEEPROM = function() return getFile(eeprom) end,
|
||||||
setEEPROM = function(_, text)
|
setEEPROM = function(_, text)
|
||||||
local h = apis.fs.open("/startup.lua", "w")
|
local h = apis.fs.open(eeprom, "w")
|
||||||
h.write(text)
|
h.write(text)
|
||||||
h.close()
|
h.close()
|
||||||
end
|
end,
|
||||||
}
|
initfs=fs,
|
||||||
|
disks=initFs,
|
||||||
local icolors = {
|
screenCtl={
|
||||||
[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,
|
print = function(_, text) write(text .. "\n") end,
|
||||||
printInline = function(_, text) write(text) end,
|
printInline = function(_, text) write(text) end,
|
||||||
clear = function()
|
clear = function()
|
||||||
@@ -239,26 +280,51 @@ local ok, err = xpcall(function()
|
|||||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||||
getSize = function() return apis.term.getSize() end,
|
getSize = function() return apis.term.getSize() end,
|
||||||
setBackgroundColor = function(_, color)
|
setBackgroundColor = function(_, color)
|
||||||
apis.term.setBackgroundColor(colors[color])
|
apis.term.setBackgroundColor(aprox(color))
|
||||||
end,
|
end,
|
||||||
setTextColor = function(_, color)
|
setTextColor = function(_, color)
|
||||||
apis.term.setTextColor(colors[color])
|
apis.term.setTextColor(aprox(color))
|
||||||
end,
|
end,
|
||||||
getBackgroundColor = function()
|
getBackgroundColor = function()
|
||||||
return icolors[apis.term.getBackgroundColor()]
|
return bg
|
||||||
end,
|
end,
|
||||||
getTextColor = function()
|
getTextColor = function()
|
||||||
return icolors[apis.term.getTextColor()]
|
return fg
|
||||||
|
end,
|
||||||
|
enable=function() end,
|
||||||
|
disable=function() end
|
||||||
|
},
|
||||||
|
architecture="cct",
|
||||||
|
getNvram = function() return getFile("/nvram.dat") end,
|
||||||
|
setNvram = function(_, text)
|
||||||
|
local h = apis.fs.open("/nvram.dat", "w")
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
end,
|
||||||
|
firmware=apis,
|
||||||
|
reboot=false
|
||||||
|
}
|
||||||
|
|
||||||
|
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, EFI)
|
||||||
|
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||||
|
if err then
|
||||||
|
apis.os.reboot()
|
||||||
|
else
|
||||||
|
apis.os.shutdown()
|
||||||
end
|
end
|
||||||
}, computer, fs, "$")
|
|
||||||
if not ok then displaySuperBadError(err) end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- time is in milliseconds
|
|
||||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
local startTime = computer.time()
|
local startTime = EFI.getEpochMs()
|
||||||
debug.sethook(co, function()
|
debug.sethook(co, function()
|
||||||
if computer.time() - startTime > timeout then
|
if EFI.getEpochMs() > startTime + timeout then
|
||||||
return coroutine.yield("timeout")
|
return coroutine.yield("timeout")
|
||||||
end
|
end
|
||||||
end, "", 1000)
|
end, "", 1000)
|
||||||
@@ -294,16 +360,28 @@ local ok, err = xpcall(function()
|
|||||||
queueEvent("componentAdded", "disk")
|
queueEvent("componentAdded", "disk")
|
||||||
elseif event[1] == "disk_eject" then
|
elseif event[1] == "disk_eject" then
|
||||||
queueEvent("componentRemoved", "disk")
|
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
|
elseif event[1] == "NoSleep" then
|
||||||
exit = true
|
exit = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||||
|
if EFI.reboot then
|
||||||
|
apis.os.reboot()
|
||||||
|
end
|
||||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||||
coroutine.yield("key")
|
coroutine.yield("key")
|
||||||
end
|
end
|
||||||
|
initFs:refresh()
|
||||||
end
|
end
|
||||||
end, debug.traceback)
|
end, debug.traceback)
|
||||||
|
|
||||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||||
while true do coroutine.yield() end
|
while true do coroutine.yield("key") end
|
||||||
272
Src/Hyperion-firmware-ccpc/boot/ccpc/initdisks
Normal file
272
Src/Hyperion-firmware-ccpc/boot/ccpc/initdisks
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local apis = ({...})[1]
|
||||||
|
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||||
|
local fs = apis.fs
|
||||||
|
local native = apis.peripheral
|
||||||
|
local peripheral = {}
|
||||||
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
|
function peripheral.getNames()
|
||||||
|
local results = {}
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.isPresent(side) then
|
||||||
|
table.insert(results, side)
|
||||||
|
if native.hasType(side, "peripheral_hub") then
|
||||||
|
local remote = native.call(side, "getNamesRemote")
|
||||||
|
for _, name in ipairs(remote) do
|
||||||
|
table.insert(results, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.isPresent(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getType(peripheral)
|
||||||
|
if type(peripheral) == "string" then
|
||||||
|
if native.isPresent(peripheral) then
|
||||||
|
return native.getType(peripheral)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||||
|
return native.call(side, "getTypeRemote", peripheral)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return table.unpack(mt.types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.hasType(peripheral, peripheral_type)
|
||||||
|
if type(peripheral) == "string" then
|
||||||
|
if native.isPresent(peripheral) then
|
||||||
|
return native.hasType(peripheral, peripheral_type)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||||
|
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return mt.types[peripheral_type] ~= nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getMethods(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.getMethods(name)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "getMethodsRemote", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getName(peripheral)
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return mt.name
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.call(name, method, ...)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.call(name, method, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "callRemote", name, method, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.wrap(name)
|
||||||
|
local methods = peripheral.getMethods(name)
|
||||||
|
if not methods then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local types = { peripheral.getType(name) }
|
||||||
|
for i = 1, #types do types[types[i]] = true end
|
||||||
|
local result = setmetatable({}, {
|
||||||
|
__name = "peripheral",
|
||||||
|
name = name,
|
||||||
|
type = types[1],
|
||||||
|
types = types,
|
||||||
|
})
|
||||||
|
for _, method in ipairs(methods) do
|
||||||
|
result[method] = function(...)
|
||||||
|
return peripheral.call(name, method, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.find(ty, filter)
|
||||||
|
local results = {}
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.hasType(name, ty) then
|
||||||
|
local wrapped = peripheral.wrap(name)
|
||||||
|
if filter == nil or filter(name, wrapped) then
|
||||||
|
table.insert(results, wrapped)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.unpack(results)
|
||||||
|
end
|
||||||
|
|
||||||
|
local 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
|
||||||
|
})
|
||||||
|
|
||||||
|
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||||
|
setLabel = function(label)
|
||||||
|
error("Device is read-only")
|
||||||
|
end,
|
||||||
|
getLabel = function()
|
||||||
|
return "cctrom"
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
local function refresh()
|
||||||
|
disks={}
|
||||||
|
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||||
|
if disk.isDiskPresent() then
|
||||||
|
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||||
|
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}
|
||||||
@@ -37,7 +37,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 +58,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
|
||||||
@@ -311,13 +311,24 @@ local fifo = kernel.newFifo()
|
|||||||
kernel.processes.cctmond = function()
|
kernel.processes.cctmond = function()
|
||||||
local timeout = false
|
local timeout = false
|
||||||
while true do
|
while true do
|
||||||
local event = {kernel.computer:getMachineEvent()}
|
local event = {kernel.EFI:getMachineEvent()}
|
||||||
|
|
||||||
if event[1] then
|
if event[1] then
|
||||||
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 +336,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 +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,
|
||||||
@@ -154,17 +152,262 @@ local ok, err = xpcall(function()
|
|||||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||||
|
|
||||||
|
if not apis.fs.exists("/nvram.dat") then
|
||||||
|
local file = apis.fs.open("/nvram.dat", "w")
|
||||||
|
file.write("Hello, World!")
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local eeprom
|
||||||
|
if apis.fs.exists("/startup.lua") then
|
||||||
|
eeprom="/startup.lua"
|
||||||
|
elseif apis.fs.exists("/eeprom") then
|
||||||
|
eeprom="/eeprom"
|
||||||
|
end
|
||||||
|
|
||||||
local eventQueue = {}
|
local eventQueue = {}
|
||||||
|
|
||||||
local function queueEvent(event, ...)
|
local function queueEvent(event, ...)
|
||||||
table.insert(eventQueue, {event, ...})
|
table.insert(eventQueue, {event, ...})
|
||||||
end
|
end
|
||||||
|
|
||||||
local computer = {
|
local colors = {
|
||||||
time = function() return apis.os.epoch("utc") end,
|
[0x000000]=0x0001,
|
||||||
clock = function() return apis.os.clock() * 1000 end,
|
[0xFFFFFF]=0x0002,
|
||||||
shutdown = apis.os.shutdown,
|
[0xFF0000]=0x0004,
|
||||||
reboot = apis.os.reboot,
|
[0x00FF00]=0x0008,
|
||||||
|
[0x0000FF]=0x0010,
|
||||||
|
[0x00FFFF]=0x0020,
|
||||||
|
[0xFF00FF]=0x0040,
|
||||||
|
[0xFFFF00]=0x0080,
|
||||||
|
[0xFF6D00]=0x0100,
|
||||||
|
[0x6DFF55]=0x0200,
|
||||||
|
[0x24FFFF]=0x0400,
|
||||||
|
[0x924900]=0x0800,
|
||||||
|
[0x6D6D55]=0x1000,
|
||||||
|
[0xDBDBAA]=0x2000,
|
||||||
|
[0x6D00FF]=0x4000,
|
||||||
|
[0xB6FF00]=0x8000
|
||||||
|
}
|
||||||
|
|
||||||
|
local fg,bg=0x6D6D55,0x000000
|
||||||
|
local l1f,l1d,l2,ops={},{},{},0
|
||||||
|
|
||||||
|
local function findClosest(tbl, target)
|
||||||
|
local closest = nil
|
||||||
|
local smallestDiff = math.huge
|
||||||
|
|
||||||
|
for k, _ in pairs(tbl) do
|
||||||
|
if k==target then return k end
|
||||||
|
local diff = math.abs(k - target)
|
||||||
|
if diff < smallestDiff then
|
||||||
|
smallestDiff = diff
|
||||||
|
closest = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return closest
|
||||||
|
end
|
||||||
|
|
||||||
|
local function aprox(c24)
|
||||||
|
ops = ops + 1
|
||||||
|
|
||||||
|
if ops % 1024 == 0 then
|
||||||
|
l1d = {}
|
||||||
|
l1f = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if ops % 8192 == 0 then
|
||||||
|
l2 = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if l2[c24] ~= nil then
|
||||||
|
return l2[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
if l1d[c24] ~= nil then
|
||||||
|
l1f[c24] = l1f[c24] + 1
|
||||||
|
|
||||||
|
if l1f[c24] >= 16 then
|
||||||
|
l2[c24] = l1d[c24]
|
||||||
|
l1d[c24] = nil
|
||||||
|
l1f[c24] = nil
|
||||||
|
return l2[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
return l1d[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
local closestKey = findClosest(colors, c24)
|
||||||
|
if not closestKey then return nil end
|
||||||
|
|
||||||
|
local value = colors[closestKey]
|
||||||
|
|
||||||
|
l1d[c24] = value
|
||||||
|
l1f[c24] = 1
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
local peripheral={}
|
||||||
|
local native = apis.peripheral
|
||||||
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
|
function peripheral.getNames()
|
||||||
|
local results = {}
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.isPresent(side) then
|
||||||
|
table.insert(results, side)
|
||||||
|
if native.hasType(side, "peripheral_hub") then
|
||||||
|
local remote = native.call(side, "getNamesRemote")
|
||||||
|
for _, name in ipairs(remote) do
|
||||||
|
table.insert(results, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.isPresent(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getType(peripheral)
|
||||||
|
if type(peripheral) == "string" then
|
||||||
|
if native.isPresent(peripheral) then
|
||||||
|
return native.getType(peripheral)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||||
|
return native.call(side, "getTypeRemote", peripheral)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return table.unpack(mt.types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.hasType(peripheral, peripheral_type)
|
||||||
|
if type(peripheral) == "string" then
|
||||||
|
if native.isPresent(peripheral) then
|
||||||
|
return native.hasType(peripheral, peripheral_type)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||||
|
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return mt.types[peripheral_type] ~= nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getMethods(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.getMethods(name)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "getMethodsRemote", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getName(peripheral)
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return mt.name
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.call(name, method, ...)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.call(name, method, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "callRemote", name, method, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.wrap(name)
|
||||||
|
local methods = peripheral.getMethods(name)
|
||||||
|
if not methods then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local types = { peripheral.getType(name) }
|
||||||
|
for i = 1, #types do types[types[i]] = true end
|
||||||
|
local result = setmetatable({}, {
|
||||||
|
__name = "peripheral",
|
||||||
|
name = name,
|
||||||
|
type = types[1],
|
||||||
|
types = types,
|
||||||
|
})
|
||||||
|
for _, method in ipairs(methods) do
|
||||||
|
result[method] = function(...)
|
||||||
|
return peripheral.call(name, method, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.find(ty, filter)
|
||||||
|
local results = {}
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.hasType(name, ty) then
|
||||||
|
local wrapped = peripheral.wrap(name)
|
||||||
|
if filter == nil or filter(name, wrapped) then
|
||||||
|
table.insert(results, wrapped)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.unpack(results)
|
||||||
|
end
|
||||||
|
|
||||||
|
local allscreens = {peripheral.find("monitor")}
|
||||||
|
for i=1, #allscreens do
|
||||||
|
allscreens[i].setTextScale(.5)
|
||||||
|
allscreens[i].clear()
|
||||||
|
allscreens[i].setCursorPos(1,1)
|
||||||
|
allscreens[i].write("Initializing...")
|
||||||
|
end
|
||||||
|
|
||||||
|
local EFI = {
|
||||||
|
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||||
|
getUptime = function() return apis.os.clock() * 1000 end,
|
||||||
|
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||||
getMachineEvent = function()
|
getMachineEvent = function()
|
||||||
if #eventQueue > 0 then
|
if #eventQueue > 0 then
|
||||||
return table.unpack(table.remove(eventQueue, 1))
|
return table.unpack(table.remove(eventQueue, 1))
|
||||||
@@ -172,61 +415,15 @@ local ok, err = xpcall(function()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
getEEPROM = function() return getFile("/startup.lua") end,
|
getEEPROM = function() return getFile(eeprom) end,
|
||||||
setEEPROM = function(_, text)
|
setEEPROM = function(_, text)
|
||||||
local h = apis.fs.open("/startup.lua", "w")
|
local h = apis.fs.open(eeprom, "w")
|
||||||
h.write(text)
|
h.write(text)
|
||||||
h.close()
|
h.close()
|
||||||
end
|
end,
|
||||||
}
|
initfs=fs,
|
||||||
|
disks=initFs,
|
||||||
local icolors = {
|
screenCtl={
|
||||||
[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,
|
print = function(_, text) write(text .. "\n") end,
|
||||||
printInline = function(_, text) write(text) end,
|
printInline = function(_, text) write(text) end,
|
||||||
clear = function()
|
clear = function()
|
||||||
@@ -239,26 +436,52 @@ local ok, err = xpcall(function()
|
|||||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||||
getSize = function() return apis.term.getSize() end,
|
getSize = function() return apis.term.getSize() end,
|
||||||
setBackgroundColor = function(_, color)
|
setBackgroundColor = function(_, color)
|
||||||
apis.term.setBackgroundColor(colors[color])
|
apis.term.setBackgroundColor(aprox(color))
|
||||||
end,
|
end,
|
||||||
setTextColor = function(_, color)
|
setTextColor = function(_, color)
|
||||||
apis.term.setTextColor(colors[color])
|
apis.term.setTextColor(aprox(color))
|
||||||
end,
|
end,
|
||||||
getBackgroundColor = function()
|
getBackgroundColor = function()
|
||||||
return icolors[apis.term.getBackgroundColor()]
|
return bg
|
||||||
end,
|
end,
|
||||||
getTextColor = function()
|
getTextColor = function()
|
||||||
return icolors[apis.term.getTextColor()]
|
return fg
|
||||||
|
end,
|
||||||
|
enable=function() end,
|
||||||
|
disable=function() end
|
||||||
|
},
|
||||||
|
architecture="cct",
|
||||||
|
getNvram = function() return getFile("/nvram.dat") end,
|
||||||
|
setNvram = function(_, text)
|
||||||
|
local h = apis.fs.open("/nvram.dat", "w")
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
end,
|
||||||
|
firmware=apis,
|
||||||
|
reboot=false,
|
||||||
|
beep=function() end
|
||||||
|
}
|
||||||
|
|
||||||
|
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, EFI)
|
||||||
|
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||||
|
if err then
|
||||||
|
apis.os.reboot()
|
||||||
|
else
|
||||||
|
apis.os.shutdown()
|
||||||
end
|
end
|
||||||
}, computer, fs, "$")
|
|
||||||
if not ok then displaySuperBadError(err) end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- time is in milliseconds
|
|
||||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
local startTime = computer.time()
|
local startTime = EFI.getEpochMs()
|
||||||
debug.sethook(co, function()
|
debug.sethook(co, function()
|
||||||
if computer.time() > startTime + timeout then
|
if EFI.getEpochMs() > startTime + timeout then
|
||||||
return coroutine.yield("timeout")
|
return coroutine.yield("timeout")
|
||||||
end
|
end
|
||||||
end, "", 1000)
|
end, "", 1000)
|
||||||
@@ -273,7 +496,7 @@ local ok, err = xpcall(function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
EFI.screenCtl:print("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||||
@@ -294,16 +517,28 @@ local ok, err = xpcall(function()
|
|||||||
queueEvent("componentAdded", "disk")
|
queueEvent("componentAdded", "disk")
|
||||||
elseif event[1] == "disk_eject" then
|
elseif event[1] == "disk_eject" then
|
||||||
queueEvent("componentRemoved", "disk")
|
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
|
elseif event[1] == "NoSleep" then
|
||||||
exit = true
|
exit = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||||
|
if EFI.reboot then
|
||||||
|
apis.os.reboot()
|
||||||
|
end
|
||||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||||
coroutine.yield("key")
|
coroutine.yield("key")
|
||||||
end
|
end
|
||||||
|
initFs:refresh()
|
||||||
end
|
end
|
||||||
end, debug.traceback)
|
end, debug.traceback)
|
||||||
|
|
||||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||||
while true do coroutine.yield() end
|
while true do coroutine.yield("key") end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
sleep(1)
|
|
||||||
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
||||||
-- UnBIOS by JackMacWindows
|
-- 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
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
|
|||||||
@@ -1,36 +1,152 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local apis = ({...})[1]
|
local apis = ({...})[1]
|
||||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||||
local fs = apis.fs
|
local fs = apis.fs
|
||||||
local native = apis.peripheral
|
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 = {}
|
||||||
@@ -125,19 +241,20 @@ internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
|||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
local function refresh()
|
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||||
for id, _ in pairs(disks) do
|
setLabel = function(label)
|
||||||
if not peripheral.getType(id) then disks[id] = nil end
|
error("Device is read-only")
|
||||||
|
end,
|
||||||
|
getLabel = function()
|
||||||
|
return "cctrom"
|
||||||
end
|
end
|
||||||
|
})
|
||||||
|
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
local function refresh()
|
||||||
if peripheral.getType(name) == "disk" then
|
disks={}
|
||||||
if not disks[name] then
|
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||||
local mount = disk.getMountPath(name)
|
if disk.isDiskPresent() then
|
||||||
if mount then
|
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||||
disks[name] = createDisk(name, mount, false, disk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,363 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local apis = kernel.apis
|
|
||||||
local native = apis.peripheral
|
|
||||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
|
||||||
local peripheral={}
|
|
||||||
|
|
||||||
function peripheral.getNames()
|
|
||||||
local results = {}
|
|
||||||
for n = 1, #sides do
|
|
||||||
local side = sides[n]
|
|
||||||
if native.isPresent(side) then
|
|
||||||
table.insert(results, side)
|
|
||||||
if native.hasType(side, "peripheral_hub") then
|
|
||||||
local remote = native.call(side, "getNamesRemote")
|
|
||||||
for _, name in ipairs(remote) do
|
|
||||||
table.insert(results, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return results
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.isPresent(name)
|
|
||||||
if native.isPresent(name) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
for n = 1, #sides do
|
|
||||||
local side = sides[n]
|
|
||||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.getType(peripheral)
|
|
||||||
if type(peripheral) == "string" then -- Peripheral name passed
|
|
||||||
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 -- Peripheral name passed
|
|
||||||
if native.isPresent(peripheral) then
|
|
||||||
return native.hasType(peripheral, peripheral_type)
|
|
||||||
end
|
|
||||||
for n = 1, #sides do
|
|
||||||
local side = sides[n]
|
|
||||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
|
||||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
else
|
|
||||||
local mt = getmetatable(peripheral)
|
|
||||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
|
||||||
error("bad argument #1 (table is not a peripheral)", 2)
|
|
||||||
end
|
|
||||||
return mt.types[peripheral_type] ~= nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.getMethods(name)
|
|
||||||
if native.isPresent(name) then
|
|
||||||
return native.getMethods(name)
|
|
||||||
end
|
|
||||||
for n = 1, #sides do
|
|
||||||
local side = sides[n]
|
|
||||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
|
||||||
return native.call(side, "getMethodsRemote", name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.getName(peripheral)
|
|
||||||
local mt = getmetatable(peripheral)
|
|
||||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
|
||||||
error("bad argument #1 (table is not a peripheral)", 2)
|
|
||||||
end
|
|
||||||
return mt.name
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.call(name, method, ...)
|
|
||||||
if native.isPresent(name) then
|
|
||||||
return native.call(name, method, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
for n = 1, #sides do
|
|
||||||
local side = sides[n]
|
|
||||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
|
||||||
return native.call(side, "callRemote", name, method, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.wrap(name)
|
|
||||||
local methods = peripheral.getMethods(name)
|
|
||||||
if not methods then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local types = { peripheral.getType(name) }
|
|
||||||
for i = 1, #types do types[types[i]] = true end
|
|
||||||
local result = setmetatable({}, {
|
|
||||||
__name = "peripheral",
|
|
||||||
name = name,
|
|
||||||
type = types[1],
|
|
||||||
types = types,
|
|
||||||
})
|
|
||||||
for _, method in ipairs(methods) do
|
|
||||||
result[method] = function(...)
|
|
||||||
return peripheral.call(name, method, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function peripheral.find(ty, filter)
|
|
||||||
local results = {}
|
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
|
||||||
if peripheral.hasType(name, ty) then
|
|
||||||
local wrapped = peripheral.wrap(name)
|
|
||||||
if filter == nil or filter(name, wrapped) then
|
|
||||||
table.insert(results, wrapped)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.unpack(results)
|
|
||||||
end
|
|
||||||
|
|
||||||
local icolors = {
|
|
||||||
[0x1] = 1, -- #000000
|
|
||||||
[0x2] = 2, -- #FFFFFF
|
|
||||||
[0x4] = 3, -- #FF0000
|
|
||||||
[0x8] = 4, -- #00FF00
|
|
||||||
[0x10] = 5, -- #0000FF
|
|
||||||
[0x20] = 6, -- #00FFFF
|
|
||||||
[0x40] = 7, -- #FF00FF
|
|
||||||
[0x80] = 8, -- #FFFF00
|
|
||||||
[0x100] = 9, -- #FF6D00
|
|
||||||
[0x200] = 10, -- #6DFF55
|
|
||||||
[0x400] = 11, -- #24FFFF
|
|
||||||
[0x800] = 12, -- #924900
|
|
||||||
[0x1000] = 13, -- #6D6D55
|
|
||||||
[0x2000] = 14, -- #DBDBAA
|
|
||||||
[0x4000] = 15, -- #6D00FF
|
|
||||||
[0x8000] = 16 -- #B6FF00
|
|
||||||
}
|
|
||||||
|
|
||||||
local colors = {
|
|
||||||
0x0001, -- #000000
|
|
||||||
0x0002, -- #FFFFFF
|
|
||||||
0x0004, -- #FF0000
|
|
||||||
0x0008, -- #00FF00
|
|
||||||
0x0010, -- #0000FF
|
|
||||||
0x0020, -- #00FFFF
|
|
||||||
0x0040, -- #FF00FF
|
|
||||||
0x0080, -- #FFFF00
|
|
||||||
0x0100, -- #FF6D00
|
|
||||||
0x0200, -- #6DFF55
|
|
||||||
0x0400, -- #24FFFF
|
|
||||||
0x0800, -- #924900
|
|
||||||
0x1000, -- #6D6D55
|
|
||||||
0x2000, -- #DBDBAA
|
|
||||||
0x4000, -- #6D00FF
|
|
||||||
0x8000 -- #B6FF00
|
|
||||||
}
|
|
||||||
|
|
||||||
local function write(text, term)
|
|
||||||
local x, y = term.getCursorPos()
|
|
||||||
local w, h = term.getSize()
|
|
||||||
|
|
||||||
for i = 1, #text do
|
|
||||||
local c = text:sub(i, i)
|
|
||||||
|
|
||||||
if c == "\n" then
|
|
||||||
y = y + 1
|
|
||||||
x = 1
|
|
||||||
elseif c == "\t" then
|
|
||||||
local tabSize = 4
|
|
||||||
local spaces = tabSize - ((x - 1) % tabSize)
|
|
||||||
term.write(string.rep(" ", spaces))
|
|
||||||
x = x + spaces
|
|
||||||
elseif c == "\b" then
|
|
||||||
if x > 1 then
|
|
||||||
x = x - 1
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
term.write(" ")
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if x <= w and y <= h then
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
term.write(c)
|
|
||||||
x = x + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if x > w then
|
|
||||||
x = 1
|
|
||||||
y = y + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if y - 1 >= h then
|
|
||||||
term.scroll(1)
|
|
||||||
y = h
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.devfs.data.tty={}
|
|
||||||
local ctrl,alt = false, false
|
|
||||||
|
|
||||||
local function serializeBool(bool)
|
|
||||||
if bool then
|
|
||||||
return "T"
|
|
||||||
else
|
|
||||||
return "F"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newtty(obj, id, ev)
|
|
||||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
local h = {
|
|
||||||
read=function(amount)
|
|
||||||
local rv=""
|
|
||||||
for i=1, amount or 1 do
|
|
||||||
local event = {ev()}
|
|
||||||
if event[1] then
|
|
||||||
rv=rv..event[1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if rv=="" then rv=nil end
|
|
||||||
return rv
|
|
||||||
end,
|
|
||||||
write=function(content)
|
|
||||||
write(content, obj)
|
|
||||||
end,
|
|
||||||
size=function()
|
|
||||||
local s={obj.getSize()}
|
|
||||||
return table.concat(s,";")
|
|
||||||
end,
|
|
||||||
clear=function()
|
|
||||||
obj.clear()
|
|
||||||
obj.setCursorPos(1,1)
|
|
||||||
end,
|
|
||||||
gpos=function()
|
|
||||||
local s={obj.getCursorPos()}
|
|
||||||
return table.concat(s,";")
|
|
||||||
end,
|
|
||||||
spos=function(x,y)
|
|
||||||
return obj.setCursorPos(x,y)
|
|
||||||
end,
|
|
||||||
sfgc=function(c)
|
|
||||||
return obj.setTextColor(colors[c])
|
|
||||||
end,
|
|
||||||
sbgc=function(c)
|
|
||||||
return obj.setBackgroundColor(colors[c])
|
|
||||||
end,
|
|
||||||
gfgc=function()
|
|
||||||
return icolors[obj.getTextColor()]
|
|
||||||
end,
|
|
||||||
gbgc=function()
|
|
||||||
return icolors[obj.getBackgroundColor()]
|
|
||||||
end,
|
|
||||||
gctrl=function()
|
|
||||||
return serializeBool(ctrl)..";"..serializeBool(alt)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
if mode=="rw" then
|
|
||||||
return h
|
|
||||||
elseif mode=="r" then
|
|
||||||
h["write"]=nil
|
|
||||||
return h
|
|
||||||
elseif mode=="w" then
|
|
||||||
h["read"]=nil
|
|
||||||
return h
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local fifo = kernel.newFifo()
|
|
||||||
|
|
||||||
kernel.processes.cctmond = function()
|
|
||||||
local timeout = false
|
|
||||||
while true do
|
|
||||||
local event = {kernel.computer:getMachineEvent()}
|
|
||||||
|
|
||||||
if event[1] then
|
|
||||||
local eventType = event[1]
|
|
||||||
local charOrKey = event[3]
|
|
||||||
|
|
||||||
-- Update modifier keys
|
|
||||||
if eventType == "keyPressed" then
|
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
|
||||||
ctrl = true
|
|
||||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
|
||||||
alt = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Handle Ctrl+C
|
|
||||||
if ctrl and charOrKey == apis.keys.c then
|
|
||||||
for _, task in ipairs(syscall.getTasks()) do
|
|
||||||
syscall.sigsend(task, 1) -- SIGINT
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif eventType == "keyReleased" then
|
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
|
||||||
ctrl = false
|
|
||||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
|
||||||
alt = false
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif eventType == "keyTyped" then
|
|
||||||
if charOrKey then fifo.push(charOrKey) end
|
|
||||||
end
|
|
||||||
|
|
||||||
timeout = false
|
|
||||||
else
|
|
||||||
timeout = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if timeout then
|
|
||||||
sleep(0.05)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
newtty(apis.term, "TTY1", fifo.pop)
|
|
||||||
|
|
||||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
|
||||||
v.setTextScale(.5)
|
|
||||||
v.write("Initializing...")
|
|
||||||
newtty(v,"TTY"..tostring(i+1),function () end)
|
|
||||||
end
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
local args={...}
|
|
||||||
local kernel=args[1]
|
|
||||||
local driver={}
|
|
||||||
|
|
||||||
driver.name="CCT Term Module"
|
|
||||||
driver.version="0.1.0"
|
|
||||||
driver.type="gpio"
|
|
||||||
driver.description="CCT redstone Module Kernel Module"
|
|
||||||
driver.arch="cct"
|
|
||||||
driver.author="HyperionOS Dev Team"
|
|
||||||
driver.license="MIT"
|
|
||||||
driver.api={}
|
|
||||||
|
|
||||||
function driver.load()
|
|
||||||
-- will
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.unload()
|
|
||||||
-- Nothing to unload
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.main()
|
|
||||||
-- Nothing to run
|
|
||||||
end
|
|
||||||
|
|
||||||
-- kernel.drivers.register(driver)
|
|
||||||
152
Src/Hyperion-firmware-cct/lib/modules/cc-tweaked/01_periph.kmod
Normal file
152
Src/Hyperion-firmware-cct/lib/modules/cc-tweaked/01_periph.kmod
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
--:Minify:--
|
||||||
|
-- CCT driver peripheral helper
|
||||||
|
local kernel=...
|
||||||
|
kernel.cct={}
|
||||||
|
kernel.cct.peripheral={}
|
||||||
|
local peripheral=kernel.cct.peripheral
|
||||||
|
local apis = kernel.apis
|
||||||
|
local native = apis.peripheral
|
||||||
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
|
function peripheral.getNames()
|
||||||
|
local results = {}
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.isPresent(side) then
|
||||||
|
table.insert(results, side)
|
||||||
|
if native.hasType(side, "peripheral_hub") then
|
||||||
|
local remote = native.call(side, "getNamesRemote")
|
||||||
|
for _, name in ipairs(remote) do
|
||||||
|
table.insert(results, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.isPresent(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getType(peripheral)
|
||||||
|
if type(peripheral) == "string" then
|
||||||
|
if native.isPresent(peripheral) then
|
||||||
|
return native.getType(peripheral)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||||
|
return native.call(side, "getTypeRemote", peripheral)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return table.unpack(mt.types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.hasType(peripheral, peripheral_type)
|
||||||
|
if type(peripheral) == "string" then
|
||||||
|
if native.isPresent(peripheral) then
|
||||||
|
return native.hasType(peripheral, peripheral_type)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||||
|
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return mt.types[peripheral_type] ~= nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getMethods(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.getMethods(name)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "getMethodsRemote", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getName(peripheral)
|
||||||
|
local mt = getmetatable(peripheral)
|
||||||
|
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||||
|
error("bad argument #1 (table is not a peripheral)", 2)
|
||||||
|
end
|
||||||
|
return mt.name
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.call(name, method, ...)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.call(name, method, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "callRemote", name, method, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.wrap(name)
|
||||||
|
local methods = peripheral.getMethods(name)
|
||||||
|
if not methods then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local types = { peripheral.getType(name) }
|
||||||
|
for i = 1, #types do types[types[i]] = true end
|
||||||
|
local result = setmetatable({}, {
|
||||||
|
__name = "peripheral",
|
||||||
|
name = name,
|
||||||
|
type = types[1],
|
||||||
|
types = types,
|
||||||
|
})
|
||||||
|
for _, method in ipairs(methods) do
|
||||||
|
result[method] = function(...)
|
||||||
|
return peripheral.call(name, method, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.find(ty, filter)
|
||||||
|
local results = {}
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.hasType(name, ty) then
|
||||||
|
local wrapped = peripheral.wrap(name)
|
||||||
|
if filter == nil or filter(name, wrapped) then
|
||||||
|
table.insert(results, wrapped)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.unpack(results)
|
||||||
|
end
|
||||||
252
Src/Hyperion-firmware-cct/lib/modules/cc-tweaked/25_tty.kmod
Normal file
252
Src/Hyperion-firmware-cct/lib/modules/cc-tweaked/25_tty.kmod
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
local peripheral=kernel.cct.peripheral
|
||||||
|
|
||||||
|
local colors = {
|
||||||
|
[0xFFFFFF]=0x0001,
|
||||||
|
[0xFF0000]=0x0002,
|
||||||
|
[0x00FF00]=0x0004,
|
||||||
|
[0x0000FF]=0x0008,
|
||||||
|
[0x00FFFF]=0x0010,
|
||||||
|
[0xFF00FF]=0x0020,
|
||||||
|
[0xFFFF00]=0x0040,
|
||||||
|
[0xFF6D00]=0x0080,
|
||||||
|
[0x6DFF55]=0x0100,
|
||||||
|
[0x24FFFF]=0x0200,
|
||||||
|
[0x924900]=0x0400,
|
||||||
|
[0x6D6D55]=0x0800,
|
||||||
|
[0xDBDBAA]=0x1000,
|
||||||
|
[0x6D00FF]=0x2000,
|
||||||
|
[0xB6FF00]=0x4000,
|
||||||
|
[0x000000]=0x8000
|
||||||
|
}
|
||||||
|
|
||||||
|
local plt = {
|
||||||
|
0xFFFFFF,
|
||||||
|
0xFF0000,
|
||||||
|
0x00FF00,
|
||||||
|
0x0000FF,
|
||||||
|
0x00FFFF,
|
||||||
|
0xFF00FF,
|
||||||
|
0xFFFF00,
|
||||||
|
0xFF6D00,
|
||||||
|
0x6DFF55,
|
||||||
|
0x24FFFF,
|
||||||
|
0x924900,
|
||||||
|
0x6D6D55,
|
||||||
|
0xDBDBAA,
|
||||||
|
0x6D00FF,
|
||||||
|
0xB6FF00,
|
||||||
|
0x000000
|
||||||
|
}
|
||||||
|
|
||||||
|
local fg,bg=0x6D6D55,0x000000
|
||||||
|
local l1f,l1d,l2,ops={},{},{},0
|
||||||
|
|
||||||
|
local function findClosest(tbl, target)
|
||||||
|
local closest = nil
|
||||||
|
local smallestDiff = math.huge
|
||||||
|
|
||||||
|
for k, _ in pairs(tbl) do
|
||||||
|
if k==target then return k end
|
||||||
|
local diff = math.abs(k - target)
|
||||||
|
if diff < smallestDiff then
|
||||||
|
smallestDiff = diff
|
||||||
|
closest = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return closest
|
||||||
|
end
|
||||||
|
|
||||||
|
local function aprox(c24)
|
||||||
|
ops = ops + 1
|
||||||
|
|
||||||
|
if ops % 1024 == 0 then
|
||||||
|
l1d = {}
|
||||||
|
l1f = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if ops % 8192 == 0 then
|
||||||
|
l2 = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if l2[c24] ~= nil then
|
||||||
|
return l2[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
if l1d[c24] ~= nil then
|
||||||
|
l1f[c24] = l1f[c24] + 1
|
||||||
|
|
||||||
|
if l1f[c24] >= 16 then
|
||||||
|
l2[c24] = l1d[c24]
|
||||||
|
l1d[c24] = nil
|
||||||
|
l1f[c24] = nil
|
||||||
|
return l2[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
return l1d[c24]
|
||||||
|
end
|
||||||
|
|
||||||
|
local closestKey = findClosest(colors, c24)
|
||||||
|
if not closestKey then return nil end
|
||||||
|
|
||||||
|
local value = colors[closestKey]
|
||||||
|
|
||||||
|
l1d[c24] = value
|
||||||
|
l1f[c24] = 1
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write(text, term)
|
||||||
|
local x, y = term.getCursorPos()
|
||||||
|
local w, h = term.getSize()
|
||||||
|
|
||||||
|
for i = 1, #text do
|
||||||
|
local c = text:sub(i, i)
|
||||||
|
|
||||||
|
if c == "\n" then
|
||||||
|
y = y + 1
|
||||||
|
x = 1
|
||||||
|
elseif c == "\t" then
|
||||||
|
local tabSize = 4
|
||||||
|
local spaces = tabSize - ((x - 1) % tabSize)
|
||||||
|
term.write(string.rep(" ", spaces))
|
||||||
|
x = x + spaces
|
||||||
|
elseif c == "\b" then
|
||||||
|
if x > 1 then
|
||||||
|
x = x - 1
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
term.write(" ")
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if x <= w and y <= h then
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
term.write(c)
|
||||||
|
x = x + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if x > w then
|
||||||
|
x = 1
|
||||||
|
y = y + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if y - 1 >= h then
|
||||||
|
term.scroll(1)
|
||||||
|
y = h
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.devfs.data.tty={}
|
||||||
|
kernel.cct.ctrl,kernel.cct.alt = false, false
|
||||||
|
|
||||||
|
local function serializeBool(bool)
|
||||||
|
if bool then
|
||||||
|
return "T"
|
||||||
|
else
|
||||||
|
return "F"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
if op=="type" then
|
||||||
|
return "Terminal"
|
||||||
|
elseif op=="open" then
|
||||||
|
local h = {
|
||||||
|
read=function(amount)
|
||||||
|
local rv=""
|
||||||
|
for i=1, amount or 1 do
|
||||||
|
local event = {ev()}
|
||||||
|
if event[1] then
|
||||||
|
rv=rv..event[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if rv=="" then rv=nil end
|
||||||
|
return rv
|
||||||
|
end,
|
||||||
|
write=function(content)
|
||||||
|
write(content, obj)
|
||||||
|
end,
|
||||||
|
size=function()
|
||||||
|
local s={obj.getSize()}
|
||||||
|
return table.concat(s,";")
|
||||||
|
end,
|
||||||
|
clear=function()
|
||||||
|
obj.clear()
|
||||||
|
obj.setCursorPos(1,1)
|
||||||
|
end,
|
||||||
|
gpos=function()
|
||||||
|
local s={obj.getCursorPos()}
|
||||||
|
return table.concat(s,";")
|
||||||
|
end,
|
||||||
|
spos=function(x,y)
|
||||||
|
return obj.setCursorPos(x,y)
|
||||||
|
end,
|
||||||
|
sfgc=function(c)
|
||||||
|
fg=c
|
||||||
|
return obj.setTextColor(aprox(c))
|
||||||
|
end,
|
||||||
|
sbgc=function(c)
|
||||||
|
bg=c
|
||||||
|
return obj.setBackgroundColor(aprox(c))
|
||||||
|
end,
|
||||||
|
gfgc=function()
|
||||||
|
return fg
|
||||||
|
end,
|
||||||
|
gbgc=function()
|
||||||
|
return bg
|
||||||
|
end,
|
||||||
|
gctrl=function()
|
||||||
|
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
|
||||||
|
end,
|
||||||
|
gplt=function()
|
||||||
|
return plt
|
||||||
|
end
|
||||||
|
}
|
||||||
|
if mode=="rw" then
|
||||||
|
return h
|
||||||
|
elseif mode=="r" then
|
||||||
|
h["write"]=nil
|
||||||
|
return h
|
||||||
|
elseif mode=="w" then
|
||||||
|
h["read"]=nil
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local fifo = kernel.newFifo()
|
||||||
|
kernel.cct.fifo=fifo
|
||||||
|
|
||||||
|
newtty(kernel.apis.term, "1", fifo.pop)
|
||||||
|
|
||||||
|
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||||
|
v.setTextScale(.5)
|
||||||
|
v.write("Initializing...")
|
||||||
|
newtty(v,tostring(i+1),function () end)
|
||||||
|
end
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel=...
|
||||||
|
local keys=kernel.apis.keys
|
||||||
|
|
||||||
|
kernel.processes.cctdeamon = function()
|
||||||
|
local timeout = false
|
||||||
|
while true do
|
||||||
|
local event = {kernel.EFI:getMachineEvent()}
|
||||||
|
|
||||||
|
if event[1] then
|
||||||
|
local eventType = event[1]
|
||||||
|
local charOrKey = event[3]
|
||||||
|
|
||||||
|
local ctrlKeyMap = {
|
||||||
|
[keys.a]=1, [keys.b]=2, [keys.c]=3,
|
||||||
|
[keys.d]=4, [keys.e]=5, [keys.f]=6,
|
||||||
|
[keys.g]=7, [keys.h]=8, [keys.i]=9,
|
||||||
|
[keys.j]=10, [keys.k]=11, [keys.l]=12,
|
||||||
|
[keys.m]=13, [keys.n]=14, [keys.o]=15,
|
||||||
|
[keys.p]=16, [keys.q]=17, [keys.r]=18,
|
||||||
|
[keys.s]=19, [keys.t]=20, [keys.u]=21,
|
||||||
|
[keys.v]=22, [keys.w]=23, [keys.x]=24,
|
||||||
|
[keys.y]=25, [keys.z]=26,
|
||||||
|
}
|
||||||
|
|
||||||
|
if eventType == "keyPressed" then
|
||||||
|
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
|
||||||
|
kernel.cct.ctrl = true
|
||||||
|
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
|
||||||
|
kernel.cct.alt = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if kernel.cct.ctrl then
|
||||||
|
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||||
|
if ctrlByte then
|
||||||
|
if ctrlByte == 3 then
|
||||||
|
for _, task in ipairs(syscall.getTasks()) do
|
||||||
|
syscall.sigsend(task, 1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
kernel.cct.fifo.push(string.char(ctrlByte))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local specialKeyMap = {
|
||||||
|
[keys.up] = "[A",
|
||||||
|
[keys.down] = "[B",
|
||||||
|
[keys.right] = "[C",
|
||||||
|
[keys.left] = "[D",
|
||||||
|
[keys.home] = "[H",
|
||||||
|
[keys["end"]] = "[F",
|
||||||
|
[keys.pageUp] = "[5~",
|
||||||
|
[keys.pageDown] = "[6~",
|
||||||
|
[keys.delete] = "[3~",
|
||||||
|
}
|
||||||
|
local special = specialKeyMap[charOrKey]
|
||||||
|
if special then kernel.cct.fifo.push(special) end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif eventType == "keyReleased" then
|
||||||
|
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
|
||||||
|
kernel.cct.ctrl = false
|
||||||
|
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
|
||||||
|
kernel.cct.alt = false
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif eventType == "keyTyped" then
|
||||||
|
if charOrKey then kernel.cct.fifo.push(charOrKey) end
|
||||||
|
elseif eventType == "http_success" then
|
||||||
|
kernel.cct.httpqueue[event[2]]=nil
|
||||||
|
kernel.cct.httpresponse[event[2]]=event[3]
|
||||||
|
elseif eventType == "http_failure" then
|
||||||
|
kernel.cct.httpqueue[event[2]]=nil
|
||||||
|
kernel.cct.httperror[event[2]]=event[3]
|
||||||
|
end
|
||||||
|
|
||||||
|
timeout = false
|
||||||
|
else
|
||||||
|
timeout = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if timeout then
|
||||||
|
sleep(0.05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel=...
|
||||||
|
local rs=kernel.apis.rs
|
||||||
|
local sides = {top=1, bottom=2, left=3, right=4, front=5, back=6}
|
||||||
|
local function newGPIO(side)
|
||||||
|
return function(mode, data)
|
||||||
|
if mode=="w" then
|
||||||
|
if type(data)~="boolean" then error("data: expected bool") end
|
||||||
|
rs.setOutput(side, data)
|
||||||
|
elseif mode=="wa" then
|
||||||
|
if type(data)~="number" then error("data: expected bool") end
|
||||||
|
rs.setAnalogOutput(side, data)
|
||||||
|
elseif mode=="r" then
|
||||||
|
return rs.getInput(side)
|
||||||
|
elseif mode=="ra" then
|
||||||
|
return rs.getAnalogInput(side)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for side, alt in pairs(sides) do
|
||||||
|
local func=newGPIO(side)
|
||||||
|
kernel.gpio[side]=func
|
||||||
|
kernel.gpio[alt]=func
|
||||||
|
end
|
||||||
Binary file not shown.
@@ -1,155 +0,0 @@
|
|||||||
-- :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}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
U $;/
|
U $;/
|
||||||
U devfs0000;/dev/
|
U devfs0000;/dev/
|
||||||
U tmpfs0000;/tmp/
|
U tmpfs0000;/tmp/
|
||||||
|
U procfs0000;/proc/
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local args = {...}
|
local EFI=...
|
||||||
local apis = args[1]
|
EFI.beep(440, 500)
|
||||||
local disks = args[2]
|
local screen=EFI.screenCtl
|
||||||
local arch = args[3]
|
local ifs=EFI.initfs
|
||||||
local screen = args[5]
|
local disks=EFI.disks
|
||||||
local computer = args[6]
|
local arch=EFI.architecture
|
||||||
local ifs = args[7]
|
|
||||||
local kernel = {}
|
local kernel = {}
|
||||||
|
|
||||||
kernel.LOG_Text=""
|
kernel.LOG_Text=""
|
||||||
kernel.version="HyperionOS V1.0.0"
|
kernel.version="HyperionOS V1.2.4"
|
||||||
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,28 +25,31 @@ _G.sleep=nil
|
|||||||
local windowsExp = false
|
local windowsExp = false
|
||||||
|
|
||||||
function kernel.log(msg, level, c)
|
function kernel.log(msg, level, c)
|
||||||
c=c or 12
|
c=c or 0x6D6D6D
|
||||||
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..tostring(EFI:date()).." "..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(EFI:date()).." "..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)
|
local file=kernel.vfs.open("/dev/console", "w")
|
||||||
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.devctl(file,"sfgc",c)
|
||||||
|
kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||||
|
kernel.vfs.close(file)
|
||||||
kernel.currentTask=kernel.standbyTask
|
kernel.currentTask=kernel.standbyTask
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function kernel.PANIC(msg)
|
function kernel.PANIC(msg)
|
||||||
if kernel.status~="Panic" then
|
if kernel.status~="Panic" then
|
||||||
kernel.log("PANIC: "..msg, "PANIC")
|
kernel.log("PANIC: "..msg, "PANIC", 0xFF0000)
|
||||||
pcall(kernel["saveLog"])
|
pcall(kernel["saveLog"])
|
||||||
kernel.status="Panic"
|
kernel.status="Panic"
|
||||||
kernel.reason=msg
|
kernel.reason=msg
|
||||||
screen:setTextColor(2)
|
screen:enable()
|
||||||
screen:setBackgroundColor(16)
|
screen:setTextColor(0xFF0000)
|
||||||
|
screen:setBackgroundColor(0x000000)
|
||||||
screen:clear()
|
screen:clear()
|
||||||
screen:setCursorPos(1,1)
|
screen:setCursorPos(1,1)
|
||||||
screen:print(kernel.LOG_Text)
|
screen:print(kernel.LOG_Text)
|
||||||
@@ -57,18 +58,19 @@ function kernel.PANIC(msg)
|
|||||||
kernel.exitMain = true
|
kernel.exitMain = true
|
||||||
end
|
end
|
||||||
while true do
|
while true do
|
||||||
local event={computer:getMachineEvent()}
|
local event={EFI:getMachineEvent()}
|
||||||
if event[1]=="keyPressed" then
|
if event[1]=="keyPressed" then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
computer:reboot()
|
EFI.reboot=true
|
||||||
|
error("KERNEL PANIC")
|
||||||
end
|
end
|
||||||
kernel.panic=kernel.PANIC
|
kernel.panic=kernel.PANIC
|
||||||
|
|
||||||
if windowsExp then
|
if windowsExp then
|
||||||
screen:setTextColor(1)
|
screen:setTextColor(0xFFFFFF)
|
||||||
screen:setBackgroundColor(4)
|
screen:setBackgroundColor(0x0000FF)
|
||||||
screen:clear()
|
screen:clear()
|
||||||
local w,h = screen:getSize()
|
local w,h = screen:getSize()
|
||||||
screen:setCursorPos(3,5)
|
screen:setCursorPos(3,5)
|
||||||
@@ -114,8 +116,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", 0x00FF00)
|
||||||
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")
|
||||||
@@ -130,25 +133,35 @@ if not initCfgStatus then
|
|||||||
end
|
end
|
||||||
kernel.config = config
|
kernel.config = config
|
||||||
|
|
||||||
|
local skip=false
|
||||||
for i,v in ipairs(split(fstab,"\n")) do
|
for i,v in ipairs(split(fstab,"\n")) do
|
||||||
if v:sub(1,1)=="U" then
|
if v:sub(1,1)=="U" then
|
||||||
local id=""
|
local id=""
|
||||||
for i=3,#v do
|
for i=3,#v do
|
||||||
if v:sub(i,i)==";" then
|
if v:sub(i,i)==";" then
|
||||||
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN", 0xFF8800) skip = true break end
|
||||||
id=v:sub(3,i-1)
|
id=v:sub(3,i-1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not skip then
|
||||||
local path=v:sub(#id+4)
|
local path=v:sub(#id+4)
|
||||||
ifs.mount(id,path)
|
ifs.mount(id,path)
|
||||||
::endline::
|
else
|
||||||
|
skip=false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
kernel.log("Disks initialized")
|
kernel.log("Disks initialized")
|
||||||
|
|
||||||
function kernel.saveLog()
|
function kernel.saveLog()
|
||||||
|
if kernel.status=="running" then
|
||||||
|
local file = kernel.vfs.open("/var/log/syslog.log", "w")
|
||||||
|
kernel.vfs.write(file, kernel.LOG_Text)
|
||||||
|
kernel.vfs.close(file)
|
||||||
|
else
|
||||||
ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text)
|
ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function kernel.newFifo()
|
function kernel.newFifo()
|
||||||
local fifo = {}
|
local fifo = {}
|
||||||
@@ -185,15 +198,22 @@ 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", 0xFF8800)
|
||||||
|
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=EFI.firmware
|
||||||
kernel.computer=computer
|
kernel.EFI=EFI
|
||||||
kernel.arch=arch
|
kernel.arch=arch
|
||||||
kernel.initdisks=disks
|
kernel.initdisks=disks
|
||||||
kernel.screen=screen
|
kernel.screen=screen
|
||||||
@@ -222,16 +242,19 @@ kernel.kernelTask = {
|
|||||||
kernel.currentTask = kernel.kernelTask
|
kernel.currentTask = kernel.kernelTask
|
||||||
|
|
||||||
function kernel.shutdown()
|
function kernel.shutdown()
|
||||||
kernel.computer:shutdown()
|
kernel.exitMain=true
|
||||||
|
kernel.status="shutdown"
|
||||||
end
|
end
|
||||||
|
|
||||||
function kernel.reboot()
|
function kernel.reboot()
|
||||||
kernel.computer:reboot()
|
kernel.exitMain=true
|
||||||
|
kernel.status="reboot"
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.syscalls["time"]=function() return kernel.computer:time() end
|
kernel.syscalls["time"]=function() return kernel.EFI:getEpochMs() end
|
||||||
|
kernel.syscalls["date"]=function() return kernel.EFI:date() end
|
||||||
kernel.syscalls["log"]=kernel.log
|
kernel.syscalls["log"]=kernel.log
|
||||||
kernel.syscalls["getUptime"]=function() return kernel.computer:clock() end
|
kernel.syscalls["getUptime"]=function() return kernel.EFI:getUptime() end
|
||||||
kernel.syscalls["getUsername"]=function(uid) return kernel.users[uid or kernel.uid] end
|
kernel.syscalls["getUsername"]=function(uid) return kernel.users[uid or kernel.uid] end
|
||||||
kernel.syscalls["getHostname"]=function() return kernel.hostname end
|
kernel.syscalls["getHostname"]=function() return kernel.hostname end
|
||||||
kernel.syscalls["getHost"]=function() return kernel.apis._HOST end
|
kernel.syscalls["getHost"]=function() return kernel.apis._HOST end
|
||||||
@@ -245,30 +268,34 @@ kernel.syscalls["sysdump"]=function()
|
|||||||
end
|
end
|
||||||
return rv
|
return rv
|
||||||
end
|
end
|
||||||
kernel.syscalls["test"]=function() return true end
|
kernel.syscalls["reboot"]=kernel.reboot
|
||||||
|
kernel.syscalls["shutdown"]=kernel.shutdown
|
||||||
|
|
||||||
kernel.log("Running modules")
|
kernel.log("Running modules")
|
||||||
for _,p in ipairs(modules) do
|
for _,p in ipairs(modules) do
|
||||||
for _,v in ipairs(p) do
|
for _,v in ipairs(p) do
|
||||||
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 5) end
|
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 0x00FFFF) end
|
||||||
local code=ifs.readAllText(v)
|
local code=ifs.readAllText(v)
|
||||||
if not code then
|
if not code then
|
||||||
kernel.log("ModuReadErr: "..v, "WARN", 8)
|
kernel.panic("Failed to read module "..v)
|
||||||
goto skip
|
|
||||||
end
|
end
|
||||||
local func,err=load(code,"@"..v)
|
local func,err=load(code,"@"..v)
|
||||||
if not func then kernel.panic("ModuLoadErr: "..tostring(err)) goto skip end
|
if not func then kernel.panic("ModuLoadErr: "..tostring(err)) end
|
||||||
local status, err = xpcall(func,debug.traceback, kernel)
|
local status, err = xpcall(func,debug.traceback, kernel)
|
||||||
if not status then kernel.panic("ModuRunErr: "..tostring(err)) end
|
if not status then kernel.panic("ModuRunErr: "..tostring(err)) end
|
||||||
if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 5) end
|
if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 0x00FFFF) end
|
||||||
::skip::
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("Kernel initialized successfully.")
|
kernel.log("Kernel initialized successfully.")
|
||||||
|
kernel.saveLog()
|
||||||
kernel.status="running"
|
kernel.status="running"
|
||||||
|
screen:disable()
|
||||||
kernel.main()
|
kernel.main()
|
||||||
if kernel.status=="panic" then
|
if kernel.status=="panic" then
|
||||||
kernel.panic()
|
kernel.panic(kernel.reason)
|
||||||
|
end
|
||||||
|
if kernel.status=="reboot" then
|
||||||
|
EFI.reboot=true
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
kernel.PANIC("Execution complete")
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
-- 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,
|
||||||
|
logTaskExit=true
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1 @@
|
|||||||
0:0:root:/root:/bin/hysh
|
0:0:root:/root:/bin/hysh
|
||||||
1000:1000:testuser:/home/testuser:/bin/hysh
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
0:bcdefghijklmnopqrstuvwxyzABCDEzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789lmnopqrstuvwxyzABCDEOPQRSTUVWXYBCDEFGHIJKLMNOPQRSTUVWXYZ01qrstuvwxyzABCDEFGHIJKLMNOPQRSklmnopqrstuvwxyzABCDKLMNOPQRSTUVWXYZ012345CDEFGHIJKLMNOPQRSTUVWXYZ012345:ae6dedb263f6d68c01a49a2bb6f2512c3ea2854dbac9d786fba7c774b47b601d
|
|
||||||
1000:hijklmnopqrstuvwxyzABCDDEFGHIJKLMNOPQRSTUdefghijklmnopqrstuvwxyzABCDEFGHIJKLLMNOPklmnopqrstuvwxyzABCDEFGHIJKLMvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZYZ0123456BCDEFGHIJKLMNOPQRSTUV:27b25b4cc851e668a4ac14f453e1906b6a07c50a175dd636632d8036a1e91485
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
function string.hasSuffix(str, suffix)
|
|
||||||
return string.sub(str, #suffix + 1) == suffix
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.hasPrefix(str, prefix)
|
|
||||||
return string.sub(str, 1, #prefix) == prefix
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.getSuffix(str, prefix) return string.sub(str, #prefix + 1) end
|
|
||||||
|
|
||||||
function string.getPrefix(str, suffix) return string.sub(str, 1, #suffix) end
|
|
||||||
|
|
||||||
function string.join(str, ...) return table.concat(table.pack(str, ...)) end
|
|
||||||
|
|
||||||
function string.delim(str, ...) return table.concat(table.pack(...), str) end
|
|
||||||
|
|
||||||
function string.split(str, delim, maxResultCountOrNil)
|
|
||||||
assert(#delim == 1, "only delim len 1 supported for now")
|
|
||||||
if not str then return false end
|
|
||||||
maxResultCountOrNil = (maxResultCountOrNil or 0) - 1
|
|
||||||
local rv = {}
|
|
||||||
local buf = ""
|
|
||||||
for i = 1, #str do
|
|
||||||
local c = string.sub(str, i, i)
|
|
||||||
if #rv ~= maxResultCountOrNil and c == delim then
|
|
||||||
table.insert(rv, buf)
|
|
||||||
buf = ""
|
|
||||||
else
|
|
||||||
buf = buf .. c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(rv, buf)
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.deepcopy(orig, copies)
|
|
||||||
copies = copies or {}
|
|
||||||
|
|
||||||
if type(orig) ~= 'table' then
|
|
||||||
return orig
|
|
||||||
elseif copies[orig] then
|
|
||||||
return copies[orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
local copy = {}
|
|
||||||
copies[orig] = copy
|
|
||||||
|
|
||||||
for k, v in next, orig, nil do
|
|
||||||
local copied_key = table.deepcopy(k, copies)
|
|
||||||
local copied_val = table.deepcopy(v, copies)
|
|
||||||
copy[copied_key] = copied_val
|
|
||||||
end
|
|
||||||
|
|
||||||
return copy
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.hasKey(tabl, query)
|
|
||||||
for i, v in pairs(tabl) do if i == query then return v end end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.hasVal(tabl, query)
|
|
||||||
for i, v in pairs(tabl) do if v == query then return i end end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function serialize(tbl, seen)
|
|
||||||
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
|
|
||||||
|
|
||||||
-- Mark this table as seen
|
|
||||||
seen[tbl] = true
|
|
||||||
|
|
||||||
local output = "{"
|
|
||||||
local first = true
|
|
||||||
|
|
||||||
for i, v in pairs(tbl) do
|
|
||||||
-- Handle comma placement more cleanly
|
|
||||||
if not first then output = output .. "," end
|
|
||||||
first = false
|
|
||||||
|
|
||||||
-- Serialize Key
|
|
||||||
if type(i) == "string" then
|
|
||||||
output = output .. "[\"" .. i .. "\"]="
|
|
||||||
elseif type(i) == "number" then
|
|
||||||
output = output .. "[" .. tostring(i) .. "]="
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Serialize Value
|
|
||||||
if type(v) == "table" then
|
|
||||||
-- Pass the 'seen' table down to the recursive call
|
|
||||||
output = output .. serialize(v, seen)
|
|
||||||
elseif type(v) == "string" then
|
|
||||||
output = output .. "[=[" .. v .. "]=]"
|
|
||||||
elseif type(v) == "number" or type(v) == "boolean" then
|
|
||||||
output = output .. tostring(v)
|
|
||||||
elseif type(v) == "function" then
|
|
||||||
output = output .. "\"" .. tostring(v) .. "\""
|
|
||||||
elseif type(v) == "thread" then
|
|
||||||
output = output .. "\"" .. tostring(v) .. "\""
|
|
||||||
else
|
|
||||||
error("serialization of type \"" .. type(v) .. "\" is not supported")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
seen[tbl] = nil
|
|
||||||
|
|
||||||
output = output .. "}"
|
|
||||||
return output
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldtype = type
|
|
||||||
local oldgetmetatable = getmetatable
|
|
||||||
function type(object, trueType)
|
|
||||||
if trueType then return oldtype(object) end
|
|
||||||
if oldtype(object) ~= "table" then
|
|
||||||
return oldtype(object)
|
|
||||||
else
|
|
||||||
if oldtype(oldgetmetatable(object)) == "table" then
|
|
||||||
local metatable = oldgetmetatable(object)
|
|
||||||
---@diagnostic disable-next-line: need-check-nil
|
|
||||||
if metatable.__type then return metatable.__type end
|
|
||||||
else
|
|
||||||
return "table"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function getmetatable(object)
|
|
||||||
if oldtype(object) ~= "table" then return end
|
|
||||||
if oldtype(oldgetmetatable(object)) == "table" then
|
|
||||||
if oldgetmetatable(object).__isuserdata then
|
|
||||||
if oldtype(oldgetmetatable(object).__usermeta) == "function" then
|
|
||||||
return oldgetmetatable(object).__usermeta()
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object).__usermeta
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function isEqualToAny(a, ...)
|
|
||||||
local args = {...}
|
|
||||||
for i = 0, #args do if a == args[i] then return true end end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function isEqualToAll(a, ...)
|
|
||||||
local args = {...}
|
|
||||||
for i = 0, #args do if a ~= args[i] then return false end end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.keys(t)
|
|
||||||
local a = {}
|
|
||||||
for n in pairs(t) do table.insert(a, n) end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.values(t)
|
|
||||||
local a = {}
|
|
||||||
for _, n in pairs(t) do table.insert(a, n) end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.indexOf(t, value)
|
|
||||||
for i, v in ipairs(t) do if v == value then return i end end
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.replace(s, target, repl)
|
|
||||||
local result = {}
|
|
||||||
local i = 1
|
|
||||||
local n = #s
|
|
||||||
local t_len = #target
|
|
||||||
|
|
||||||
while i <= n do
|
|
||||||
local match = true
|
|
||||||
if i + t_len - 1 <= n then
|
|
||||||
for j = 1, t_len do
|
|
||||||
if s:sub(i + j - 1, i + j - 1) ~= target:sub(j, j) then
|
|
||||||
match = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
match = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if match then
|
|
||||||
table.insert(result, repl)
|
|
||||||
i = i + t_len
|
|
||||||
else
|
|
||||||
table.insert(result, s:sub(i, i))
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
function toHex(num)
|
|
||||||
return string.format("%X", num)
|
|
||||||
end
|
|
||||||
|
|
||||||
syscall = setmetatable({}, {
|
|
||||||
__index = function(self, name)
|
|
||||||
return function(...)
|
|
||||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
|
||||||
if res[1] then
|
|
||||||
return table.unpack(res, 2, res.n)
|
|
||||||
else
|
|
||||||
error(res[2], 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
table.serialize = serialize
|
|
||||||
@@ -1,629 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local vfs = {}
|
|
||||||
kernel.vfs = vfs
|
|
||||||
vfs.mounts = {["$"] = "/"}
|
|
||||||
vfs.disks = kernel.disks
|
|
||||||
|
|
||||||
-- Path normalization
|
|
||||||
local function normalizePath(path)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local cwd = task.cwd or "/"
|
|
||||||
if path:sub(1, 1) ~= "/" then path = cwd .. "/" .. path end
|
|
||||||
local parts = {}
|
|
||||||
for part in path:gmatch("[^/]+") do
|
|
||||||
if part == ".." then
|
|
||||||
if #parts > 0 then table.remove(parts) end
|
|
||||||
elseif part ~= "." and part ~= "" then
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "/" .. table.concat(parts, "/")
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.splitPath(path)
|
|
||||||
local rv=string.split(path,"/")
|
|
||||||
while table.indexOf(rv, "") ~= -1 do
|
|
||||||
table.remove(rv, table.indexOf(rv, ""))
|
|
||||||
end
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Resolve mount and disk path
|
|
||||||
local function resolvePath(path)
|
|
||||||
path = normalizePath(path)
|
|
||||||
|
|
||||||
local mountPoint = nil
|
|
||||||
local mountId = nil
|
|
||||||
|
|
||||||
for id, mp in pairs(vfs.mounts) do
|
|
||||||
if path == mp or (mp == "/" and path:sub(1, 1) == "/") or path:sub(1, #mp + 1) == mp .. "/" then
|
|
||||||
if not mountPoint or #mp > #mountPoint then
|
|
||||||
mountPoint = mp
|
|
||||||
mountId = id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not mountId then
|
|
||||||
error("ENODEV")
|
|
||||||
end
|
|
||||||
|
|
||||||
local diskPath = path:sub(#mountPoint + 1)
|
|
||||||
if diskPath == "" then
|
|
||||||
diskPath = "/"
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.config.logPathResolution then
|
|
||||||
kernel.log("Path '"..path.."' resolved to disk '"..mountId.."' and path '"..diskPath.."'")
|
|
||||||
end
|
|
||||||
|
|
||||||
return vfs.disks[mountId], diskPath
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Allocate file descriptor for current task
|
|
||||||
local function allocFD(task)
|
|
||||||
local fd = 0
|
|
||||||
while task.fd[fd] do fd = fd + 1 end
|
|
||||||
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
-- System-wide open file limit
|
|
||||||
local total = 0
|
|
||||||
local function checkSystemLimit()
|
|
||||||
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- File object constructor
|
|
||||||
local function newFileObj(handle, mode, path, meta, type)
|
|
||||||
return {
|
|
||||||
handle = handle,
|
|
||||||
mode = mode,
|
|
||||||
path = path,
|
|
||||||
meta = meta,
|
|
||||||
type = type,
|
|
||||||
refcount = 1
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.newfd(fdobj)
|
|
||||||
checkSystemLimit()
|
|
||||||
total=total+1
|
|
||||||
local fd = allocFD(kernel.currentTask)
|
|
||||||
kernel.currentTask.fd[fd]=fdobj
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse metafile
|
|
||||||
local function parseMetafile(file)
|
|
||||||
if not file or file == "" then return {} end
|
|
||||||
|
|
||||||
local ret = {}
|
|
||||||
local pointer = 1
|
|
||||||
|
|
||||||
while pointer <= #file do
|
|
||||||
local namelen = file:byte(pointer)
|
|
||||||
pointer = pointer + 1
|
|
||||||
|
|
||||||
local name = file:sub(pointer, pointer + namelen - 1)
|
|
||||||
pointer = pointer + namelen
|
|
||||||
|
|
||||||
local owner = file:byte(pointer)
|
|
||||||
local group = file:byte(pointer + 1)
|
|
||||||
local perms = file:byte(pointer + 2)
|
|
||||||
pointer = pointer + 3
|
|
||||||
|
|
||||||
local cmetalen = file:byte(pointer)
|
|
||||||
pointer = pointer + 1
|
|
||||||
|
|
||||||
local cmeta = ""
|
|
||||||
if cmetalen > 0 then
|
|
||||||
cmeta = file:sub(pointer, pointer + cmetalen - 1)
|
|
||||||
pointer = pointer + cmetalen
|
|
||||||
end
|
|
||||||
|
|
||||||
ret[name] = {owner = owner, group = group, perms = perms, cmeta = cmeta}
|
|
||||||
end
|
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Build metafile
|
|
||||||
local function makeMetafile(meta)
|
|
||||||
local file = ""
|
|
||||||
for name, m in pairs(meta) do
|
|
||||||
local entry = ""
|
|
||||||
entry = entry .. string.char(#name) .. name
|
|
||||||
entry = entry .. string.char(m.owner, m.group, m.perms)
|
|
||||||
entry = entry .. string.char(#m.cmeta) .. m.cmeta
|
|
||||||
file = file .. entry
|
|
||||||
end
|
|
||||||
return file
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get file metadata object
|
|
||||||
local function getFileMeta(path)
|
|
||||||
local disk, fullPath = resolvePath(path)
|
|
||||||
fullPath = normalizePath(fullPath)
|
|
||||||
|
|
||||||
local parts = {}
|
|
||||||
for p in fullPath:gmatch("[^/]+") do table.insert(parts, p) end
|
|
||||||
|
|
||||||
-- default fallback
|
|
||||||
local default = {owner = 0, group = 0, perms = 63, cmeta = ""}
|
|
||||||
|
|
||||||
-- walk from deepest parent upward
|
|
||||||
for i = #parts, 1, -1 do
|
|
||||||
local parent = "/" .. table.concat(parts, "/", 1, i - 1)
|
|
||||||
if parent ~= "/" then parent = parent .. "/" end
|
|
||||||
|
|
||||||
local target = parts[i]
|
|
||||||
if target == ".meta" then error("Cannot open metafile") end
|
|
||||||
local metaPath = parent .. ".meta"
|
|
||||||
|
|
||||||
if disk:fileExists(metaPath) then
|
|
||||||
local f = disk:open(metaPath, "r")
|
|
||||||
local text = f.read(65535)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
local parsed = parseMetafile(text)
|
|
||||||
if parsed[target] then return parsed[target] end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
|
|
||||||
local function ensureParentMeta(path)
|
|
||||||
local disk, fullPath = resolvePath(path)
|
|
||||||
fullPath = normalizePath(fullPath)
|
|
||||||
|
|
||||||
-- split parent + name
|
|
||||||
local parent, name = fullPath:match("^(.*)/([^/]+)$")
|
|
||||||
if not parent then
|
|
||||||
parent = "/"
|
|
||||||
name = fullPath:gsub("^/", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
if name == ".meta" then error("Cannot open metafile") end
|
|
||||||
|
|
||||||
if parent ~= "/" and parent:sub(-1) ~= "/" then parent = parent .. "/" end
|
|
||||||
|
|
||||||
local metaPath = parent .. ".meta"
|
|
||||||
|
|
||||||
if not disk:fileExists(metaPath) then
|
|
||||||
local f = disk:open(metaPath, "w")
|
|
||||||
f.write("")
|
|
||||||
f.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
return metaPath, name
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Permission checking
|
|
||||||
local function checkperms(meta, mode)
|
|
||||||
local modes = {
|
|
||||||
r = {owner = 5, group = 3, everyone = 1},
|
|
||||||
w = {owner = 4, group = 2, everyone = 0},
|
|
||||||
a = {owner = 4, group = 2, everyone = 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
local bits = meta.perms
|
|
||||||
local function bit_is_set(num, bit)
|
|
||||||
return math.floor(num / (2 ^ bit)) % 2 == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.uid == 0 then return true end
|
|
||||||
if kernel.uid == meta.owner and bit_is_set(bits, modes[mode].owner) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
if meta.group and kernel.groups then
|
|
||||||
for _, gid in ipairs(kernel.groups) do
|
|
||||||
if gid == meta.group and bit_is_set(bits, modes[mode].group) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if bit_is_set(bits, modes[mode].everyone) then return true end
|
|
||||||
error("EACCES")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- mounts
|
|
||||||
local function normalizeMountPoint(path)
|
|
||||||
path = normalizePath(path)
|
|
||||||
if path ~= "/" and path:sub(-1) == "/" then path = path:sub(1, -2) end
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
|
|
||||||
local required = {
|
|
||||||
"open",
|
|
||||||
"type",
|
|
||||||
"list",
|
|
||||||
"attributes",
|
|
||||||
"fileExists",
|
|
||||||
"makeDirectory",
|
|
||||||
"remove"
|
|
||||||
}
|
|
||||||
|
|
||||||
local function check(disk)
|
|
||||||
for _, name in ipairs(required) do
|
|
||||||
if type(disk[name]) ~= "function" then
|
|
||||||
error("Invalid disk: missing method '" .. name .. "'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.mount(target, diskOrId)
|
|
||||||
if kernel.uid ~= 0 then error("EPERM") end
|
|
||||||
if not target then error("EINVAL") end
|
|
||||||
|
|
||||||
target = normalizeMountPoint(target)
|
|
||||||
if not vfs.exists(target) then vfs.mkdir(target) end
|
|
||||||
if vfs.type(target) ~= "directory" then error("EINVAL") end
|
|
||||||
|
|
||||||
local disk
|
|
||||||
local id
|
|
||||||
|
|
||||||
if type(diskOrId) == "string" then
|
|
||||||
disk = kernel.disks[diskOrId]
|
|
||||||
if not disk then error("ENODEV") end
|
|
||||||
check(disk)
|
|
||||||
id = diskOrId
|
|
||||||
elseif type(diskOrId) == "table" then
|
|
||||||
check(disk)
|
|
||||||
disk = diskOrId
|
|
||||||
id = disk.address
|
|
||||||
vfs.disks[id] = disk
|
|
||||||
else
|
|
||||||
error("EINVAL")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Prevent shadowing an existing mount
|
|
||||||
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
|
|
||||||
|
|
||||||
vfs.mounts[id] = target
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.umount(target)
|
|
||||||
if kernel.uid ~= 0 then error("EPERM") end
|
|
||||||
if not target then error("EINVAL") end
|
|
||||||
|
|
||||||
target = normalizeMountPoint(target)
|
|
||||||
|
|
||||||
for id, mp in pairs(vfs.mounts) do
|
|
||||||
if mp == target then
|
|
||||||
if id == "$" then error("EBUSY") end -- root fs
|
|
||||||
vfs.mounts[id] = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
error("EINVAL")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open file
|
|
||||||
function vfs.open(path, mode)
|
|
||||||
checkSystemLimit()
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local fd = allocFD(task)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("NODISK") end
|
|
||||||
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, mode)
|
|
||||||
|
|
||||||
local handle
|
|
||||||
if disk:type(diskPath)~="directory" then
|
|
||||||
handle = disk:open(diskPath, mode)
|
|
||||||
if type(handle)~="table" then error("ENFILE") end
|
|
||||||
end
|
|
||||||
|
|
||||||
task.fd[fd] = newFileObj(handle, mode, path, meta, disk:type(diskPath))
|
|
||||||
if not disk.isvirt then
|
|
||||||
total = total + 1
|
|
||||||
end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Read
|
|
||||||
function vfs.read(fd, count)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.read then error("EBADF") end
|
|
||||||
return file.handle.read(count or 1) or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Write
|
|
||||||
function vfs.write(fd, content)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.write then error("EBADF") end
|
|
||||||
return file.handle.write(content)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pread / Pwrite
|
|
||||||
function vfs.pread(fd, count, offset)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.read then error("EBADF") end
|
|
||||||
if not file.handle.seek then error("EBADF") end
|
|
||||||
file.handle.seek("set", offset)
|
|
||||||
return file.handle.read(count or 1) or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.pwrite(fd, content, offset)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.write then error("EBADF") end
|
|
||||||
if not file.handle.seek then error("EBADF") end
|
|
||||||
file.handle.seek("set", offset)
|
|
||||||
return file.handle.write(content)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Seek
|
|
||||||
function vfs.lseek(fd, offset, whence)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.seek then error("EBADF") end
|
|
||||||
return file.handle.seek(whence or "set", offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fsync
|
|
||||||
function vfs.fsync(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.handle.flush then error("EBADF") end
|
|
||||||
if file.mode ~= "w" and file.mode ~= "a" then error("EBADF") end
|
|
||||||
file.handle.flush()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Close
|
|
||||||
function vfs.close(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
task.fd[fd] = nil
|
|
||||||
total = total - 1
|
|
||||||
|
|
||||||
file.refcount = file.refcount - 1
|
|
||||||
if file.refcount <= 0 then
|
|
||||||
if file.handle.close then
|
|
||||||
file.handle.close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sendfile
|
|
||||||
function vfs.sendfile(outfd, infd, count)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local inFile = task.fd[infd]
|
|
||||||
local outFile = task.fd[outfd]
|
|
||||||
if not inFile or not outFile then error("EBADF") end
|
|
||||||
if not inFile.handle.read then error("EBADF") end
|
|
||||||
if not outFile.handle.write then error("EBADF") end
|
|
||||||
local data = inFile.handle.read(count or 1024)
|
|
||||||
if not data or data == "" then return end
|
|
||||||
return outFile.handle.write(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Stat / Fstat
|
|
||||||
function vfs.stat(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
local attrs = disk:attributes(diskPath)
|
|
||||||
return {
|
|
||||||
size = attrs.size,
|
|
||||||
modified = attrs.modified,
|
|
||||||
created = attrs.created,
|
|
||||||
owner = meta.owner,
|
|
||||||
group = meta.group,
|
|
||||||
xattr = meta.cmeta
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.fstat(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
local disk, path = resolvePath(file.path)
|
|
||||||
local attrs = disk:attributes(path)
|
|
||||||
return {
|
|
||||||
size = attrs.size,
|
|
||||||
modified = attrs.modified,
|
|
||||||
created = attrs.created,
|
|
||||||
owner = file.meta.owner,
|
|
||||||
group = file.meta.group,
|
|
||||||
xattr = file.meta.cmeta
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Directory operations
|
|
||||||
function vfs.listdir(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if disk:type(diskPath) ~= "directory" then error("ENOENT") end
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "r")
|
|
||||||
local list = disk:list(diskPath)
|
|
||||||
if table.indexOf(list, ".meta") ~= -1 then
|
|
||||||
table.remove(list, table.indexOf(list, ".meta"))
|
|
||||||
end
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.mkdir(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "w")
|
|
||||||
disk:makeDirectory(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.remove(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "w")
|
|
||||||
disk:remove(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Permission functions
|
|
||||||
function vfs.chmod(path, perms)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
|
|
||||||
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
|
|
||||||
meta.perms = perms
|
|
||||||
|
|
||||||
local mpath, target = ensureParentMeta(path)
|
|
||||||
|
|
||||||
local mf = disk:open(mpath, "r")
|
|
||||||
local text = mf.read(65535)
|
|
||||||
mf.close()
|
|
||||||
|
|
||||||
local parsed = parseMetafile(text)
|
|
||||||
parsed[target] = meta
|
|
||||||
|
|
||||||
local f = disk:open(mpath, "w")
|
|
||||||
f.write(makeMetafile(parsed))
|
|
||||||
f.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.fchmod(fd, perms)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
vfs.chmod(file.path, perms)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.chown(path, uid, gid)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
|
|
||||||
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
|
|
||||||
meta.owner = uid
|
|
||||||
meta.group = gid
|
|
||||||
|
|
||||||
local mpath, target = ensureParentMeta(path)
|
|
||||||
|
|
||||||
local mf = disk:open(mpath, "r")
|
|
||||||
local text = mf.read(65535)
|
|
||||||
mf.close()
|
|
||||||
|
|
||||||
local parsed = parseMetafile(text)
|
|
||||||
parsed[target] = meta
|
|
||||||
|
|
||||||
local f = disk:open(mpath, "w")
|
|
||||||
f.write(makeMetafile(parsed))
|
|
||||||
f.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.fchown(fd, uid, gid)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
vfs.chown(file.path, uid, gid)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.exists(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "r")
|
|
||||||
return disk:fileExists(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.type(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
local meta = getFileMeta(path)
|
|
||||||
checkperms(meta, "r")
|
|
||||||
return disk:type(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.getcwd() return kernel.currentTask.cwd end
|
|
||||||
|
|
||||||
function vfs.chdir(path) kernel.currentTask.cwd = path end
|
|
||||||
|
|
||||||
function vfs.dup(oldfd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[oldfd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
checkSystemLimit()
|
|
||||||
|
|
||||||
local newfd = allocFD(task)
|
|
||||||
file.refcount = file.refcount + 1
|
|
||||||
task.fd[newfd] = file
|
|
||||||
total = total + 1
|
|
||||||
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.dup2(oldfd, newfd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[oldfd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if newfd < 0 or newfd >= kernel.config.maxFilesPerTask then
|
|
||||||
error("EBADF")
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldfd == newfd then
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
if task.fd[newfd] then
|
|
||||||
vfs.close(newfd)
|
|
||||||
end
|
|
||||||
|
|
||||||
checkSystemLimit()
|
|
||||||
|
|
||||||
file.refcount = file.refcount + 1
|
|
||||||
task.fd[newfd] = file
|
|
||||||
total = total + 1
|
|
||||||
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.devctl(fd, method, ...)
|
|
||||||
if not kernel.currentTask.fd[fd] then error("EBADF") end
|
|
||||||
if not kernel.currentTask.fd[fd].handle[method] then error("EINVAL") end
|
|
||||||
return kernel.currentTask.fd[fd].handle[method](...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Export syscalls
|
|
||||||
local sys = kernel.syscalls
|
|
||||||
sys["open"] = vfs.open
|
|
||||||
sys["close"] = vfs.close
|
|
||||||
sys["read"] = vfs.read
|
|
||||||
sys["write"] = vfs.write
|
|
||||||
sys["pread"] = vfs.pread
|
|
||||||
sys["pwrite"] = vfs.pwrite
|
|
||||||
sys["lseek"] = vfs.lseek
|
|
||||||
sys["fsync"] = vfs.fsync
|
|
||||||
sys["sendfile"] = vfs.sendfile
|
|
||||||
sys["stat"] = vfs.stat
|
|
||||||
sys["fstat"] = vfs.fstat
|
|
||||||
sys["mkdir"] = vfs.mkdir
|
|
||||||
sys["remove"] = vfs.remove
|
|
||||||
sys["listdir"] = vfs.listdir
|
|
||||||
sys["chmod"] = vfs.chmod
|
|
||||||
sys["fchmod"] = vfs.fchmod
|
|
||||||
sys["chown"] = vfs.chown
|
|
||||||
sys["fchown"] = vfs.fchown
|
|
||||||
sys["exists"] = vfs.exists
|
|
||||||
sys["type"] = vfs.type
|
|
||||||
sys["mount"] = vfs.mount
|
|
||||||
sys["umount"] = vfs.umount
|
|
||||||
sys["getcwd"] = vfs.getcwd
|
|
||||||
sys["chdir"] = vfs.chdir
|
|
||||||
sys["dup"] = vfs.dup
|
|
||||||
sys["dup2"] = vfs.dup2
|
|
||||||
sys["devctl"] = vfs.devctl
|
|
||||||
|
|
||||||
kernel.log("VFS module loaded")
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local cache = {}
|
|
||||||
kernel.searchpaths = {
|
|
||||||
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
|
|
||||||
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
|
|
||||||
}
|
|
||||||
|
|
||||||
function require(module, ...)
|
|
||||||
if cache[module] then return cache[module] end
|
|
||||||
local modpath = module:gsub("%.", "/")
|
|
||||||
local failed = {}
|
|
||||||
for _, path in ipairs(kernel.searchpaths) do
|
|
||||||
local full_path = string.replace(path, "?", modpath)
|
|
||||||
if full_path:sub(1, 1) ~= "/" then
|
|
||||||
full_path = kernel.currentTask.cwd .. full_path
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.vfs.exists(full_path) then
|
|
||||||
if kernel.vfs.type(full_path) == "directory" then
|
|
||||||
full_path = full_path .. "/init"
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.vfs.exists(full_path) then
|
|
||||||
local handle = kernel.vfs.open(full_path, "r")
|
|
||||||
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
return
|
|
||||||
assert(load(file_content, full_path, "t", kernel._U))(...)
|
|
||||||
else
|
|
||||||
table.insert(failed, full_path)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(failed, full_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
|
|
||||||
end
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local proxy = {}
|
|
||||||
local data = {}
|
|
||||||
|
|
||||||
proxy.address = "devfs0000"
|
|
||||||
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 "devfs" end
|
|
||||||
proxy.attributes = function(path) return {
|
|
||||||
size = 0,
|
|
||||||
modified = 0,
|
|
||||||
created = 0,
|
|
||||||
} end
|
|
||||||
|
|
||||||
function proxy:open(path, mode)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
for i=1, #steps-1 do
|
|
||||||
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
|
|
||||||
error("ENFILE")
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:type(path, mode)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
if #steps == 0 then
|
|
||||||
return "directory"
|
|
||||||
end
|
|
||||||
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
|
|
||||||
error("ENOENT")
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:list(path)
|
|
||||||
local steps = kernel.vfs.splitPath(path)
|
|
||||||
local step = data
|
|
||||||
if #steps == 0 then
|
|
||||||
return table.keys(data)
|
|
||||||
end
|
|
||||||
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
|
|
||||||
error("ENOENT")
|
|
||||||
end
|
|
||||||
|
|
||||||
function proxy:fileExists(path)
|
|
||||||
local ok = pcall(function()
|
|
||||||
return self:type(path)
|
|
||||||
end)
|
|
||||||
return ok
|
|
||||||
end
|
|
||||||
|
|
||||||
function data.random(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
if mode=="r" then
|
|
||||||
return {
|
|
||||||
read=function(amount)
|
|
||||||
local str = ""
|
|
||||||
for i=1, amount or 1 do
|
|
||||||
str=str..string.char(math.random(0, 255))
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
}
|
|
||||||
elseif mode=="w" or mode=="a" then
|
|
||||||
return {
|
|
||||||
write=function() end
|
|
||||||
}
|
|
||||||
else error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function data.null(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
if mode=="r" then
|
|
||||||
return {
|
|
||||||
read=function(amount) end
|
|
||||||
}
|
|
||||||
elseif mode=="w" or mode=="a" then
|
|
||||||
return {
|
|
||||||
write=function() end
|
|
||||||
}
|
|
||||||
else error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function data.zero(op, mode)
|
|
||||||
if op=="type" then
|
|
||||||
return "character device"
|
|
||||||
elseif op=="open" then
|
|
||||||
if mode=="r" then
|
|
||||||
return {
|
|
||||||
read=function(amount)
|
|
||||||
local str = ""
|
|
||||||
for i=1, amount or 1 do
|
|
||||||
str=str..string.char(0)
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
}
|
|
||||||
elseif mode=="w" or mode=="a" then
|
|
||||||
return {
|
|
||||||
write=function() end
|
|
||||||
}
|
|
||||||
else error("EACCES")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
data["disk"]={}
|
|
||||||
kernel.devfs={}
|
|
||||||
kernel.devfs.data=data
|
|
||||||
kernel.devfs.proxy=proxy
|
|
||||||
kernel.disks["devfs0000"]=proxy
|
|
||||||
@@ -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,27 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local signal = {}
|
|
||||||
kernel.signal=signal
|
|
||||||
|
|
||||||
function signal.sigsend(pid, sig)
|
|
||||||
if sig<0 or sig>256 then error("EINVAL") end
|
|
||||||
local task = kernel.tasks[tostring(pid)]
|
|
||||||
if not task then error("ENOENT") end
|
|
||||||
if not task.sigq then return end
|
|
||||||
task.sigq[#task.sigq+1] = sig
|
|
||||||
end
|
|
||||||
|
|
||||||
function signal.sigcatch(handler)
|
|
||||||
kernel.currentTask.sigh=handler
|
|
||||||
if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function signal.sigignore()
|
|
||||||
kernel.currentTask.sigh=nil
|
|
||||||
kernel.currentTask.sigq=nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local s=kernel.syscalls
|
|
||||||
s["sigsend"] = signal.sigsend
|
|
||||||
s["sigcatch"] = signal.sigcatch
|
|
||||||
s["sigignore"] = signal.sigignore
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local socket = {}
|
|
||||||
|
|
||||||
function socket.socket()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function socket.bind()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.socket=socket
|
|
||||||
kernel.log("Loaded socket module")
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
kernel.vfs.open("/dev/null", "r")
|
|
||||||
kernel.vfs.open("/dev/tty/TTY1", "w")
|
|
||||||
kernel.vfs.open("/dev/null", "w")
|
|
||||||
kernel.status="term"
|
|
||||||
@@ -1,625 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local auth = {}
|
|
||||||
kernel.auth = auth
|
|
||||||
|
|
||||||
-- PASSWD FILE FORMAT: uid:gid:username:homedir:shell
|
|
||||||
-- SHADOW FILE FORMAT: uid:salt:hash
|
|
||||||
|
|
||||||
local function getFile(path)
|
|
||||||
local file = kernel.vfs.open(path, "r")
|
|
||||||
if not file then error("Failed to open file: " .. path) end
|
|
||||||
local content = kernel.vfs.read(file, 1024000)
|
|
||||||
kernel.vfs.close(file)
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
local function writeFile(path, content)
|
|
||||||
local file = kernel.vfs.open(path, "w")
|
|
||||||
if not file then error("Failed to open file for writing: " .. path) end
|
|
||||||
kernel.vfs.write(file, content)
|
|
||||||
kernel.vfs.close(file)
|
|
||||||
end
|
|
||||||
|
|
||||||
local blake2s
|
|
||||||
do
|
|
||||||
local MOD32 = 2^32
|
|
||||||
local function norm(x) return x % MOD32 end
|
|
||||||
local function tobits(x)
|
|
||||||
x = norm(x)
|
|
||||||
local t = {}
|
|
||||||
for i = 0, 31 do local b = x % 2; t[i] = b; x = (x - b) / 2 end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
local function frombits(t)
|
|
||||||
local x, p = 0, 1
|
|
||||||
for i = 0, 31 do if t[i] == 1 then x = x + p end; p = p * 2 end
|
|
||||||
return norm(x)
|
|
||||||
end
|
|
||||||
local function bor(...)
|
|
||||||
local args = {...}
|
|
||||||
if #args == 0 then return 0 end
|
|
||||||
local bits = tobits(args[1])
|
|
||||||
for i = 2, #args do
|
|
||||||
local b = tobits(args[i])
|
|
||||||
for j = 0, 31 do bits[j] = (bits[j] == 1 or b[j] == 1) and 1 or 0 end
|
|
||||||
end
|
|
||||||
return frombits(bits)
|
|
||||||
end
|
|
||||||
local function bxor(...)
|
|
||||||
local args = {...}
|
|
||||||
if #args == 0 then return 0 end
|
|
||||||
local bits = tobits(args[1])
|
|
||||||
for i = 2, #args do
|
|
||||||
local b = tobits(args[i])
|
|
||||||
for j = 0, 31 do bits[j] = (bits[j] ~= b[j]) and 1 or 0 end
|
|
||||||
end
|
|
||||||
return frombits(bits)
|
|
||||||
end
|
|
||||||
local function lshift(x, n) return norm(norm(x) * 2^n) end
|
|
||||||
local function rshift(x, n) return math.floor(norm(x) / 2^n) end
|
|
||||||
local function rotr(x, n) return bor(rshift(x, n), lshift(x, 32 - n)) end
|
|
||||||
local IV = {
|
|
||||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
|
||||||
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
|
||||||
}
|
|
||||||
local SIGMA = {
|
|
||||||
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
|
|
||||||
{14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3},
|
|
||||||
{11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4},
|
|
||||||
{7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8},
|
|
||||||
{9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13},
|
|
||||||
{2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9},
|
|
||||||
{12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11},
|
|
||||||
{13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10},
|
|
||||||
{6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5},
|
|
||||||
{10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0}
|
|
||||||
}
|
|
||||||
local function G(v, a, b, c, d, x, y)
|
|
||||||
v[a] = (v[a] + v[b] + x) % MOD32
|
|
||||||
v[d] = rotr(bxor(v[d], v[a]), 16)
|
|
||||||
v[c] = (v[c] + v[d]) % MOD32
|
|
||||||
v[b] = rotr(bxor(v[b], v[c]), 12)
|
|
||||||
v[a] = (v[a] + v[b] + y) % MOD32
|
|
||||||
v[d] = rotr(bxor(v[d], v[a]), 8)
|
|
||||||
v[c] = (v[c] + v[d]) % MOD32
|
|
||||||
v[b] = rotr(bxor(v[b], v[c]), 7)
|
|
||||||
end
|
|
||||||
local function compress(h, block, t, last)
|
|
||||||
local v = {}
|
|
||||||
for i = 1, 8 do v[i] = h[i] end
|
|
||||||
for i = 1, 8 do v[i + 8] = IV[i] end
|
|
||||||
v[13] = bxor(v[13], t)
|
|
||||||
if last then v[15] = bxor(v[15], 0xFFFFFFFF) end
|
|
||||||
local m = {}
|
|
||||||
for i = 0, 15 do
|
|
||||||
local p = i * 4 + 1
|
|
||||||
m[i] = (block:byte(p) or 0)
|
|
||||||
+ ((block:byte(p+1) or 0) * 0x100)
|
|
||||||
+ ((block:byte(p+2) or 0) * 0x10000)
|
|
||||||
+ ((block:byte(p+3) or 0) * 0x1000000)
|
|
||||||
end
|
|
||||||
for r = 1, 10 do
|
|
||||||
local s = SIGMA[r]
|
|
||||||
G(v,1,5,9,13, m[s[1]], m[s[2]])
|
|
||||||
G(v,2,6,10,14, m[s[3]], m[s[4]])
|
|
||||||
G(v,3,7,11,15, m[s[5]], m[s[6]])
|
|
||||||
G(v,4,8,12,16, m[s[7]], m[s[8]])
|
|
||||||
G(v,1,6,11,16, m[s[9]], m[s[10]])
|
|
||||||
G(v,2,7,12,13, m[s[11]], m[s[12]])
|
|
||||||
G(v,3,8,9,14, m[s[13]], m[s[14]])
|
|
||||||
G(v,4,5,10,15, m[s[15]], m[s[16]])
|
|
||||||
end
|
|
||||||
for i = 1, 8 do h[i] = bxor(h[i], v[i], v[i+8]) end
|
|
||||||
end
|
|
||||||
function blake2s(msg, key)
|
|
||||||
key = key or ""
|
|
||||||
local h = {}
|
|
||||||
for i = 1, 8 do h[i] = IV[i] end
|
|
||||||
local outlen = 32
|
|
||||||
h[1] = bxor(h[1], 0x01010000 + lshift(#key, 8) + outlen)
|
|
||||||
local t = 0
|
|
||||||
if #key > 0 then
|
|
||||||
local block = key .. string.rep("\0", 64 - #key)
|
|
||||||
t = #key
|
|
||||||
compress(h, block, t, false)
|
|
||||||
end
|
|
||||||
for i = 1, #msg, 64 do
|
|
||||||
local block = msg:sub(i, i + 63)
|
|
||||||
if #block < 64 then block = block .. string.rep("\0", 64 - #block) end
|
|
||||||
t = t + math.min(64, #msg - i + 1)
|
|
||||||
compress(h, block, t, i + 64 > #msg)
|
|
||||||
end
|
|
||||||
local out = ""
|
|
||||||
for i = 1, 8 do out = out .. string.format("%08x", h[i]) end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not blake2s then error("Failed to load blake2s") end
|
|
||||||
|
|
||||||
if not kernel.vfs.exists("/etc/pam.d/secret") then
|
|
||||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT")
|
|
||||||
local key = ""
|
|
||||||
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
|
|
||||||
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
|
|
||||||
kernel.vfs.write(handle, key)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
end
|
|
||||||
|
|
||||||
local pepper = getFile("/etc/pam.d/secret")
|
|
||||||
|
|
||||||
local function genSalt()
|
|
||||||
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
|
|
||||||
local s = ""
|
|
||||||
for i = 1, 16 do
|
|
||||||
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
|
|
||||||
end
|
|
||||||
return s
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hashPassword(password, salt)
|
|
||||||
local key = (pepper .. salt):sub(1, 32)
|
|
||||||
return blake2s(password, key)
|
|
||||||
end
|
|
||||||
|
|
||||||
local passwdFile = getFile("/etc/passwd")
|
|
||||||
local shadowFile = getFile("/etc/shadow")
|
|
||||||
|
|
||||||
local passwdLines = string.split(passwdFile, "\n")
|
|
||||||
local shadowLines = string.split(shadowFile, "\n")
|
|
||||||
|
|
||||||
local passwd, shadow = {}, {}
|
|
||||||
for _, v in ipairs(passwdLines) do
|
|
||||||
local fields = string.split(v, ":")
|
|
||||||
if fields[1] and fields[1] ~= "" then
|
|
||||||
passwd[#passwd + 1] = fields
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, v in ipairs(shadowLines) do
|
|
||||||
local fields = string.split(v, ":")
|
|
||||||
if fields[1] and fields[1] ~= "" then
|
|
||||||
shadow[#shadow + 1] = fields
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, v in ipairs(passwd) do
|
|
||||||
local uid = tonumber(v[1])
|
|
||||||
if uid then kernel.users[uid] = v[3] end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.passwd = passwd
|
|
||||||
|
|
||||||
local function flushPasswd()
|
|
||||||
local lines = {}
|
|
||||||
for _, v in ipairs(passwd) do
|
|
||||||
lines[#lines + 1] = table.concat(v, ":")
|
|
||||||
end
|
|
||||||
writeFile("/etc/passwd", table.concat(lines, "\n"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function flushShadow()
|
|
||||||
local lines = {}
|
|
||||||
for _, v in ipairs(shadow) do
|
|
||||||
lines[#lines + 1] = table.concat(v, ":")
|
|
||||||
end
|
|
||||||
writeFile("/etc/shadow", table.concat(lines, "\n"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getPasswdByUID(uid)
|
|
||||||
for _, v in ipairs(passwd) do
|
|
||||||
if tonumber(v[1]) == uid then return v end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getShadowByUID(uid)
|
|
||||||
for _, v in ipairs(shadow) do
|
|
||||||
if tonumber(v[1]) == uid then return v end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getPasswdByUsername(username)
|
|
||||||
for _, v in ipairs(passwd) do
|
|
||||||
if v[3] == username then return v end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function nextUID()
|
|
||||||
local max = 999
|
|
||||||
for _, v in ipairs(passwd) do
|
|
||||||
local uid = tonumber(v[1])
|
|
||||||
if uid and uid >= 1000 and uid > max then max = uid end
|
|
||||||
end
|
|
||||||
return max + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.login(username, password)
|
|
||||||
if type(username) ~= "string" or type(password) ~= "string" then
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry = getPasswdByUsername(username)
|
|
||||||
if not entry then
|
|
||||||
-- timing attack resistance
|
|
||||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
local uid = tonumber(entry[1])
|
|
||||||
local sEntry = getShadowByUID(uid)
|
|
||||||
if not sEntry then
|
|
||||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
local salt = sEntry[2]
|
|
||||||
local storedHash = sEntry[3]
|
|
||||||
|
|
||||||
local computed = hashPassword(password, salt)
|
|
||||||
if computed ~= storedHash then
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.currentUID = uid
|
|
||||||
if kernel.currentProcess then
|
|
||||||
kernel.currentProcess.uid = uid
|
|
||||||
kernel.currentProcess.euid = uid
|
|
||||||
kernel.currentProcess.gid = tonumber(entry[2]) or uid
|
|
||||||
kernel.currentProcess.egid = tonumber(entry[2]) or uid
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.setPassword(uid, newPassword)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
|
|
||||||
or kernel.currentUID or 0
|
|
||||||
|
|
||||||
if callerUID ~= 0 and callerUID ~= uid then
|
|
||||||
return nil, "Permission denied"
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(newPassword) ~= "string" or #newPassword == 0 then
|
|
||||||
return nil, "Password may not be empty"
|
|
||||||
end
|
|
||||||
|
|
||||||
if #newPassword < 6 then
|
|
||||||
return nil, "Password is too short (minimum 6 characters)"
|
|
||||||
end
|
|
||||||
|
|
||||||
local salt = genSalt()
|
|
||||||
local hash = hashPassword(newPassword, salt)
|
|
||||||
|
|
||||||
local sEntry = getShadowByUID(uid)
|
|
||||||
if sEntry then
|
|
||||||
sEntry[2] = salt
|
|
||||||
sEntry[3] = hash
|
|
||||||
else
|
|
||||||
shadow[#shadow + 1] = { tostring(uid), salt, hash }
|
|
||||||
end
|
|
||||||
|
|
||||||
flushShadow()
|
|
||||||
kernel.log("AUTH: password changed for uid=" .. tostring(uid))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.setUsername(uid, newUsername)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
|
|
||||||
or kernel.currentUID or 0
|
|
||||||
|
|
||||||
if callerUID ~= 0 then
|
|
||||||
return nil, "Permission denied (root only)"
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(newUsername) ~= "string" or #newUsername == 0 then
|
|
||||||
return nil, "Invalid username"
|
|
||||||
end
|
|
||||||
|
|
||||||
if not newUsername:match("^[a-z_][a-z0-9_%-]*$") or #newUsername > 32 then
|
|
||||||
return nil, "Invalid username format"
|
|
||||||
end
|
|
||||||
|
|
||||||
if getPasswdByUsername(newUsername) then
|
|
||||||
return nil, "Username already taken"
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry = getPasswdByUID(uid)
|
|
||||||
if not entry then return nil, "No such user" end
|
|
||||||
|
|
||||||
local oldName = entry[3]
|
|
||||||
entry[3] = newUsername
|
|
||||||
kernel.users[uid] = newUsername
|
|
||||||
|
|
||||||
flushPasswd()
|
|
||||||
kernel.log("AUTH: uid=" .. tostring(uid) .. " renamed '" .. oldName .. "' → '" .. newUsername .. "'")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.newUser(username, password, gid, homedir, shell)
|
|
||||||
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
|
|
||||||
or kernel.currentUID or 0
|
|
||||||
|
|
||||||
if callerUID ~= 0 then
|
|
||||||
return nil, "Permission denied (root only)"
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(username) ~= "string" or #username == 0 then
|
|
||||||
return nil, "Invalid username"
|
|
||||||
end
|
|
||||||
|
|
||||||
if not username:match("^[a-z_][a-z0-9_%-]*$") or #username > 32 then
|
|
||||||
return nil, "Invalid username format"
|
|
||||||
end
|
|
||||||
|
|
||||||
if getPasswdByUsername(username) then
|
|
||||||
return nil, "Username already exists"
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(password) ~= "string" or #password < 6 then
|
|
||||||
return nil, "Password is too short (minimum 6 characters)"
|
|
||||||
end
|
|
||||||
|
|
||||||
local uid = nextUID()
|
|
||||||
gid = tonumber(gid) or uid
|
|
||||||
homedir = homedir or ("/home/" .. username)
|
|
||||||
shell = shell or "/bin/sh"
|
|
||||||
|
|
||||||
passwd[#passwd + 1] = {
|
|
||||||
tostring(uid),
|
|
||||||
tostring(gid),
|
|
||||||
username,
|
|
||||||
homedir,
|
|
||||||
shell
|
|
||||||
}
|
|
||||||
kernel.users[uid] = username
|
|
||||||
|
|
||||||
local salt = genSalt()
|
|
||||||
local hash = hashPassword(password, salt)
|
|
||||||
shadow[#shadow + 1] = { tostring(uid), salt, hash }
|
|
||||||
|
|
||||||
flushPasswd()
|
|
||||||
flushShadow()
|
|
||||||
|
|
||||||
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
|
||||||
kernel.vfs.mkdir(homedir)
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
|
||||||
return uid
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.whoami()
|
|
||||||
local uid = (kernel.currentProcess and kernel.currentProcess.euid)
|
|
||||||
or kernel.currentUID
|
|
||||||
if not uid then return nil, "Not logged in" end
|
|
||||||
return kernel.users[uid] or ("uid=" .. tostring(uid))
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.getUID(username)
|
|
||||||
local entry = getPasswdByUsername(username)
|
|
||||||
if entry then return tonumber(entry[1]) end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.getPasswd(uid)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
local entry = getPasswdByUID(uid)
|
|
||||||
if not entry then return nil end
|
|
||||||
return {
|
|
||||||
uid = tonumber(entry[1]),
|
|
||||||
gid = tonumber(entry[2]),
|
|
||||||
username = entry[3],
|
|
||||||
homedir = entry[4],
|
|
||||||
shell = entry[5],
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.deleteUser(uid)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = kernel.uid or 0
|
|
||||||
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
|
||||||
if uid == 0 then return nil, "Cannot delete root" end
|
|
||||||
|
|
||||||
local entry = getPasswdByUID(uid)
|
|
||||||
if not entry then return nil, "No such user" end
|
|
||||||
local username = entry[3]
|
|
||||||
|
|
||||||
-- Remove from passwd
|
|
||||||
for i, v in ipairs(passwd) do
|
|
||||||
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
|
|
||||||
end
|
|
||||||
-- Remove from shadow
|
|
||||||
for i, v in ipairs(shadow) do
|
|
||||||
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
|
|
||||||
end
|
|
||||||
kernel.users[uid] = nil
|
|
||||||
|
|
||||||
flushPasswd()
|
|
||||||
flushShadow()
|
|
||||||
kernel.log("AUTH: deleted user '" .. username .. "' uid=" .. tostring(uid))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.lockUser(uid)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = kernel.uid or 0
|
|
||||||
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
|
||||||
if uid == 0 then return nil, "Cannot lock root" end
|
|
||||||
|
|
||||||
local sEntry = getShadowByUID(uid)
|
|
||||||
if not sEntry then return nil, "No shadow entry for uid" end
|
|
||||||
|
|
||||||
-- Prefix hash with ! to lock (standard Linux convention)
|
|
||||||
if sEntry[3]:sub(1,1) ~= "!" then
|
|
||||||
sEntry[3] = "!" .. sEntry[3]
|
|
||||||
end
|
|
||||||
flushShadow()
|
|
||||||
kernel.log("AUTH: locked uid=" .. tostring(uid))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.unlockUser(uid)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = kernel.uid or 0
|
|
||||||
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
|
||||||
|
|
||||||
local sEntry = getShadowByUID(uid)
|
|
||||||
if not sEntry then return nil, "No shadow entry for uid" end
|
|
||||||
|
|
||||||
if sEntry[3]:sub(1,1) == "!" then
|
|
||||||
sEntry[3] = sEntry[3]:sub(2)
|
|
||||||
end
|
|
||||||
flushShadow()
|
|
||||||
kernel.log("AUTH: unlocked uid=" .. tostring(uid))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.listUsers()
|
|
||||||
local result = {}
|
|
||||||
for _, v in ipairs(passwd) do
|
|
||||||
local uid = tonumber(v[1])
|
|
||||||
local sEntry = getShadowByUID(uid)
|
|
||||||
local locked = sEntry and sEntry[3]:sub(1,1) == "!"
|
|
||||||
result[#result+1] = {
|
|
||||||
uid = uid,
|
|
||||||
gid = tonumber(v[2]),
|
|
||||||
username = v[3],
|
|
||||||
homedir = v[4],
|
|
||||||
shell = v[5],
|
|
||||||
locked = locked or false,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.setShell(uid, shell)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = kernel.uid or 0
|
|
||||||
if callerUID ~= 0 and callerUID ~= uid then
|
|
||||||
return nil, "Permission denied"
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(shell) ~= "string" or #shell == 0 then
|
|
||||||
return nil, "Invalid shell"
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry = getPasswdByUID(uid)
|
|
||||||
if not entry then return nil, "No such user" end
|
|
||||||
entry[5] = shell
|
|
||||||
|
|
||||||
flushPasswd()
|
|
||||||
kernel.log("AUTH: uid=" .. tostring(uid) .. " shell -> " .. shell)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.setHomedir(uid, homedir)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
if not uid then return nil, "Invalid uid" end
|
|
||||||
|
|
||||||
local callerUID = kernel.uid or 0
|
|
||||||
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
|
||||||
|
|
||||||
if type(homedir) ~= "string" or #homedir == 0 then
|
|
||||||
return nil, "Invalid homedir"
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry = getPasswdByUID(uid)
|
|
||||||
if not entry then return nil, "No such user" end
|
|
||||||
entry[4] = homedir
|
|
||||||
|
|
||||||
flushPasswd()
|
|
||||||
kernel.log("AUTH: uid=" .. tostring(uid) .. " homedir -> " .. homedir)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function auth.setGID(uid, gid)
|
|
||||||
uid = tonumber(uid)
|
|
||||||
gid = tonumber(gid)
|
|
||||||
if not uid or not gid then return nil, "Invalid uid or gid" end
|
|
||||||
|
|
||||||
local callerUID = kernel.uid or 0
|
|
||||||
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
|
||||||
|
|
||||||
local entry = getPasswdByUID(uid)
|
|
||||||
if not entry then return nil, "No such user" end
|
|
||||||
entry[2] = tostring(gid)
|
|
||||||
|
|
||||||
flushPasswd()
|
|
||||||
kernel.log("AUTH: uid=" .. tostring(uid) .. " gid -> " .. tostring(gid))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Elevate the calling task to targetUid after verifying targetUsername's password.
|
|
||||||
-- This is the kernel-side primitive for su/sudo — it bypasses the kernel.uid==0
|
|
||||||
-- check in sys.setuid because the auth module itself is trusted kernel code.
|
|
||||||
function auth.elevate(targetUsername, password)
|
|
||||||
if type(targetUsername) ~= "string" or type(password) ~= "string" then
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry = getPasswdByUsername(targetUsername)
|
|
||||||
if not entry then
|
|
||||||
hashPassword(password, "aaaaaaaaaaaaaaaa") -- timing resistance
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
local uid = tonumber(entry[1])
|
|
||||||
local sEntry = getShadowByUID(uid)
|
|
||||||
if not sEntry then
|
|
||||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
local computed = hashPassword(password, sEntry[2])
|
|
||||||
if computed ~= sEntry[3] then
|
|
||||||
return nil, "Authentication failure"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Directly set the calling task's uid — trusted kernel path
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local prevUid = task.uid
|
|
||||||
task.uid = uid
|
|
||||||
task.euid = uid
|
|
||||||
task.gid = tonumber(entry[2]) or uid
|
|
||||||
task.egid = tonumber(entry[2]) or uid
|
|
||||||
kernel.uid = uid
|
|
||||||
|
|
||||||
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")")
|
|
||||||
return true, uid
|
|
||||||
end
|
|
||||||
|
|
||||||
if kernel.syscalls then
|
|
||||||
kernel.syscalls["auth_login"] = auth.login
|
|
||||||
kernel.syscalls["auth_setpassword"] = auth.setPassword
|
|
||||||
kernel.syscalls["auth_setusername"] = auth.setUsername
|
|
||||||
kernel.syscalls["auth_newuser"] = auth.newUser
|
|
||||||
kernel.syscalls["auth_whoami"] = auth.whoami
|
|
||||||
kernel.syscalls["auth_getuid"] = auth.getUID
|
|
||||||
kernel.syscalls["auth_getpasswd"] = auth.getPasswd
|
|
||||||
kernel.syscalls["auth_elevate"] = auth.elevate
|
|
||||||
kernel.syscalls["auth_deleteuser"] = auth.deleteUser
|
|
||||||
kernel.syscalls["auth_lockuser"] = auth.lockUser
|
|
||||||
kernel.syscalls["auth_unlockuser"] = auth.unlockUser
|
|
||||||
kernel.syscalls["auth_listusers"] = auth.listUsers
|
|
||||||
kernel.syscalls["auth_setshell"] = auth.setShell
|
|
||||||
kernel.syscalls["auth_sethomedir"] = auth.setHomedir
|
|
||||||
kernel.syscalls["auth_setgid"] = auth.setGID
|
|
||||||
end
|
|
||||||
@@ -1,447 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local tasks = {}
|
|
||||||
local sys = {}
|
|
||||||
local nextpid = 2
|
|
||||||
kernel.exitMain = false
|
|
||||||
|
|
||||||
function sys.spawn(func, name, envars, args, tgid)
|
|
||||||
local id = nextpid
|
|
||||||
nextpid = nextpid + 1
|
|
||||||
|
|
||||||
tasks[tostring(id)] = {
|
|
||||||
coro = coroutine.create(function()
|
|
||||||
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
|
|
||||||
if not ok then
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
kernel.log(
|
|
||||||
"Task " .. tostring(id) .. " exited with err: " ..
|
|
||||||
tostring(err), "ERROR", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(err) == "number" then
|
|
||||||
tasks[tostring(id)].exit = err
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
if err then
|
|
||||||
kernel.log("Task " .. tostring(id) ..
|
|
||||||
" exited with code: " .. tostring(err),
|
|
||||||
"INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Task " .. tostring(id) ..
|
|
||||||
" exited without code", "INFO")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(err) == "number" then
|
|
||||||
tasks[tostring(id)].exit = err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for v, _ in ipairs(tasks[tostring(id)].fd) do pcall(kernel.vfs.close,v) end
|
|
||||||
tasks[tostring(id)].status = "Z"
|
|
||||||
|
|
||||||
end),
|
|
||||||
name = name or ("task" .. tostring(id)),
|
|
||||||
envars = envars or kernel.currentTask.envars,
|
|
||||||
args = args or {},
|
|
||||||
status = "R",
|
|
||||||
pid = id,
|
|
||||||
tgid = tgid or kernel.currentTask.tgid,
|
|
||||||
uid = kernel.uid,
|
|
||||||
fd = {},
|
|
||||||
sleep = 0,
|
|
||||||
ivs = 0,
|
|
||||||
vs = 0,
|
|
||||||
children = {},
|
|
||||||
parent = kernel.currentTask,
|
|
||||||
siblings = kernel.currentTask.children,
|
|
||||||
syscallReturn = {},
|
|
||||||
cwd = kernel.currentTask.cwd,
|
|
||||||
timeSlice = 0,
|
|
||||||
lastTime = 0,
|
|
||||||
totalTime = 0,
|
|
||||||
numRuns = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(kernel.currentTask.children, tasks[tostring(id)])
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.sleep(s)
|
|
||||||
kernel.currentTask.status = "S"
|
|
||||||
kernel.currentTask.sleep = kernel.computer:time() + s * 1000
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getTask(pid)
|
|
||||||
if tasks[tostring(pid)] then
|
|
||||||
local task = tasks[tostring(pid)]
|
|
||||||
local children = {}
|
|
||||||
local siblings = {}
|
|
||||||
|
|
||||||
for i, v in ipairs(task.children) do children[i] = v.pid end
|
|
||||||
for i, v in ipairs(task.siblings) do siblings[i] = v.pid end
|
|
||||||
|
|
||||||
return {
|
|
||||||
name = task.name,
|
|
||||||
status = task.status,
|
|
||||||
pid = task.pid,
|
|
||||||
tgid = task.tgid,
|
|
||||||
username = kernel.users[task.uid],
|
|
||||||
uid = task.uid,
|
|
||||||
exit = task.exit,
|
|
||||||
sleep = task.sleep,
|
|
||||||
ivs = task.ivs,
|
|
||||||
vs = task.vs,
|
|
||||||
children = children,
|
|
||||||
siblings = siblings,
|
|
||||||
parent = task.parent.pid,
|
|
||||||
cwd = task.cwd,
|
|
||||||
term = task.term
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.collect(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status ~= "Z" then
|
|
||||||
return false, "Task must exit to collect status"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].reapTime = 0
|
|
||||||
return true, tasks[tostring(pid)].exit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.kill(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status == "Z" then
|
|
||||||
return false, "Task is already dead"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status = "Z"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.stop(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status ~= "R" then
|
|
||||||
return false, "Cannot stop non running task"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status = "T"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.continue(pid)
|
|
||||||
local children = {}
|
|
||||||
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
|
|
||||||
elseif tasks[tostring(pid)].status ~= "T" then
|
|
||||||
return false, "Task is not stopped"
|
|
||||||
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status = "R"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getpid() return kernel.currentTask.pid end
|
|
||||||
|
|
||||||
function sys.getppid() return kernel.currentTask.parent.pid end
|
|
||||||
|
|
||||||
function sys.getTasks()
|
|
||||||
local ret = {}
|
|
||||||
for i, v in pairs(tasks) do ret[#ret + 1] = v.pid end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getEnviron(key) return kernel.currentTask.envars[key] end
|
|
||||||
|
|
||||||
function sys.setEnviron(key, value) kernel.currentTask.envars[key] = value end
|
|
||||||
|
|
||||||
function sys.exit(code)
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
if code then
|
|
||||||
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited with code: " .. tostring(code), "INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited without code", "INFO")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tasks[tostring(kernel.currentTask.pid)].status = "Z"
|
|
||||||
if type(code) == "number" then
|
|
||||||
tasks[tostring(kernel.currentTask.pid)].exit = code
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.setuid(uid)
|
|
||||||
if kernel.uid ~= 0 then error("EACCES") end
|
|
||||||
kernel.currentTask.uid = uid
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getuid() return kernel.currentTask.uid end
|
|
||||||
|
|
||||||
local sysc = kernel.syscalls
|
|
||||||
sysc["spawn"] = sys.spawn
|
|
||||||
sysc["sleep"] = sys.sleep
|
|
||||||
sysc["getTask"] = sys.getTask
|
|
||||||
sysc["collect"] = sys.collect
|
|
||||||
sysc["kill"] = sys.kill
|
|
||||||
sysc["stop"] = sys.stop
|
|
||||||
sysc["continue"] = sys.continue
|
|
||||||
sysc["getpid"] = sys.getpid
|
|
||||||
sysc["getppid"] = sys.getppid
|
|
||||||
sysc["getTasks"] = sys.getTasks
|
|
||||||
sysc["setEnviron"] = sys.setEnviron
|
|
||||||
sysc["getEnviron"] = sys.getEnviron
|
|
||||||
sysc["exit"] = sys.exit
|
|
||||||
sysc["setuid"] = sys.setuid
|
|
||||||
sysc["getuid"] = sys.getuid
|
|
||||||
kernel._G.sleep = function(...) coroutine.yield("syscall", "sleep", ...) end
|
|
||||||
|
|
||||||
local function reapDeadTasks()
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.status == "Z" and not task.reapTime then
|
|
||||||
kernel.currentTask = task
|
|
||||||
kernel.uid = task.uid
|
|
||||||
kernel.process = task.name
|
|
||||||
task.coro = nil
|
|
||||||
task.ivs = nil
|
|
||||||
task.vs = nil
|
|
||||||
task.args = nil
|
|
||||||
task.envars = nil
|
|
||||||
task.cwd = nil
|
|
||||||
task.numRuns = nil
|
|
||||||
task.totalTime = nil
|
|
||||||
task.lastTime = nil
|
|
||||||
task.timeSlice = nil
|
|
||||||
task.syscallReturn = nil
|
|
||||||
task.sleep = nil
|
|
||||||
task.fd = nil
|
|
||||||
task.reapTime = kernel.computer:time() + 30000
|
|
||||||
|
|
||||||
elseif task.reapTime and kernel.computer:time() > task.reapTime and
|
|
||||||
task.status == "Z" then
|
|
||||||
for _, child in ipairs(task.children) do
|
|
||||||
child.parent = tasks["1"]
|
|
||||||
child.siblings = tasks["1"].children
|
|
||||||
table.insert(tasks["1"].children, child)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i, sibling in ipairs(task.siblings) do
|
|
||||||
if sibling.pid == task.pid then
|
|
||||||
table.remove(task.siblings, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tasks[pid] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local alpha = 0.85
|
|
||||||
local C_target = 0.01
|
|
||||||
local Tmin = 0.0005
|
|
||||||
local Tmax = 0.5
|
|
||||||
local lambda_budget = 0.08
|
|
||||||
local lambda_clamp = 0.03
|
|
||||||
local lambda_var = 0.02
|
|
||||||
local k_min = 0.5
|
|
||||||
local k_max = 0.5
|
|
||||||
local B = 0.01
|
|
||||||
|
|
||||||
function kernel.main()
|
|
||||||
while not kernel.exitMain do
|
|
||||||
local N = 0
|
|
||||||
local Tmin_hit = 0
|
|
||||||
local Tmax_hit = 0
|
|
||||||
local totalTaskTime = 0
|
|
||||||
local taskTimes = {}
|
|
||||||
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.status == "S" then
|
|
||||||
if kernel.computer:time() >= task.sleep then
|
|
||||||
task.status = "R"
|
|
||||||
task.sleep = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if task.status == "R" then
|
|
||||||
kernel.currentTask = task
|
|
||||||
kernel.uid = task.uid
|
|
||||||
kernel.process = task.name
|
|
||||||
N = N + 1
|
|
||||||
|
|
||||||
-- assign adaptive time slice
|
|
||||||
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
|
|
||||||
|
|
||||||
if task.sigq and #task.sigq~=0 and task.sigh then
|
|
||||||
local coro = coroutine.create(task.sigh)
|
|
||||||
if kernel.config.preempt then
|
|
||||||
coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
|
|
||||||
else
|
|
||||||
coroutine.resume(coro, table.remove(task.sigq, 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check for exit/stop
|
|
||||||
if task.status=="R" then
|
|
||||||
-- measure execution time
|
|
||||||
local startTime = kernel.computer:time()
|
|
||||||
local ret
|
|
||||||
if kernel.config.preempt then
|
|
||||||
ret = {
|
|
||||||
coroutine.resumeWithTimeout(
|
|
||||||
task.coro,
|
|
||||||
task.timeSlice,
|
|
||||||
table.unpack(task.syscallReturn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ret = {
|
|
||||||
coroutine.resume(
|
|
||||||
task.coro,
|
|
||||||
table.unpack(task.syscallReturn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local elapsed = kernel.computer:time() - startTime
|
|
||||||
task.lastTime = elapsed
|
|
||||||
task.totalTime = (task.totalTime or 0) + elapsed
|
|
||||||
task.numRuns = (task.numRuns or 0) + 1
|
|
||||||
|
|
||||||
taskTimes[#taskTimes + 1] = elapsed
|
|
||||||
totalTaskTime = totalTaskTime + elapsed
|
|
||||||
|
|
||||||
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
|
|
||||||
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
|
|
||||||
|
|
||||||
-- handle task results
|
|
||||||
if ret[1] == "error" or ret[1] == false then
|
|
||||||
kernel.log("processHandlerException: " .. ret[2], "ERROR", 2)
|
|
||||||
task.status = "Z"
|
|
||||||
task.exit = "processHandlerException: " .. ret[2]
|
|
||||||
|
|
||||||
elseif ret[1] == "timeout" then
|
|
||||||
task.ivs = task.ivs + 1
|
|
||||||
task.syscallReturn = {}
|
|
||||||
|
|
||||||
elseif ret[1] == "success" or ret[1] == true then
|
|
||||||
task.vs = task.vs + 1
|
|
||||||
|
|
||||||
if ret[2] == "syscall" then
|
|
||||||
if kernel.syscalls[ret[3]] then
|
|
||||||
if kernel.config.debugSyscalls then
|
|
||||||
kernel.log("Task " .. task.pid .. " invoking syscall: " .. ret[3], "DBUG", 5)
|
|
||||||
|
|
||||||
for i = 4, #ret do
|
|
||||||
kernel.log(" inval[" .. tostring(i - 3) .. "] = " .. tostring(ret[i]), "DBUG", 5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local sysret = {
|
|
||||||
xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))
|
|
||||||
}
|
|
||||||
|
|
||||||
if kernel.config.debugSyscalls then
|
|
||||||
if not sysret[1] then
|
|
||||||
kernel.log(
|
|
||||||
"Task " .. task.pid .. " syscall " .. ret[3] .. " failed: " .. tostring(sysret[2]), "ERROR", 2
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
|
||||||
kernel.log(
|
|
||||||
"Task " .. task.pid .. " syscall " .. ret[3] .. " completed returning " .. tostring(#sysret - 1) .. " values", "DBUG", 5
|
|
||||||
)
|
|
||||||
|
|
||||||
for i = 2, #sysret do
|
|
||||||
if type(sysret[i]) == "table" then
|
|
||||||
kernel.log(
|
|
||||||
" retval[" .. tostring(i - 1) .. "] = " .. table.serialize(sysret[i]),"DBUG", 5
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
|
||||||
kernel.log(
|
|
||||||
" retval[" .. tostring(i - 1) .. "] = " .. tostring(sysret[i]), "DBUG", 5
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not sysret[1] then
|
|
||||||
task.syscallReturn = {false, sysret[2]}
|
|
||||||
|
|
||||||
else
|
|
||||||
task.syscallReturn = {
|
|
||||||
true, table.unpack(sysret, 2)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
task.syscallReturn = {
|
|
||||||
false, "Unknown syscall: " .. tostring(ret[3])
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
|
|
||||||
local T_prev_var = 0
|
|
||||||
|
|
||||||
for _, t in ipairs(taskTimes) do
|
|
||||||
T_prev_var = T_prev_var + (t - T_prev_avg) ^ 2
|
|
||||||
end
|
|
||||||
if N > 0 then T_prev_var = T_prev_var / N end
|
|
||||||
|
|
||||||
if N > 0 then
|
|
||||||
local f_clamp = k_min * (Tmin_hit / N) - k_max * (Tmax_hit / N)
|
|
||||||
local B_budget = (C_target * (N ^ (alpha - 1))) /
|
|
||||||
math.max(T_prev_avg, 1e-8)
|
|
||||||
B = B + lambda_budget * (B_budget - B) + lambda_clamp * f_clamp -
|
|
||||||
lambda_var * T_prev_var
|
|
||||||
end
|
|
||||||
|
|
||||||
-- clean up dead tasks
|
|
||||||
reapDeadTasks()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.tasks = tasks
|
|
||||||
kernel.hpv = sys
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
local debug=debug
|
|
||||||
kernel._G.debug={
|
|
||||||
getinfo=debug.getinfo,
|
|
||||||
traceback=debug.traceback
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
local kernel=...
|
|
||||||
local sysc=kernel.syscalls
|
|
||||||
kernel.gpio={}
|
|
||||||
|
|
||||||
sysc["gpio_write"]=function(pin, data)
|
|
||||||
if kernel.gpio[pin] then
|
|
||||||
return kernel.gpio[pin]("w", data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
sysc["gpio_read"]=function(pin)
|
|
||||||
if kernel.gpio[pin] then
|
|
||||||
return kernel.gpio[pin]("r")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
kernel.log("Loading init system...")
|
|
||||||
kernel.log("InitPath: " .. kernel.config.initPath)
|
|
||||||
|
|
||||||
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
|
||||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
|
|
||||||
if not initFunc then error("Failed to load init system: " .. err) end
|
|
||||||
|
|
||||||
kernel.tasks["1"] = {
|
|
||||||
coro = coroutine.create(function()
|
|
||||||
local ok, err = xpcall(initFunc, debug.traceback, kernel)
|
|
||||||
if not ok then
|
|
||||||
kernel.panic("Init system crashed: " .. tostring(err))
|
|
||||||
else
|
|
||||||
kernel.panic("Init system exited: " .. tostring(err))
|
|
||||||
end
|
|
||||||
end),
|
|
||||||
|
|
||||||
name = "sysinit",
|
|
||||||
status = "R",
|
|
||||||
pid = 1,
|
|
||||||
tgid = 1,
|
|
||||||
uid = 0,
|
|
||||||
fd = {},
|
|
||||||
envars = {},
|
|
||||||
args = {},
|
|
||||||
exit = "",
|
|
||||||
sleep = 0,
|
|
||||||
ivs = 0,
|
|
||||||
vs = 0,
|
|
||||||
parent = kernel.kernelTask,
|
|
||||||
siblings = kernel.kernelTask.children,
|
|
||||||
children = {},
|
|
||||||
syscallReturn = {},
|
|
||||||
cwd = "/",
|
|
||||||
timeSlice = 0,
|
|
||||||
lastTime = 0,
|
|
||||||
totalTime = 0,
|
|
||||||
numRuns = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel.log("created init task with PID 1")
|
|
||||||
kernel.log("Initializing init system...")
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
-- It runs at uid 0 so it can call setuid() to drop privileges to the logged in user
|
|
||||||
kernel.processes.login = function()
|
|
||||||
local handle = kernel.vfs.open("/bin/login", "r")
|
|
||||||
local text = kernel.vfs.read(handle, 1024 * 1024)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
local fn, err = load(text, "@/bin/login", "t", kernel._U)
|
|
||||||
if not fn then
|
|
||||||
kernel.log("Failed to load /bin/login: " .. tostring(err), "ERROR", 2)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
fn()
|
|
||||||
end
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local bit32 = require("bit32")
|
|
||||||
local bor = bit32.bor
|
|
||||||
local lshift = bit32.lshift
|
|
||||||
|
|
||||||
-- bit 0 = everyone-write, bit 1 = everyone-read
|
|
||||||
-- bit 2 = group-write, bit 3 = group-read
|
|
||||||
-- bit 4 = owner-write, bit 5 = owner-read
|
|
||||||
-- bit 6 = suid
|
|
||||||
local P_OWNER_R = lshift(1, 5)
|
|
||||||
local P_OWNER_W = lshift(1, 4)
|
|
||||||
local P_GROUP_R = lshift(1, 3)
|
|
||||||
local P_GROUP_W = lshift(1, 2)
|
|
||||||
local P_WORLD_R = lshift(1, 1)
|
|
||||||
local P_WORLD_W = lshift(1, 0)
|
|
||||||
local P_SUID = lshift(1, 6)
|
|
||||||
|
|
||||||
local RW_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 644 / rw-r--r--
|
|
||||||
local RWX_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 755 / rwxr--r--
|
|
||||||
local RW_R__ = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R) -- 640 / rw-r-----
|
|
||||||
local RW____ = bor(P_OWNER_R, P_OWNER_W) -- 600 / rw-------
|
|
||||||
local SUID_755 = bor(P_SUID, P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 4755
|
|
||||||
|
|
||||||
local function metaEntry(name, owner, group, perms)
|
|
||||||
return string.char(#name) .. name
|
|
||||||
.. string.char(owner, group, perms)
|
|
||||||
.. string.char(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
local rootDisk = kernel.disks["$"]
|
|
||||||
|
|
||||||
local function writeMeta(dir, entries)
|
|
||||||
local diskDir = dir == "/" and "/" or dir
|
|
||||||
local path = (diskDir:sub(-1) == "/" and diskDir or diskDir .. "/") .. ".meta"
|
|
||||||
if path:sub(1,1) == "/" then path = path:sub(2) end
|
|
||||||
if path == "" then path = ".meta" end
|
|
||||||
|
|
||||||
local data = ""
|
|
||||||
for _, e in ipairs(entries) do
|
|
||||||
data = data .. metaEntry(e[1], e[2], e[3], e[4])
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, err = pcall(function()
|
|
||||||
local f = rootDisk:open(path, "w")
|
|
||||||
f.write(data)
|
|
||||||
f.close()
|
|
||||||
end)
|
|
||||||
if not ok then
|
|
||||||
kernel.log("permissions: failed to write /" .. path .. ": " .. tostring(err), "WARN", 8)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if rootDisk:fileExists(".meta") then
|
|
||||||
kernel.log("Permissions already seeded, skipping.", "INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Seeding filesystem permissions...", "INFO")
|
|
||||||
|
|
||||||
writeMeta("/", {
|
|
||||||
{"bin", 0, 0, RWX_R_R},
|
|
||||||
{"boot", 0, 0, RWX_R_R},
|
|
||||||
{"dev", 0, 0, RWX_R_R},
|
|
||||||
{"etc", 0, 0, RWX_R_R},
|
|
||||||
{"home", 0, 0, RWX_R_R},
|
|
||||||
{"lib", 0, 0, RWX_R_R},
|
|
||||||
{"root", 0, 0, RW____ },
|
|
||||||
{"sbin", 0, 0, RWX_R_R},
|
|
||||||
{"tmp", 0, 0, bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_GROUP_W, P_WORLD_R, P_WORLD_W)},
|
|
||||||
{"usr", 0, 0, RWX_R_R},
|
|
||||||
{"var", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/bin", {
|
|
||||||
{"cat", 0, 0, RWX_R_R},
|
|
||||||
{"clear", 0, 0, RWX_R_R},
|
|
||||||
{"echo", 0, 0, RWX_R_R},
|
|
||||||
{"hfetch", 0, 0, RWX_R_R},
|
|
||||||
{"hysh", 0, 0, RWX_R_R},
|
|
||||||
{"hyshex", 0, 0, RWX_R_R},
|
|
||||||
{"install", 0, 0, RWX_R_R},
|
|
||||||
{"login", 0, 0, SUID_755},
|
|
||||||
{"ls", 0, 0, RWX_R_R},
|
|
||||||
{"lua", 0, 0, RWX_R_R},
|
|
||||||
{"luaold", 0, 0, RWX_R_R},
|
|
||||||
{"mkdir", 0, 0, RWX_R_R},
|
|
||||||
{"ps", 0, 0, RWX_R_R},
|
|
||||||
{"pwd", 0, 0, RWX_R_R},
|
|
||||||
{"spm", 0, 0, RWX_R_R},
|
|
||||||
{"su", 0, 0, SUID_755},
|
|
||||||
{"sudo", 0, 0, SUID_755},
|
|
||||||
{"sysdump", 0, 0, RWX_R_R},
|
|
||||||
{"whoami", 0, 0, RWX_R_R},
|
|
||||||
{"yes", 0, 0, RWX_R_R},
|
|
||||||
{"startup", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/bin/startup", {
|
|
||||||
{"test.lua", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/etc", {
|
|
||||||
{"passwd", 0, 0, RW_R_R},
|
|
||||||
{"shadow", 0, 0, RW____ },
|
|
||||||
{"pam.d", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/etc/pam.d", {
|
|
||||||
{"secret", 0, 0, RW____},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/sbin", {
|
|
||||||
{"init.lua", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/boot", {
|
|
||||||
{"kernel.lua", 0, 0, RW_R_R},
|
|
||||||
{"boot.cfg", 0, 0, RW_R_R},
|
|
||||||
{"safeboot.cfg", 0, 0, RW_R_R},
|
|
||||||
{"fstab", 0, 0, RW_R_R},
|
|
||||||
{"initfs", 0, 0, RW_R_R},
|
|
||||||
{"cct", 0, 0, RWX_R_R},
|
|
||||||
{"oc", 0, 0, RWX_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
writeMeta("/lib", {
|
|
||||||
{"sys", 0, 0, RWX_R_R},
|
|
||||||
{"modules", 0, 0, RWX_R_R},
|
|
||||||
{"crypto", 0, 0, RWX_R_R},
|
|
||||||
{"store", 0, 0, RWX_R_R},
|
|
||||||
{"snip", 0, 0, RW_R_R},
|
|
||||||
{"io", 0, 0, RW_R_R},
|
|
||||||
{"bit32", 0, 0, RW_R_R},
|
|
||||||
})
|
|
||||||
|
|
||||||
kernel.log("Filesystem permissions seeded.", "INFO")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: move this to vfs.kmod
|
|
||||||
local _orig_open = kernel.vfs.open
|
|
||||||
kernel.vfs.open = function(path, mode)
|
|
||||||
local fd = _orig_open(path, mode)
|
|
||||||
if mode == "r" then
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local fobj = task.fd[fd]
|
|
||||||
if fobj and fobj.meta then
|
|
||||||
local suid_set = bit32.extract(fobj.meta.perms, 6) == 1
|
|
||||||
if suid_set then
|
|
||||||
fobj.suid_owner = fobj.meta.owner
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls["fget_suid"] = function(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local fobj = task and task.fd[fd]
|
|
||||||
if fobj and fobj.suid_owner then
|
|
||||||
return fobj.suid_owner
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
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
|
||||||
1119
Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod
Normal file
1119
Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user