Compare commits
2
Commits
1.2.3
..
5b8043b6ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b8043b6ec | ||
|
|
2a6a11a701 |
Vendored
+1
-2
@@ -5,7 +5,6 @@
|
|||||||
"syscall",
|
"syscall",
|
||||||
"printf",
|
"printf",
|
||||||
"printInline",
|
"printInline",
|
||||||
"toHex",
|
"toHex"
|
||||||
"loadcstr"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
PYTHON := python3
|
|
||||||
ARCH_FLAG := $(if $(ARCH),--arch $(ARCH),)
|
|
||||||
MODE_FLAG := $(if $(DEV),--dev,--release)
|
|
||||||
|
|
||||||
.PHONY: build build-mini build-test build-mini-test clean
|
|
||||||
|
|
||||||
build:
|
|
||||||
$(PYTHON) build.py build $(ARCH_FLAG) $(MODE_FLAG)
|
|
||||||
|
|
||||||
build-mini:
|
|
||||||
$(PYTHON) build.py build-mini $(ARCH_FLAG) $(MODE_FLAG)
|
|
||||||
|
|
||||||
build-test:
|
|
||||||
$(PYTHON) build.py build-test $(ARCH_FLAG) $(MODE_FLAG)
|
|
||||||
|
|
||||||
build-mini-test:
|
|
||||||
$(PYTHON) build.py build-mini-test $(ARCH_FLAG) $(MODE_FLAG)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(PYTHON) build.py clean
|
|
||||||
@@ -1,16 +1,9 @@
|
|||||||
[](https://pinestore.cc/projects/225/hyperionos)
|
|
||||||
# HyperionOS
|
# HyperionOS
|
||||||
|
|
||||||
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
|
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
See `building.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Functionality is split into kernel modules (`.kmod`)
|
- Functionality is split into kernel modules (`.kmod`)
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,295 @@
|
|||||||
|
--: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
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
--: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
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
local args = {...}
|
||||||
|
local name = syscall.getTask(syscall.getpid()).name
|
||||||
|
local fs = require("sys.fs")
|
||||||
|
local filePath = (args[1] or "")
|
||||||
|
if filePath:sub(1, 1) ~= "/" then
|
||||||
|
filePath = syscall.getcwd().."/"..filePath
|
||||||
|
end
|
||||||
|
|
||||||
|
if not fs.exists(filePath) and args[1] then
|
||||||
|
print(name..": Cannot access '"..args[1].."': No such file.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = 0
|
||||||
|
if args[1] then
|
||||||
|
file = syscall.open(filePath, "r")
|
||||||
|
end
|
||||||
|
local content=""
|
||||||
|
while content~=nil or file == 0 do
|
||||||
|
content=syscall.read(file, 1024)
|
||||||
|
printInline(content)
|
||||||
|
end
|
||||||
|
syscall.close(file)
|
||||||
|
print("")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
syscall.devctl(1,"clear")
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
local args = {...}
|
||||||
|
print(table.concat(args, " "))
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
--:Minify:--
|
||||||
|
-- help: display command reference with paged scrolling
|
||||||
|
|
||||||
|
local COMMANDS = {
|
||||||
|
-- {name, usage, description, {flags...}}
|
||||||
|
-- flags: {flag, desc}
|
||||||
|
{
|
||||||
|
name = "cat",
|
||||||
|
usage = "cat [file]",
|
||||||
|
desc = "Print file contents to stdout. Reads from stdin if no file given.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "cd",
|
||||||
|
usage = "cd [dir]",
|
||||||
|
desc = "Change working directory. Use '-' to return to previous directory.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "clear",
|
||||||
|
usage = "clear",
|
||||||
|
desc = "Clear the terminal screen.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "echo",
|
||||||
|
usage = "echo [text...]",
|
||||||
|
desc = "Print arguments to stdout.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "hfetch",
|
||||||
|
usage = "hfetch",
|
||||||
|
desc = "Display system information in a neofetch-style layout.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "id",
|
||||||
|
usage = "id [username]",
|
||||||
|
desc = "Print user identity (uid, gid). Defaults to current user.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "login",
|
||||||
|
usage = "login",
|
||||||
|
desc = "System login prompt. Launched automatically at boot.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "ls",
|
||||||
|
usage = "ls [-alh] [dir]",
|
||||||
|
desc = "List directory contents.",
|
||||||
|
flags = {
|
||||||
|
{"-a", "Show hidden files (starting with .)"},
|
||||||
|
{"-l", "Long format: permissions, owner, size"},
|
||||||
|
{"-h", "Human-readable file sizes"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "lsusers",
|
||||||
|
usage = "lsusers",
|
||||||
|
desc = "List all user accounts with uid, gid, home, and shell.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "lua",
|
||||||
|
usage = "lua",
|
||||||
|
desc = "Interactive Lua REPL prompt.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "mkdir",
|
||||||
|
usage = "mkdir <dir>",
|
||||||
|
desc = "Create a directory.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "passwd",
|
||||||
|
usage = "passwd [username]",
|
||||||
|
desc = "Change a user password. Non-root must verify current password first.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "ps",
|
||||||
|
usage = "ps",
|
||||||
|
desc = "List running tasks with pid, user, name, and status.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "pwd",
|
||||||
|
usage = "pwd",
|
||||||
|
desc = "Print current working directory.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "su",
|
||||||
|
usage = "su [username]",
|
||||||
|
desc = "Switch user. Defaults to root. Root can switch without a password.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "sudo",
|
||||||
|
usage = "sudo [-u user] <command> [args...]",
|
||||||
|
desc = "Run a command as another user (default root). Authenticates as current user.",
|
||||||
|
flags = {
|
||||||
|
{"-u user", "Run as the specified user (name or uid)"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "sysdump",
|
||||||
|
usage = "sysdump",
|
||||||
|
desc = "List all registered kernel syscalls.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "useradd",
|
||||||
|
usage = "useradd [-p pw] [-g gid] [-d home] [-s shell] [-M] <username>",
|
||||||
|
desc = "Create a new user account.",
|
||||||
|
flags = {
|
||||||
|
{"-p pw", "Set password (prompted interactively if omitted)"},
|
||||||
|
{"-g gid", "Set primary group id"},
|
||||||
|
{"-d home", "Set home directory (default: /home/username)"},
|
||||||
|
{"-s shell", "Set login shell (default: /bin/hysh)"},
|
||||||
|
{"-M", "Do not create home directory"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "userdel",
|
||||||
|
usage = "userdel [-r] <username>",
|
||||||
|
desc = "Delete a user account.",
|
||||||
|
flags = {
|
||||||
|
{"-r", "Also recursively remove the user's home directory"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "usermod",
|
||||||
|
usage = "usermod [-l name] [-p pw] [-g gid] [-d home] [-s shell] [-L] [-U] <username>",
|
||||||
|
desc = "Modify an existing user account.",
|
||||||
|
flags = {
|
||||||
|
{"-l name", "Rename the user"},
|
||||||
|
{"-p pw", "Set new password"},
|
||||||
|
{"-g gid", "Change primary group id"},
|
||||||
|
{"-d home", "Change home directory"},
|
||||||
|
{"-s shell", "Change login shell"},
|
||||||
|
{"-L", "Lock the account (disable login)"},
|
||||||
|
{"-U", "Unlock the account"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "whoami",
|
||||||
|
usage = "whoami",
|
||||||
|
desc = "Print the current username.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "yes",
|
||||||
|
usage = "yes [text]",
|
||||||
|
desc = "Repeatedly print 'y' (or given text) until interrupted with Ctrl+C.",
|
||||||
|
flags = {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Build lines to display
|
||||||
|
local C_HEAD = 7 -- yellow (section headers)
|
||||||
|
local C_CMD = 5 -- cyan (command name)
|
||||||
|
local C_USAGE = 1 -- white (usage line)
|
||||||
|
local C_DESC = 13 -- grey (description)
|
||||||
|
local C_FLAG = 3 -- green (flag name)
|
||||||
|
local C_DIM = 12 -- dark grey
|
||||||
|
|
||||||
|
-- Each entry is {text, color}
|
||||||
|
local lines = {}
|
||||||
|
local function push(text, col) lines[#lines+1] = {text, col or 1} end
|
||||||
|
|
||||||
|
push("HyperionOS Command Reference", C_HEAD)
|
||||||
|
push(string.rep("=", 50), C_DIM)
|
||||||
|
push("", 1)
|
||||||
|
|
||||||
|
local args = {...}
|
||||||
|
local filter = args[1] -- optional: help <command>
|
||||||
|
|
||||||
|
local function addCmd(cmd)
|
||||||
|
push(cmd.name, C_CMD)
|
||||||
|
push(" Usage: " .. cmd.usage, C_USAGE)
|
||||||
|
push(" " .. cmd.desc, C_DESC)
|
||||||
|
if #cmd.flags > 0 then
|
||||||
|
for _, f in ipairs(cmd.flags) do
|
||||||
|
push(" " .. f[1], C_FLAG)
|
||||||
|
push(" " .. f[2], C_DESC)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
push("", 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if filter then
|
||||||
|
local found = false
|
||||||
|
for _, cmd in ipairs(COMMANDS) do
|
||||||
|
if cmd.name == filter then addCmd(cmd); found = true; break end
|
||||||
|
end
|
||||||
|
if not found then
|
||||||
|
push("help: unknown command '" .. filter .. "'", 2)
|
||||||
|
push("Run 'help' with no arguments for the full list.", C_DESC)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
push("Run 'help <command>' for details on a specific command.", C_DESC)
|
||||||
|
push("", 1)
|
||||||
|
for _, cmd in ipairs(COMMANDS) do addCmd(cmd) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pager
|
||||||
|
local sizeStr = syscall.devctl(1, "size")
|
||||||
|
local screenW = tonumber(sizeStr:sub(1, sizeStr:find(";")-1)) or 51
|
||||||
|
local screenH = tonumber(sizeStr:sub(sizeStr:find(";")+1)) or 19
|
||||||
|
local pageSize = screenH - 2 -- reserve bottom row for status bar
|
||||||
|
|
||||||
|
local scroll = 0
|
||||||
|
local totalLines = #lines
|
||||||
|
local dirty = true
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
syscall.devctl(1, "clear")
|
||||||
|
syscall.devctl(1, "spos", 1, 1)
|
||||||
|
for row = 1, pageSize do
|
||||||
|
local li = scroll + row
|
||||||
|
if li <= totalLines then
|
||||||
|
local text, col = lines[li][1], lines[li][2]
|
||||||
|
syscall.devctl(1, "sfgc", col)
|
||||||
|
-- truncate to screen width
|
||||||
|
if #text > screenW then text = text:sub(1, screenW) end
|
||||||
|
syscall.write(1, text .. "\n")
|
||||||
|
else
|
||||||
|
syscall.write(1, "\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- status bar
|
||||||
|
syscall.devctl(1, "sfgc", 16)
|
||||||
|
syscall.devctl(1, "sbgc", 13)
|
||||||
|
local pct = math.floor(math.min(100, (scroll + pageSize) / totalLines * 100))
|
||||||
|
local status = string.format(" help -- line %d/%d (%d%%) [up/down: scroll q: quit] ",
|
||||||
|
scroll + 1, totalLines, pct)
|
||||||
|
if #status > screenW then status = status:sub(1, screenW) end
|
||||||
|
syscall.devctl(1, "spos", 1, screenH)
|
||||||
|
syscall.write(1, status .. string.rep(" ", screenW - #status))
|
||||||
|
syscall.devctl(1, "sfgc", 1)
|
||||||
|
syscall.devctl(1, "sbgc", 16)
|
||||||
|
dirty = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If output fits on one screen, just print without pager
|
||||||
|
if totalLines <= pageSize then
|
||||||
|
syscall.devctl(1, "clear")
|
||||||
|
syscall.devctl(1, "spos", 1, 1)
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
syscall.devctl(1, "sfgc", line[2])
|
||||||
|
syscall.write(1, line[1] .. "\n")
|
||||||
|
end
|
||||||
|
syscall.devctl(1, "sfgc", 1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
render()
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
-- idle
|
||||||
|
elseif ch == "q" or ch == "Q" then
|
||||||
|
break
|
||||||
|
elseif ch == "\17" then -- up arrow
|
||||||
|
if scroll > 0 then
|
||||||
|
scroll = scroll - 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif ch == "\18" then -- down arrow
|
||||||
|
if scroll + pageSize < totalLines then
|
||||||
|
scroll = scroll + 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif ch == "\19" then -- left / page up (reuse left arrow)
|
||||||
|
scroll = math.max(0, scroll - pageSize)
|
||||||
|
dirty = true
|
||||||
|
elseif ch == "\20" then -- right / page down
|
||||||
|
scroll = math.min(totalLines - pageSize, scroll + pageSize)
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
if dirty then render() end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Restore screen
|
||||||
|
syscall.devctl(1, "clear")
|
||||||
|
syscall.devctl(1, "spos", 1, 1)
|
||||||
|
syscall.devctl(1, "sfgc", 1)
|
||||||
|
syscall.devctl(1, "sbgc", 16)
|
||||||
@@ -33,6 +33,7 @@ local host_str = syscall.getHost() or "Unknown"
|
|||||||
local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str
|
local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str
|
||||||
|
|
||||||
local info = {
|
local info = {
|
||||||
|
-- {label, value} label=nil means print value as-is (userhost / separator)
|
||||||
{nil, userhost},
|
{nil, userhost},
|
||||||
{nil, string.rep("-", #userhost)},
|
{nil, string.rep("-", #userhost)},
|
||||||
{"OS", syscall.version() or "Unknown"},
|
{"OS", syscall.version() or "Unknown"},
|
||||||
@@ -41,7 +42,7 @@ local info = {
|
|||||||
{"Uptime", formatUptime(syscall.getUptime() or 0)},
|
{"Uptime", formatUptime(syscall.getUptime() or 0)},
|
||||||
{"Tasks", tostring(#(syscall.getTasks() or {}))},
|
{"Tasks", tostring(#(syscall.getTasks() or {}))},
|
||||||
{"Shell", syscall.getEnviron("SHELL") or "Unknown"},
|
{"Shell", syscall.getEnviron("SHELL") or "Unknown"},
|
||||||
{"Terminal", "tty1"},
|
{"Terminal", "TTY1"},
|
||||||
{"UID", tostring(syscall.getuid())},
|
{"UID", tostring(syscall.getuid())},
|
||||||
{"Packages", "n/a (spm)"},
|
{"Packages", "n/a (spm)"},
|
||||||
}
|
}
|
||||||
@@ -70,12 +71,15 @@ local lines = math.max(#logo, #info)
|
|||||||
for i = 1, lines do
|
for i = 1, lines do
|
||||||
local logo_str = logo[i] or string.rep(" ", 36)
|
local logo_str = logo[i] or string.rep(" ", 36)
|
||||||
|
|
||||||
|
-- print logo segment in cyan
|
||||||
c(C_LOGO)
|
c(C_LOGO)
|
||||||
printInline(logo_str)
|
printInline(logo_str)
|
||||||
|
|
||||||
|
-- print separator pipe
|
||||||
c(C_LABEL)
|
c(C_LABEL)
|
||||||
printInline("| ")
|
printInline("| ")
|
||||||
|
|
||||||
|
-- print info segment
|
||||||
local row = info[i]
|
local row = info[i]
|
||||||
if row then
|
if row then
|
||||||
if row[1] == nil and i == 1 then
|
if row[1] == nil and i == 1 then
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
--:Minify:--
|
||||||
|
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
|
||||||
|
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1)
|
||||||
|
syscall.open("/dev/null","w") --stderr (device 2)
|
||||||
|
|
||||||
|
local success, errorMsg = xpcall(function()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local fs = require("sys.fs")
|
||||||
|
|
||||||
|
syscall.devctl(1,"clear")
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
syscall.devctl(1,"spos",1,1)
|
||||||
|
print("HyperionOS hysh Shell")
|
||||||
|
|
||||||
|
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
||||||
|
local commandHistory = {}
|
||||||
|
local terminate = false
|
||||||
|
syscall.setEnviron("SHELL","rtbash")
|
||||||
|
syscall.setEnviron("PATH","/bin/")
|
||||||
|
syscall.chdir("/")
|
||||||
|
local oldWD = ""
|
||||||
|
|
||||||
|
for i = 1, 16 do
|
||||||
|
syscall.devctl(1,"sbgc",i)
|
||||||
|
printInline(" ");
|
||||||
|
end
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
syscall.sigcatch(function(sig)
|
||||||
|
if sig == 1 then
|
||||||
|
terminate = true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local builtinCmds = {}
|
||||||
|
|
||||||
|
builtinCmds.cd = function(path)
|
||||||
|
local cwd = syscall.getcwd()
|
||||||
|
local dirIn = (path or "")
|
||||||
|
if dirIn == "-" then
|
||||||
|
if oldWD == "" then
|
||||||
|
print("hysh-cd: No previous working directory set.")
|
||||||
|
else
|
||||||
|
print(oldWD)
|
||||||
|
syscall.chdir(oldWD)
|
||||||
|
oldWD = cwd
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local dirInMod = dirIn
|
||||||
|
if dirIn:sub(1, 1) ~= "/" then dirInMod = cwd .. "/" .. dirIn end
|
||||||
|
local parts = {}
|
||||||
|
for part in dirInMod:gmatch("[^/]+") do
|
||||||
|
if part == ".." then
|
||||||
|
if #parts > 0 then table.remove(parts) end
|
||||||
|
elseif part ~= "." and part ~= "" then
|
||||||
|
table.insert(parts, part)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local normDir = "/" .. table.concat(parts, "/")
|
||||||
|
if normDir:sub(#normDir, #normDir) ~= "/" then normDir = normDir .. "/" end
|
||||||
|
|
||||||
|
if not fs.isDir(normDir) then
|
||||||
|
print("hysh-cd: "..dirIn..": No such directory.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
oldWD = cwd
|
||||||
|
syscall.chdir(normDir)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function getUserInput()
|
||||||
|
syscall.devctl(1,"sfgc",3)
|
||||||
|
syscall.write(1, userhost)
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
syscall.write(1, ":")
|
||||||
|
syscall.devctl(1,"sfgc",10)
|
||||||
|
syscall.write(1, syscall.getcwd())
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
syscall.write(1, "$ ")
|
||||||
|
local curOffsetStr = syscall.devctl(1, "gpos")
|
||||||
|
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
|
||||||
|
local curOffsetY = tonumber(curOffsetStr:sub(curOffsetStr:find(";")+1))
|
||||||
|
|
||||||
|
local input = ""
|
||||||
|
local blinkState = false
|
||||||
|
local cursorPos = 1
|
||||||
|
local history = 0
|
||||||
|
local dirty = true -- redraw on first iteration
|
||||||
|
|
||||||
|
local function redraw()
|
||||||
|
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||||
|
-- text before cursor
|
||||||
|
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
||||||
|
-- cursor character (inverted if blinking on)
|
||||||
|
if blinkState then
|
||||||
|
syscall.devctl(1,"sfgc",16)
|
||||||
|
syscall.devctl(1,"sbgc",1)
|
||||||
|
end
|
||||||
|
if cursorPos > #input then
|
||||||
|
syscall.write(1, " ")
|
||||||
|
else
|
||||||
|
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
||||||
|
end
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
syscall.devctl(1,"sbgc",16)
|
||||||
|
-- text after cursor + trailing space to erase old chars
|
||||||
|
syscall.write(1, string.sub(input, cursorPos+1) .. " ")
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local key = syscall.read(0)
|
||||||
|
if key and key ~= "" then
|
||||||
|
if key == "\19" then -- left arrow
|
||||||
|
if cursorPos > 1 then
|
||||||
|
cursorPos = cursorPos - 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif key == "\20" then -- right arrow
|
||||||
|
if cursorPos <= #input then
|
||||||
|
cursorPos = cursorPos + 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif key == "\17" then -- up arrow
|
||||||
|
if history < #commandHistory then
|
||||||
|
history = history + 1
|
||||||
|
input = commandHistory[#commandHistory - history + 1]
|
||||||
|
cursorPos = #input + 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif key == "\18" then -- down arrow
|
||||||
|
if history > 1 then
|
||||||
|
history = history - 1
|
||||||
|
input = commandHistory[#commandHistory - history + 1]
|
||||||
|
cursorPos = #input + 1
|
||||||
|
dirty = true
|
||||||
|
elseif history == 1 then
|
||||||
|
history = 0
|
||||||
|
input = ""
|
||||||
|
cursorPos = 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif key == "\b" then
|
||||||
|
if cursorPos > 1 then
|
||||||
|
input = string.sub(input, 1, cursorPos-2) .. string.sub(input, cursorPos)
|
||||||
|
cursorPos = cursorPos - 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
elseif key == "\n" then
|
||||||
|
-- redraw cleanly with no cursor highlight before committing
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
syscall.devctl(1,"sbgc",16)
|
||||||
|
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||||
|
syscall.write(1, input .. " \n")
|
||||||
|
return input
|
||||||
|
else
|
||||||
|
input = string.sub(input, 1, cursorPos-1) .. key .. string.sub(input, cursorPos)
|
||||||
|
cursorPos = cursorPos + 1
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- cursor blink
|
||||||
|
local curBlink = ((math.floor(syscall.getUptime() / 500) % 2) == 0)
|
||||||
|
if curBlink ~= blinkState then
|
||||||
|
blinkState = curBlink
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if dirty then
|
||||||
|
redraw()
|
||||||
|
dirty = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function runCommand(command)
|
||||||
|
do
|
||||||
|
local func = load("return " .. command, "@equation", "t", {})
|
||||||
|
if func then
|
||||||
|
local success, result = pcall(func)
|
||||||
|
if success and type(result) == "number" then
|
||||||
|
print(result)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
terminate = false
|
||||||
|
local args = string.split(command, " ")
|
||||||
|
if builtinCmds[args[1]] then
|
||||||
|
local success, msg = pcall(builtinCmds[args[1]], table.unpack(args, 2))
|
||||||
|
if not success then
|
||||||
|
local errSL = string.sub(msg, string.find(msg, "]") + 2)
|
||||||
|
syscall.devctl(1,"sfgc",2)
|
||||||
|
printInline(args[1]..": Program runtime error on line ")
|
||||||
|
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmdPath = ""
|
||||||
|
if string.find(args[1], "/") then
|
||||||
|
if fs.exists(args[1]) then
|
||||||
|
cmdPath = args[1]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local paths = string.split(syscall.getEnviron("PATH"), ":")
|
||||||
|
for _, path in pairs(paths) do
|
||||||
|
if fs.exists(path..args[1]) then
|
||||||
|
cmdPath = path..args[1]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if cmdPath == "" then
|
||||||
|
print(args[1]..": Command not found")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local progName = string.sub(cmdPath, #cmdPath - string.find(string.reverse(cmdPath), "/") + 2)
|
||||||
|
|
||||||
|
local text = fs.readAllText(cmdPath)
|
||||||
|
local program, err = load(text, progName)
|
||||||
|
if not program then
|
||||||
|
local errSL = string.sub(err, string.find(err, ":") + 1)
|
||||||
|
syscall.devctl(1,"sfgc",2)
|
||||||
|
printInline(progName..": Program load error on line ")
|
||||||
|
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local proc = syscall.spawn(function(...)
|
||||||
|
syscall.open("/dev/tty/TTY1","r")
|
||||||
|
syscall.open("/dev/tty/TTY1","w")
|
||||||
|
syscall.open("/dev/null","w")
|
||||||
|
local success, msg = pcall(program, ...)
|
||||||
|
if not success then
|
||||||
|
local errSL = string.sub(msg, string.find(msg, ":") + 1)
|
||||||
|
syscall.devctl(1,"sfgc",2)
|
||||||
|
printInline(progName..": Program runtime error on line ")
|
||||||
|
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
||||||
|
end
|
||||||
|
end, progName, nil, {table.unpack(args, 2)})
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local exited, code = syscall.collect(proc)
|
||||||
|
if exited then
|
||||||
|
if code then
|
||||||
|
print("\nTask exited with code:\n"..tostring(code))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if terminate then
|
||||||
|
local success, err = syscall.kill(proc)
|
||||||
|
if success then
|
||||||
|
syscall.devctl(1,"sbgc",16)
|
||||||
|
syscall.devctl(1,"sfgc",2)
|
||||||
|
print("\nProgram Terminated.")
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
end
|
||||||
|
terminate = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
sleep(0.05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local command = getUserInput()
|
||||||
|
if command ~= "" then
|
||||||
|
if command ~= commandHistory[#commandHistory] then
|
||||||
|
table.insert(commandHistory, command)
|
||||||
|
end
|
||||||
|
runCommand(command)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--ERROR HANDLING
|
||||||
|
end, debug.traceback)
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
syscall.log("Error running shell: "..errorMsg, "ERROR")
|
||||||
|
syscall.devctl(1,"sfgc",2)
|
||||||
|
syscall.devctl(1,"sbgc",16)
|
||||||
|
print()
|
||||||
|
print("Error running shell: ")
|
||||||
|
print(errorMsg)
|
||||||
|
end
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
--:Minify:--
|
||||||
|
syscall.open("/dev/tty/TTY1","r")
|
||||||
|
syscall.open("/dev/tty/TTY1","w")
|
||||||
|
syscall.open("/dev/null","r")
|
||||||
|
syscall.devctl(1,"clear")
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
syscall.devctl(1,"spos",1,1)
|
||||||
|
print("HyperionOS hysh Shell")
|
||||||
|
local str=""
|
||||||
|
local stopInput=false
|
||||||
|
local proc=0
|
||||||
|
local fs=require("sys.fs")
|
||||||
|
local timeout=false
|
||||||
|
syscall.setEnviron("SHELL","simpleshell")
|
||||||
|
printInline("> ")
|
||||||
|
syscall.sigcatch(function(sig)
|
||||||
|
if sig==1 then
|
||||||
|
syscall.kill(proc)
|
||||||
|
print("Terminated")
|
||||||
|
printInline("> ")
|
||||||
|
stopInput=false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
if not stopInput then
|
||||||
|
local input=syscall.read(0)
|
||||||
|
if input then
|
||||||
|
if input=="\b" then
|
||||||
|
if #str>0 then
|
||||||
|
str=str:sub(1,#str-1)
|
||||||
|
printInline("\b")
|
||||||
|
end
|
||||||
|
elseif input=="\n" then
|
||||||
|
print("")
|
||||||
|
stopInput=true
|
||||||
|
if str == "" then
|
||||||
|
printInline("> ")
|
||||||
|
stopInput=false
|
||||||
|
else
|
||||||
|
local path=nil
|
||||||
|
local split=string.split(str, " ")
|
||||||
|
if fs.exists("/bin/"..split[1]) then
|
||||||
|
path="/bin/"..split[1]
|
||||||
|
elseif fs.exists("/bin/"..split[1]..".lua") then
|
||||||
|
path="/bin/"..split[1]..".lua"
|
||||||
|
end
|
||||||
|
if not path then
|
||||||
|
print("Program not found")
|
||||||
|
printInline("> ")
|
||||||
|
stopInput=false
|
||||||
|
else
|
||||||
|
local text = fs.readAllText(path)
|
||||||
|
local program, err = load(text, path)
|
||||||
|
if not program then
|
||||||
|
print(err)
|
||||||
|
printInline("> ")
|
||||||
|
end
|
||||||
|
proc = syscall.spawn(function(...)
|
||||||
|
syscall.open("/dev/tty/TTY1","r")
|
||||||
|
syscall.open("/dev/tty/TTY1","w")
|
||||||
|
syscall.open("/dev/null","w")
|
||||||
|
program(...)
|
||||||
|
end, path, nil, {table.unpack(split, 2)})
|
||||||
|
end
|
||||||
|
str=""
|
||||||
|
end
|
||||||
|
else
|
||||||
|
str=str..input
|
||||||
|
printInline(input)
|
||||||
|
end
|
||||||
|
timeout=false
|
||||||
|
else
|
||||||
|
timeout=true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local exited, code = syscall.collect(proc)
|
||||||
|
if exited then
|
||||||
|
if code then
|
||||||
|
print("\nTask exited with code:\n"..tostring(code))
|
||||||
|
end
|
||||||
|
printInline("> ")
|
||||||
|
stopInput=false
|
||||||
|
end
|
||||||
|
timeout=true
|
||||||
|
end
|
||||||
|
if timeout then
|
||||||
|
if stopInput then
|
||||||
|
sleep(.5)
|
||||||
|
else
|
||||||
|
sleep(.05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,7 +3,7 @@ local args = {...}
|
|||||||
local uid
|
local uid
|
||||||
|
|
||||||
if args[1] then
|
if args[1] then
|
||||||
uid = syscall.getuid(args[1])
|
uid = syscall.auth_getuid(args[1])
|
||||||
if not uid then
|
if not uid then
|
||||||
print("id: user '" .. args[1] .. "' does not exist")
|
print("id: user '" .. args[1] .. "' does not exist")
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
@@ -12,7 +12,7 @@ else
|
|||||||
uid = syscall.getuid()
|
uid = syscall.getuid()
|
||||||
end
|
end
|
||||||
|
|
||||||
local pwent = syscall.getpasswd(uid)
|
local pwent = syscall.auth_getpasswd(uid)
|
||||||
local name = (pwent and pwent.username) or tostring(uid)
|
local name = (pwent and pwent.username) or tostring(uid)
|
||||||
local gid = (pwent and pwent.gid) or uid
|
local gid = (pwent and pwent.gid) or uid
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
syscall.open("/dev/tty/1", "r") --stdin (fd 0)
|
syscall.open("/dev/tty/TTY1","r") --stdin (fd 0)
|
||||||
syscall.open("/dev/tty/1", "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 MAX_ATTEMPTS = 3
|
local MAX_ATTEMPTS = 3
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ local function readLine(mask)
|
|||||||
while true do
|
while true do
|
||||||
local ch = syscall.read(0)
|
local ch = syscall.read(0)
|
||||||
if not ch or ch == "" then
|
if not ch or ch == "" then
|
||||||
|
-- buffer empty, spin
|
||||||
elseif ch == "\n" then
|
elseif ch == "\n" then
|
||||||
syscall.write(1, "\n")
|
syscall.write(1, "\n")
|
||||||
return input
|
return input
|
||||||
@@ -27,12 +29,7 @@ local function readLine(mask)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function firstBoot()
|
local function firstBoot()
|
||||||
local shadow = ""
|
local shadow = fs.readAllText("/etc/shadow") or ""
|
||||||
local _fd, _fderr = pcall(function()
|
|
||||||
local fd = syscall.open("/etc/shadow", "r")
|
|
||||||
shadow = syscall.read(fd, 65535) or ""
|
|
||||||
syscall.close(fd)
|
|
||||||
end)
|
|
||||||
if shadow:match("%S") then return end
|
if shadow:match("%S") then return end
|
||||||
|
|
||||||
syscall.devctl(1, "clear")
|
syscall.devctl(1, "clear")
|
||||||
@@ -57,7 +54,7 @@ local function firstBoot()
|
|||||||
syscall.write(1, "Password too short (minimum 6 characters).\n\n")
|
syscall.write(1, "Password too short (minimum 6 characters).\n\n")
|
||||||
syscall.devctl(1, "sfgc", 1)
|
syscall.devctl(1, "sfgc", 1)
|
||||||
else
|
else
|
||||||
local ok, err = syscall.setpassword(0, pw1)
|
local ok, err = syscall.auth_setpassword(0, pw1)
|
||||||
if ok then
|
if ok then
|
||||||
syscall.devctl(1, "sfgc", 3)
|
syscall.devctl(1, "sfgc", 3)
|
||||||
syscall.write(1, "Root password set.\n\n")
|
syscall.write(1, "Root password set.\n\n")
|
||||||
@@ -74,40 +71,53 @@ local function firstBoot()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function spawnShell(username, uid, shell, homedir)
|
local function spawnShell(username, uid, shell, homedir)
|
||||||
local existsOk, existsErr = pcall(syscall.exists, shell)
|
local shellText = fs.readAllText(shell)
|
||||||
if not existsOk or not existsErr then
|
if not shellText then
|
||||||
syscall.write(1, "login: shell not found: " .. shell .. "\n")
|
syscall.write(1, "login: shell not found: " .. shell .. "\n")
|
||||||
sleep(2)
|
sleep(2)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local accessOk, accessErr = pcall(syscall.access, shell, "rx")
|
-- Spawn a wrapper that loads and runs the shell, reporting any error back
|
||||||
|
-- via exit code channel so we can display it
|
||||||
|
local errFifo = {}
|
||||||
|
|
||||||
|
local proc = syscall.spawn(function()
|
||||||
|
syscall.setuid(uid)
|
||||||
|
syscall.chdir(homedir)
|
||||||
syscall.setEnviron("HOME", homedir)
|
syscall.setEnviron("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 setuidOk, setuidErr = pcall(syscall.setuid, uid)
|
local shellFn, loadErr = load(shellText, "@" .. shell)
|
||||||
if not setuidOk then
|
if not shellFn then
|
||||||
syscall.write(1, "login: setuid failed: " .. tostring(setuidErr) .. "\n")
|
-- Report load error via log and a recognizable exit code
|
||||||
sleep(2)
|
syscall.log("login: shell load error: " .. tostring(loadErr), "ERROR")
|
||||||
return false
|
syscall.exit(-1)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local chdirOk, chdirErr = pcall(syscall.chdir, homedir)
|
local ok, runErr = xpcall(shellFn, debug.traceback)
|
||||||
if not chdirOk then
|
|
||||||
pcall(syscall.chdir, "/")
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, err = pcall(syscall.execspawn, shell, username .. ":shell")
|
|
||||||
if not ok then
|
if not ok then
|
||||||
syscall.write(1, "login: failed to launch shell: " .. tostring(err) .. "\n")
|
syscall.log("login: shell runtime error: " .. tostring(runErr), "ERROR")
|
||||||
sleep(2)
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
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()
|
||||||
@@ -131,11 +141,10 @@ local function doLogin()
|
|||||||
syscall.write(1, "Password: ")
|
syscall.write(1, "Password: ")
|
||||||
local password = readLine("*")
|
local password = readLine("*")
|
||||||
|
|
||||||
local ok, err = syscall.login(username, password)
|
local ok, err = syscall.auth_login(username, password)
|
||||||
if ok then
|
if ok then
|
||||||
local uid = syscall.getuid()
|
local uid = syscall.auth_getuid(username)
|
||||||
local pwent = syscall.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 "/"
|
||||||
|
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
local cloptions = {
|
||||||
|
a = false,
|
||||||
|
h = false,
|
||||||
|
l = false,
|
||||||
|
help = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
local inpArgs = {...}
|
||||||
|
local args = {}
|
||||||
|
local name = syscall.getTask(syscall.getpid()).name
|
||||||
|
|
||||||
|
local optToSet = false
|
||||||
|
for _, v in pairs(inpArgs) do
|
||||||
|
if optToSet then
|
||||||
|
cloptions[optToSet] = v
|
||||||
|
optToSet = false
|
||||||
|
elseif v:sub(1, 2) == "--" then
|
||||||
|
local opt = v:sub(3)
|
||||||
|
if cloptions[opt] == nil then
|
||||||
|
print(name..": unrecognized option '"..v.."'.")
|
||||||
|
if cloptions.help ~= nil then
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
end
|
||||||
|
return
|
||||||
|
elseif cloptions[opt] == false then
|
||||||
|
cloptions[opt] = true
|
||||||
|
else
|
||||||
|
optToSet = opt
|
||||||
|
end
|
||||||
|
elseif v:sub(1, 1) == "-" then
|
||||||
|
for i = 2, #v do
|
||||||
|
local opt = v:sub(i, i)
|
||||||
|
if cloptions[opt] == nil then
|
||||||
|
print(name..": invalid option '-"..opt.."'.")
|
||||||
|
if cloptions.help ~= nil then
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
end
|
||||||
|
return
|
||||||
|
else
|
||||||
|
cloptions[opt] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(args, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if cloptions.help then
|
||||||
|
print("Usage: "..name.." [OPTION]... [DIR]")
|
||||||
|
print("List all entries in the specified DIRectory, or cwd if not specified")
|
||||||
|
print("\nOptions:")
|
||||||
|
print(" -a Do not ignore entries starting with .")
|
||||||
|
print(" -h with -l, print sizes in a human readble format")
|
||||||
|
print(" -l Use a long listing format")
|
||||||
|
print(" --help Display this help and exit")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local fs = require("sys.fs")
|
||||||
|
local dir = (args[1] or "")
|
||||||
|
if dir:sub(1, 1) ~= "/" then
|
||||||
|
dir = syscall.getcwd().."/"..dir
|
||||||
|
end
|
||||||
|
|
||||||
|
if dir:sub(#dir, #dir) ~= "/" then
|
||||||
|
dir = dir.."/"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not fs.isDir(dir) then
|
||||||
|
print(name..": Cannot access '"..args[1].."': No such directory.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local screenSizeStr = syscall.devctl(1, "size")
|
||||||
|
local sizeX = tonumber(screenSizeStr:sub(1, screenSizeStr:find(";")-1))
|
||||||
|
local sizeY = tonumber(screenSizeStr:sub(screenSizeStr:find(";")+1))
|
||||||
|
|
||||||
|
local list = fs.list(dir)
|
||||||
|
if not cloptions.a then
|
||||||
|
for i = #list, 1, -1 do
|
||||||
|
if list[i]:sub(1, 1) == "." then
|
||||||
|
table.remove(list, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local colWidth = 0
|
||||||
|
local numCols = 1
|
||||||
|
if not cloptions.l then
|
||||||
|
for _, item in pairs(list) do
|
||||||
|
if #item + 2 > colWidth then
|
||||||
|
colWidth = #item + 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
numCols = math.floor(sizeX / colWidth)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sizePrefixes = {"K", "M", "G"}
|
||||||
|
|
||||||
|
for i,v in ipairs(list) do
|
||||||
|
local fileStats = syscall.stat(dir..v)
|
||||||
|
local isDir = fs.isDir(dir..v)
|
||||||
|
if cloptions.l then
|
||||||
|
if isDir then
|
||||||
|
printInline("d")
|
||||||
|
else
|
||||||
|
printInline("-")
|
||||||
|
end
|
||||||
|
printInline("------ ")
|
||||||
|
printInline(fileStats.owner.." ")
|
||||||
|
printInline(fileStats.group.." ")
|
||||||
|
local size = fileStats.size
|
||||||
|
if cloptions.h then
|
||||||
|
local scale = 0
|
||||||
|
while size > 1024 do
|
||||||
|
size = size / 1024
|
||||||
|
scale = scale + 1
|
||||||
|
end
|
||||||
|
if scale > 0 then
|
||||||
|
if size < 10 then
|
||||||
|
size = math.floor(size).."."..math.floor((size * 10) % 10)..sizePrefixes[scale]
|
||||||
|
else
|
||||||
|
size = math.floor(size)..sizePrefixes[scale]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
printInline(size.." ")
|
||||||
|
printInline(math.floor(fileStats.modified / 1000).." ")
|
||||||
|
end
|
||||||
|
if isDir then
|
||||||
|
syscall.devctl(1,"sfgc",4)
|
||||||
|
else
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
end
|
||||||
|
printInline(v)
|
||||||
|
printInline((" "):rep(colWidth - #v))
|
||||||
|
syscall.devctl(1,"sfgc",1)
|
||||||
|
if i % numCols == 0 then
|
||||||
|
print("")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #list % numCols ~= 0 then
|
||||||
|
print("")
|
||||||
|
end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local users = syscall.listusers()
|
local users = syscall.auth_listusers()
|
||||||
if not users or #users == 0 then
|
if not users or #users == 0 then
|
||||||
print("No users found.")
|
print("No users found.")
|
||||||
return
|
return
|
||||||
@@ -260,17 +260,17 @@ local function getUserInput(prompt, history)
|
|||||||
while true do
|
while true do
|
||||||
local key = syscall.read(0)
|
local key = syscall.read(0)
|
||||||
if key and key ~= "" then
|
if key and key ~= "" then
|
||||||
if key == "[C" then
|
if key == "\19" then
|
||||||
if cursor > 1 then cursor = cursor - 1; dirty = true end
|
if cursor > 1 then cursor = cursor - 1; dirty = true end
|
||||||
elseif key == "[D" then
|
elseif key == "\20" then
|
||||||
if cursor <= #input then cursor = cursor + 1; dirty = true end
|
if cursor <= #input then cursor = cursor + 1; dirty = true end
|
||||||
elseif key == "[A" then
|
elseif key == "\17" then
|
||||||
if history and histIdx < #history then
|
if history and histIdx < #history then
|
||||||
histIdx = histIdx + 1
|
histIdx = histIdx + 1
|
||||||
input = history[#history - histIdx + 1]
|
input = history[#history - histIdx + 1]
|
||||||
cursor = #input + 1; dirty = true
|
cursor = #input + 1; dirty = true
|
||||||
end
|
end
|
||||||
elseif key == "[B" then
|
elseif key == "\18" then
|
||||||
if histIdx > 1 then
|
if histIdx > 1 then
|
||||||
histIdx = histIdx - 1
|
histIdx = histIdx - 1
|
||||||
input = history[#history - histIdx + 1]
|
input = history[#history - histIdx + 1]
|
||||||
@@ -288,9 +288,9 @@ local function getUserInput(prompt, history)
|
|||||||
syscall.devctl(1,"spos",ox,oy)
|
syscall.devctl(1,"spos",ox,oy)
|
||||||
w(input .. " \n")
|
w(input .. " \n")
|
||||||
return input
|
return input
|
||||||
elseif #key == 1 and key:byte(1) >= 32 and key:byte(1) < 127 then
|
else
|
||||||
input=string.sub(input,1,cursor-1)..key..string.sub(input,cursor)
|
input = input:sub(1, cursor-1) .. key .. input:sub(cursor)
|
||||||
cursor=cursor+1;dirty=true
|
cursor = cursor + 1; dirty = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local nb = (math.floor(syscall.getUptime() / 500) % 2) == 0
|
local nb = (math.floor(syscall.getUptime() / 500) % 2) == 0
|
||||||
@@ -299,6 +299,8 @@ local function getUserInput(prompt, history)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
syscall.devctl(1, "clear")
|
||||||
|
syscall.devctl(1, "spos", 1, 1)
|
||||||
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
|
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
|
||||||
c(C_NIL)
|
c(C_NIL)
|
||||||
w("Interactive Lua REPL. exit() to quit.\n\n")
|
w("Interactive Lua REPL. exit() to quit.\n\n")
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
--:Minify:--
|
||||||
|
print("HyperionOS lua")
|
||||||
|
local str=""
|
||||||
|
local stopInput=false
|
||||||
|
local timeout=false
|
||||||
|
local luaEnv=setmetatable({},{__index=_ENV})
|
||||||
|
printInline("> ")
|
||||||
|
while true do
|
||||||
|
local input=syscall.read(0)
|
||||||
|
if input then
|
||||||
|
if input=="\b" then
|
||||||
|
if #str>0 then
|
||||||
|
str=str:sub(1,#str-1)
|
||||||
|
printInline("\b")
|
||||||
|
end
|
||||||
|
elseif input=="\n" then
|
||||||
|
print("")
|
||||||
|
stopInput=true
|
||||||
|
if str == "" then
|
||||||
|
printInline("> ")
|
||||||
|
stopInput=false
|
||||||
|
elseif str == "exit()" then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
local func=load(str,"@Lua","t",luaEnv)
|
||||||
|
local ok,err = xpcall(func, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
print(err)
|
||||||
|
end
|
||||||
|
printInline("\n> ")
|
||||||
|
str=""
|
||||||
|
end
|
||||||
|
str=""
|
||||||
|
else
|
||||||
|
str=str..input
|
||||||
|
printInline(input)
|
||||||
|
end
|
||||||
|
timeout=false
|
||||||
|
else
|
||||||
|
timeout=true
|
||||||
|
end
|
||||||
|
|
||||||
|
if timeout then
|
||||||
|
if stopInput then
|
||||||
|
sleep(.5)
|
||||||
|
else
|
||||||
|
sleep(.05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
local args = {...}
|
||||||
|
local name = syscall.getTask(syscall.getpid()).name
|
||||||
|
if #args == 0 then
|
||||||
|
print(name..": Missing operand.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local fs = require("sys.fs")
|
||||||
|
local newDir = args[1]
|
||||||
|
if newDir:sub(1, 1) ~= "/" then
|
||||||
|
newDir = syscall.getcwd().."/"..newDir
|
||||||
|
end
|
||||||
|
|
||||||
|
if newDir:sub(#newDir, #newDir) ~= "/" then
|
||||||
|
newDir = newDir.."/"
|
||||||
|
end
|
||||||
|
|
||||||
|
if fs.isDir(newDir) then
|
||||||
|
print(name..": Cannot create directory '"..args[1].."': Directory already exists.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
fs.mkdir(newDir)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
||||||
|
print(".. *. .. | "..userhost)
|
||||||
|
print(" *= +@* +* | "..string.rep("-",#userhost))
|
||||||
|
print(" .@#. -@@@= :#@. | OS: "..(syscall.version() or "Unknown"))
|
||||||
|
print(" =@@+ *@@@# +@@= | Host: "..(syscall.getHost() or "Unknown"))
|
||||||
|
print(" %@@%: *@@@# -%@@% | Uptime: "..(syscall.getUptime() or "Unknown"))
|
||||||
|
print(" :@@@@+ *@@@# .*@@@@: | Tasks: "..tostring((#syscall.getTasks() or "Unknown")))
|
||||||
|
print(" :*@@@%- *@@@# -@@@@*: | Packages: ".."Unknown")
|
||||||
|
print(" =%@@#. *@@@# .#@@%= | Shell: "..(syscall.getEnviron("SHELL") or "Unknown"))
|
||||||
|
print(" :=. :*@@= *@@@# =@@+: .=: | ")
|
||||||
|
print(" %@#=..*# +@@@# #*..=#@# | ")
|
||||||
|
print(" .@@@@+=# .%@%: #=+@@@@. | ")
|
||||||
|
print(" .....=# -@= *+...:. | ")
|
||||||
|
print(" -*%*-@= - =@-*%*- | ")
|
||||||
|
print(" -@*. -@%. :%@- :*@- | ")
|
||||||
|
print(" .#@#@* | ")
|
||||||
|
print(" -#- | ")
|
||||||
|
print(" | ")
|
||||||
@@ -9,7 +9,7 @@ local currentUid = syscall.getuid()
|
|||||||
|
|
||||||
local targetUid
|
local targetUid
|
||||||
if targetName then
|
if targetName then
|
||||||
targetUid = syscall.getuid(targetName)
|
targetUid = syscall.auth_getuid(targetName)
|
||||||
if not targetUid then
|
if not targetUid then
|
||||||
print("passwd: user '" .. targetName .. "' does not exist")
|
print("passwd: user '" .. targetName .. "' does not exist")
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
@@ -36,7 +36,7 @@ if currentUid ~= 0 then
|
|||||||
if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end
|
if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
else cur=cur..ch; syscall.write(1,"*") end
|
else cur=cur..ch; syscall.write(1,"*") end
|
||||||
end
|
end
|
||||||
local ok, err = syscall.elevate(targetName, cur)
|
local ok, err = syscall.auth_elevate(targetName, cur)
|
||||||
if not ok then
|
if not ok then
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("passwd: authentication failure")
|
print("passwd: authentication failure")
|
||||||
@@ -71,7 +71,7 @@ if pw1 ~= pw2 then
|
|||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = syscall.setpassword(targetUid, pw1)
|
local ok, err = syscall.auth_setpassword(targetUid, pw1)
|
||||||
if not ok then
|
if not ok then
|
||||||
print("passwd: " .. tostring(err))
|
print("passwd: " .. tostring(err))
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
--:Minify:--
|
|
||||||
for i,v in ipairs(syscall.getTasks()) do
|
for i,v in ipairs(syscall.getTasks()) do
|
||||||
local task = syscall.getTask(v)
|
local task = syscall.getTask(v)
|
||||||
print(task.pid,task.username,task.name,task.status)
|
print(task.pid,task.username,task.name,task.status)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print(syscall.getcwd())
|
||||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
local fs = require("sys.fs")
|
||||||
|
local bashStr = fs.readAllText("/bin/bash")
|
||||||
|
local bashFun = load(bashStr)
|
||||||
|
syscall.spawn(bashFun, "bash")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
syscall.chown("/bin", 0, 0)
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local targetUser = ({ ... })[1] or "root"
|
local fs = require("sys.fs")
|
||||||
local currentUid = syscall.getuid()
|
|
||||||
local targetUid = syscall.getuidbyname(targetUser)
|
|
||||||
|
|
||||||
|
local targetUser = ({...})[1] or "root"
|
||||||
|
|
||||||
|
local currentUid = syscall.getuid()
|
||||||
|
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||||
|
|
||||||
|
local targetUid = syscall.auth_getuid(targetUser)
|
||||||
if not targetUid then
|
if not targetUid then
|
||||||
print("su: user '" .. targetUser .. "' does not exist")
|
print("su: user '" .. targetUser .. "' does not exist")
|
||||||
syscall.exit(1)
|
syscall.exit(1)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if currentUid ~= 0 then
|
local ok, err
|
||||||
|
if currentUid == 0 then
|
||||||
|
ok = true
|
||||||
|
else
|
||||||
printInline("Password: ")
|
printInline("Password: ")
|
||||||
local pw = ""
|
local pw = ""
|
||||||
while true do
|
while true do
|
||||||
@@ -21,11 +28,12 @@ if currentUid ~= 0 then
|
|||||||
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; syscall.write(1, "*")
|
pw = pw .. ch
|
||||||
|
syscall.write(1, "*")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = syscall.elevate(targetUser, pw)
|
ok, err = syscall.auth_elevate(targetUser, pw)
|
||||||
if not ok then
|
if not ok then
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("su: Authentication failure")
|
print("su: Authentication failure")
|
||||||
@@ -34,23 +42,31 @@ if currentUid ~= 0 then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
syscall.setuid(targetUid)
|
if currentUid == 0 then
|
||||||
|
syscall.setuid(targetUid)
|
||||||
|
end
|
||||||
|
|
||||||
local pwent = syscall.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 "/"
|
||||||
|
|
||||||
local ok_cd, err_cd = pcall(syscall.chdir, homedir)
|
syscall.chdir(homedir)
|
||||||
if not ok_cd then
|
|
||||||
homedir = "/"
|
|
||||||
syscall.chdir(homedir)
|
|
||||||
end
|
|
||||||
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 ok, err = pcall(syscall.exec, shell)
|
local shellText = fs.readAllText(shell)
|
||||||
if not ok then
|
if not shellText then
|
||||||
print("su: cannot exec shell '" .. shell .. "': " .. tostring(err))
|
print("su: shell not found: " .. shell)
|
||||||
syscall.exit(1)
|
syscall.exit(1)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local shellFn, loadErr = load(shellText, "@" .. shell)
|
||||||
|
if not shellFn then
|
||||||
|
print("su: cannot load shell: " .. tostring(loadErr))
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
shellFn()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local fs = require("fs")
|
local fs = require("sys.fs")
|
||||||
|
|
||||||
local cmdArgs = {...}
|
local cmdArgs = {...}
|
||||||
local targetUser = "root"
|
local targetUser = "root"
|
||||||
@@ -10,7 +10,7 @@ if cmdArgs[i] == "-u" then
|
|||||||
local uarg = cmdArgs[i] or "root"
|
local uarg = cmdArgs[i] or "root"
|
||||||
local numUid = tonumber(uarg)
|
local numUid = tonumber(uarg)
|
||||||
if numUid then
|
if numUid then
|
||||||
local pwent = syscall.getpasswd(numUid)
|
local pwent = syscall.auth_getpasswd(numUid)
|
||||||
targetUser = (pwent and pwent.username) or uarg
|
targetUser = (pwent and pwent.username) or uarg
|
||||||
else
|
else
|
||||||
targetUser = uarg
|
targetUser = uarg
|
||||||
@@ -31,7 +31,7 @@ for j = i + 1, #cmdArgs do restArgs[#restArgs + 1] = cmdArgs[j] end
|
|||||||
local currentUid = syscall.getuid()
|
local currentUid = syscall.getuid()
|
||||||
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||||
|
|
||||||
local targetUid = syscall.getuidbyname(targetUser)
|
local targetUid = syscall.auth_getuid(targetUser)
|
||||||
if not targetUid then
|
if not targetUid then
|
||||||
print("sudo: user '" .. targetUser .. "' does not exist")
|
print("sudo: user '" .. targetUser .. "' does not exist")
|
||||||
syscall.exit(1)
|
syscall.exit(1)
|
||||||
@@ -39,7 +39,7 @@ if not targetUid then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if currentUid ~= 0 then
|
if currentUid ~= 0 then
|
||||||
printInline("[sudo] password for root: ")
|
printInline("[sudo] password for " .. currentUser .. ": ")
|
||||||
local pw = ""
|
local pw = ""
|
||||||
while true do
|
while true do
|
||||||
local ch = syscall.read(0)
|
local ch = syscall.read(0)
|
||||||
@@ -55,7 +55,7 @@ if currentUid ~= 0 then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = syscall.elevate("root", pw)
|
local ok, err = syscall.auth_elevate(currentUser, pw)
|
||||||
if not ok then
|
if not ok then
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("sudo: Authentication failure")
|
print("sudo: Authentication failure")
|
||||||
@@ -63,7 +63,7 @@ if currentUid ~= 0 then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if targetUid ~= currentUid then
|
if targetUid ~= 0 then
|
||||||
syscall.setuid(targetUid)
|
syscall.setuid(targetUid)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -97,7 +97,7 @@ if not program then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local pwent = syscall.getpasswd(targetUid)
|
local pwent = syscall.auth_getpasswd(targetUid)
|
||||||
if pwent and pwent.homedir then
|
if pwent and pwent.homedir then
|
||||||
syscall.setEnviron("HOME", pwent.homedir)
|
syscall.setEnviron("HOME", pwent.homedir)
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
local syscalls=syscall.sysdump()
|
||||||
|
for i=1, #syscalls do
|
||||||
|
print(syscalls[i])
|
||||||
|
end
|
||||||
|
print("Total # of syscalls: "..tostring(#syscalls))
|
||||||
@@ -48,7 +48,7 @@ if not password then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local uid, err = syscall.newuser(
|
local uid, err = syscall.auth_newuser(
|
||||||
opt.username, password, opt.gid, opt.homedir, opt.shell or "/bin/hysh"
|
opt.username, password, opt.gid, opt.homedir, opt.shell or "/bin/hysh"
|
||||||
)
|
)
|
||||||
if not uid then
|
if not uid then
|
||||||
@@ -14,22 +14,22 @@ if not username then
|
|||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
end
|
end
|
||||||
|
|
||||||
local uid = syscall.getuid(username)
|
local uid = syscall.auth_getuid(username)
|
||||||
if not uid then
|
if not uid then
|
||||||
print("userdel: user '" .. username .. "' does not exist")
|
print("userdel: user '" .. username .. "' does not exist")
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
end
|
end
|
||||||
|
|
||||||
local pwent = syscall.getpasswd(uid)
|
local pwent = syscall.auth_getpasswd(uid)
|
||||||
|
|
||||||
local ok, err = syscall.deleteuser(uid)
|
local ok, err = syscall.auth_deleteuser(uid)
|
||||||
if not ok then
|
if not ok then
|
||||||
print("userdel: " .. tostring(err))
|
print("userdel: " .. tostring(err))
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
end
|
end
|
||||||
|
|
||||||
if removeHome and pwent and pwent.homedir then
|
if removeHome and pwent and pwent.homedir then
|
||||||
local fs = require("fs")
|
local fs = require("sys.fs")
|
||||||
local ok2, err2 = pcall(function()
|
local ok2, err2 = pcall(function()
|
||||||
local function rmdir(path)
|
local function rmdir(path)
|
||||||
for _, f in ipairs(fs.list(path) or {}) do
|
for _, f in ipairs(fs.list(path) or {}) do
|
||||||
@@ -27,7 +27,7 @@ if opt.lock and opt.unlock then
|
|||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
end
|
end
|
||||||
|
|
||||||
local uid = syscall.getuid(opt.username)
|
local uid = syscall.auth_getuid(opt.username)
|
||||||
if not uid then
|
if not uid then
|
||||||
print("usermod: user '" .. opt.username .. "' does not exist")
|
print("usermod: user '" .. opt.username .. "' does not exist")
|
||||||
syscall.exit(1); return
|
syscall.exit(1); return
|
||||||
@@ -38,12 +38,12 @@ local function apply(fn, ...)
|
|||||||
if not ok then print("usermod: " .. tostring(err)); syscall.exit(1) end
|
if not ok then print("usermod: " .. tostring(err)); syscall.exit(1) end
|
||||||
end
|
end
|
||||||
|
|
||||||
if opt.newname then apply(syscall.setusername, uid, opt.newname) end
|
if opt.newname then apply(syscall.auth_setusername, uid, opt.newname) end
|
||||||
if opt.password then apply(syscall.setpassword, uid, opt.password) end
|
if opt.password then apply(syscall.auth_setpassword, uid, opt.password) end
|
||||||
if opt.gid then apply(syscall.setgid, uid, opt.gid) end
|
if opt.gid then apply(syscall.auth_setgid, uid, opt.gid) end
|
||||||
if opt.homedir then apply(syscall.sethomedir, uid, opt.homedir) end
|
if opt.homedir then apply(syscall.auth_sethomedir, uid, opt.homedir) end
|
||||||
if opt.shell then apply(syscall.setshell, uid, opt.shell) end
|
if opt.shell then apply(syscall.auth_setshell, uid, opt.shell) end
|
||||||
if opt.lock then apply(syscall.lockuser, uid) end
|
if opt.lock then apply(syscall.auth_lockuser, uid) end
|
||||||
if opt.unlock then apply(syscall.unlockuser, uid) end
|
if opt.unlock then apply(syscall.auth_unlockuser, uid) end
|
||||||
|
|
||||||
print("usermod: updated user '" .. opt.username .. "'")
|
print("usermod: updated user '" .. opt.username .. "'")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print((syscall.auth_whoami() or "Unknown"))
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
--:Minify:--
|
|
||||||
local args = {...}
|
local args = {...}
|
||||||
while true do
|
while true do
|
||||||
if #args == 0 then
|
if #args == 0 then
|
||||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
local io = {}
|
local io = {}
|
||||||
local fs = require("fs")
|
local fs = require("sys.fs")
|
||||||
|
|
||||||
function io.open(path, mode)
|
function io.open(path, mode)
|
||||||
return fs.open(path, mode)
|
return fs.open(path, mode)
|
||||||
|
|||||||
+9
-40
@@ -1,4 +1,4 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
local apis = kernel.apis
|
local apis = kernel.apis
|
||||||
local native = apis.peripheral
|
local native = apis.peripheral
|
||||||
@@ -37,7 +37,7 @@ function peripheral.isPresent(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.getType(peripheral)
|
function peripheral.getType(peripheral)
|
||||||
if type(peripheral) == "string" then
|
if type(peripheral) == "string" then -- Peripheral name passed
|
||||||
if native.isPresent(peripheral) then
|
if native.isPresent(peripheral) then
|
||||||
return native.getType(peripheral)
|
return native.getType(peripheral)
|
||||||
end
|
end
|
||||||
@@ -58,7 +58,7 @@ function peripheral.getType(peripheral)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.hasType(peripheral, peripheral_type)
|
function peripheral.hasType(peripheral, peripheral_type)
|
||||||
if type(peripheral) == "string" then
|
if type(peripheral) == "string" then -- Peripheral name passed
|
||||||
if native.isPresent(peripheral) then
|
if native.isPresent(peripheral) then
|
||||||
return native.hasType(peripheral, peripheral_type)
|
return native.hasType(peripheral, peripheral_type)
|
||||||
end
|
end
|
||||||
@@ -317,18 +317,7 @@ kernel.processes.cctmond = function()
|
|||||||
local eventType = event[1]
|
local eventType = event[1]
|
||||||
local charOrKey = event[3]
|
local charOrKey = event[3]
|
||||||
|
|
||||||
local ctrlKeyMap = {
|
-- Update modifier keys
|
||||||
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
|
|
||||||
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
|
|
||||||
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
|
|
||||||
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
|
|
||||||
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
|
|
||||||
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
|
|
||||||
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
|
|
||||||
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
|
|
||||||
[apis.keys.y]=25, [apis.keys.z]=26,
|
|
||||||
}
|
|
||||||
|
|
||||||
if eventType == "keyPressed" then
|
if eventType == "keyPressed" then
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||||
ctrl = true
|
ctrl = true
|
||||||
@@ -336,31 +325,11 @@ kernel.processes.cctmond = function()
|
|||||||
alt = true
|
alt = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ctrl then
|
-- Handle Ctrl+C
|
||||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
if ctrl and charOrKey == apis.keys.c then
|
||||||
if ctrlByte then
|
|
||||||
if ctrlByte == 3 then
|
|
||||||
for _, task in ipairs(syscall.getTasks()) do
|
for _, task in ipairs(syscall.getTasks()) do
|
||||||
syscall.sigsend(task, 1)
|
syscall.sigsend(task, 1) -- SIGINT
|
||||||
end
|
end
|
||||||
else
|
|
||||||
fifo.push(string.char(ctrlByte))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local specialKeyMap = {
|
|
||||||
[apis.keys.up] = "[A",
|
|
||||||
[apis.keys.down] = "[B",
|
|
||||||
[apis.keys.right] = "[C",
|
|
||||||
[apis.keys.left] = "[D",
|
|
||||||
[apis.keys.home] = "[H",
|
|
||||||
[apis.keys["end"]] = "[F",
|
|
||||||
[apis.keys.pageUp] = "[5~",
|
|
||||||
[apis.keys.pageDown] = "[6~",
|
|
||||||
[apis.keys.delete] = "[3~",
|
|
||||||
}
|
|
||||||
local special = specialKeyMap[charOrKey]
|
|
||||||
if special then fifo.push(special) end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif eventType == "keyReleased" then
|
elseif eventType == "keyReleased" then
|
||||||
@@ -385,10 +354,10 @@ kernel.processes.cctmond = function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
newtty(apis.term, "1", fifo.pop)
|
newtty(apis.term, "TTY1", fifo.pop)
|
||||||
|
|
||||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||||
v.setTextScale(.5)
|
v.setTextScale(.5)
|
||||||
v.write("Initializing...")
|
v.write("Initializing...")
|
||||||
newtty(v,tostring(i+1),function () end)
|
newtty(v,"TTY"..tostring(i+1),function () end)
|
||||||
end
|
end
|
||||||
+10
-42
@@ -1,8 +1,4 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
--- @diagnostic disable: duplicate-set-field
|
|
||||||
local kernel = ...
|
|
||||||
kernel.allowGlobalOverwrites = true
|
|
||||||
|
|
||||||
function string.hasSuffix(str, suffix)
|
function string.hasSuffix(str, suffix)
|
||||||
return string.sub(str, #suffix + 1) == suffix
|
return string.sub(str, #suffix + 1) == suffix
|
||||||
end
|
end
|
||||||
@@ -72,24 +68,30 @@ end
|
|||||||
local function serialize(tbl, seen)
|
local function serialize(tbl, seen)
|
||||||
seen = seen or {}
|
seen = seen or {}
|
||||||
|
|
||||||
|
-- If we've seen this table before, return a placeholder to prevent infinite loops
|
||||||
if seen[tbl] then return '"[Circular Reference]"' end
|
if seen[tbl] then return '"[Circular Reference]"' end
|
||||||
|
|
||||||
|
-- Mark this table as seen
|
||||||
seen[tbl] = true
|
seen[tbl] = true
|
||||||
|
|
||||||
local output = "{"
|
local output = "{"
|
||||||
local first = true
|
local first = true
|
||||||
|
|
||||||
for i, v in pairs(tbl) do
|
for i, v in pairs(tbl) do
|
||||||
|
-- Handle comma placement more cleanly
|
||||||
if not first then output = output .. "," end
|
if not first then output = output .. "," end
|
||||||
first = false
|
first = false
|
||||||
|
|
||||||
|
-- Serialize Key
|
||||||
if type(i) == "string" then
|
if type(i) == "string" then
|
||||||
output = output .. "[\"" .. i .. "\"]="
|
output = output .. "[\"" .. i .. "\"]="
|
||||||
elseif type(i) == "number" then
|
elseif type(i) == "number" then
|
||||||
output = output .. "[" .. tostring(i) .. "]="
|
output = output .. "[" .. tostring(i) .. "]="
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Serialize Value
|
||||||
if type(v) == "table" then
|
if type(v) == "table" then
|
||||||
|
-- Pass the 'seen' table down to the recursive call
|
||||||
output = output .. serialize(v, seen)
|
output = output .. serialize(v, seen)
|
||||||
elseif type(v) == "string" then
|
elseif type(v) == "string" then
|
||||||
output = output .. "[=[" .. v .. "]=]"
|
output = output .. "[=[" .. v .. "]=]"
|
||||||
@@ -173,27 +175,6 @@ function table.indexOf(t, value)
|
|||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
|
||||||
function table.merge(...)
|
|
||||||
local args={...}
|
|
||||||
local out = {}
|
|
||||||
local outi = {}
|
|
||||||
|
|
||||||
for _,t in ipairs(args) do
|
|
||||||
for i,v in pairs(t) do
|
|
||||||
out[i]=v
|
|
||||||
end
|
|
||||||
for i,v in ipairs(t) do
|
|
||||||
outi[#outi+1]=v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i,v in ipairs(outi) do
|
|
||||||
out[i]=v
|
|
||||||
end
|
|
||||||
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.replace(s, target, repl)
|
function string.replace(s, target, repl)
|
||||||
local result = {}
|
local result = {}
|
||||||
local i = 1
|
local i = 1
|
||||||
@@ -229,12 +210,8 @@ function toHex(num)
|
|||||||
return string.format("%X", num)
|
return string.format("%X", num)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function makeSyscallProxy()
|
syscall = setmetatable({}, {
|
||||||
local backing = {}
|
|
||||||
return setmetatable(backing, {
|
|
||||||
__index = function(self, name)
|
__index = function(self, name)
|
||||||
local raw = rawget(self, name)
|
|
||||||
if raw ~= nil then return raw end
|
|
||||||
return function(...)
|
return function(...)
|
||||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
local res = table.pack(coroutine.yield("syscall", name, ...))
|
||||||
if res[1] then
|
if res[1] then
|
||||||
@@ -243,16 +220,7 @@ local function makeSyscallProxy()
|
|||||||
error(res[2], 2)
|
error(res[2], 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end
|
||||||
__newindex = function(self, k, v)
|
})
|
||||||
rawset(self, k, v)
|
|
||||||
end,
|
|
||||||
__metatable=false
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
syscall = makeSyscallProxy()
|
|
||||||
|
|
||||||
_makeSyscallProxy = makeSyscallProxy
|
|
||||||
|
|
||||||
table.serialize = serialize
|
table.serialize = serialize
|
||||||
@@ -0,0 +1,629 @@
|
|||||||
|
-- :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
-1
@@ -1,4 +1,4 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
local cache = {}
|
local cache = {}
|
||||||
kernel.searchpaths = {
|
kernel.searchpaths = {
|
||||||
+1
-1
@@ -16,7 +16,7 @@ proxy.getLabel = function() return "devfs" end
|
|||||||
proxy.attributes = function(path) return {
|
proxy.attributes = function(path) return {
|
||||||
size = 0,
|
size = 0,
|
||||||
modified = 0,
|
modified = 0,
|
||||||
created = 0
|
created = 0,
|
||||||
} end
|
} end
|
||||||
|
|
||||||
function proxy:open(path, mode)
|
function proxy:open(path, mode)
|
||||||
+8
-13
@@ -1,4 +1,3 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
|
|
||||||
local proxy = {}
|
local proxy = {}
|
||||||
@@ -8,9 +7,11 @@ proxy.address = "tmpfs0000"
|
|||||||
proxy.isvirt = true
|
proxy.isvirt = true
|
||||||
proxy.isReadOnly = function() return false end
|
proxy.isReadOnly = function() return false end
|
||||||
|
|
||||||
|
-- Space functions (just placeholders)
|
||||||
proxy.spaceUsed = function() return 0 end
|
proxy.spaceUsed = function() return 0 end
|
||||||
proxy.spaceTotal = function() return 0 end
|
proxy.spaceTotal = function() return 0 end
|
||||||
|
|
||||||
|
-- Writable operations
|
||||||
proxy.makeDirectory = function(_, path)
|
proxy.makeDirectory = function(_, path)
|
||||||
local steps = kernel.vfs.splitPath(path)
|
local steps = kernel.vfs.splitPath(path)
|
||||||
local step = data
|
local step = data
|
||||||
@@ -51,6 +52,7 @@ proxy.attributes = function(_, path)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Open files
|
||||||
function proxy:open(path, mode)
|
function proxy:open(path, mode)
|
||||||
local steps = kernel.vfs.splitPath(path)
|
local steps = kernel.vfs.splitPath(path)
|
||||||
local step = data
|
local step = data
|
||||||
@@ -74,27 +76,21 @@ function proxy:open(path, mode)
|
|||||||
local chunk = content:sub(pos, pos+amount-1)
|
local chunk = content:sub(pos, pos+amount-1)
|
||||||
pos = pos + #chunk
|
pos = pos + #chunk
|
||||||
return chunk
|
return chunk
|
||||||
end,
|
end
|
||||||
close = function() end,
|
|
||||||
}
|
}
|
||||||
elseif mode == "w" then
|
elseif mode == "w" then
|
||||||
step[filename] = ""
|
step[filename] = ""
|
||||||
local buf = {}
|
|
||||||
return {
|
return {
|
||||||
write = function(str)
|
write = function(str)
|
||||||
buf[#buf + 1] = str
|
step[filename] = str
|
||||||
end,
|
end
|
||||||
close = function()
|
|
||||||
step[filename] = table.concat(buf)
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
elseif mode == "a" then
|
elseif mode == "a" then
|
||||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||||
return {
|
return {
|
||||||
write = function(str)
|
write = function(str)
|
||||||
step[filename] = step[filename] .. str
|
step[filename] = step[filename] .. str
|
||||||
end,
|
end
|
||||||
close = function() end,
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error("EACCES")
|
error("EACCES")
|
||||||
@@ -127,8 +123,7 @@ function proxy:list(path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function proxy:fileExists(path)
|
function proxy:fileExists(path)
|
||||||
local t = self:type(path)
|
return pcall(function() return self:type(path) end)
|
||||||
return t == "file" or t == "directory"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.disks["tmpfs0000"] = proxy
|
kernel.disks["tmpfs0000"] = proxy
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---- :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
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
local socket = {}
|
||||||
|
|
||||||
|
function socket.socket()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function socket.bind()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.socket=socket
|
||||||
|
kernel.log("Loaded socket module")
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
--: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"
|
||||||
+5
-22
@@ -1,9 +1,8 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local args = {...}
|
local args = {...}
|
||||||
local kernel = args[1]
|
local kernel = args[1]
|
||||||
kernel._G = _G
|
kernel._G = _G
|
||||||
|
|
||||||
|
|
||||||
local function readonly(tbl)
|
local function readonly(tbl)
|
||||||
return setmetatable({}, {
|
return setmetatable({}, {
|
||||||
__index = function(_, key)
|
__index = function(_, key)
|
||||||
@@ -21,7 +20,7 @@ local function readonly(tbl)
|
|||||||
error("Attempt to modify global variable '" .. k .. "'", 2)
|
error("Attempt to modify global variable '" .. k .. "'", 2)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__pairs = function(self)
|
__pairs = function()
|
||||||
local function iter(_, key)
|
local function iter(_, key)
|
||||||
local nextKey, value = next(tbl, key)
|
local nextKey, value = next(tbl, key)
|
||||||
if type(value) == "table" then
|
if type(value) == "table" then
|
||||||
@@ -29,7 +28,7 @@ local function readonly(tbl)
|
|||||||
end
|
end
|
||||||
return nextKey, value
|
return nextKey, value
|
||||||
end
|
end
|
||||||
return iter, self, nil
|
return iter, tbl, nil
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__ipairs = function()
|
__ipairs = function()
|
||||||
@@ -50,24 +49,8 @@ local function readonly(tbl)
|
|||||||
__metatable = false
|
__metatable = false
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
local origLoad = load
|
|
||||||
|
|
||||||
kernel._U = readonly(kernel._G)
|
kernel._U = readonly(kernel._G)
|
||||||
|
kernel.allowGlobalOverwrites = true
|
||||||
kernel._U._G = kernel._U
|
kernel._U._G = kernel._U
|
||||||
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
|
kernel.allowGlobalOverwrites = false
|
||||||
|
|
||||||
function kernel.freshUserEnv()
|
|
||||||
local locals = {}
|
|
||||||
locals.syscall = _makeSyscallProxy()
|
|
||||||
|
|
||||||
local env = setmetatable(locals, {
|
|
||||||
__index = kernel._U,
|
|
||||||
__newindex = function(_, k, v) rawset(locals, k, v) end,
|
|
||||||
__metatable=false
|
|
||||||
})
|
|
||||||
|
|
||||||
locals._G = env
|
|
||||||
locals.load = function(a, b, c, d) return origLoad(a, b, c, d or env) end
|
|
||||||
|
|
||||||
return env
|
|
||||||
end
|
|
||||||
+34
-31
@@ -264,13 +264,11 @@ function auth.login(username, password)
|
|||||||
end
|
end
|
||||||
|
|
||||||
kernel.currentUID = uid
|
kernel.currentUID = uid
|
||||||
|
if kernel.currentProcess then
|
||||||
local _task = kernel.currentTask
|
kernel.currentProcess.uid = uid
|
||||||
if _task then
|
kernel.currentProcess.euid = uid
|
||||||
_task.uid = uid
|
kernel.currentProcess.gid = tonumber(entry[2]) or uid
|
||||||
_task.euid = uid
|
kernel.currentProcess.egid = tonumber(entry[2]) or uid
|
||||||
_task.gid = tonumber(entry[2]) or uid
|
|
||||||
_task.egid = tonumber(entry[2]) or uid
|
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
|
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
|
||||||
@@ -374,7 +372,7 @@ function auth.newUser(username, password, gid, homedir, shell)
|
|||||||
local uid = nextUID()
|
local uid = nextUID()
|
||||||
gid = tonumber(gid) or uid
|
gid = tonumber(gid) or uid
|
||||||
homedir = homedir or ("/home/" .. username)
|
homedir = homedir or ("/home/" .. username)
|
||||||
shell = shell or "/bin/hysh"
|
shell = shell or "/bin/sh"
|
||||||
|
|
||||||
passwd[#passwd + 1] = {
|
passwd[#passwd + 1] = {
|
||||||
tostring(uid),
|
tostring(uid),
|
||||||
@@ -394,8 +392,6 @@ function auth.newUser(username, password, gid, homedir, shell)
|
|||||||
|
|
||||||
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
||||||
kernel.vfs.mkdir(homedir)
|
kernel.vfs.mkdir(homedir)
|
||||||
-- Homedir must be owned by the new user, not root
|
|
||||||
pcall(kernel.vfs.chown, homedir, uid, uid)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
||||||
@@ -440,9 +436,11 @@ function auth.deleteUser(uid)
|
|||||||
if not entry then return nil, "No such user" end
|
if not entry then return nil, "No such user" end
|
||||||
local username = entry[3]
|
local username = entry[3]
|
||||||
|
|
||||||
|
-- Remove from passwd
|
||||||
for i, v in ipairs(passwd) do
|
for i, v in ipairs(passwd) do
|
||||||
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
|
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
|
||||||
end
|
end
|
||||||
|
-- Remove from shadow
|
||||||
for i, v in ipairs(shadow) do
|
for i, v in ipairs(shadow) do
|
||||||
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
|
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
|
||||||
end
|
end
|
||||||
@@ -465,6 +463,7 @@ function auth.lockUser(uid)
|
|||||||
local sEntry = getShadowByUID(uid)
|
local sEntry = getShadowByUID(uid)
|
||||||
if not sEntry then return nil, "No shadow entry for uid" end
|
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
|
if sEntry[3]:sub(1,1) ~= "!" then
|
||||||
sEntry[3] = "!" .. sEntry[3]
|
sEntry[3] = "!" .. sEntry[3]
|
||||||
end
|
end
|
||||||
@@ -568,6 +567,9 @@ function auth.setGID(uid, gid)
|
|||||||
return true
|
return true
|
||||||
end
|
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)
|
function auth.elevate(targetUsername, password)
|
||||||
if type(targetUsername) ~= "string" or type(password) ~= "string" then
|
if type(targetUsername) ~= "string" or type(password) ~= "string" then
|
||||||
return nil, "Authentication failure"
|
return nil, "Authentication failure"
|
||||||
@@ -591,32 +593,33 @@ function auth.elevate(targetUsername, password)
|
|||||||
return nil, "Authentication failure"
|
return nil, "Authentication failure"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Directly set the calling task's uid — trusted kernel path
|
||||||
local task = kernel.currentTask
|
local task = kernel.currentTask
|
||||||
local prevUid = task.uid
|
local prevUid = task.uid
|
||||||
task.uid = 0
|
task.uid = uid
|
||||||
task.euid = 0
|
task.euid = uid
|
||||||
task.gid = 0
|
task.gid = tonumber(entry[2]) or uid
|
||||||
task.egid = 0
|
task.egid = tonumber(entry[2]) or uid
|
||||||
kernel.uid = 0
|
kernel.uid = uid
|
||||||
|
|
||||||
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")")
|
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")")
|
||||||
return true, uid
|
return true, uid
|
||||||
end
|
end
|
||||||
|
|
||||||
if kernel.syscalls then
|
if kernel.syscalls then
|
||||||
kernel.syscalls["login"] = auth.login
|
kernel.syscalls["auth_login"] = auth.login
|
||||||
kernel.syscalls["setpassword"] = auth.setPassword
|
kernel.syscalls["auth_setpassword"] = auth.setPassword
|
||||||
kernel.syscalls["setusername"] = auth.setUsername
|
kernel.syscalls["auth_setusername"] = auth.setUsername
|
||||||
kernel.syscalls["newuser"] = auth.newUser
|
kernel.syscalls["auth_newuser"] = auth.newUser
|
||||||
kernel.syscalls["whoami"] = auth.whoami
|
kernel.syscalls["auth_whoami"] = auth.whoami
|
||||||
kernel.syscalls["getuidbyname"]= auth.getUID
|
kernel.syscalls["auth_getuid"] = auth.getUID
|
||||||
kernel.syscalls["getpasswd"] = auth.getPasswd
|
kernel.syscalls["auth_getpasswd"] = auth.getPasswd
|
||||||
kernel.syscalls["elevate"] = auth.elevate
|
kernel.syscalls["auth_elevate"] = auth.elevate
|
||||||
kernel.syscalls["deleteuser"] = auth.deleteUser
|
kernel.syscalls["auth_deleteuser"] = auth.deleteUser
|
||||||
kernel.syscalls["lockuser"] = auth.lockUser
|
kernel.syscalls["auth_lockuser"] = auth.lockUser
|
||||||
kernel.syscalls["unlockuser"] = auth.unlockUser
|
kernel.syscalls["auth_unlockuser"] = auth.unlockUser
|
||||||
kernel.syscalls["listusers"] = auth.listUsers
|
kernel.syscalls["auth_listusers"] = auth.listUsers
|
||||||
kernel.syscalls["setshell"] = auth.setShell
|
kernel.syscalls["auth_setshell"] = auth.setShell
|
||||||
kernel.syscalls["sethomedir"] = auth.setHomedir
|
kernel.syscalls["auth_sethomedir"] = auth.setHomedir
|
||||||
kernel.syscalls["setgid"] = auth.setGID
|
kernel.syscalls["auth_setgid"] = auth.setGID
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
-- :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
@@ -1,4 +1,3 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
local kernel=...
|
||||||
local sysc=kernel.syscalls
|
local sysc=kernel.syscalls
|
||||||
kernel.gpio={}
|
kernel.gpio={}
|
||||||
+3
-1
@@ -1,4 +1,4 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
function print(...)
|
function print(...)
|
||||||
local args = {...}
|
local args = {...}
|
||||||
@@ -9,11 +9,13 @@ 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
|
||||||
+1
-6
@@ -1,13 +1,8 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
kernel.log("Loading init system...")
|
kernel.log("Loading init system...")
|
||||||
kernel.log("InitPath: " .. kernel.config.initPath)
|
kernel.log("InitPath: " .. kernel.config.initPath)
|
||||||
|
|
||||||
local initOk, initErr = pcall(kernel.vfs.access, kernel.config.initPath, "rx")
|
|
||||||
if not initOk then
|
|
||||||
kernel.PANIC("Init binary not executable: " .. kernel.config.initPath .. " (" .. tostring(initErr) .. ")")
|
|
||||||
end
|
|
||||||
|
|
||||||
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
||||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||||
kernel.vfs.close(handle)
|
kernel.vfs.close(handle)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
--: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
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
--: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")
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
local sys = {}
|
||||||
|
local fs = require("sys.fs")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return sys
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
local sys = {}
|
||||||
|
sys.fs = require("sys.fs")
|
||||||
|
sys.hpv = require("sys.hpv")
|
||||||
|
sys.ipc = require("sys.ipc")
|
||||||
|
return sys
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
local ipc = {}
|
||||||
|
|
||||||
|
return ipc
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
local term = {}
|
||||||
|
|
||||||
|
function term.clear()
|
||||||
|
coroutine.yield("VFS_write", 1, "\27C\25")
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.setCursorPos(x, y)
|
||||||
|
coroutine.yield("VFS_write", 1, "\27cs"..tostring(y)..";"..tostring(x).."\25")
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.size()
|
||||||
|
coroutine.yield("VFS_write", 1, "\27ts\25")
|
||||||
|
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
|
||||||
|
if not ok then error("Failed to get terminal size") end
|
||||||
|
local x, y = string.match(data, "%R(%d+);(%d+)\25")
|
||||||
|
return tonumber(x), tonumber(y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.getCursorPos()
|
||||||
|
coroutine.yield("VFS_write", 1, "\27gc\25")
|
||||||
|
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
|
||||||
|
if not ok then error("Failed to get cursor position") end
|
||||||
|
local y, x = string.match(data, "%R(%d+);(%d+)\25")
|
||||||
|
return tonumber(x), tonumber(y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.write(data)
|
||||||
|
coroutine.yield("VFS_write", 1, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.setTextColor(color)
|
||||||
|
local ok, err = coroutine.yield("VFS_type", 1)
|
||||||
|
if not ok then error(err) end
|
||||||
|
if ok ~= "tty" then return end
|
||||||
|
coroutine.yield("VFS_write", 1, "\27f"..tostring(color).."\25")
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.setBackgroundColor(color)
|
||||||
|
local ok, err = coroutine.yield("VFS_type", 1)
|
||||||
|
if not ok then error(err) end
|
||||||
|
if ok ~= "tty" then return end
|
||||||
|
coroutine.yield("VFS_write", 1, "\27b"..tostring(color).."\25")
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.isColor()
|
||||||
|
local ok, err = coroutine.yield("VFS_type", 1)
|
||||||
|
if not ok then error(err) end
|
||||||
|
return ok == "tty"
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.scroll(n)
|
||||||
|
coroutine.yield("VFS_write", 1, "\27S"..tostring(n).."\25")
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.setDefault(color, layer)
|
||||||
|
if layer then
|
||||||
|
coroutine.yield("VFS_write", 1, "\27F"..tostring(color).."\25")
|
||||||
|
else
|
||||||
|
coroutine.yield("VFS_write", 1, "\27B"..tostring(color).."\25")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.showCursor(show)
|
||||||
|
if show then
|
||||||
|
coroutine.yield("VFS_write", 1, "\27sc\25")
|
||||||
|
else
|
||||||
|
coroutine.yield("VFS_write", 1, "\27hc\25")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return term
|
||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local kernel=...
|
local kernel=...
|
||||||
local fs=require("fs")
|
local fs=require("sys.fs")
|
||||||
|
|
||||||
for i,v in pairs(kernel.processes) do
|
for i,v in pairs(kernel.processes) do
|
||||||
kernel.log("Spawning kernel task "..i)
|
kernel.log("Spawning kernel task "..i)
|
||||||
@@ -14,9 +14,6 @@ for i,v in pairs(kernel.processes) do
|
|||||||
end, i)
|
end, i)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not fs.exists("/bin/startup") then
|
|
||||||
fs.mkdir("/bin/startup")
|
|
||||||
end
|
|
||||||
local files = fs.list("/bin/startup")
|
local files = fs.list("/bin/startup")
|
||||||
if not files then error("Failed to list /bin/startup") end
|
if not files then error("Failed to list /bin/startup") end
|
||||||
for i,v in ipairs(files) do
|
for i,v in ipairs(files) do
|
||||||
@@ -41,6 +38,6 @@ for i,v in ipairs(files) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
sleep(5)
|
sleep(1)
|
||||||
kernel.saveLog()
|
kernel.saveLog()
|
||||||
end
|
end
|
||||||
@@ -71,6 +71,7 @@ local ok, err = xpcall(function()
|
|||||||
collectgarbage = true,
|
collectgarbage = true,
|
||||||
error = true,
|
error = true,
|
||||||
gcinfo = true,
|
gcinfo = true,
|
||||||
|
getfenv = true,
|
||||||
getmetatable = true,
|
getmetatable = true,
|
||||||
ipairs = true,
|
ipairs = true,
|
||||||
__inext = true,
|
__inext = true,
|
||||||
@@ -84,6 +85,7 @@ local ok, err = xpcall(function()
|
|||||||
rawlen = true,
|
rawlen = true,
|
||||||
rawset = true,
|
rawset = true,
|
||||||
select = true,
|
select = true,
|
||||||
|
setfenv = true,
|
||||||
setmetatable = true,
|
setmetatable = true,
|
||||||
string = true,
|
string = true,
|
||||||
table = true,
|
table = true,
|
||||||
@@ -252,6 +254,7 @@ local ok, err = xpcall(function()
|
|||||||
if not ok then displaySuperBadError(err) end
|
if not ok then displaySuperBadError(err) end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- time is in milliseconds
|
||||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
local startTime = computer.time()
|
local startTime = computer.time()
|
||||||
debug.sethook(co, function()
|
debug.sethook(co, function()
|
||||||
@@ -291,14 +294,6 @@ local ok, err = xpcall(function()
|
|||||||
queueEvent("componentAdded", "disk")
|
queueEvent("componentAdded", "disk")
|
||||||
elseif event[1] == "disk_eject" then
|
elseif event[1] == "disk_eject" then
|
||||||
queueEvent("componentRemoved", "disk")
|
queueEvent("componentRemoved", "disk")
|
||||||
elseif event[1] == "modem_message" then
|
|
||||||
queueEvent("modem_message", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "rednet_message" then
|
|
||||||
queueEvent("rednet_message", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "http_success" then
|
|
||||||
queueEvent("http_success", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "http_failure" then
|
|
||||||
queueEvent("http_failure", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "NoSleep" then
|
elseif event[1] == "NoSleep" then
|
||||||
exit = true
|
exit = true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
|
sleep(1)
|
||||||
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
||||||
-- UnBIOS by JackMacWindows
|
-- UnBIOS by JackMacWindows
|
||||||
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local kernel = ...
|
local kernel = ...
|
||||||
local apis = kernel.apis
|
local apis = kernel.apis
|
||||||
local native = apis.peripheral
|
local native = apis.peripheral
|
||||||
@@ -37,7 +37,7 @@ function peripheral.isPresent(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.getType(peripheral)
|
function peripheral.getType(peripheral)
|
||||||
if type(peripheral) == "string" then
|
if type(peripheral) == "string" then -- Peripheral name passed
|
||||||
if native.isPresent(peripheral) then
|
if native.isPresent(peripheral) then
|
||||||
return native.getType(peripheral)
|
return native.getType(peripheral)
|
||||||
end
|
end
|
||||||
@@ -58,7 +58,7 @@ function peripheral.getType(peripheral)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function peripheral.hasType(peripheral, peripheral_type)
|
function peripheral.hasType(peripheral, peripheral_type)
|
||||||
if type(peripheral) == "string" then
|
if type(peripheral) == "string" then -- Peripheral name passed
|
||||||
if native.isPresent(peripheral) then
|
if native.isPresent(peripheral) then
|
||||||
return native.hasType(peripheral, peripheral_type)
|
return native.hasType(peripheral, peripheral_type)
|
||||||
end
|
end
|
||||||
@@ -243,22 +243,6 @@ local function serializeBool(bool)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function newtty(obj, id, ev)
|
local function newtty(obj, id, ev)
|
||||||
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
|
||||||
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
|
||||||
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
|
||||||
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
|
||||||
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
|
||||||
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
|
||||||
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
|
||||||
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
|
||||||
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
|
||||||
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
|
||||||
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
|
||||||
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
|
||||||
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
|
||||||
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
|
||||||
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
|
||||||
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
|
||||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||||
if op=="type" then
|
if op=="type" then
|
||||||
return "character device"
|
return "character device"
|
||||||
@@ -333,18 +317,7 @@ kernel.processes.cctmond = function()
|
|||||||
local eventType = event[1]
|
local eventType = event[1]
|
||||||
local charOrKey = event[3]
|
local charOrKey = event[3]
|
||||||
|
|
||||||
local ctrlKeyMap = {
|
-- Update modifier keys
|
||||||
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
|
|
||||||
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
|
|
||||||
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
|
|
||||||
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
|
|
||||||
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
|
|
||||||
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
|
|
||||||
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
|
|
||||||
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
|
|
||||||
[apis.keys.y]=25, [apis.keys.z]=26,
|
|
||||||
}
|
|
||||||
|
|
||||||
if eventType == "keyPressed" then
|
if eventType == "keyPressed" then
|
||||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||||
ctrl = true
|
ctrl = true
|
||||||
@@ -352,31 +325,11 @@ kernel.processes.cctmond = function()
|
|||||||
alt = true
|
alt = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ctrl then
|
-- Handle Ctrl+C
|
||||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
if ctrl and charOrKey == apis.keys.c then
|
||||||
if ctrlByte then
|
|
||||||
if ctrlByte == 3 then
|
|
||||||
for _, task in ipairs(syscall.getTasks()) do
|
for _, task in ipairs(syscall.getTasks()) do
|
||||||
syscall.sigsend(task, 1)
|
syscall.sigsend(task, 1) -- SIGINT
|
||||||
end
|
end
|
||||||
else
|
|
||||||
fifo.push(string.char(ctrlByte))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local specialKeyMap = {
|
|
||||||
[apis.keys.up] = "[A",
|
|
||||||
[apis.keys.down] = "[B",
|
|
||||||
[apis.keys.right] = "[C",
|
|
||||||
[apis.keys.left] = "[D",
|
|
||||||
[apis.keys.home] = "[H",
|
|
||||||
[apis.keys["end"]] = "[F",
|
|
||||||
[apis.keys.pageUp] = "[5~",
|
|
||||||
[apis.keys.pageDown] = "[6~",
|
|
||||||
[apis.keys.delete] = "[3~",
|
|
||||||
}
|
|
||||||
local special = specialKeyMap[charOrKey]
|
|
||||||
if special then fifo.push(special) end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif eventType == "keyReleased" then
|
elseif eventType == "keyReleased" then
|
||||||
@@ -401,10 +354,10 @@ kernel.processes.cctmond = function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
newtty(apis.term, "1", fifo.pop)
|
newtty(apis.term, "TTY1", fifo.pop)
|
||||||
|
|
||||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||||
v.setTextScale(.5)
|
v.setTextScale(.5)
|
||||||
v.write("Initializing...")
|
v.write("Initializing...")
|
||||||
newtty(v,tostring(i+1),function () end)
|
newtty(v,"TTY"..tostring(i+1),function () end)
|
||||||
end
|
end
|
||||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
-- 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
|
||||||
|
}
|
||||||
+2
-9
@@ -254,10 +254,11 @@ local ok, err = xpcall(function()
|
|||||||
if not ok then displaySuperBadError(err) end
|
if not ok then displaySuperBadError(err) end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- time is in milliseconds
|
||||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
local startTime = computer.time()
|
local startTime = computer.time()
|
||||||
debug.sethook(co, function()
|
debug.sethook(co, function()
|
||||||
if computer.time() > startTime + timeout then
|
if computer.time() - startTime > timeout then
|
||||||
return coroutine.yield("timeout")
|
return coroutine.yield("timeout")
|
||||||
end
|
end
|
||||||
end, "", 1000)
|
end, "", 1000)
|
||||||
@@ -293,14 +294,6 @@ local ok, err = xpcall(function()
|
|||||||
queueEvent("componentAdded", "disk")
|
queueEvent("componentAdded", "disk")
|
||||||
elseif event[1] == "disk_eject" then
|
elseif event[1] == "disk_eject" then
|
||||||
queueEvent("componentRemoved", "disk")
|
queueEvent("componentRemoved", "disk")
|
||||||
elseif event[1] == "modem_message" then
|
|
||||||
queueEvent("modem_message", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "rednet_message" then
|
|
||||||
queueEvent("rednet_message", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "http_success" then
|
|
||||||
queueEvent("http_success", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "http_failure" then
|
|
||||||
queueEvent("http_failure", table.unpack(event, 2))
|
|
||||||
elseif event[1] == "NoSleep" then
|
elseif event[1] == "NoSleep" then
|
||||||
exit = true
|
exit = true
|
||||||
end
|
end
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
U $;/
|
U $;/
|
||||||
U devfs0000;/dev/
|
U devfs0000;/dev/
|
||||||
U tmpfs0000;/tmp/
|
U tmpfs0000;/tmp/
|
||||||
U procfs0000;/proc/
|
|
||||||
@@ -7,8 +7,9 @@ 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.2.3"
|
kernel.version="HyperionOS V1.0.0"
|
||||||
kernel.process = "Kernel"
|
kernel.process = "Kernel"
|
||||||
kernel.users={[0]="root",[1]="User"}
|
kernel.users={[0]="root",[1]="User"}
|
||||||
kernel.hostname = "hyperion"
|
kernel.hostname = "hyperion"
|
||||||
@@ -27,15 +28,15 @@ local windowsExp = false
|
|||||||
|
|
||||||
function kernel.log(msg, level, c)
|
function kernel.log(msg, level, c)
|
||||||
c=c or 12
|
c=c or 12
|
||||||
kernel.LOG_Text = kernel.LOG_Text..string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
kernel.LOG_Text = kernel.LOG_Text..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||||
if kernel.status == "start" then
|
if kernel.status == "start" then
|
||||||
screen:setTextColor(c)
|
screen:setTextColor(c)
|
||||||
screen:print(tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
screen:print(string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||||
elseif kernel.status == "term" then
|
elseif kernel.status == "term" then
|
||||||
kernel.standbyTask=kernel.currentTask
|
kernel.standbyTask=kernel.currentTask
|
||||||
kernel.currentTask=kernel.kernelTask
|
kernel.currentTask=kernel.kernelTask
|
||||||
kernel.vfs.devctl(1,"sfgc",c)
|
kernel.vfs.devctl(1,"sfgc",c)
|
||||||
kernel.vfs.write(1,tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
kernel.vfs.write(1,string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||||
kernel.currentTask=kernel.standbyTask
|
kernel.currentTask=kernel.standbyTask
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -113,9 +114,8 @@ local split = function(str, delim, maxResultCountOrNil)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not ifs.isFile("/boot/boot.cfg") then
|
if not ifs.isFile("/boot/boot.cfg") then
|
||||||
kernel.log("First boot detected writing boot.cfg", "INFO", 3)
|
kernel.log("boot.cfg missing or corrupted!, Attempting to write recovery boot.cfg", "ERROR", 2)
|
||||||
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
||||||
kernel.firstBoot=true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
||||||
@@ -185,17 +185,10 @@ end
|
|||||||
|
|
||||||
kernel.log("Gathering modules")
|
kernel.log("Gathering modules")
|
||||||
for _, i in ipairs(ifs.list("/lib/modules")) do
|
for _, i in ipairs(ifs.list("/lib/modules")) do
|
||||||
local modlist = ifs.list("/lib/modules/"..i)
|
for _,v in ipairs(ifs.list("/lib/modules/"..i)) do
|
||||||
if not modlist then
|
|
||||||
kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8)
|
|
||||||
else
|
|
||||||
for _,v in ipairs(modlist) do
|
|
||||||
local prior=tonumber(v:sub(1,2))
|
local prior=tonumber(v:sub(1,2))
|
||||||
if prior then
|
|
||||||
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.ifs=ifs
|
kernel.ifs=ifs
|
||||||
@@ -252,12 +245,7 @@ kernel.syscalls["sysdump"]=function()
|
|||||||
end
|
end
|
||||||
return rv
|
return rv
|
||||||
end
|
end
|
||||||
kernel.syscalls["reboot"]=function()
|
kernel.syscalls["test"]=function() return true end
|
||||||
kernel.computer:reboot()
|
|
||||||
end
|
|
||||||
kernel.syscalls["shutdown"]=function()
|
|
||||||
kernel.computer:reboot()
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("Running modules")
|
kernel.log("Running modules")
|
||||||
for _,p in ipairs(modules) do
|
for _,p in ipairs(modules) do
|
||||||
@@ -278,7 +266,6 @@ for _,p in ipairs(modules) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
kernel.log("Kernel initialized successfully.")
|
kernel.log("Kernel initialized successfully.")
|
||||||
kernel.saveLog()
|
|
||||||
kernel.status="running"
|
kernel.status="running"
|
||||||
kernel.main()
|
kernel.main()
|
||||||
if kernel.status=="panic" then
|
if kernel.status=="panic" then
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
local fs={}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
-- This file is auto-generated during the build process.
|
-- This file is auto-generated during the build process.
|
||||||
-- DEFAULT BOOT CONFIGURATION FILE
|
-- DEFAULT BOOT CONFIGURATION FILE
|
||||||
return {
|
return {
|
||||||
initPath = "/sbin/init",
|
initPath = "/sbin/init.lua",
|
||||||
maxOpenFiles = 128,
|
maxOpenFiles = 128,
|
||||||
maxFilesPerTask = 16,
|
maxFilesPerTask = 16,
|
||||||
preempt=true
|
preempt=true
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1,2 @@
|
|||||||
0:0:root:/root:/bin/hysh
|
0:0:root:/root:/bin/hysh
|
||||||
|
1000:1000:testuser:/home/testuser:/bin/hysh
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0:bcdefghijklmnopqrstuvwxyzABCDEzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789lmnopqrstuvwxyzABCDEOPQRSTUVWXYBCDEFGHIJKLMNOPQRSTUVWXYZ01qrstuvwxyzABCDEFGHIJKLMNOPQRSklmnopqrstuvwxyzABCDKLMNOPQRSTUVWXYZ012345CDEFGHIJKLMNOPQRSTUVWXYZ012345:ae6dedb263f6d68c01a49a2bb6f2512c3ea2854dbac9d786fba7c774b47b601d
|
||||||
|
1000:hijklmnopqrstuvwxyzABCDDEFGHIJKLMNOPQRSTUdefghijklmnopqrstuvwxyzABCDEFGHIJKLLMNOPklmnopqrstuvwxyzABCDEFGHIJKLMvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZYZ0123456BCDEFGHIJKLMNOPQRSTUV:27b25b4cc851e668a4ac14f453e1906b6a07c50a175dd636632d8036a1e91485
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
-- :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
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user