forked from Hyperion/HyperionOS
Compare commits
6 Commits
5b8043b6ec
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 93c3bab263 | |||
| 5bd16b8fd4 | |||
| 3cc1459769 | |||
| 0655f2a39e | |||
| 57b1d46837 | |||
| 875759022a |
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
|
||||||
@@ -4,6 +4,12 @@ HyperionOS is a modular, hybrid kernel operating system written entirely in Lua.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 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,7 +1,7 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
syscall.open("/dev/tty/TTY1","r") --stdin (fd 0)
|
syscall.open("/dev/tty/TTY1", "r") --stdin (fd 0)
|
||||||
syscall.open("/dev/tty/TTY1","w") --stdout (fd 1)
|
syscall.open("/dev/tty/TTY1", "w") --stdout (fd 1)
|
||||||
syscall.open("/dev/null","w") --stderr (fd 2)
|
syscall.open("/dev/null", "w") --stderr (fd 2)
|
||||||
|
|
||||||
local fs = require("sys.fs")
|
local fs = require("sys.fs")
|
||||||
|
|
||||||
@@ -78,21 +78,18 @@ local function spawnShell(username, uid, shell, homedir)
|
|||||||
return false
|
return false
|
||||||
end
|
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 errFifo = {}
|
||||||
|
|
||||||
local proc = syscall.spawn(function()
|
local proc = syscall.spawn(function()
|
||||||
syscall.setuid(uid)
|
syscall.setuid(uid)
|
||||||
syscall.chdir(homedir)
|
syscall.chdir(homedir)
|
||||||
syscall.setEnviron("HOME", homedir)
|
syscall.setEnviron("HOME", homedir)
|
||||||
syscall.setEnviron("USER", username)
|
syscall.setEnviron("USER", username)
|
||||||
syscall.setEnviron("SHELL", shell)
|
syscall.setEnviron("SHELL", shell)
|
||||||
syscall.setEnviron("PATH", "/bin/")
|
syscall.setEnviron("PATH", "/bin/")
|
||||||
|
|
||||||
local shellFn, loadErr = load(shellText, "@" .. shell)
|
local shellFn, loadErr = load(shellText, "@" .. shell)
|
||||||
if not shellFn then
|
if not shellFn then
|
||||||
-- Report load error via log and a recognizable exit code
|
|
||||||
syscall.log("login: shell load error: " .. tostring(loadErr), "ERROR")
|
syscall.log("login: shell load error: " .. tostring(loadErr), "ERROR")
|
||||||
syscall.exit(-1)
|
syscall.exit(-1)
|
||||||
return
|
return
|
||||||
@@ -103,21 +100,7 @@ local function spawnShell(username, uid, shell, homedir)
|
|||||||
syscall.log("login: shell runtime error: " .. tostring(runErr), "ERROR")
|
syscall.log("login: shell runtime error: " .. tostring(runErr), "ERROR")
|
||||||
end
|
end
|
||||||
end, username .. ":shell")
|
end, username .. ":shell")
|
||||||
|
syscall.exit(0)
|
||||||
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
|
end
|
||||||
|
|
||||||
local function doLogin()
|
local function doLogin()
|
||||||
@@ -145,7 +128,7 @@ local function doLogin()
|
|||||||
if ok then
|
if ok then
|
||||||
local uid = syscall.auth_getuid(username)
|
local uid = syscall.auth_getuid(username)
|
||||||
local pwent = uid and syscall.auth_getpasswd(uid)
|
local pwent = uid and syscall.auth_getpasswd(uid)
|
||||||
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||||
local homedir = (pwent and pwent.homedir) or "/"
|
local homedir = (pwent and pwent.homedir) or "/"
|
||||||
|
|
||||||
syscall.devctl(1, "sfgc", 3)
|
syscall.devctl(1, "sfgc", 3)
|
||||||
|
|||||||
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,7 +1,6 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local fs = require("sys.fs")
|
local fs = require("sys.fs")
|
||||||
|
local targetUser = ({ ... })[1] or "root"
|
||||||
local targetUser = ({...})[1] or "root"
|
|
||||||
|
|
||||||
local currentUid = syscall.getuid()
|
local currentUid = syscall.getuid()
|
||||||
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||||
@@ -26,7 +25,9 @@ else
|
|||||||
syscall.write(1, "\n")
|
syscall.write(1, "\n")
|
||||||
break
|
break
|
||||||
elseif ch == "\b" then
|
elseif ch == "\b" then
|
||||||
if #pw > 0 then pw = pw:sub(1, -2); syscall.write(1, "\b \b") end
|
if #pw > 0 then
|
||||||
|
pw = pw:sub(1, -2); syscall.write(1, "\b \b")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
pw = pw .. ch
|
pw = pw .. ch
|
||||||
syscall.write(1, "*")
|
syscall.write(1, "*")
|
||||||
@@ -46,13 +47,13 @@ if currentUid == 0 then
|
|||||||
syscall.setuid(targetUid)
|
syscall.setuid(targetUid)
|
||||||
end
|
end
|
||||||
|
|
||||||
local pwent = syscall.auth_getpasswd(targetUid)
|
local pwent = syscall.auth_getpasswd(targetUid)
|
||||||
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||||
local homedir = (pwent and pwent.homedir) or "/"
|
local homedir = (pwent and pwent.homedir) or "/"
|
||||||
|
|
||||||
syscall.chdir(homedir)
|
syscall.chdir(homedir)
|
||||||
syscall.setEnviron("HOME", homedir)
|
syscall.setEnviron("HOME", homedir)
|
||||||
syscall.setEnviron("USER", targetUser)
|
syscall.setEnviron("USER", targetUser)
|
||||||
syscall.setEnviron("SHELL", shell)
|
syscall.setEnviron("SHELL", shell)
|
||||||
|
|
||||||
local shellText = fs.readAllText(shell)
|
local shellText = fs.readAllText(shell)
|
||||||
@@ -63,10 +64,17 @@ if not shellText then
|
|||||||
end
|
end
|
||||||
|
|
||||||
local shellFn, loadErr = load(shellText, "@" .. shell)
|
local shellFn, loadErr = load(shellText, "@" .. shell)
|
||||||
|
|
||||||
if not shellFn then
|
if not shellFn then
|
||||||
print("su: cannot load shell: " .. tostring(loadErr))
|
print("su: cannot load shell: " .. tostring(loadErr))
|
||||||
syscall.exit(1)
|
syscall.exit(1)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
shellFn()
|
local success, err = syscall.kill(syscall.getppid())
|
||||||
|
if success then
|
||||||
|
syscall.spawn(shellFn, targetUser .. ":" .. shell, syscall.getEnviron())
|
||||||
|
syscall.exit(0)
|
||||||
|
else
|
||||||
|
print("su: "..err)
|
||||||
|
end
|
||||||
@@ -1 +1 @@
|
|||||||
print((syscall.auth_whoami() or "Unknown"))
|
print((syscall.getUsername() or "Unknown"))
|
||||||
Binary file not shown.
@@ -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)
|
|
||||||
@@ -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,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,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,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,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")
|
|
||||||
Binary file not shown.
@@ -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
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +0,0 @@
|
|||||||
-- DO NOT EDIT THIS FILE IF YOU DO NOT KNOW WHAT YOU ARE DOING!
|
|
||||||
-- DOING SO MAY RENDER YOUR SYSTEM UNBOOTABLE!
|
|
||||||
|
|
||||||
-- This file is auto-generated during the build process.
|
|
||||||
-- DEFAULT BOOT CONFIGURATION FILE
|
|
||||||
return {
|
|
||||||
initPath = "/sbin/init.lua",
|
|
||||||
maxOpenFiles = 128,
|
|
||||||
maxFilesPerTask = 16,
|
|
||||||
preempt=true
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
-- :Minify:--
|
|
||||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
|
||||||
---@diagnostic disable-next-line: undefined-global
|
|
||||||
local term = term
|
|
||||||
local os = os
|
|
||||||
local function write(text)
|
|
||||||
local x, y = term.getCursorPos()
|
|
||||||
local w, h = term.getSize()
|
|
||||||
|
|
||||||
for i = 1, #text do
|
|
||||||
local c = text:sub(i, i)
|
|
||||||
|
|
||||||
if c == "\n" then
|
|
||||||
y = y + 1
|
|
||||||
x = 1
|
|
||||||
elseif c == "\t" then
|
|
||||||
local tabSize = 4
|
|
||||||
local spaces = tabSize - ((x - 1) % tabSize)
|
|
||||||
term.write(string.rep(" ", spaces))
|
|
||||||
x = x + spaces
|
|
||||||
elseif c == "\b" then
|
|
||||||
if x > 1 then
|
|
||||||
x = x - 1
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
term.write(" ")
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if x <= w and y <= h then
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
term.write(c)
|
|
||||||
x = x + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if x > w then
|
|
||||||
x = 1
|
|
||||||
y = y + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if y - 1 >= h then
|
|
||||||
term.scroll(1)
|
|
||||||
y = h
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function displaySuperBadError(err)
|
|
||||||
term.setBackgroundColor(0x1)
|
|
||||||
term.setTextColor(0x4)
|
|
||||||
term.clear()
|
|
||||||
term.setCursorPos(1, 1)
|
|
||||||
term.write("A critical error occurred while loading the system:")
|
|
||||||
term.setCursorPos(1, 3)
|
|
||||||
write(err)
|
|
||||||
while true do end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setCursorBlink(false)
|
|
||||||
local ok, err = xpcall(function()
|
|
||||||
local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH}
|
|
||||||
|
|
||||||
local lua = {
|
|
||||||
coroutine = true,
|
|
||||||
debug = true,
|
|
||||||
_VERSION = true,
|
|
||||||
assert = true,
|
|
||||||
collectgarbage = true,
|
|
||||||
error = true,
|
|
||||||
gcinfo = true,
|
|
||||||
getfenv = true,
|
|
||||||
getmetatable = true,
|
|
||||||
ipairs = true,
|
|
||||||
__inext = true,
|
|
||||||
load = true,
|
|
||||||
math = true,
|
|
||||||
next = true,
|
|
||||||
pairs = true,
|
|
||||||
pcall = true,
|
|
||||||
rawequal = true,
|
|
||||||
rawget = true,
|
|
||||||
rawlen = true,
|
|
||||||
rawset = true,
|
|
||||||
select = true,
|
|
||||||
setfenv = true,
|
|
||||||
setmetatable = true,
|
|
||||||
string = true,
|
|
||||||
table = true,
|
|
||||||
tonumber = true,
|
|
||||||
tostring = true,
|
|
||||||
type = true,
|
|
||||||
xpcall = true,
|
|
||||||
_G = true
|
|
||||||
}
|
|
||||||
|
|
||||||
local debug = debug
|
|
||||||
for i, v in pairs(_G) do
|
|
||||||
if not lua[i] or lua[i] == nil then
|
|
||||||
apis[i] = v
|
|
||||||
_G[i] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local acekeys={
|
|
||||||
[apis.keys.enter]="\n",
|
|
||||||
[apis.keys.tab]="\t",
|
|
||||||
[apis.keys.backspace]="\b",
|
|
||||||
[apis.keys.up]="\17",
|
|
||||||
[apis.keys.down]="\18",
|
|
||||||
[apis.keys.left]="\19",
|
|
||||||
[apis.keys.right]="\20",
|
|
||||||
}
|
|
||||||
|
|
||||||
function sleep(time)
|
|
||||||
local stoptime = apis.os.clock() + (time)
|
|
||||||
while stoptime > apis.os.clock() do end
|
|
||||||
end
|
|
||||||
|
|
||||||
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
|
||||||
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
|
||||||
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
|
||||||
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
|
||||||
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
|
||||||
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
|
||||||
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
|
||||||
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
|
||||||
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
|
||||||
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
|
||||||
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
|
||||||
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
|
||||||
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
|
||||||
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
|
||||||
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
|
||||||
apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
|
||||||
|
|
||||||
local function getFile(path)
|
|
||||||
local file = apis.fs.open(path, "r")
|
|
||||||
if not file then
|
|
||||||
displaySuperBadError("Could not open file: " .. path)
|
|
||||||
end
|
|
||||||
local content = file.readAll()
|
|
||||||
file.close()
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel")
|
|
||||||
local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis)
|
|
||||||
local fs = load(getFile(BOOT_DRIVE_PATH .. "/boot/initfs"), "@InitFs")()
|
|
||||||
|
|
||||||
if not Kernel then displaySuperBadError("Could not load kernel.") end
|
|
||||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
|
||||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
|
||||||
|
|
||||||
local eventQueue = {}
|
|
||||||
|
|
||||||
local function queueEvent(event, ...)
|
|
||||||
table.insert(eventQueue, {event, ...})
|
|
||||||
end
|
|
||||||
|
|
||||||
local computer = {
|
|
||||||
time = function() return apis.os.epoch("utc") end,
|
|
||||||
clock = function() return apis.os.clock() * 1000 end,
|
|
||||||
shutdown = apis.os.shutdown,
|
|
||||||
reboot = apis.os.reboot,
|
|
||||||
getMachineEvent = function()
|
|
||||||
if #eventQueue > 0 then
|
|
||||||
return table.unpack(table.remove(eventQueue, 1))
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
getEEPROM = function() return getFile("/startup.lua") end,
|
|
||||||
setEEPROM = function(_, text)
|
|
||||||
local h = apis.fs.open("/startup.lua", "w")
|
|
||||||
h.write(text)
|
|
||||||
h.close()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local icolors = {
|
|
||||||
[0x1] = 1, -- #000000
|
|
||||||
[0x2] = 2, -- #FFFFFF
|
|
||||||
[0x4] = 3, -- #FF0000
|
|
||||||
[0x8] = 4, -- #00FF00
|
|
||||||
[0x10] = 5, -- #0000FF
|
|
||||||
[0x20] = 6, -- #00FFFF
|
|
||||||
[0x40] = 7, -- #FF00FF
|
|
||||||
[0x80] = 8, -- #FFFF00
|
|
||||||
[0x100] = 9, -- #FF6D00
|
|
||||||
[0x200] = 10, -- #6DFF55
|
|
||||||
[0x400] = 11, -- #24FFFF
|
|
||||||
[0x800] = 12, -- #924900
|
|
||||||
[0x1000] = 13, -- #6D6D55
|
|
||||||
[0x2000] = 14, -- #DBDBAA
|
|
||||||
[0x4000] = 15, -- #6D00FF
|
|
||||||
[0x8000] = 16 -- #B6FF00
|
|
||||||
}
|
|
||||||
|
|
||||||
local colors = {
|
|
||||||
0x0001, -- #000000
|
|
||||||
0x0002, -- #FFFFFF
|
|
||||||
0x0004, -- #FF0000
|
|
||||||
0x0008, -- #00FF00
|
|
||||||
0x0010, -- #0000FF
|
|
||||||
0x0020, -- #00FFFF
|
|
||||||
0x0040, -- #FF00FF
|
|
||||||
0x0080, -- #FFFF00
|
|
||||||
0x0100, -- #FF6D00
|
|
||||||
0x0200, -- #6DFF55
|
|
||||||
0x0400, -- #24FFFF
|
|
||||||
0x0800, -- #924900
|
|
||||||
0x1000, -- #6D6D55
|
|
||||||
0x2000, -- #DBDBAA
|
|
||||||
0x4000, -- #6D00FF
|
|
||||||
0x8000 -- #B6FF00
|
|
||||||
}
|
|
||||||
|
|
||||||
apis.term.setBackgroundColor(0x8000)
|
|
||||||
apis.term.setTextColor(0x1000)
|
|
||||||
apis.term.clear()
|
|
||||||
apis.term.setCursorPos(1, 1)
|
|
||||||
|
|
||||||
local kernelCoro = coroutine.create(function()
|
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
|
||||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
|
||||||
{
|
|
||||||
print = function(_, text) write(text .. "\n") end,
|
|
||||||
printInline = function(_, text) write(text) end,
|
|
||||||
clear = function()
|
|
||||||
apis.term.clear()
|
|
||||||
apis.term.setCursorPos(1, 1)
|
|
||||||
end,
|
|
||||||
setCursorPos = function(_, x, y)
|
|
||||||
apis.term.setCursorPos(x, y)
|
|
||||||
end,
|
|
||||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
|
||||||
getSize = function() return apis.term.getSize() end,
|
|
||||||
setBackgroundColor = function(_, color)
|
|
||||||
apis.term.setBackgroundColor(colors[color])
|
|
||||||
end,
|
|
||||||
setTextColor = function(_, color)
|
|
||||||
apis.term.setTextColor(colors[color])
|
|
||||||
end,
|
|
||||||
getBackgroundColor = function()
|
|
||||||
return icolors[apis.term.getBackgroundColor()]
|
|
||||||
end,
|
|
||||||
getTextColor = function()
|
|
||||||
return icolors[apis.term.getTextColor()]
|
|
||||||
end
|
|
||||||
}, computer, fs, "$")
|
|
||||||
if not ok then displaySuperBadError(err) end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- time is in milliseconds
|
|
||||||
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] == "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
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
|
||||||
-- UnBIOS by JackMacWindows
|
|
||||||
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
|
||||||
-- To use, just place a `bios.lua` in the root of the drive, and run this program
|
|
||||||
-- Here's a list of things that are irreversibly changed:
|
|
||||||
-- * both `bit` and `bit32` are kept for compatibility
|
|
||||||
-- * string metatable blocking (on old versions of CC)
|
|
||||||
-- In addition, if `debug` is not available these things are also irreversibly changed:
|
|
||||||
-- * old Lua 5.1 `load` function (for loading from a function)
|
|
||||||
-- * `loadstring` prefixing (before CC:T 1.96.0)
|
|
||||||
-- * `http.request`
|
|
||||||
-- * `os.shutdown` and `os.reboot`
|
|
||||||
-- * `peripheral`
|
|
||||||
-- * `turtle.equip[Left|Right]`
|
|
||||||
-- Licensed under the MIT license
|
|
||||||
local args = {...}
|
|
||||||
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
|
||||||
local t = {}
|
|
||||||
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
|
||||||
for _,k in ipairs(t) do _G[k] = nil end
|
|
||||||
local native = _G.term.native()
|
|
||||||
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
|
|
||||||
_G.term = native
|
|
||||||
if _G.http then
|
|
||||||
_G.http.checkURL = _G.http.checkURLAsync
|
|
||||||
_G.http.websocket = _G.http.websocketAsync
|
|
||||||
end
|
|
||||||
if _G.commands then _G.commands = _G.commands.native end
|
|
||||||
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
|
||||||
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
|
||||||
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
|
||||||
-- Set up TLCO
|
|
||||||
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
|
||||||
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
|
||||||
-- empty placeholder to let it continue and return without throwing. This results
|
|
||||||
-- in the `pcall` returning successfully, preventing the error-displaying code
|
|
||||||
-- from running - essentially making it so that `os.shutdown` is called immediately
|
|
||||||
-- after the new BIOS exits.
|
|
||||||
--
|
|
||||||
-- From there, the setup code is placed in `term.native` since it's the first
|
|
||||||
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
|
|
||||||
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
|
|
||||||
-- allow it to be the last function called in the original BIOS, and returns.
|
|
||||||
-- From there execution continues, calling the `term.redirect` dummy, skipping
|
|
||||||
-- over the error-handling code (since `pcall` returned ok), and calling
|
|
||||||
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
|
|
||||||
-- called, which effectively makes it run as the main chunk.
|
|
||||||
local olderror = error
|
|
||||||
_G.error = function() end
|
|
||||||
_G.term.redirect = function() end
|
|
||||||
function _G.term.native()
|
|
||||||
_G.term.native = nil
|
|
||||||
_G.term.redirect = nil
|
|
||||||
_G.error = olderror
|
|
||||||
term.setBackgroundColor(32768)
|
|
||||||
term.setTextColor(1)
|
|
||||||
term.setCursorPos(1, 1)
|
|
||||||
term.setCursorBlink(true)
|
|
||||||
term.clear()
|
|
||||||
local file = fs.open(BOOT_DRIVE_PATH.."/boot/cct/boot.lua", "r")
|
|
||||||
if file == nil then
|
|
||||||
term.setCursorBlink(false)
|
|
||||||
term.setTextColor(16384)
|
|
||||||
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
|
|
||||||
term.setCursorPos(1, 2)
|
|
||||||
term.write("Press any key to continue")
|
|
||||||
coroutine.yield("key")
|
|
||||||
os.shutdown()
|
|
||||||
end
|
|
||||||
local fn, err = loadstring(file.readAll(), "@bootloader")
|
|
||||||
file.close()
|
|
||||||
if fn == nil then
|
|
||||||
term.setCursorBlink(false)
|
|
||||||
term.setTextColor(16384)
|
|
||||||
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
|
|
||||||
term.setCursorPos(1, 2)
|
|
||||||
term.write(err)
|
|
||||||
term.setCursorPos(1, 3)
|
|
||||||
term.write("Press any key to continue")
|
|
||||||
coroutine.yield("key")
|
|
||||||
os.shutdown()
|
|
||||||
end
|
|
||||||
setfenv(fn, _G)
|
|
||||||
local oldshutdown = os.shutdown
|
|
||||||
os.shutdown = function()
|
|
||||||
os.shutdown = oldshutdown
|
|
||||||
return fn(BOOT_DRIVE_PATH)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if debug then
|
|
||||||
-- Restore functions that were overwritten in the BIOS
|
|
||||||
-- Apparently this has to be done *after* redefining term.native
|
|
||||||
local function restoreValue(tab, idx, name, hint)
|
|
||||||
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
|
|
||||||
while key ~= name and key ~= nil do
|
|
||||||
key, value = debug.getupvalue(tab[idx], i)
|
|
||||||
i=i+1
|
|
||||||
end
|
|
||||||
tab[idx] = value or tab[idx]
|
|
||||||
end
|
|
||||||
restoreValue(_G, "loadstring", "nativeloadstring", 1)
|
|
||||||
restoreValue(_G, "load", "nativeload", 5)
|
|
||||||
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
|
|
||||||
restoreValue(os, "shutdown", "nativeShutdown", 1)
|
|
||||||
restoreValue(os, "reboot", "nativeReboot", 1)
|
|
||||||
if turtle then
|
|
||||||
restoreValue(turtle, "equipLeft", "v", 1)
|
|
||||||
restoreValue(turtle, "equipRight", "v", 1)
|
|
||||||
end
|
|
||||||
do
|
|
||||||
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
|
|
||||||
while key ~= "native" and key ~= nil do
|
|
||||||
key, value = debug.getupvalue(peripheral.isPresent, i)
|
|
||||||
i=i+1
|
|
||||||
end
|
|
||||||
_G.peripheral = value or peripheral
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -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}
|
|
||||||
@@ -7,7 +7,6 @@ local screen = args[5]
|
|||||||
local computer = args[6]
|
local computer = args[6]
|
||||||
local ifs = args[7]
|
local ifs = args[7]
|
||||||
local kernel = {}
|
local kernel = {}
|
||||||
|
|
||||||
kernel.LOG_Text=""
|
kernel.LOG_Text=""
|
||||||
kernel.version="HyperionOS V1.0.0"
|
kernel.version="HyperionOS V1.0.0"
|
||||||
kernel.process = "Kernel"
|
kernel.process = "Kernel"
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
local lua = {
|
|
||||||
coroutine = true,
|
|
||||||
debug = true,
|
|
||||||
_HOST = true,
|
|
||||||
_VERSION = true,
|
|
||||||
assert = true,
|
|
||||||
collectgarbage = true,
|
|
||||||
error = true,
|
|
||||||
gcinfo = true,
|
|
||||||
getfenv = true,
|
|
||||||
getmetatable = true,
|
|
||||||
ipairs = true,
|
|
||||||
__inext = true,
|
|
||||||
load = true,
|
|
||||||
math = true,
|
|
||||||
next = true,
|
|
||||||
pairs = true,
|
|
||||||
pcall = true,
|
|
||||||
rawequal = true,
|
|
||||||
rawget = true,
|
|
||||||
rawlen = true,
|
|
||||||
rawset = true,
|
|
||||||
select = true,
|
|
||||||
setfenv = true,
|
|
||||||
setmetatable = true,
|
|
||||||
string = true,
|
|
||||||
table = true,
|
|
||||||
tonumber = true,
|
|
||||||
tostring = true,
|
|
||||||
type = true,
|
|
||||||
xpcall = true,
|
|
||||||
_G=true
|
|
||||||
}
|
|
||||||
|
|
||||||
local apis={}
|
|
||||||
for i,v in pairs(_G) do
|
|
||||||
if not lua[i] or lua[i]==nil then
|
|
||||||
apis[i]=v
|
|
||||||
_G[i]=nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
checkArg=nil
|
|
||||||
local oldcomputer=computer
|
|
||||||
_G.computer=nil
|
|
||||||
local os=os
|
|
||||||
_G.os=nil
|
|
||||||
|
|
||||||
function component.wrap(address)
|
|
||||||
local methods=oldcomponent.methods(address)
|
|
||||||
local object={}
|
|
||||||
for _,method in ipairs(methods) do
|
|
||||||
object[method]=function(_,...)
|
|
||||||
return oldcomponent.invoke(address,method,...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return object
|
|
||||||
end
|
|
||||||
|
|
||||||
local
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
local fs={}
|
|
||||||
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
|
|
||||||
@@ -129,9 +129,6 @@ function sys.kill(pid)
|
|||||||
if not tasks[tostring(pid)] then
|
if not tasks[tostring(pid)] then
|
||||||
return false, "Task does not exist"
|
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
|
elseif tasks[tostring(pid)].status == "Z" then
|
||||||
return false, "Task is already dead"
|
return false, "Task is already dead"
|
||||||
|
|
||||||
@@ -148,9 +145,6 @@ function sys.stop(pid)
|
|||||||
if not tasks[tostring(pid)] then
|
if not tasks[tostring(pid)] then
|
||||||
return false, "Task does not exist"
|
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
|
elseif tasks[tostring(pid)].status ~= "R" then
|
||||||
return false, "Cannot stop non running task"
|
return false, "Cannot stop non running task"
|
||||||
|
|
||||||
@@ -166,9 +160,6 @@ function sys.continue(pid)
|
|||||||
if not tasks[tostring(pid)] then
|
if not tasks[tostring(pid)] then
|
||||||
return false, "Task does not exist"
|
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
|
elseif tasks[tostring(pid)].status ~= "T" then
|
||||||
return false, "Task is not stopped"
|
return false, "Task is not stopped"
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,11 @@ function print(...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function printf(fmt, ...)
|
function printf(fmt, ...)
|
||||||
coroutine.yield()
|
|
||||||
local output = string.format(fmt, ...)
|
local output = string.format(fmt, ...)
|
||||||
syscall.write(1, output.."\n")
|
syscall.write(1, output.."\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
function printInline(...)
|
function printInline(...)
|
||||||
coroutine.yield()
|
|
||||||
local args = {...}
|
local args = {...}
|
||||||
local output = ""
|
local output = ""
|
||||||
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
||||||
|
|||||||
154
build.py
Normal file
154
build.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Usage:
|
||||||
|
python build.py <target> [--arch cct|oc] [--release|--dev]
|
||||||
|
|
||||||
|
Targets:
|
||||||
|
build
|
||||||
|
build-mini
|
||||||
|
build-test
|
||||||
|
build-mini-test
|
||||||
|
clean
|
||||||
|
|
||||||
|
Arch flags:
|
||||||
|
--arch cct
|
||||||
|
--arch oc
|
||||||
|
|
||||||
|
Release flags:
|
||||||
|
--release
|
||||||
|
--dev
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent
|
||||||
|
SRC_ROOT = PROJECT_ROOT / "Src"
|
||||||
|
TEST_ROOT = PROJECT_ROOT / "Test"
|
||||||
|
BUILD_ROOT = PROJECT_ROOT / "Build"
|
||||||
|
|
||||||
|
ARCH_BOOT_DIR = {
|
||||||
|
"cct": Path("boot") / "cct",
|
||||||
|
"oc": Path("boot") / "oc",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def clean():
|
||||||
|
if BUILD_ROOT.exists():
|
||||||
|
print(f"Removing {BUILD_ROOT} ...")
|
||||||
|
shutil.rmtree(BUILD_ROOT)
|
||||||
|
else:
|
||||||
|
print("Nothing to clean.")
|
||||||
|
|
||||||
|
|
||||||
|
def process_root(src_root: Path, out_root: Path, minify: bool):
|
||||||
|
print(f"Building from {src_root}")
|
||||||
|
print(f"Output to {out_root}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for pkg_dir in sorted(src_root.iterdir()):
|
||||||
|
if not pkg_dir.is_dir():
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"== Package: {pkg_dir.name} ==")
|
||||||
|
|
||||||
|
for src in sorted(pkg_dir.rglob("*")):
|
||||||
|
if not src.is_file():
|
||||||
|
continue
|
||||||
|
|
||||||
|
rel = src.relative_to(pkg_dir)
|
||||||
|
dst = out_root / rel
|
||||||
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
print(f" Processing: {src.relative_to(src_root)}")
|
||||||
|
|
||||||
|
if minify and has_minify_header(src):
|
||||||
|
print(" > Minifying")
|
||||||
|
result = subprocess.run(
|
||||||
|
["luamin", "-f", str(src)],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f" ! luamin failed: {result.stderr.strip()}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
dst.write_text(result.stdout, encoding="utf-8")
|
||||||
|
else:
|
||||||
|
print(" > Copying")
|
||||||
|
shutil.copy2(src, dst)
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def install_bootloader(arch: str, release: bool):
|
||||||
|
boot_dir = BUILD_ROOT / "$" / ARCH_BOOT_DIR[arch]
|
||||||
|
boot_lua = boot_dir / "boot.lua"
|
||||||
|
eeprom = boot_dir / "eeprom"
|
||||||
|
|
||||||
|
for src in (boot_lua, eeprom):
|
||||||
|
if not src.exists():
|
||||||
|
print(f" ! Bootloader file not found: {src}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f" Installing: boot.lua -> Build/boot.lua")
|
||||||
|
shutil.copy2(boot_lua, BUILD_ROOT / "boot.lua")
|
||||||
|
|
||||||
|
eeprom_dst_name = "startup.lua" if release else "eeprom"
|
||||||
|
print(f" Installing: eeprom -> Build/{eeprom_dst_name}")
|
||||||
|
shutil.copy2(eeprom, BUILD_ROOT / eeprom_dst_name)
|
||||||
|
|
||||||
|
|
||||||
|
def has_minify_header(path: Path) -> bool:
|
||||||
|
try:
|
||||||
|
with path.open("r", encoding="utf-8", errors="ignore") as f:
|
||||||
|
for _ in range(3):
|
||||||
|
if "--:Minify:--" in f.readline():
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def run_build(minify: bool, include_test: bool, arch: str | None, release: bool):
|
||||||
|
clean()
|
||||||
|
BUILD_ROOT.mkdir()
|
||||||
|
|
||||||
|
out_root = BUILD_ROOT / "$" if arch else BUILD_ROOT
|
||||||
|
|
||||||
|
process_root(SRC_ROOT, out_root, minify)
|
||||||
|
if include_test:
|
||||||
|
process_root(TEST_ROOT, out_root, minify)
|
||||||
|
|
||||||
|
if arch:
|
||||||
|
print("Installing bootloader files ...")
|
||||||
|
install_bootloader(arch, release)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="HyperionOS build script")
|
||||||
|
parser.add_argument("target", choices=["build", "build-mini", "build-test", "build-mini-test", "clean"])
|
||||||
|
parser.add_argument("--arch", choices=["cct", "oc"], default=None,
|
||||||
|
help="Target architecture (cct or oc)")
|
||||||
|
parser.add_argument("--release", dest="release", action="store_true", default=True,
|
||||||
|
help="Release build: eeprom placed as startup.lua (default)")
|
||||||
|
parser.add_argument("--dev", dest="release", action="store_false",
|
||||||
|
help="Dev build: boot.lua and eeprom copied unchanged")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.target == "clean":
|
||||||
|
clean()
|
||||||
|
return
|
||||||
|
|
||||||
|
minify = "mini" in args.target
|
||||||
|
include_test = "test" in args.target
|
||||||
|
|
||||||
|
run_build(minify=minify, include_test=include_test, arch=args.arch, release=args.release)
|
||||||
|
print("Build complete.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
68
building.md
Normal file
68
building.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
## Building
|
||||||
|
|
||||||
|
### Linux / `make`-compatible systems
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional variables:
|
||||||
|
|
||||||
|
* **`ARCH=`**
|
||||||
|
|
||||||
|
* `cct` Build using the cct bootloader
|
||||||
|
* `oc` Build using the oc bootloader
|
||||||
|
|
||||||
|
* **`DEV=1`**
|
||||||
|
|
||||||
|
* Builds in development mode
|
||||||
|
* Bootloader does not start automatically on system startup
|
||||||
|
|
||||||
|
If `DEV` is not specified:
|
||||||
|
|
||||||
|
* Default is release mode
|
||||||
|
* Bootloader starts automatically on system startup
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build ARCH=cct
|
||||||
|
make build ARCH=oc DEV=1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Windows / Any system with Python 3.7+
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build.py build
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional arguments:
|
||||||
|
|
||||||
|
* **`--arch {cct|oc}`**
|
||||||
|
Select bootloader
|
||||||
|
|
||||||
|
* `cct` Use the cct bootloader
|
||||||
|
* `oc` Use the oc bootloader
|
||||||
|
|
||||||
|
* **`--dev`**
|
||||||
|
|
||||||
|
* Development mode
|
||||||
|
* Bootloader does not start automatically
|
||||||
|
|
||||||
|
* **`--release`** (default)
|
||||||
|
|
||||||
|
* Release mode
|
||||||
|
* Bootloader starts automatically
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build.py build --arch cct
|
||||||
|
python build.py build --arch oc --dev
|
||||||
|
```
|
||||||
@@ -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)
|
|
||||||
@@ -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,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,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,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,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,44 +0,0 @@
|
|||||||
$testRoot = Join-Path $PSScriptRoot "..\src"
|
|
||||||
$buildRoot = Join-Path $PSScriptRoot "..\Build"
|
|
||||||
|
|
||||||
$testRoot = Resolve-Path $testRoot
|
|
||||||
|
|
||||||
# Clean Build folder
|
|
||||||
if (Test-Path $buildRoot) {
|
|
||||||
Remove-Item -LiteralPath $buildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $buildRoot | Out-Null
|
|
||||||
|
|
||||||
Write-Host "Building from $testRoot"
|
|
||||||
Write-Host "Output to $buildRoot"
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Each top-level folder in test/
|
|
||||||
Get-ChildItem -Path $testRoot -Directory | ForEach-Object {
|
|
||||||
|
|
||||||
$folderRoot = $_.FullName
|
|
||||||
Write-Host "== Package: $($_.Name) =="
|
|
||||||
|
|
||||||
# Walk files inside this folder
|
|
||||||
Get-ChildItem -Path $folderRoot -File -Recurse | ForEach-Object {
|
|
||||||
|
|
||||||
$src = $_.FullName
|
|
||||||
$rel = $src.Substring($folderRoot.Length).TrimStart("\")
|
|
||||||
$dst = Join-Path $buildRoot $rel
|
|
||||||
$dstDir = Split-Path $dst
|
|
||||||
|
|
||||||
if (-not (Test-Path $dstDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $dstDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processing: $rel"
|
|
||||||
Write-Host " > Copying"
|
|
||||||
Copy-Item -LiteralPath $src -Destination $dst -Force
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Build complete."
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
TEST_ROOT="$(realpath "$SCRIPT_ROOT/../src")"
|
|
||||||
BUILD_ROOT="$SCRIPT_ROOT/../Build"
|
|
||||||
|
|
||||||
if [[ -d "$BUILD_ROOT" ]]; then
|
|
||||||
rm -rf "$BUILD_ROOT"
|
|
||||||
fi
|
|
||||||
mkdir -p "$BUILD_ROOT"
|
|
||||||
|
|
||||||
echo "Building from $TEST_ROOT"
|
|
||||||
echo "Output to $BUILD_ROOT"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for folder in "$TEST_ROOT"/*/; do
|
|
||||||
[[ -d "$folder" ]] || continue
|
|
||||||
|
|
||||||
folder_root="$folder"
|
|
||||||
package_name="$(basename "$folder_root")"
|
|
||||||
echo "== Package: $package_name =="
|
|
||||||
|
|
||||||
find "$folder_root" -type f | while IFS= read -r src; do
|
|
||||||
rel="${src#$folder_root}"
|
|
||||||
dst="$BUILD_ROOT/$rel"
|
|
||||||
dst_dir="$(dirname "$dst")"
|
|
||||||
|
|
||||||
if [[ ! -d "$dst_dir" ]]; then
|
|
||||||
mkdir -p "$dst_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Processing: $rel"
|
|
||||||
echo " > Copying"
|
|
||||||
cp -f "$src" "$dst"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Build complete."
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
$testRoot = Join-Path $PSScriptRoot "..\src"
|
|
||||||
$buildRoot = Join-Path $PSScriptRoot "..\Build"
|
|
||||||
|
|
||||||
$testRoot = Resolve-Path $testRoot
|
|
||||||
|
|
||||||
# Clean Build folder
|
|
||||||
if (Test-Path $buildRoot) {
|
|
||||||
Remove-Item -LiteralPath $buildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $buildRoot | Out-Null
|
|
||||||
|
|
||||||
Write-Host "Building from $testRoot"
|
|
||||||
Write-Host "Output to $buildRoot"
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Each top-level folder in test/
|
|
||||||
Get-ChildItem -Path $testRoot -Directory | ForEach-Object {
|
|
||||||
|
|
||||||
$folderRoot = $_.FullName
|
|
||||||
Write-Host "== Package: $($_.Name) =="
|
|
||||||
|
|
||||||
# Walk files inside this folder
|
|
||||||
Get-ChildItem -Path $folderRoot -File -Recurse | ForEach-Object {
|
|
||||||
|
|
||||||
$src = $_.FullName
|
|
||||||
$rel = $src.Substring($folderRoot.Length).TrimStart("\")
|
|
||||||
$dst = Join-Path $buildRoot $rel
|
|
||||||
$dstDir = Split-Path $dst
|
|
||||||
|
|
||||||
if (-not (Test-Path $dstDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $dstDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processing: $rel"
|
|
||||||
|
|
||||||
# Read first 3 lines
|
|
||||||
$header = Get-Content $src -TotalCount 3 -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($header -match "--:Minify:--") {
|
|
||||||
Write-Host " > Minifying"
|
|
||||||
$content = luamin -f "$src"
|
|
||||||
|
|
||||||
# UTF8 encoding without BOM because it breaks lua
|
|
||||||
$utf8NoBOM = New-Object System.Text.UTF8Encoding($false)
|
|
||||||
[System.IO.File]::WriteAllText($dst, $content, $utf8NoBOM)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host " > Copying"
|
|
||||||
Copy-Item -LiteralPath $src -Destination $dst -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Build complete."
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
TEST_ROOT="$(realpath "$SCRIPT_ROOT/../src")"
|
|
||||||
BUILD_ROOT="$SCRIPT_ROOT/../Build"
|
|
||||||
|
|
||||||
if [[ -d "$BUILD_ROOT" ]]; then
|
|
||||||
rm -rf "$BUILD_ROOT"
|
|
||||||
fi
|
|
||||||
mkdir -p "$BUILD_ROOT"
|
|
||||||
|
|
||||||
echo "Building from $TEST_ROOT"
|
|
||||||
echo "Output to $BUILD_ROOT"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for folder in "$TEST_ROOT"/*/; do
|
|
||||||
[[ -d "$folder" ]] || continue
|
|
||||||
|
|
||||||
folder_root="$folder"
|
|
||||||
package_name="$(basename "$folder_root")"
|
|
||||||
echo "== Package: $package_name =="
|
|
||||||
|
|
||||||
find "$folder_root" -type f | while IFS= read -r src; do
|
|
||||||
rel="${src#$folder_root}"
|
|
||||||
dst="$BUILD_ROOT/$rel"
|
|
||||||
dst_dir="$(dirname "$dst")"
|
|
||||||
|
|
||||||
if [[ ! -d "$dst_dir" ]]; then
|
|
||||||
mkdir -p "$dst_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Processing: $rel"
|
|
||||||
|
|
||||||
header="$(head -n 3 "$src" 2>/dev/null || true)"
|
|
||||||
|
|
||||||
if echo "$header" | grep -q -- "--:Minify:--"; then
|
|
||||||
echo " > Minifying"
|
|
||||||
luamin -f "$src" > "$dst"
|
|
||||||
else
|
|
||||||
echo " > Copying"
|
|
||||||
cp -f "$src" "$dst"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Build complete."
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
$testRoot = Join-Path $PSScriptRoot "..\src"
|
|
||||||
$buildRoot = Join-Path $PSScriptRoot "..\Build"
|
|
||||||
|
|
||||||
$testRoot = Resolve-Path $testRoot
|
|
||||||
|
|
||||||
# Clean Build folder
|
|
||||||
if (Test-Path $buildRoot) {
|
|
||||||
Remove-Item -LiteralPath $buildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $buildRoot | Out-Null
|
|
||||||
|
|
||||||
Write-Host "Building from $testRoot"
|
|
||||||
Write-Host "Output to $buildRoot"
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Each top-level folder in test/
|
|
||||||
Get-ChildItem -Path $testRoot -Directory | ForEach-Object {
|
|
||||||
|
|
||||||
$folderRoot = $_.FullName
|
|
||||||
Write-Host "== Package: $($_.Name) =="
|
|
||||||
|
|
||||||
# Walk files inside this folder
|
|
||||||
Get-ChildItem -Path $folderRoot -File -Recurse | ForEach-Object {
|
|
||||||
|
|
||||||
$src = $_.FullName
|
|
||||||
$rel = $src.Substring($folderRoot.Length).TrimStart("\")
|
|
||||||
$dst = Join-Path $buildRoot $rel
|
|
||||||
$dstDir = Split-Path $dst
|
|
||||||
|
|
||||||
if (-not (Test-Path $dstDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $dstDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processing: $rel"
|
|
||||||
|
|
||||||
# Read first 3 lines
|
|
||||||
$header = Get-Content $src -TotalCount 3 -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($header -match "--:Minify:--") {
|
|
||||||
Write-Host " > Minifying"
|
|
||||||
$content = luamin -f "$src"
|
|
||||||
|
|
||||||
# UTF8 encoding without BOM because it breaks lua
|
|
||||||
$utf8NoBOM = New-Object System.Text.UTF8Encoding($false)
|
|
||||||
[System.IO.File]::WriteAllText($dst, $content, $utf8NoBOM)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host " > Copying"
|
|
||||||
Copy-Item -LiteralPath $src -Destination $dst -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Build complete."
|
|
||||||
|
|
||||||
$testRoot = Join-Path $PSScriptRoot "..\test"
|
|
||||||
$buildRoot = Join-Path $PSScriptRoot "..\Build"
|
|
||||||
|
|
||||||
$testRoot = Resolve-Path $testRoot
|
|
||||||
|
|
||||||
Write-Host "Building from $testRoot"
|
|
||||||
Write-Host "Output to $buildRoot"
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Each top-level folder in test/
|
|
||||||
Get-ChildItem -Path $testRoot -Directory | ForEach-Object {
|
|
||||||
|
|
||||||
$folderRoot = $_.FullName
|
|
||||||
Write-Host "== Package: $($_.Name) =="
|
|
||||||
|
|
||||||
# Walk files inside this folder
|
|
||||||
Get-ChildItem -Path $folderRoot -File -Recurse | ForEach-Object {
|
|
||||||
|
|
||||||
$src = $_.FullName
|
|
||||||
$rel = $src.Substring($folderRoot.Length).TrimStart("\")
|
|
||||||
$dst = Join-Path $buildRoot $rel
|
|
||||||
$dstDir = Split-Path $dst
|
|
||||||
|
|
||||||
if (-not (Test-Path $dstDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $dstDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processing: $rel"
|
|
||||||
|
|
||||||
# Read first 3 lines
|
|
||||||
$header = Get-Content $src -TotalCount 3 -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($header -match "--:Minify:--") {
|
|
||||||
Write-Host " > Minifying"
|
|
||||||
$content = luamin -f "$src"
|
|
||||||
|
|
||||||
# UTF8 encoding without BOM because it breaks lua
|
|
||||||
$utf8NoBOM = New-Object System.Text.UTF8Encoding($false)
|
|
||||||
[System.IO.File]::WriteAllText($dst, $content, $utf8NoBOM)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host " > Copying"
|
|
||||||
Copy-Item -LiteralPath $src -Destination $dst -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Build complete."
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
build_from_root() {
|
|
||||||
local TEST_ROOT
|
|
||||||
TEST_ROOT="$(realpath "$1")"
|
|
||||||
local BUILD_ROOT="$SCRIPT_ROOT/../Build"
|
|
||||||
|
|
||||||
if [[ -d "$BUILD_ROOT" ]]; then
|
|
||||||
rm -rf "$BUILD_ROOT"
|
|
||||||
fi
|
|
||||||
mkdir -p "$BUILD_ROOT"
|
|
||||||
|
|
||||||
echo "Building from $TEST_ROOT"
|
|
||||||
echo "Output to $BUILD_ROOT"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for folder in "$TEST_ROOT"/*/; do
|
|
||||||
[[ -d "$folder" ]] || continue
|
|
||||||
|
|
||||||
local package_name
|
|
||||||
package_name="$(basename "$folder")"
|
|
||||||
echo "== Package: $package_name =="
|
|
||||||
|
|
||||||
find "$folder" -type f | while IFS= read -r src; do
|
|
||||||
rel="${src#$folder}"
|
|
||||||
dst="$BUILD_ROOT/$rel"
|
|
||||||
dst_dir="$(dirname "$dst")"
|
|
||||||
|
|
||||||
mkdir -p "$dst_dir"
|
|
||||||
|
|
||||||
echo "Processing: $rel"
|
|
||||||
|
|
||||||
header="$(head -n 3 "$src" 2>/dev/null || true)"
|
|
||||||
|
|
||||||
if echo "$header" | grep -q -- "--:Minify:--"; then
|
|
||||||
echo " > Minifying"
|
|
||||||
luamin -f "$src" > "$dst"
|
|
||||||
else
|
|
||||||
echo " > Copying"
|
|
||||||
cp -f "$src" "$dst"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Build complete."
|
|
||||||
}
|
|
||||||
|
|
||||||
build_from_root "$SCRIPT_ROOT/../src"
|
|
||||||
|
|
||||||
build_from_root "$SCRIPT_ROOT/../test"
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
$testRoot = Join-Path $PSScriptRoot "..\src"
|
|
||||||
$buildRoot = Join-Path $PSScriptRoot "..\Build"
|
|
||||||
|
|
||||||
$testRoot = Resolve-Path $testRoot
|
|
||||||
|
|
||||||
# Clean Build folder
|
|
||||||
if (Test-Path $buildRoot) {
|
|
||||||
Remove-Item -LiteralPath $buildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $buildRoot | Out-Null
|
|
||||||
|
|
||||||
Write-Host "Building from $testRoot"
|
|
||||||
Write-Host "Output to $buildRoot"
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Each top-level folder in test/
|
|
||||||
Get-ChildItem -Path $testRoot -Directory | ForEach-Object {
|
|
||||||
|
|
||||||
$folderRoot = $_.FullName
|
|
||||||
Write-Host "== Package: $($_.Name) =="
|
|
||||||
|
|
||||||
# Walk files inside this folder
|
|
||||||
Get-ChildItem -Path $folderRoot -File -Recurse | ForEach-Object {
|
|
||||||
|
|
||||||
$src = $_.FullName
|
|
||||||
$rel = $src.Substring($folderRoot.Length).TrimStart("\")
|
|
||||||
$dst = Join-Path $buildRoot $rel
|
|
||||||
$dstDir = Split-Path $dst
|
|
||||||
|
|
||||||
if (-not (Test-Path $dstDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $dstDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processing: $rel"
|
|
||||||
Write-Host " > Copying"
|
|
||||||
Copy-Item -LiteralPath $src -Destination $dst -Force
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Build complete."
|
|
||||||
|
|
||||||
|
|
||||||
$testRoot = Join-Path $PSScriptRoot "..\test"
|
|
||||||
$buildRoot = Join-Path $PSScriptRoot "..\Build"
|
|
||||||
|
|
||||||
$testRoot = Resolve-Path $testRoot
|
|
||||||
|
|
||||||
Write-Host "Building from $testRoot"
|
|
||||||
Write-Host "Output to $buildRoot"
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Each top-level folder in test/
|
|
||||||
Get-ChildItem -Path $testRoot -Directory | ForEach-Object {
|
|
||||||
|
|
||||||
$folderRoot = $_.FullName
|
|
||||||
Write-Host "== Package: $($_.Name) =="
|
|
||||||
|
|
||||||
# Walk files inside this folder
|
|
||||||
Get-ChildItem -Path $folderRoot -File -Recurse | ForEach-Object {
|
|
||||||
|
|
||||||
$src = $_.FullName
|
|
||||||
$rel = $src.Substring($folderRoot.Length).TrimStart("\")
|
|
||||||
$dst = Join-Path $buildRoot $rel
|
|
||||||
$dstDir = Split-Path $dst
|
|
||||||
|
|
||||||
if (-not (Test-Path $dstDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $dstDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processing: $rel"
|
|
||||||
|
|
||||||
Write-Host " > Copying"
|
|
||||||
Copy-Item -LiteralPath $src -Destination $dst -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Build complete."
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
TEST_ROOT="$(realpath "$SCRIPT_ROOT/../src")"
|
|
||||||
BUILD_ROOT="$(realpath "$SCRIPT_ROOT/../Build" 2>/dev/null || echo "$SCRIPT_ROOT/../Build")"
|
|
||||||
|
|
||||||
if [[ -d "$BUILD_ROOT" ]]; then
|
|
||||||
rm -rf "$BUILD_ROOT"
|
|
||||||
fi
|
|
||||||
mkdir -p "$BUILD_ROOT"
|
|
||||||
|
|
||||||
echo "Building from $TEST_ROOT"
|
|
||||||
echo "Output to $BUILD_ROOT"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for folder in "$TEST_ROOT"/*/; do
|
|
||||||
[[ -d "$folder" ]] || continue
|
|
||||||
|
|
||||||
package_name="$(basename "$folder")"
|
|
||||||
echo "== Package: $package_name =="
|
|
||||||
|
|
||||||
find "$folder" -type f | while read -r src; do
|
|
||||||
rel="${src#$folder}"
|
|
||||||
dst="$BUILD_ROOT/$rel"
|
|
||||||
dst_dir="$(dirname "$dst")"
|
|
||||||
|
|
||||||
mkdir -p "$dst_dir"
|
|
||||||
|
|
||||||
echo "Processing: $rel"
|
|
||||||
echo " > Copying"
|
|
||||||
cp -f "$src" "$dst"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Build complete."
|
|
||||||
|
|
||||||
TEST_ROOT="$(realpath "$SCRIPT_ROOT/../test")"
|
|
||||||
BUILD_ROOT="$(realpath "$SCRIPT_ROOT/../Build" 2>/dev/null || echo "$SCRIPT_ROOT/../Build")"
|
|
||||||
|
|
||||||
echo "Building from $TEST_ROOT"
|
|
||||||
echo "Output to $BUILD_ROOT"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for folder in "$TEST_ROOT"/*/; do
|
|
||||||
[[ -d "$folder" ]] || continue
|
|
||||||
|
|
||||||
package_name="$(basename "$folder")"
|
|
||||||
echo "== Package: $package_name =="
|
|
||||||
|
|
||||||
find "$folder" -type f | while read -r src; do
|
|
||||||
rel="${src#$folder}"
|
|
||||||
dst="$BUILD_ROOT/$rel"
|
|
||||||
dst_dir="$(dirname "$dst")"
|
|
||||||
|
|
||||||
mkdir -p "$dst_dir"
|
|
||||||
|
|
||||||
echo "Processing: $rel"
|
|
||||||
echo " > Copying"
|
|
||||||
cp -f "$src" "$dst"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Build complete."
|
|
||||||
Reference in New Issue
Block a user