Compare commits
2 Commits
1.2.4-dev_
...
5b8043b6ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b8043b6ec | ||
|
|
2a6a11a701 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -5,7 +5,6 @@
|
||||
"syscall",
|
||||
"printf",
|
||||
"printInline",
|
||||
"toHex",
|
||||
"loadcstr"
|
||||
"toHex"
|
||||
]
|
||||
}
|
||||
20
Makefile
20
Makefile
@@ -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 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
|
||||
|
||||
- Functionality is split into kernel modules (`.kmod`)
|
||||
|
||||
BIN
Src/Hyperion-bash/bin/.meta
Normal file
BIN
Src/Hyperion-bash/bin/.meta
Normal file
Binary file not shown.
295
Src/Hyperion-bash/bin/bash
Normal file
295
Src/Hyperion-bash/bin/bash
Normal file
@@ -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
|
||||
94
Src/Hyperion-bash/bin/bashex
Normal file
94
Src/Hyperion-bash/bin/bashex
Normal file
@@ -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
|
||||
24
Src/Hyperion-bash/bin/cat
Normal file
24
Src/Hyperion-bash/bin/cat
Normal file
@@ -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("")
|
||||
1
Src/Hyperion-bash/bin/clear
Normal file
1
Src/Hyperion-bash/bin/clear
Normal file
@@ -0,0 +1 @@
|
||||
syscall.devctl(1,"clear")
|
||||
2
Src/Hyperion-bash/bin/echo
Normal file
2
Src/Hyperion-bash/bin/echo
Normal file
@@ -0,0 +1,2 @@
|
||||
local args = {...}
|
||||
print(table.concat(args, " "))
|
||||
292
Src/Hyperion-bash/bin/help
Normal file
292
Src/Hyperion-bash/bin/help
Normal file
@@ -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)
|
||||
@@ -3,11 +3,11 @@
|
||||
-- 1=white, 2=red, 3=green, 4=blue, 5=cyan, 6=magenta, 7=yellow
|
||||
-- 8=orange, 9=lime, 10=lightcyan, 11=brown, 12=darkgrey, 13=lightgrey, 14=purple, 15=chartreuse, 16=black
|
||||
|
||||
local C_LOGO = 0x00FFFF -- cyan
|
||||
local C_WHITE = 0xFFFFFF -- white
|
||||
local C_LABEL = 0xDBDBDB -- light grey (key names)
|
||||
local C_SEP = 0x6D6D6D -- dark grey (---- separator)
|
||||
local C_USER = 0x00FF00 -- green (user@host)
|
||||
local C_LOGO = 5 -- cyan
|
||||
local C_WHITE = 1 -- white
|
||||
local C_LABEL = 13 -- light grey (key names)
|
||||
local C_SEP = 12 -- dark grey (---- separator)
|
||||
local C_USER = 3 -- green (user@host)
|
||||
|
||||
local function c(col) syscall.devctl(1, "sfgc", col) end
|
||||
|
||||
@@ -33,6 +33,7 @@ local host_str = syscall.getHost() or "Unknown"
|
||||
local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str
|
||||
|
||||
local info = {
|
||||
-- {label, value} label=nil means print value as-is (userhost / separator)
|
||||
{nil, userhost},
|
||||
{nil, string.rep("-", #userhost)},
|
||||
{"OS", syscall.version() or "Unknown"},
|
||||
@@ -41,7 +42,7 @@ local info = {
|
||||
{"Uptime", formatUptime(syscall.getUptime() or 0)},
|
||||
{"Tasks", tostring(#(syscall.getTasks() or {}))},
|
||||
{"Shell", syscall.getEnviron("SHELL") or "Unknown"},
|
||||
{"Terminal", "tty1"},
|
||||
{"Terminal", "TTY1"},
|
||||
{"UID", tostring(syscall.getuid())},
|
||||
{"Packages", "n/a (spm)"},
|
||||
}
|
||||
@@ -70,12 +71,15 @@ local lines = math.max(#logo, #info)
|
||||
for i = 1, lines do
|
||||
local logo_str = logo[i] or string.rep(" ", 36)
|
||||
|
||||
-- print logo segment in cyan
|
||||
c(C_LOGO)
|
||||
printInline(logo_str)
|
||||
|
||||
-- print separator pipe
|
||||
c(C_LABEL)
|
||||
printInline("| ")
|
||||
|
||||
-- print info segment
|
||||
local row = info[i]
|
||||
if row then
|
||||
if row[1] == nil and i == 1 then
|
||||
299
Src/Hyperion-bash/bin/hysh
Normal file
299
Src/Hyperion-bash/bin/hysh
Normal file
@@ -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
|
||||
94
Src/Hyperion-bash/bin/hyshex
Normal file
94
Src/Hyperion-bash/bin/hyshex
Normal file
@@ -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
|
||||
|
||||
if args[1] then
|
||||
uid = syscall.getuid(args[1])
|
||||
uid = syscall.auth_getuid(args[1])
|
||||
if not uid then
|
||||
print("id: user '" .. args[1] .. "' does not exist")
|
||||
syscall.exit(1); return
|
||||
@@ -12,7 +12,7 @@ else
|
||||
uid = syscall.getuid()
|
||||
end
|
||||
|
||||
local pwent = syscall.getpasswd(uid)
|
||||
local pwent = syscall.auth_getpasswd(uid)
|
||||
local name = (pwent and pwent.username) or tostring(uid)
|
||||
local gid = (pwent and pwent.gid) or uid
|
||||
|
||||
178
Src/Hyperion-bash/bin/login
Normal file
178
Src/Hyperion-bash/bin/login
Normal file
@@ -0,0 +1,178 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/TTY1","r") --stdin (fd 0)
|
||||
syscall.open("/dev/tty/TTY1","w") --stdout (fd 1)
|
||||
syscall.open("/dev/null","w") --stderr (fd 2)
|
||||
|
||||
local fs = require("sys.fs")
|
||||
|
||||
local MAX_ATTEMPTS = 3
|
||||
|
||||
local function readLine(mask)
|
||||
local input = ""
|
||||
while true do
|
||||
local ch = syscall.read(0)
|
||||
if not ch or ch == "" then
|
||||
-- buffer empty, spin
|
||||
elseif ch == "\n" then
|
||||
syscall.write(1, "\n")
|
||||
return input
|
||||
elseif ch == "\b" then
|
||||
if #input > 0 then
|
||||
input = input:sub(1, -2)
|
||||
syscall.write(1, "\b \b")
|
||||
end
|
||||
else
|
||||
input = input .. ch
|
||||
syscall.write(1, mask or ch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function firstBoot()
|
||||
local shadow = fs.readAllText("/etc/shadow") or ""
|
||||
if shadow:match("%S") then return end
|
||||
|
||||
syscall.devctl(1, "clear")
|
||||
syscall.devctl(1, "spos", 1, 1)
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
syscall.write(1, "HyperionOS First Boot Setup\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.write(1, "No root password is set. Please create one now.\n\n")
|
||||
|
||||
while true do
|
||||
syscall.write(1, "New root password: ")
|
||||
local pw1 = readLine("*")
|
||||
syscall.write(1, "Confirm password: ")
|
||||
local pw2 = readLine("*")
|
||||
|
||||
if pw1 ~= pw2 then
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.write(1, "Passwords do not match. Try again.\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
elseif #pw1 < 6 then
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.write(1, "Password too short (minimum 6 characters).\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
else
|
||||
local ok, err = syscall.auth_setpassword(0, pw1)
|
||||
if ok then
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
syscall.write(1, "Root password set.\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
sleep(0.5)
|
||||
break
|
||||
else
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.write(1, "Error: " .. tostring(err) .. "\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function spawnShell(username, uid, shell, homedir)
|
||||
local shellText = fs.readAllText(shell)
|
||||
if not shellText then
|
||||
syscall.write(1, "login: shell not found: " .. shell .. "\n")
|
||||
sleep(2)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Spawn a wrapper that loads and runs the shell, reporting any error back
|
||||
-- via exit code channel so we can display it
|
||||
local errFifo = {}
|
||||
|
||||
local proc = syscall.spawn(function()
|
||||
syscall.setuid(uid)
|
||||
syscall.chdir(homedir)
|
||||
syscall.setEnviron("HOME", homedir)
|
||||
syscall.setEnviron("USER", username)
|
||||
syscall.setEnviron("SHELL", shell)
|
||||
syscall.setEnviron("PATH", "/bin/")
|
||||
|
||||
local shellFn, loadErr = load(shellText, "@" .. shell)
|
||||
if not shellFn then
|
||||
-- Report load error via log and a recognizable exit code
|
||||
syscall.log("login: shell load error: " .. tostring(loadErr), "ERROR")
|
||||
syscall.exit(-1)
|
||||
return
|
||||
end
|
||||
|
||||
local ok, runErr = xpcall(shellFn, debug.traceback)
|
||||
if not ok then
|
||||
syscall.log("login: shell runtime error: " .. tostring(runErr), "ERROR")
|
||||
end
|
||||
end, username .. ":shell")
|
||||
|
||||
while true do
|
||||
local exited, code = syscall.collect(proc)
|
||||
if exited then
|
||||
if code then
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.write(1, "\nShell exited with code: " .. tostring(code) .. "\n")
|
||||
syscall.write(1, "(Check /var/log/syslog.log for details)\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
sleep(2)
|
||||
end
|
||||
return true
|
||||
end
|
||||
sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
local function doLogin()
|
||||
syscall.devctl(1, "clear")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sbgc", 16)
|
||||
syscall.devctl(1, "spos", 1, 1)
|
||||
|
||||
local hostname = syscall.getHostname() or "hyperion"
|
||||
syscall.write(1, "HyperionOS\n")
|
||||
syscall.write(1, hostname .. " login\n\n")
|
||||
|
||||
local attempts = 0
|
||||
while attempts < MAX_ATTEMPTS do
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.write(1, "Username: ")
|
||||
local username = readLine(nil)
|
||||
|
||||
if username == "" then goto continue end
|
||||
|
||||
syscall.write(1, "Password: ")
|
||||
local password = readLine("*")
|
||||
|
||||
local ok, err = syscall.auth_login(username, password)
|
||||
if ok then
|
||||
local uid = syscall.auth_getuid(username)
|
||||
local pwent = uid and syscall.auth_getpasswd(uid)
|
||||
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||
local homedir = (pwent and pwent.homedir) or "/"
|
||||
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
syscall.write(1, "\nWelcome, " .. username .. "!\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
sleep(0.3)
|
||||
|
||||
spawnShell(username, uid, shell, homedir)
|
||||
return -- back to login prompt
|
||||
else
|
||||
attempts = attempts + 1
|
||||
sleep(1)
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.write(1, "Login incorrect.\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.write(1, "Maximum login attempts exceeded.\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
sleep(5)
|
||||
end
|
||||
|
||||
firstBoot()
|
||||
while true do
|
||||
doLogin()
|
||||
end
|
||||
145
Src/Hyperion-bash/bin/ls
Normal file
145
Src/Hyperion-bash/bin/ls
Normal file
@@ -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,19 +1,19 @@
|
||||
--:Minify:--
|
||||
local users = syscall.listusers()
|
||||
local users = syscall.auth_listusers()
|
||||
if not users or #users == 0 then
|
||||
print("No users found.")
|
||||
return
|
||||
end
|
||||
|
||||
syscall.devctl(1,"sfgc",0xDBDBDB)
|
||||
syscall.devctl(1,"sfgc",13)
|
||||
print(string.format("%-6s %-6s %-16s %-20s %s", "UID", "GID", "Username", "Home", "Shell"))
|
||||
print(string.rep("-", 65))
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
|
||||
for _, u in ipairs(users) do
|
||||
local lock_marker = u.locked and " [locked]" or ""
|
||||
if u.locked then syscall.devctl(1,"sfgc",0xFF0000) end
|
||||
if u.locked then syscall.devctl(1,"sfgc",2) end
|
||||
print(string.format("%-6d %-6d %-16s %-20s %s%s",
|
||||
u.uid, u.gid, u.username, u.homedir, u.shell, lock_marker))
|
||||
if u.locked then syscall.devctl(1,"sfgc",0xFFFFFF) end
|
||||
if u.locked then syscall.devctl(1,"sfgc",1) end
|
||||
end
|
||||
@@ -1,14 +1,14 @@
|
||||
--:Minify:--
|
||||
local C_PROMPT = 0xFFFF00
|
||||
local C_CONT = 0xDBDBDB
|
||||
local C_OUT = 0x00FFFF
|
||||
local C_ERR = 0xFF0000
|
||||
local C_KEY = 0x00FF00
|
||||
local C_STR = 0x00FF88
|
||||
local C_NUM = 0x24FFFF
|
||||
local C_BOOL = 0xFF6D00
|
||||
local C_NIL = 0x6D6D6D
|
||||
local C_TABLE = 0xDBDBDB
|
||||
local C_PROMPT = 7
|
||||
local C_CONT = 13
|
||||
local C_OUT = 5
|
||||
local C_ERR = 2
|
||||
local C_KEY = 3
|
||||
local C_STR = 9
|
||||
local C_NUM = 10
|
||||
local C_BOOL = 8
|
||||
local C_NIL = 12
|
||||
local C_TABLE = 13
|
||||
|
||||
local function c(col) syscall.devctl(1, "sfgc", col) end
|
||||
local function w(s) syscall.write(1, tostring(s)) end
|
||||
@@ -250,9 +250,9 @@ local function getUserInput(prompt, history)
|
||||
local function redraw()
|
||||
syscall.devctl(1, "spos", ox, oy)
|
||||
w(input:sub(1, cursor-1))
|
||||
if blink then syscall.devctl(1,"sfgc",0x000000); syscall.devctl(1,"sbgc",1) end
|
||||
if blink then syscall.devctl(1,"sfgc",16); syscall.devctl(1,"sbgc",1) end
|
||||
w(cursor > #input and " " or input:sub(cursor, cursor))
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
||||
w(input:sub(cursor+1) .. " ")
|
||||
dirty = false
|
||||
end
|
||||
@@ -260,17 +260,17 @@ local function getUserInput(prompt, history)
|
||||
while true do
|
||||
local key = syscall.read(0)
|
||||
if key and key ~= "" then
|
||||
if key == "[C" then
|
||||
if key == "\19" then
|
||||
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
|
||||
elseif key == "[A" then
|
||||
elseif key == "\17" then
|
||||
if history and histIdx < #history then
|
||||
histIdx = histIdx + 1
|
||||
input = history[#history - histIdx + 1]
|
||||
cursor = #input + 1; dirty = true
|
||||
end
|
||||
elseif key == "[B" then
|
||||
elseif key == "\18" then
|
||||
if histIdx > 1 then
|
||||
histIdx = histIdx - 1
|
||||
input = history[#history - histIdx + 1]
|
||||
@@ -284,13 +284,13 @@ local function getUserInput(prompt, history)
|
||||
cursor = cursor - 1; dirty = true
|
||||
end
|
||||
elseif key == "\n" then
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"spos",ox,oy)
|
||||
w(input .. " \n")
|
||||
return input
|
||||
elseif #key == 1 and key:byte(1) >= 32 and key:byte(1) < 127 then
|
||||
input=string.sub(input,1,cursor-1)..key..string.sub(input,cursor)
|
||||
cursor=cursor+1;dirty=true
|
||||
else
|
||||
input = input:sub(1, cursor-1) .. key .. input:sub(cursor)
|
||||
cursor = cursor + 1; dirty = true
|
||||
end
|
||||
end
|
||||
local nb = (math.floor(syscall.getUptime() / 500) % 2) == 0
|
||||
@@ -299,6 +299,8 @@ local function getUserInput(prompt, history)
|
||||
end
|
||||
end
|
||||
|
||||
syscall.devctl(1, "clear")
|
||||
syscall.devctl(1, "spos", 1, 1)
|
||||
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
|
||||
c(C_NIL)
|
||||
w("Interactive Lua REPL. exit() to quit.\n\n")
|
||||
@@ -308,16 +310,16 @@ local history = {}
|
||||
|
||||
while true do
|
||||
local code = getUserInput("lua> ", history)
|
||||
if code ~= "" then
|
||||
if code == "" then goto continue end
|
||||
|
||||
while isIncomplete(code) do
|
||||
code = code .. "\n" .. getUserInput("... ", nil)
|
||||
end
|
||||
|
||||
if code ~= history[#history] then
|
||||
history[#history+1] = code
|
||||
end
|
||||
|
||||
runCode(code)
|
||||
while isIncomplete(code) do
|
||||
code = code .. "\n" .. getUserInput("... ", nil)
|
||||
end
|
||||
|
||||
if code ~= history[#history] then
|
||||
history[#history+1] = code
|
||||
end
|
||||
|
||||
runCode(code)
|
||||
::continue::
|
||||
end
|
||||
50
Src/Hyperion-bash/bin/luaold
Normal file
50
Src/Hyperion-bash/bin/luaold
Normal file
@@ -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
|
||||
23
Src/Hyperion-bash/bin/mkdir
Normal file
23
Src/Hyperion-bash/bin/mkdir
Normal file
@@ -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)
|
||||
18
Src/Hyperion-bash/bin/neofetch
Normal file
18
Src/Hyperion-bash/bin/neofetch
Normal file
@@ -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
|
||||
if targetName then
|
||||
targetUid = syscall.getuid()
|
||||
targetUid = syscall.auth_getuid(targetName)
|
||||
if not targetUid then
|
||||
print("passwd: user '" .. targetName .. "' does not exist")
|
||||
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
|
||||
else cur=cur..ch; syscall.write(1,"*") end
|
||||
end
|
||||
local ok, err = syscall.login(targetUid, cur)
|
||||
local ok, err = syscall.auth_elevate(targetName, cur)
|
||||
if not ok then
|
||||
sleep(1)
|
||||
print("passwd: authentication failure")
|
||||
@@ -71,7 +71,7 @@ if pw1 ~= pw2 then
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
local ok, err = syscall.setpassword(targetUid, pw1)
|
||||
local ok, err = syscall.auth_setpassword(targetUid, pw1)
|
||||
if not ok then
|
||||
print("passwd: " .. tostring(err))
|
||||
syscall.exit(1); return
|
||||
@@ -1,4 +1,3 @@
|
||||
--:Minify:--
|
||||
for i,v in ipairs(syscall.getTasks()) do
|
||||
local task = syscall.getTask(v)
|
||||
print(task.pid,task.username,task.name,task.status)
|
||||
1
Src/Hyperion-bash/bin/pwd
Normal file
1
Src/Hyperion-bash/bin/pwd
Normal file
@@ -0,0 +1 @@
|
||||
print(syscall.getcwd())
|
||||
BIN
Src/Hyperion-bash/bin/startup/.meta
Normal file
BIN
Src/Hyperion-bash/bin/startup/.meta
Normal file
Binary file not shown.
4
Src/Hyperion-bash/bin/startup/runShell.lua
Normal file
4
Src/Hyperion-bash/bin/startup/runShell.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
local fs = require("sys.fs")
|
||||
local bashStr = fs.readAllText("/bin/bash")
|
||||
local bashFun = load(bashStr)
|
||||
syscall.spawn(bashFun, "bash")
|
||||
1
Src/Hyperion-bash/bin/startup/test.lua
Normal file
1
Src/Hyperion-bash/bin/startup/test.lua
Normal file
@@ -0,0 +1 @@
|
||||
syscall.chown("/bin", 0, 0)
|
||||
@@ -1,23 +1,22 @@
|
||||
--:Minify:--
|
||||
local targetUser = ({ ... })[1]
|
||||
local currentUid = syscall.getuid()
|
||||
if syscall.geteuid()~=0 then
|
||||
syscall.exec("/bin/su", {...})
|
||||
end
|
||||
local targetUid
|
||||
if targetUser then
|
||||
targetUid = syscall.getuidbyname(targetUser)
|
||||
else
|
||||
targetUid = 0
|
||||
end
|
||||
local fs = require("sys.fs")
|
||||
|
||||
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
|
||||
print("su: user '" .. targetUser .. "' does not exist")
|
||||
syscall.exit(1)
|
||||
return
|
||||
end
|
||||
|
||||
if currentUid ~= 0 then
|
||||
local ok, err
|
||||
if currentUid == 0 then
|
||||
ok = true
|
||||
else
|
||||
printInline("Password: ")
|
||||
local pw = ""
|
||||
while true do
|
||||
@@ -29,37 +28,45 @@ if currentUid ~= 0 then
|
||||
elseif ch == "\b" then
|
||||
if #pw > 0 then pw = pw:sub(1, -2); syscall.write(1, "\b \b") end
|
||||
else
|
||||
pw = pw .. ch; syscall.write(1, "*")
|
||||
pw = pw .. ch
|
||||
syscall.write(1, "*")
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = syscall.login(targetUid, pw)
|
||||
ok, err = syscall.auth_elevate(targetUser, pw)
|
||||
if not ok then
|
||||
sleep(1)
|
||||
print("su: Authentication failure")
|
||||
syscall.exit(1)
|
||||
return
|
||||
end
|
||||
else
|
||||
end
|
||||
|
||||
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 homedir = (pwent and pwent.homedir) or "/"
|
||||
local username= (pwent and pwent.username)or "Unknown"
|
||||
|
||||
local ok_cd, err_cd = pcall(syscall.chdir, homedir)
|
||||
if not ok_cd then
|
||||
homedir = "/"
|
||||
syscall.chdir(homedir)
|
||||
end
|
||||
syscall.chdir(homedir)
|
||||
syscall.setEnviron("HOME", homedir)
|
||||
syscall.setEnviron("USER", username)
|
||||
syscall.setEnviron("USER", targetUser)
|
||||
syscall.setEnviron("SHELL", shell)
|
||||
|
||||
local ok, err = pcall(syscall.exec, shell)
|
||||
if not ok then
|
||||
print("su: cannot exec shell '" .. shell .. "': " .. tostring(err))
|
||||
local shellText = fs.readAllText(shell)
|
||||
if not shellText then
|
||||
print("su: shell not found: " .. shell)
|
||||
syscall.exit(1)
|
||||
end
|
||||
return
|
||||
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:--
|
||||
local fs = require("fs")
|
||||
local fs = require("sys.fs")
|
||||
|
||||
local cmdArgs = {...}
|
||||
local targetUser = "root"
|
||||
@@ -10,7 +10,7 @@ if cmdArgs[i] == "-u" then
|
||||
local uarg = cmdArgs[i] or "root"
|
||||
local numUid = tonumber(uarg)
|
||||
if numUid then
|
||||
local pwent = syscall.getpasswd(numUid)
|
||||
local pwent = syscall.auth_getpasswd(numUid)
|
||||
targetUser = (pwent and pwent.username) or uarg
|
||||
else
|
||||
targetUser = uarg
|
||||
@@ -31,7 +31,7 @@ for j = i + 1, #cmdArgs do restArgs[#restArgs + 1] = cmdArgs[j] end
|
||||
local currentUid = syscall.getuid()
|
||||
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||
|
||||
local targetUid = syscall.getuidbyname(targetUser)
|
||||
local targetUid = syscall.auth_getuid(targetUser)
|
||||
if not targetUid then
|
||||
print("sudo: user '" .. targetUser .. "' does not exist")
|
||||
syscall.exit(1)
|
||||
@@ -39,7 +39,7 @@ if not targetUid then
|
||||
end
|
||||
|
||||
if currentUid ~= 0 then
|
||||
printInline("[sudo] password for root: ")
|
||||
printInline("[sudo] password for " .. currentUser .. ": ")
|
||||
local pw = ""
|
||||
while true do
|
||||
local ch = syscall.read(0)
|
||||
@@ -55,7 +55,7 @@ if currentUid ~= 0 then
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = syscall.login(0, pw)
|
||||
local ok, err = syscall.auth_elevate(currentUser, pw)
|
||||
if not ok then
|
||||
sleep(1)
|
||||
print("sudo: Authentication failure")
|
||||
@@ -63,7 +63,7 @@ if currentUid ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if targetUid ~= currentUid then
|
||||
if targetUid ~= 0 then
|
||||
syscall.setuid(targetUid)
|
||||
end
|
||||
else
|
||||
@@ -97,7 +97,7 @@ if not program then
|
||||
return
|
||||
end
|
||||
|
||||
local pwent = syscall.getpasswd(targetUid)
|
||||
local pwent = syscall.auth_getpasswd(targetUid)
|
||||
if pwent and pwent.homedir then
|
||||
syscall.setEnviron("HOME", pwent.homedir)
|
||||
end
|
||||
5
Src/Hyperion-bash/bin/sysdump
Normal file
5
Src/Hyperion-bash/bin/sysdump
Normal file
@@ -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
|
||||
|
||||
local uid, err = syscall.newuser(
|
||||
local uid, err = syscall.auth_newuser(
|
||||
opt.username, password, opt.gid, opt.homedir, opt.shell or "/bin/hysh"
|
||||
)
|
||||
if not uid then
|
||||
@@ -14,22 +14,22 @@ if not username then
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
local uid = syscall.getuid(username)
|
||||
local uid = syscall.auth_getuid(username)
|
||||
if not uid then
|
||||
print("userdel: user '" .. username .. "' does not exist")
|
||||
syscall.exit(1); return
|
||||
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
|
||||
print("userdel: " .. tostring(err))
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
if removeHome and pwent and pwent.homedir then
|
||||
local fs = require("fs")
|
||||
local fs = require("sys.fs")
|
||||
local ok2, err2 = pcall(function()
|
||||
local function rmdir(path)
|
||||
for _, f in ipairs(fs.list(path) or {}) do
|
||||
@@ -27,7 +27,7 @@ if opt.lock and opt.unlock then
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
local uid = syscall.getuid(opt.username)
|
||||
local uid = syscall.auth_getuid(opt.username)
|
||||
if not uid then
|
||||
print("usermod: user '" .. opt.username .. "' does not exist")
|
||||
syscall.exit(1); return
|
||||
@@ -38,12 +38,12 @@ local function apply(fn, ...)
|
||||
if not ok then print("usermod: " .. tostring(err)); syscall.exit(1) end
|
||||
end
|
||||
|
||||
if opt.newname then apply(syscall.setusername, uid, opt.newname) end
|
||||
if opt.password then apply(syscall.setpassword, uid, opt.password) end
|
||||
if opt.gid then apply(syscall.setgid, uid, opt.gid) end
|
||||
if opt.homedir then apply(syscall.sethomedir, uid, opt.homedir) end
|
||||
if opt.shell then apply(syscall.setshell, uid, opt.shell) end
|
||||
if opt.lock then apply(syscall.lockuser, uid) end
|
||||
if opt.unlock then apply(syscall.unlockuser, uid) end
|
||||
if opt.newname then apply(syscall.auth_setusername, uid, opt.newname) end
|
||||
if opt.password then apply(syscall.auth_setpassword, uid, opt.password) end
|
||||
if opt.gid then apply(syscall.auth_setgid, uid, opt.gid) end
|
||||
if opt.homedir then apply(syscall.auth_sethomedir, uid, opt.homedir) end
|
||||
if opt.shell then apply(syscall.auth_setshell, uid, opt.shell) end
|
||||
if opt.lock then apply(syscall.auth_lockuser, uid) end
|
||||
if opt.unlock then apply(syscall.auth_unlockuser, uid) end
|
||||
|
||||
print("usermod: updated user '" .. opt.username .. "'")
|
||||
1
Src/Hyperion-bash/bin/whoami
Normal file
1
Src/Hyperion-bash/bin/whoami
Normal file
@@ -0,0 +1 @@
|
||||
print((syscall.auth_whoami() or "Unknown"))
|
||||
@@ -1,4 +1,3 @@
|
||||
--:Minify:--
|
||||
local args = {...}
|
||||
while true do
|
||||
if #args == 0 then
|
||||
BIN
Src/Hyperion-core/lib/.meta
Normal file
BIN
Src/Hyperion-core/lib/.meta
Normal file
Binary file not shown.
@@ -1,18 +0,0 @@
|
||||
--:Minify:--
|
||||
return {
|
||||
white=0xFFFFFF,
|
||||
red=0xFF0000,
|
||||
green=0x00FF00,
|
||||
blue=0x0000FF,
|
||||
cyan=0x00FFFF,
|
||||
yellow=0xFFFF00,
|
||||
purple=0xFF00FF,
|
||||
black=0x000000,
|
||||
gray=0x888888,
|
||||
lightgrey=0xBBBBBB,
|
||||
darkgrey=0x444444,
|
||||
orange=0xFF8800,
|
||||
mint=0x00FF88,
|
||||
brown=0xa52a2a,
|
||||
chocolate=0xd2691e
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
local io = {}
|
||||
local fs = require("fs")
|
||||
local fs = require("sys.fs")
|
||||
|
||||
function io.open(path, mode)
|
||||
return fs.open(path, mode)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local kernel = ...
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
@@ -37,7 +37,7 @@ function peripheral.isPresent(name)
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if type(peripheral) == "string" then -- Peripheral name passed
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
@@ -58,7 +58,7 @@ function peripheral.getType(peripheral)
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if type(peripheral) == "string" then -- Peripheral name passed
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
@@ -311,24 +311,13 @@ local fifo = kernel.newFifo()
|
||||
kernel.processes.cctmond = function()
|
||||
local timeout = false
|
||||
while true do
|
||||
local event = {kernel.EFI:getMachineEvent()}
|
||||
local event = {kernel.computer:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
local ctrlKeyMap = {
|
||||
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
|
||||
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
|
||||
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
|
||||
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
|
||||
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
|
||||
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
|
||||
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
|
||||
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
|
||||
[apis.keys.y]=25, [apis.keys.z]=26,
|
||||
}
|
||||
|
||||
-- Update modifier keys
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = true
|
||||
@@ -336,31 +325,11 @@ kernel.processes.cctmond = function()
|
||||
alt = true
|
||||
end
|
||||
|
||||
if ctrl then
|
||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||
if ctrlByte then
|
||||
if ctrlByte == 3 then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1)
|
||||
end
|
||||
else
|
||||
fifo.push(string.char(ctrlByte))
|
||||
end
|
||||
-- Handle Ctrl+C
|
||||
if ctrl and charOrKey == apis.keys.c then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1) -- SIGINT
|
||||
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
|
||||
|
||||
elseif eventType == "keyReleased" then
|
||||
@@ -385,10 +354,10 @@ kernel.processes.cctmond = function()
|
||||
end
|
||||
end
|
||||
|
||||
newtty(apis.term, "1", fifo.pop)
|
||||
newtty(apis.term, "TTY1", fifo.pop)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
v.setTextScale(.5)
|
||||
v.write("Initializing...")
|
||||
newtty(v,tostring(i+1),function () end)
|
||||
newtty(v,"TTY"..tostring(i+1),function () end)
|
||||
end
|
||||
26
Src/Hyperion-core/lib/modules/CC-Tweaked/40_redstone.kmod
Normal file
26
Src/Hyperion-core/lib/modules/CC-Tweaked/40_redstone.kmod
Normal file
@@ -0,0 +1,26 @@
|
||||
local args={...}
|
||||
local kernel=args[1]
|
||||
local driver={}
|
||||
|
||||
driver.name="CCT Term Module"
|
||||
driver.version="0.1.0"
|
||||
driver.type="gpio"
|
||||
driver.description="CCT redstone Module Kernel Module"
|
||||
driver.arch="cct"
|
||||
driver.author="HyperionOS Dev Team"
|
||||
driver.license="MIT"
|
||||
driver.api={}
|
||||
|
||||
function driver.load()
|
||||
-- will
|
||||
end
|
||||
|
||||
function driver.unload()
|
||||
-- Nothing to unload
|
||||
end
|
||||
|
||||
function driver.main()
|
||||
-- Nothing to run
|
||||
end
|
||||
|
||||
-- kernel.drivers.register(driver)
|
||||
@@ -1,8 +1,4 @@
|
||||
--:Minify:--
|
||||
--- @diagnostic disable: duplicate-set-field
|
||||
local kernel = ...
|
||||
kernel.allowGlobalOverwrites = true
|
||||
|
||||
-- :Minify:--
|
||||
function string.hasSuffix(str, suffix)
|
||||
return string.sub(str, #suffix + 1) == suffix
|
||||
end
|
||||
@@ -72,24 +68,30 @@ 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 .. "]=]"
|
||||
@@ -173,27 +175,6 @@ function table.indexOf(t, value)
|
||||
return -1
|
||||
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)
|
||||
local result = {}
|
||||
local i = 1
|
||||
@@ -229,30 +210,17 @@ function toHex(num)
|
||||
return string.format("%X", num)
|
||||
end
|
||||
|
||||
local function makeSyscallProxy()
|
||||
local backing = {}
|
||||
return setmetatable(backing, {
|
||||
__index = function(self, name)
|
||||
local raw = rawget(self, name)
|
||||
if raw ~= nil then return raw end
|
||||
return function(...)
|
||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
||||
if res[1] then
|
||||
return table.unpack(res, 2, res.n)
|
||||
else
|
||||
error(res[2], 2)
|
||||
end
|
||||
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,
|
||||
__newindex = function(self, k, v)
|
||||
rawset(self, k, v)
|
||||
end,
|
||||
__metatable=false
|
||||
})
|
||||
end
|
||||
|
||||
syscall = makeSyscallProxy()
|
||||
|
||||
_makeSyscallProxy = makeSyscallProxy
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
table.serialize = serialize
|
||||
629
Src/Hyperion-core/lib/modules/Hyperion/10_vfs.kmod
Normal file
629
Src/Hyperion-core/lib/modules/Hyperion/10_vfs.kmod
Normal file
@@ -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,4 +1,4 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
@@ -16,7 +16,7 @@ proxy.getLabel = function() return "devfs" end
|
||||
proxy.attributes = function(path) return {
|
||||
size = 0,
|
||||
modified = 0,
|
||||
created = 0
|
||||
created = 0,
|
||||
} end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
@@ -50,7 +50,7 @@ function proxy:type(path, mode)
|
||||
if type(step[steps[#steps]]) == "table" then
|
||||
return "directory"
|
||||
end
|
||||
error(type(step[steps[#steps]]))
|
||||
error("ENOENT")
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
@@ -140,82 +140,6 @@ function data.zero(op, mode)
|
||||
end
|
||||
end
|
||||
|
||||
if kernel.EFI:getEEPROM() then
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,eeprom=1,kernel.EFI:getEEPROM()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return eeprom:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
local firstwrite=true
|
||||
return {
|
||||
write=function(data)
|
||||
if firstwrite then
|
||||
kernel.EFI:setEEPROM(data)
|
||||
else
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if kernel.EFI:getNvram() then
|
||||
function data.nvram(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,nvram=1,kernel.EFI:getNvram()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return nvram:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
local firstwrite=true
|
||||
return {
|
||||
write=function(data)
|
||||
if firstwrite then
|
||||
kernel.EFI:setNvram(data)
|
||||
else
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
data["disk"]={}
|
||||
kernel.devfs={}
|
||||
kernel.devfs.data=data
|
||||
@@ -1,4 +1,3 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
@@ -8,9 +7,11 @@ proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
|
||||
-- Space functions (just placeholders)
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
|
||||
-- Writable operations
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
@@ -51,6 +52,7 @@ proxy.attributes = function(_, path)
|
||||
}
|
||||
end
|
||||
|
||||
-- Open files
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
@@ -69,32 +71,26 @@ function proxy:open(path, mode)
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
return {
|
||||
read = function(amount)
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
end
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
step[filename] = str
|
||||
end
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
end
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
@@ -127,8 +123,7 @@ function proxy:list(path)
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
return pcall(function() return self:type(path) end)
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
22
Src/Hyperion-core/lib/modules/Hyperion/14_keventd.kmod
Normal file
22
Src/Hyperion-core/lib/modules/Hyperion/14_keventd.kmod
Normal file
@@ -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
|
||||
@@ -21,7 +21,7 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
||||
end
|
||||
|
||||
if not semicolon_pos or semicolon_pos == 3 then
|
||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 0xFF8800)
|
||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8)
|
||||
else
|
||||
local id = line:sub(3, semicolon_pos - 1)
|
||||
local path = trim(line:sub(semicolon_pos + 1))
|
||||
@@ -12,24 +12,13 @@ function signal.sigsend(pid, sig)
|
||||
end
|
||||
|
||||
function signal.sigcatch(handler)
|
||||
local task=kernel.currentTask
|
||||
task.sigh=handler
|
||||
if not task.sigq then task.sigq={} end
|
||||
local handle={
|
||||
error="",
|
||||
active=true
|
||||
}
|
||||
if task.sigd then task.sigd.active=false; end
|
||||
task.sigd=handle
|
||||
return handle
|
||||
kernel.currentTask.sigh=handler
|
||||
if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end
|
||||
end
|
||||
|
||||
function signal.sigignore()
|
||||
local task=kernel.currentTask
|
||||
task.sigh=nil
|
||||
task.sigq=nil
|
||||
if task.sigd then task.sigd.active=false end
|
||||
task.sigd=nil
|
||||
kernel.currentTask.sigh=nil
|
||||
kernel.currentTask.sigq=nil
|
||||
end
|
||||
|
||||
local s=kernel.syscalls
|
||||
14
Src/Hyperion-core/lib/modules/Hyperion/20_socket.kmod
Normal file
14
Src/Hyperion-core/lib/modules/Hyperion/20_socket.kmod
Normal file
@@ -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")
|
||||
6
Src/Hyperion-core/lib/modules/Hyperion/26_tty.kmod
Normal file
6
Src/Hyperion-core/lib/modules/Hyperion/26_tty.kmod
Normal file
@@ -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"
|
||||
@@ -1,9 +1,8 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local args = {...}
|
||||
local kernel = args[1]
|
||||
kernel._G = _G
|
||||
|
||||
|
||||
local function readonly(tbl)
|
||||
return setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
@@ -21,7 +20,7 @@ local function readonly(tbl)
|
||||
error("Attempt to modify global variable '" .. k .. "'", 2)
|
||||
end,
|
||||
|
||||
__pairs = function(self)
|
||||
__pairs = function()
|
||||
local function iter(_, key)
|
||||
local nextKey, value = next(tbl, key)
|
||||
if type(value) == "table" then
|
||||
@@ -29,7 +28,7 @@ local function readonly(tbl)
|
||||
end
|
||||
return nextKey, value
|
||||
end
|
||||
return iter, self, nil
|
||||
return iter, tbl, nil
|
||||
end,
|
||||
|
||||
__ipairs = function()
|
||||
@@ -50,24 +49,8 @@ local function readonly(tbl)
|
||||
__metatable = false
|
||||
})
|
||||
end
|
||||
local origLoad = load
|
||||
|
||||
kernel._U = readonly(kernel._G)
|
||||
kernel.allowGlobalOverwrites = true
|
||||
kernel._U._G = kernel._U
|
||||
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
|
||||
|
||||
function kernel.freshUserEnv()
|
||||
local locals = {}
|
||||
locals.syscall = _makeSyscallProxy()
|
||||
|
||||
local env = setmetatable(locals, {
|
||||
__index = kernel._U,
|
||||
__newindex = function(_, k, v) rawset(locals, k, v) end,
|
||||
__metatable=false
|
||||
})
|
||||
|
||||
locals._G = env
|
||||
locals.load = function(a, b, c, d) return origLoad(a, b, c, d or env) end
|
||||
|
||||
return env
|
||||
end
|
||||
kernel.allowGlobalOverwrites = false
|
||||
@@ -139,7 +139,7 @@ end
|
||||
if not blake2s then error("Failed to load blake2s") end
|
||||
|
||||
if not kernel.vfs.exists("/etc/pam.d/secret") then
|
||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT", "WARN", 0xFF8800)
|
||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT")
|
||||
local key = ""
|
||||
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
|
||||
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
|
||||
@@ -150,11 +150,16 @@ end
|
||||
local pepper = getFile("/etc/pam.d/secret")
|
||||
|
||||
local function genSalt()
|
||||
return toHex(math.random(0,2^32))
|
||||
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
|
||||
local s = ""
|
||||
for i = 1, 16 do
|
||||
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function hashPassword(password, salt)
|
||||
local key = (pepper .. salt)
|
||||
local key = (pepper .. salt):sub(1, 32)
|
||||
return blake2s(password, key)
|
||||
end
|
||||
|
||||
@@ -231,18 +236,19 @@ local function nextUID()
|
||||
return max + 1
|
||||
end
|
||||
|
||||
function auth.login(uid, password)
|
||||
if type(uid) ~= "number" or type(password) ~= "string" then
|
||||
function auth.login(username, password)
|
||||
if type(username) ~= "string" or type(password) ~= "string" then
|
||||
return nil, "Authentication failure"
|
||||
end
|
||||
|
||||
local entry = getPasswdByUID(uid)
|
||||
local entry = getPasswdByUsername(username)
|
||||
if not entry then
|
||||
-- timing attack resistance
|
||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||
return nil, "Authentication failure"
|
||||
end
|
||||
|
||||
local uid = tonumber(entry[1])
|
||||
local sEntry = getShadowByUID(uid)
|
||||
if not sEntry then
|
||||
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||
@@ -258,16 +264,14 @@ function auth.login(uid, password)
|
||||
end
|
||||
|
||||
kernel.currentUID = uid
|
||||
|
||||
local _task = kernel.currentTask
|
||||
if _task then
|
||||
_task.uid = uid
|
||||
_task.euid = uid
|
||||
_task.gid = tonumber(entry[2]) or uid
|
||||
_task.egid = tonumber(entry[2]) or uid
|
||||
if kernel.currentProcess then
|
||||
kernel.currentProcess.uid = uid
|
||||
kernel.currentProcess.euid = uid
|
||||
kernel.currentProcess.gid = tonumber(entry[2]) or uid
|
||||
kernel.currentProcess.egid = tonumber(entry[2]) or uid
|
||||
end
|
||||
|
||||
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. getPasswdByUID(uid)[3] .. ")")
|
||||
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -368,7 +372,7 @@ function auth.newUser(username, password, gid, homedir, shell)
|
||||
local uid = nextUID()
|
||||
gid = tonumber(gid) or uid
|
||||
homedir = homedir or ("/home/" .. username)
|
||||
shell = shell or "/bin/hysh"
|
||||
shell = shell or "/bin/sh"
|
||||
|
||||
passwd[#passwd + 1] = {
|
||||
tostring(uid),
|
||||
@@ -388,8 +392,6 @@ function auth.newUser(username, password, gid, homedir, shell)
|
||||
|
||||
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
||||
kernel.vfs.mkdir(homedir)
|
||||
-- Homedir must be owned by the new user, not root
|
||||
pcall(kernel.vfs.chown, homedir, uid, uid)
|
||||
end
|
||||
|
||||
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
||||
@@ -434,9 +436,11 @@ function auth.deleteUser(uid)
|
||||
if not entry then return nil, "No such user" end
|
||||
local username = entry[3]
|
||||
|
||||
-- Remove from passwd
|
||||
for i, v in ipairs(passwd) do
|
||||
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
|
||||
end
|
||||
-- Remove from shadow
|
||||
for i, v in ipairs(shadow) do
|
||||
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
|
||||
end
|
||||
@@ -459,6 +463,7 @@ function auth.lockUser(uid)
|
||||
local sEntry = getShadowByUID(uid)
|
||||
if not sEntry then return nil, "No shadow entry for uid" end
|
||||
|
||||
-- Prefix hash with ! to lock (standard Linux convention)
|
||||
if sEntry[3]:sub(1,1) ~= "!" then
|
||||
sEntry[3] = "!" .. sEntry[3]
|
||||
end
|
||||
@@ -562,6 +567,9 @@ function auth.setGID(uid, gid)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Elevate the calling task to targetUid after verifying targetUsername's password.
|
||||
-- This is the kernel-side primitive for su/sudo — it bypasses the kernel.uid==0
|
||||
-- check in sys.setuid because the auth module itself is trusted kernel code.
|
||||
function auth.elevate(targetUsername, password)
|
||||
if type(targetUsername) ~= "string" or type(password) ~= "string" then
|
||||
return nil, "Authentication failure"
|
||||
@@ -585,32 +593,33 @@ function auth.elevate(targetUsername, password)
|
||||
return nil, "Authentication failure"
|
||||
end
|
||||
|
||||
-- Directly set the calling task's uid — trusted kernel path
|
||||
local task = kernel.currentTask
|
||||
local prevUid = task.uid
|
||||
task.uid = 0
|
||||
task.euid = 0
|
||||
task.gid = 0
|
||||
task.egid = 0
|
||||
kernel.uid = 0
|
||||
task.uid = uid
|
||||
task.euid = uid
|
||||
task.gid = tonumber(entry[2]) or uid
|
||||
task.egid = tonumber(entry[2]) or uid
|
||||
kernel.uid = uid
|
||||
|
||||
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")")
|
||||
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")")
|
||||
return true, uid
|
||||
end
|
||||
|
||||
if kernel.syscalls then
|
||||
kernel.syscalls["login"] = auth.login
|
||||
kernel.syscalls["setpassword"] = auth.setPassword
|
||||
kernel.syscalls["setusername"] = auth.setUsername
|
||||
kernel.syscalls["newuser"] = auth.newUser
|
||||
kernel.syscalls["whoami"] = auth.whoami
|
||||
kernel.syscalls["getuidbyname"]= auth.getUID
|
||||
kernel.syscalls["getpasswd"] = auth.getPasswd
|
||||
kernel.syscalls["elevate"] = auth.elevate
|
||||
kernel.syscalls["deleteuser"] = auth.deleteUser
|
||||
kernel.syscalls["lockuser"] = auth.lockUser
|
||||
kernel.syscalls["unlockuser"] = auth.unlockUser
|
||||
kernel.syscalls["listusers"] = auth.listUsers
|
||||
kernel.syscalls["setshell"] = auth.setShell
|
||||
kernel.syscalls["sethomedir"] = auth.setHomedir
|
||||
kernel.syscalls["setgid"] = auth.setGID
|
||||
end
|
||||
kernel.syscalls["auth_login"] = auth.login
|
||||
kernel.syscalls["auth_setpassword"] = auth.setPassword
|
||||
kernel.syscalls["auth_setusername"] = auth.setUsername
|
||||
kernel.syscalls["auth_newuser"] = auth.newUser
|
||||
kernel.syscalls["auth_whoami"] = auth.whoami
|
||||
kernel.syscalls["auth_getuid"] = auth.getUID
|
||||
kernel.syscalls["auth_getpasswd"] = auth.getPasswd
|
||||
kernel.syscalls["auth_elevate"] = auth.elevate
|
||||
kernel.syscalls["auth_deleteuser"] = auth.deleteUser
|
||||
kernel.syscalls["auth_lockuser"] = auth.lockUser
|
||||
kernel.syscalls["auth_unlockuser"] = auth.unlockUser
|
||||
kernel.syscalls["auth_listusers"] = auth.listUsers
|
||||
kernel.syscalls["auth_setshell"] = auth.setShell
|
||||
kernel.syscalls["auth_sethomedir"] = auth.setHomedir
|
||||
kernel.syscalls["auth_setgid"] = auth.setGID
|
||||
end
|
||||
447
Src/Hyperion-core/lib/modules/Hyperion/45_hypervisor.kmod
Normal file
447
Src/Hyperion-core/lib/modules/Hyperion/45_hypervisor.kmod
Normal file
@@ -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,4 +1,3 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local sysc=kernel.syscalls
|
||||
kernel.gpio={}
|
||||
@@ -9,20 +8,8 @@ sysc["gpio_write"]=function(pin, data)
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_writeAnalog"]=function(pin, data)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("wa", data)
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_read"]=function(pin)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("r")
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_readAnalog"]=function(pin)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("ra")
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local kernel = ...
|
||||
function print(...)
|
||||
local args = {...}
|
||||
@@ -9,11 +9,13 @@ function print(...)
|
||||
end
|
||||
|
||||
function printf(fmt, ...)
|
||||
coroutine.yield()
|
||||
local output = string.format(fmt, ...)
|
||||
syscall.write(1, output.."\n")
|
||||
end
|
||||
|
||||
function printInline(...)
|
||||
coroutine.yield()
|
||||
local args = {...}
|
||||
local output = ""
|
||||
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
||||
@@ -1,21 +1,14 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local kernel = ...
|
||||
kernel.log("Loading init system...")
|
||||
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")
|
||||
if not handle then kernel.panic("Failed to open "..kernel.config.initPath) end
|
||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
if not handle then kernel.panic("Failed to read "..kernel.config.initPath) end
|
||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
kernel.vfs.close(handle)
|
||||
|
||||
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
|
||||
if not initFunc then kernel.PANIC("Failed to load init system: " .. err) end
|
||||
if not initFunc then error("Failed to load init system: " .. err) end
|
||||
|
||||
kernel.tasks["1"] = {
|
||||
coro = coroutine.create(function()
|
||||
@@ -27,27 +20,27 @@ kernel.tasks["1"] = {
|
||||
end
|
||||
end),
|
||||
|
||||
name = "sysinit",
|
||||
name = "sysinit",
|
||||
status = "R",
|
||||
pid = 1,
|
||||
tgid = 1,
|
||||
uid = 0,
|
||||
fd = {},
|
||||
pid = 1,
|
||||
tgid = 1,
|
||||
uid = 0,
|
||||
fd = {},
|
||||
envars = {},
|
||||
args = {},
|
||||
exit = "",
|
||||
sleep = 0,
|
||||
ivs = 0,
|
||||
vs = 0,
|
||||
args = {},
|
||||
exit = "",
|
||||
sleep = 0,
|
||||
ivs = 0,
|
||||
vs = 0,
|
||||
parent = kernel.kernelTask,
|
||||
siblings = kernel.kernelTask.children,
|
||||
children = {},
|
||||
syscallReturn = {},
|
||||
cwd = "/",
|
||||
cwd = "/",
|
||||
timeSlice = 0,
|
||||
lastTime = 0,
|
||||
lastTime = 0,
|
||||
totalTime = 0,
|
||||
numRuns = 0
|
||||
numRuns = 0
|
||||
}
|
||||
|
||||
kernel.log("created init task with PID 1")
|
||||
16
Src/Hyperion-core/lib/modules/Hyperion/91_login.kmod
Normal file
16
Src/Hyperion-core/lib/modules/Hyperion/91_login.kmod
Normal file
@@ -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
|
||||
165
Src/Hyperion-core/lib/modules/Hyperion/92_permissions.kmod
Normal file
165
Src/Hyperion-core/lib/modules/Hyperion/92_permissions.kmod
Normal file
@@ -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
Src/Hyperion-core/lib/snip
Normal file
0
Src/Hyperion-core/lib/snip
Normal file
6
Src/Hyperion-core/lib/sys/hpv
Normal file
6
Src/Hyperion-core/lib/sys/hpv
Normal file
@@ -0,0 +1,6 @@
|
||||
local sys = {}
|
||||
local fs = require("sys.fs")
|
||||
|
||||
|
||||
|
||||
return sys
|
||||
5
Src/Hyperion-core/lib/sys/init
Normal file
5
Src/Hyperion-core/lib/sys/init
Normal file
@@ -0,0 +1,5 @@
|
||||
local sys = {}
|
||||
sys.fs = require("sys.fs")
|
||||
sys.hpv = require("sys.hpv")
|
||||
sys.ipc = require("sys.ipc")
|
||||
return sys
|
||||
3
Src/Hyperion-core/lib/sys/ipc
Normal file
3
Src/Hyperion-core/lib/sys/ipc
Normal file
@@ -0,0 +1,3 @@
|
||||
local ipc = {}
|
||||
|
||||
return ipc
|
||||
71
Src/Hyperion-core/lib/sys/term
Normal file
71
Src/Hyperion-core/lib/sys/term
Normal file
@@ -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
|
||||
BIN
Src/Hyperion-core/sbin/.meta
Normal file
BIN
Src/Hyperion-core/sbin/.meta
Normal file
Binary file not shown.
@@ -1,41 +1,36 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local fs=require("fs")
|
||||
kernel.log("Sysinit started...")
|
||||
local fs=require("sys.fs")
|
||||
|
||||
for i,v in pairs(kernel.processes) do
|
||||
kernel.log("Spawning kernel task "..i)
|
||||
syscall.spawn(function()
|
||||
local status, err = pcall(v)
|
||||
if not status then
|
||||
kernel.log("Error executing kernel task '" .. i .. "': " .. err, "ERROR", 0xFF0000)
|
||||
kernel.log("Error executing kernel task '" .. i .. "': " .. err, "ERROR")
|
||||
else
|
||||
kernel.log("Successfully executed kernel task: " .. i)
|
||||
kernel.log("Successfully executed kernel task: " .. i, "INFO")
|
||||
end
|
||||
end, i)
|
||||
end
|
||||
|
||||
if not fs.exists("/bin/startup") then
|
||||
fs.mkdir("/bin/startup")
|
||||
end
|
||||
|
||||
local files = fs.list("/bin/startup")
|
||||
if not files then error("Failed to list /bin/startup") end
|
||||
for i,v in ipairs(files) do
|
||||
if v:sub(-4) == ".lua" then
|
||||
local filepath = "/bin/startup/" .. v
|
||||
kernel.log("Executing startup script: " .. filepath)
|
||||
kernel.log("Executing startup script: " .. filepath, "INFO")
|
||||
local startupFunc, err = load(fs.readAllText(filepath), "@" .. filepath)
|
||||
if not startupFunc then
|
||||
kernel.log("Error loading startup script '" .. filepath .. "': " .. err, "ERROR", 0xFF0000)
|
||||
kernel.log("Error loading startup script '" .. filepath .. "': " .. err, "ERROR")
|
||||
else
|
||||
syscall.spawn(function()
|
||||
syscall.setuid(1)
|
||||
local status, err = pcall(startupFunc)
|
||||
if not status then
|
||||
kernel.log("Error executing startup script '" .. filepath .. "': " .. err, "ERROR", 0xFF0000)
|
||||
kernel.log("Error executing startup script '" .. filepath .. "': " .. err, "ERROR")
|
||||
else
|
||||
kernel.log("Successfully executed startup script: " .. filepath)
|
||||
kernel.log("Successfully executed startup script: " .. filepath, "INFO")
|
||||
end
|
||||
end, "startup:" .. v)
|
||||
end
|
||||
@@ -43,6 +38,6 @@ for i,v in ipairs(files) do
|
||||
end
|
||||
|
||||
while true do
|
||||
sleep(5)
|
||||
sleep(1)
|
||||
kernel.saveLog()
|
||||
end
|
||||
@@ -1,344 +0,0 @@
|
||||
--:Minify:--
|
||||
local args={...}
|
||||
local bootdrive=args[1]
|
||||
local gpu=components:getFirst("gpu")
|
||||
local screenTextBuffer=nil
|
||||
local cursorX,cursorY=0,0
|
||||
local screenSizeX,screenSizeY=128,25
|
||||
|
||||
|
||||
if gpu then
|
||||
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||
for t,v in components:list() do
|
||||
if t == "screen" then
|
||||
gpu:assignBuffer(screenTextBuffer, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function write(text)
|
||||
cursorX, cursorY = screenTextBuffer:pasteText(cursorX, cursorY, "SCROLL_SPILL_CLEAR", text)
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 0
|
||||
if cursorY >= screenSizeY then
|
||||
cursorY = screenSizeY-1
|
||||
screenTextBuffer:newline()
|
||||
end
|
||||
end
|
||||
|
||||
local function displaySuperBadError(err)
|
||||
gpu:freeAllBuffers()
|
||||
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||
for t,v in components:list() do
|
||||
if t == "screen" then
|
||||
gpu:assignBuffer(screenTextBuffer, v)
|
||||
end
|
||||
end
|
||||
term.setBackgroundColor(0x1)
|
||||
term.setTextColor(0x4)
|
||||
term.clear()
|
||||
term.setCursorPos(1, 1)
|
||||
term.write("A critical error occurred while loading the system:")
|
||||
term.setCursorPos(1, 3)
|
||||
write(err)
|
||||
while true do end
|
||||
end
|
||||
|
||||
local ok, err = xpcall(function()
|
||||
local apis = {}
|
||||
|
||||
local lua = {
|
||||
coroutine = true,
|
||||
debug = true,
|
||||
_VERSION = true,
|
||||
assert = true,
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
gcinfo = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
__inext = true,
|
||||
load = true,
|
||||
math = true,
|
||||
next = true,
|
||||
pairs = true,
|
||||
pcall = true,
|
||||
rawequal = true,
|
||||
rawget = true,
|
||||
rawlen = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setmetatable = true,
|
||||
string = true,
|
||||
table = true,
|
||||
tonumber = true,
|
||||
tostring = true,
|
||||
type = true,
|
||||
xpcall = true,
|
||||
_G = true
|
||||
}
|
||||
|
||||
local debug = debug
|
||||
for i, v in pairs(_G) do
|
||||
if not lua[i] or lua[i] == nil then
|
||||
apis[i] = v
|
||||
_G[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function string_file(file)
|
||||
local str = file:read()
|
||||
local buf = {str or ""}
|
||||
local pos = 1
|
||||
local closed = false
|
||||
|
||||
local function content()
|
||||
return table.concat(buf)
|
||||
end
|
||||
|
||||
local function set_content(s)
|
||||
buf = {s}
|
||||
end
|
||||
|
||||
local function flush()
|
||||
file.write(content())
|
||||
end
|
||||
|
||||
local file = {}
|
||||
|
||||
function file:read(n)
|
||||
assert(not closed, "file is closed")
|
||||
|
||||
local s = content()
|
||||
local len = #s
|
||||
|
||||
if not n then
|
||||
local out = s:sub(pos)
|
||||
pos = len + 1
|
||||
return out
|
||||
end
|
||||
|
||||
local out = s:sub(pos, pos + n - 1)
|
||||
pos = pos + #out
|
||||
return out
|
||||
end
|
||||
|
||||
function file:write(data)
|
||||
assert(not closed, "file is closed")
|
||||
|
||||
local s = content()
|
||||
local before = s:sub(1, pos - 1)
|
||||
local after = s:sub(pos + #data)
|
||||
|
||||
set_content(before .. data .. after)
|
||||
pos = pos + #data
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function file:seek(whence, offset)
|
||||
assert(not closed, "file is closed")
|
||||
|
||||
local s = content()
|
||||
local len = #s
|
||||
|
||||
whence = whence or "cur"
|
||||
offset = offset or 0
|
||||
|
||||
if whence == "set" then
|
||||
pos = offset + 1
|
||||
elseif whence == "cur" then
|
||||
pos = pos + offset
|
||||
elseif whence == "end" then
|
||||
pos = len + offset + 1
|
||||
else
|
||||
error("invalid whence")
|
||||
end
|
||||
|
||||
if pos < 1 then pos = 1 end
|
||||
if pos > len + 1 then pos = len + 1 end
|
||||
|
||||
return pos - 1
|
||||
end
|
||||
|
||||
function file:close()
|
||||
assert(not closed, "file is closed")
|
||||
flush()
|
||||
closed = true
|
||||
end
|
||||
|
||||
function file:flush()
|
||||
assert(not closed, "file is closed")
|
||||
flush()
|
||||
end
|
||||
|
||||
return file
|
||||
end
|
||||
|
||||
local function getFile(path)
|
||||
local file = bootdrive:open(path, "r")
|
||||
if not file then
|
||||
displaySuperBadError("Could not open file: " .. path)
|
||||
end
|
||||
local content = file:read()
|
||||
return content
|
||||
end
|
||||
|
||||
local Kernel = load(getFile("/boot/kernel.lua"),"@Kernel")
|
||||
local initFs = load(getFile("/boot/ac/initdisks"),"@Init_disks")(apis)
|
||||
local fs = load(getFile("/boot/initfs"), "@InitFs")()
|
||||
|
||||
if not Kernel then displaySuperBadError("Could not load kernel.") end
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
local computercomp=apis.components:getFirst("computer")
|
||||
local uefi=apis.components:getFirst("uefi")
|
||||
local computer = {
|
||||
time = function() return apis.os.epoch("utc") end,
|
||||
clock = function() return apis.os.clock() * 1000 end,
|
||||
shutdown = apis.os.shutdown,
|
||||
reboot = apis.os.reboot,
|
||||
getMachineEvent = function()
|
||||
return computercomp:getMachineEvent()
|
||||
end,
|
||||
getEEPROM = function() return uefi.data end,
|
||||
setEEPROM = function(_, text)
|
||||
uefi.data=text
|
||||
end
|
||||
}
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||
{
|
||||
print = function(_, text) write(text .. "\n") end,
|
||||
printInline = function(_, text) write(text) end,
|
||||
clear = function()
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
end,
|
||||
setCursorPos = function(_, x, y)
|
||||
apis.term.setCursorPos(x, y)
|
||||
end,
|
||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||
getSize = function() return apis.term.getSize() end,
|
||||
setBackgroundColor = function(_, color)
|
||||
apis.term.setBackgroundColor(colors[color])
|
||||
end,
|
||||
setTextColor = function(_, color)
|
||||
apis.term.setTextColor(colors[color])
|
||||
end,
|
||||
getBackgroundColor = function()
|
||||
return icolors[apis.term.getBackgroundColor()]
|
||||
end,
|
||||
getTextColor = function()
|
||||
return icolors[apis.term.getTextColor()]
|
||||
end
|
||||
}, computer, fs, "$")
|
||||
if not ok then displaySuperBadError(err) end
|
||||
end)
|
||||
|
||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||
local startTime = computer.time()
|
||||
debug.sethook(co, function()
|
||||
if computer.time() > startTime + timeout then
|
||||
return coroutine.yield("timeout")
|
||||
end
|
||||
end, "", 1000)
|
||||
local ret = {coroutine.resume(co, ...)}
|
||||
if ret[1] and ret[2] == "timeout" then
|
||||
return "timeout"
|
||||
elseif ret[1] == false then
|
||||
return "error", ret[2]
|
||||
else
|
||||
debug.sethook(co)
|
||||
return "success", table.unpack(ret, 2)
|
||||
end
|
||||
end
|
||||
|
||||
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||
|
||||
while true do
|
||||
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||
apis.os.queueEvent("NoSleep")
|
||||
local exit = false
|
||||
while not exit do
|
||||
local event = {coroutine.yield()}
|
||||
if event[1] == "key" then
|
||||
queueEvent("keyPressed", 1, event[2])
|
||||
if acekeys[event[2]] then
|
||||
queueEvent("keyTyped", 1, acekeys[event[2]])
|
||||
end
|
||||
elseif event[1] == "char" then
|
||||
queueEvent("keyTyped", 1, event[2])
|
||||
elseif event[1] == "key_up" then
|
||||
queueEvent("keyReleased", 1, event[2])
|
||||
elseif event[1] == "disk" then
|
||||
queueEvent("componentAdded", "disk")
|
||||
elseif event[1] == "disk_eject" then
|
||||
queueEvent("componentRemoved", "disk")
|
||||
elseif event[1] == "modem_message" then
|
||||
queueEvent("modem_message", table.unpack(event, 2))
|
||||
elseif event[1] == "rednet_message" then
|
||||
queueEvent("rednet_message", table.unpack(event, 2))
|
||||
elseif event[1] == "http_success" then
|
||||
queueEvent("http_success", table.unpack(event, 2))
|
||||
elseif event[1] == "http_failure" then
|
||||
queueEvent("http_failure", table.unpack(event, 2))
|
||||
elseif event[1] == "NoSleep" then
|
||||
exit = true
|
||||
end
|
||||
end
|
||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||
coroutine.yield("key")
|
||||
end
|
||||
end
|
||||
end, debug.traceback)
|
||||
|
||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||
while true do coroutine.yield() end
|
||||
@@ -1,272 +0,0 @@
|
||||
--:Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local disks = {}
|
||||
local internal = {}
|
||||
|
||||
local function norm(path)
|
||||
if not path or path == "" then return "/" end
|
||||
return fs.combine("/", path)
|
||||
end
|
||||
|
||||
local function createDisk(id, basePath, readonly, periph)
|
||||
basePath = norm(basePath)
|
||||
|
||||
local disk = {address = id, isReadOnly = function() return readonly end}
|
||||
|
||||
function disk:spaceUsed()
|
||||
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||
end
|
||||
|
||||
function disk:spaceTotal() return fs.getCapacity(basePath) end
|
||||
|
||||
function disk:list(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) or not fs.isDir(p) then
|
||||
return nil, "not directory"
|
||||
end
|
||||
return fs.list(p)
|
||||
end
|
||||
|
||||
function disk:fileExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and not fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:directoryExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:type(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) then
|
||||
return nil
|
||||
elseif fs.isDir(p) then
|
||||
return "directory"
|
||||
else
|
||||
return "file"
|
||||
end
|
||||
end
|
||||
|
||||
function disk:makeDirectory(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
fs.makeDir(p)
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:remove(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if fs.exists(p) then fs.delete(p) end
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:setLabel(label) periph.setLabel(label) end
|
||||
|
||||
function disk:getLabel(label) return periph.getLabel() end
|
||||
|
||||
function disk:attributes(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.attributes(p)
|
||||
end
|
||||
|
||||
function disk:open(path, mode)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.open(p, mode)
|
||||
end
|
||||
|
||||
return disk
|
||||
end
|
||||
|
||||
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
setLabel = function(label)
|
||||
local h = fs.open("/.label", "w")
|
||||
h.write(label)
|
||||
h.close()
|
||||
end,
|
||||
getLabel = function()
|
||||
local h = fs.open("/.label", "r")
|
||||
if not h then return "$" end
|
||||
local label = h.readAll()
|
||||
h.close()
|
||||
return label
|
||||
end
|
||||
})
|
||||
|
||||
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||
setLabel = function(label)
|
||||
error("Device is read-only")
|
||||
end,
|
||||
getLabel = function()
|
||||
return "cctrom"
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
disks={}
|
||||
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||
if disk.isDiskPresent() then
|
||||
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function iter()
|
||||
refresh()
|
||||
local combined = {}
|
||||
|
||||
for id, obj in pairs(internal) do combined[id] = obj end
|
||||
for id, obj in pairs(disks) do combined[id] = obj end
|
||||
|
||||
return pairs(combined)
|
||||
end
|
||||
|
||||
return {refresh = refresh, list = iter}
|
||||
@@ -1,4 +1,4 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
local term = term
|
||||
@@ -71,6 +71,7 @@ local ok, err = xpcall(function()
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
gcinfo = true,
|
||||
getfenv = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
__inext = true,
|
||||
@@ -84,6 +85,7 @@ local ok, err = xpcall(function()
|
||||
rawlen = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setfenv = true,
|
||||
setmetatable = true,
|
||||
string = true,
|
||||
table = true,
|
||||
@@ -152,262 +154,17 @@ local ok, err = xpcall(function()
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
if not apis.fs.exists("/nvram.dat") then
|
||||
local file = apis.fs.open("/nvram.dat", "w")
|
||||
file.write("Hello, World!")
|
||||
file.close()
|
||||
end
|
||||
|
||||
local eeprom
|
||||
if apis.fs.exists("/startup.lua") then
|
||||
eeprom="/startup.lua"
|
||||
elseif apis.fs.exists("/eeprom") then
|
||||
eeprom="/eeprom"
|
||||
end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
table.insert(eventQueue, {event, ...})
|
||||
end
|
||||
|
||||
local colors = {
|
||||
[0x000000]=0x0001,
|
||||
[0xFFFFFF]=0x0002,
|
||||
[0xFF0000]=0x0004,
|
||||
[0x00FF00]=0x0008,
|
||||
[0x0000FF]=0x0010,
|
||||
[0x00FFFF]=0x0020,
|
||||
[0xFF00FF]=0x0040,
|
||||
[0xFFFF00]=0x0080,
|
||||
[0xFF6D00]=0x0100,
|
||||
[0x6DFF55]=0x0200,
|
||||
[0x24FFFF]=0x0400,
|
||||
[0x924900]=0x0800,
|
||||
[0x6D6D55]=0x1000,
|
||||
[0xDBDBAA]=0x2000,
|
||||
[0x6D00FF]=0x4000,
|
||||
[0xB6FF00]=0x8000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
if k==target then return k end
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local peripheral={}
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local allscreens = {peripheral.find("monitor")}
|
||||
for i=1, #allscreens do
|
||||
allscreens[i].setTextScale(.5)
|
||||
allscreens[i].clear()
|
||||
allscreens[i].setCursorPos(1,1)
|
||||
allscreens[i].write("Initializing...")
|
||||
end
|
||||
|
||||
local EFI = {
|
||||
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||
getUptime = function() return apis.os.clock() * 1000 end,
|
||||
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||
local computer = {
|
||||
time = function() return apis.os.epoch("utc") end,
|
||||
clock = function() return apis.os.clock() * 1000 end,
|
||||
shutdown = apis.os.shutdown,
|
||||
reboot = apis.os.reboot,
|
||||
getMachineEvent = function()
|
||||
if #eventQueue > 0 then
|
||||
return table.unpack(table.remove(eventQueue, 1))
|
||||
@@ -415,15 +172,61 @@ local ok, err = xpcall(function()
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
getEEPROM = function() return getFile(eeprom) end,
|
||||
getEEPROM = function() return getFile("/startup.lua") end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open(eeprom, "w")
|
||||
local h = apis.fs.open("/startup.lua", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
initfs=fs,
|
||||
disks=initFs,
|
||||
screenCtl={
|
||||
end
|
||||
}
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||
{
|
||||
print = function(_, text) write(text .. "\n") end,
|
||||
printInline = function(_, text) write(text) end,
|
||||
clear = function()
|
||||
@@ -436,52 +239,26 @@ local ok, err = xpcall(function()
|
||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||
getSize = function() return apis.term.getSize() end,
|
||||
setBackgroundColor = function(_, color)
|
||||
apis.term.setBackgroundColor(aprox(color))
|
||||
apis.term.setBackgroundColor(colors[color])
|
||||
end,
|
||||
setTextColor = function(_, color)
|
||||
apis.term.setTextColor(aprox(color))
|
||||
apis.term.setTextColor(colors[color])
|
||||
end,
|
||||
getBackgroundColor = function()
|
||||
return bg
|
||||
return icolors[apis.term.getBackgroundColor()]
|
||||
end,
|
||||
getTextColor = function()
|
||||
return fg
|
||||
end,
|
||||
enable=function() end,
|
||||
disable=function() end
|
||||
},
|
||||
architecture="cct",
|
||||
getNvram = function() return getFile("/nvram.dat") end,
|
||||
setNvram = function(_, text)
|
||||
local h = apis.fs.open("/nvram.dat", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
firmware=apis,
|
||||
reboot=false,
|
||||
beep=function() end
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, EFI)
|
||||
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||
if err then
|
||||
apis.os.reboot()
|
||||
else
|
||||
apis.os.shutdown()
|
||||
end
|
||||
return icolors[apis.term.getTextColor()]
|
||||
end
|
||||
}, computer, fs, "$")
|
||||
if not ok then displaySuperBadError(err) end
|
||||
end)
|
||||
|
||||
-- time is in milliseconds
|
||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||
local startTime = EFI.getEpochMs()
|
||||
local startTime = computer.time()
|
||||
debug.sethook(co, function()
|
||||
if EFI.getEpochMs() > startTime + timeout then
|
||||
if computer.time() > startTime + timeout then
|
||||
return coroutine.yield("timeout")
|
||||
end
|
||||
end, "", 1000)
|
||||
@@ -496,7 +273,7 @@ local ok, err = xpcall(function()
|
||||
end
|
||||
end
|
||||
|
||||
EFI.screenCtl:print("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||
|
||||
while true do
|
||||
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||
@@ -517,28 +294,16 @@ local ok, err = xpcall(function()
|
||||
queueEvent("componentAdded", "disk")
|
||||
elseif event[1] == "disk_eject" then
|
||||
queueEvent("componentRemoved", "disk")
|
||||
elseif event[1] == "modem_message" then
|
||||
queueEvent("modem_message", table.unpack(event, 2))
|
||||
elseif event[1] == "rednet_message" then
|
||||
queueEvent("rednet_message", table.unpack(event, 2))
|
||||
elseif event[1] == "http_success" then
|
||||
queueEvent("http_success", table.unpack(event, 2))
|
||||
elseif event[1] == "http_failure" then
|
||||
queueEvent("http_failure", table.unpack(event, 2))
|
||||
elseif event[1] == "NoSleep" then
|
||||
exit = true
|
||||
end
|
||||
end
|
||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||
if EFI.reboot then
|
||||
apis.os.reboot()
|
||||
end
|
||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||
coroutine.yield("key")
|
||||
end
|
||||
initFs:refresh()
|
||||
end
|
||||
end, debug.traceback)
|
||||
|
||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||
while true do coroutine.yield("key") end
|
||||
while true do coroutine.yield() end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
--:Minify:--
|
||||
sleep(1)
|
||||
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
||||
-- UnBIOS by JackMacWindows
|
||||
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||
|
||||
@@ -1,152 +1,36 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getType(name)
|
||||
if native.isPresent(name) then return native.getType(name) end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and
|
||||
native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getTypeRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
local names = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
if native.isPresent(side) then table.insert(names, side) end
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local hubSides = native.call(side, "getConnectedSides")
|
||||
for _, hubSide in ipairs(hubSides) do
|
||||
table.insert(names, hubSide)
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
return names
|
||||
end
|
||||
|
||||
local disks = {}
|
||||
@@ -241,20 +125,19 @@ internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
end
|
||||
})
|
||||
|
||||
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||
setLabel = function(label)
|
||||
error("Device is read-only")
|
||||
end,
|
||||
getLabel = function()
|
||||
return "cctrom"
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
disks={}
|
||||
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||
if disk.isDiskPresent() then
|
||||
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||
for id, _ in pairs(disks) do
|
||||
if not peripheral.getType(id) then disks[id] = nil end
|
||||
end
|
||||
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.getType(name) == "disk" then
|
||||
if not disks[name] then
|
||||
local mount = disk.getMountPath(name)
|
||||
if mount then
|
||||
disks[name] = createDisk(name, mount, false, disk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
363
Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod
Normal file
363
Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod
Normal file
@@ -0,0 +1,363 @@
|
||||
-- :Minify:--
|
||||
local kernel = ...
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
local peripheral={}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then -- Peripheral name passed
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then -- Peripheral name passed
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
local function write(text, term)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
|
||||
kernel.devfs.data.tty={}
|
||||
local ctrl,alt = false, false
|
||||
|
||||
local function serializeBool(bool)
|
||||
if bool then
|
||||
return "T"
|
||||
else
|
||||
return "F"
|
||||
end
|
||||
end
|
||||
|
||||
local function newtty(obj, id, ev)
|
||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {ev()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
write(content, obj)
|
||||
end,
|
||||
size=function()
|
||||
local s={obj.getSize()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
clear=function()
|
||||
obj.clear()
|
||||
obj.setCursorPos(1,1)
|
||||
end,
|
||||
gpos=function()
|
||||
local s={obj.getCursorPos()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
spos=function(x,y)
|
||||
return obj.setCursorPos(x,y)
|
||||
end,
|
||||
sfgc=function(c)
|
||||
return obj.setTextColor(colors[c])
|
||||
end,
|
||||
sbgc=function(c)
|
||||
return obj.setBackgroundColor(colors[c])
|
||||
end,
|
||||
gfgc=function()
|
||||
return icolors[obj.getTextColor()]
|
||||
end,
|
||||
gbgc=function()
|
||||
return icolors[obj.getBackgroundColor()]
|
||||
end,
|
||||
gctrl=function()
|
||||
return serializeBool(ctrl)..";"..serializeBool(alt)
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fifo = kernel.newFifo()
|
||||
|
||||
kernel.processes.cctmond = function()
|
||||
local timeout = false
|
||||
while true do
|
||||
local event = {kernel.computer:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
-- Update modifier keys
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = true
|
||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||
alt = true
|
||||
end
|
||||
|
||||
-- Handle Ctrl+C
|
||||
if ctrl and charOrKey == apis.keys.c then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1) -- SIGINT
|
||||
end
|
||||
end
|
||||
|
||||
elseif eventType == "keyReleased" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = false
|
||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||
alt = false
|
||||
end
|
||||
|
||||
elseif eventType == "keyTyped" then
|
||||
if charOrKey then fifo.push(charOrKey) end
|
||||
end
|
||||
|
||||
timeout = false
|
||||
else
|
||||
timeout = true
|
||||
end
|
||||
|
||||
if timeout then
|
||||
sleep(0.05)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newtty(apis.term, "TTY1", fifo.pop)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
v.setTextScale(.5)
|
||||
v.write("Initializing...")
|
||||
newtty(v,"TTY"..tostring(i+1),function () end)
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
local args={...}
|
||||
local kernel=args[1]
|
||||
local driver={}
|
||||
|
||||
driver.name="CCT Term Module"
|
||||
driver.version="0.1.0"
|
||||
driver.type="gpio"
|
||||
driver.description="CCT redstone Module Kernel Module"
|
||||
driver.arch="cct"
|
||||
driver.author="HyperionOS Dev Team"
|
||||
driver.license="MIT"
|
||||
driver.api={}
|
||||
|
||||
function driver.load()
|
||||
-- will
|
||||
end
|
||||
|
||||
function driver.unload()
|
||||
-- Nothing to unload
|
||||
end
|
||||
|
||||
function driver.main()
|
||||
-- Nothing to run
|
||||
end
|
||||
|
||||
-- kernel.drivers.register(driver)
|
||||
@@ -1,152 +0,0 @@
|
||||
--:Minify:--
|
||||
-- CCT driver peripheral helper
|
||||
local kernel=...
|
||||
kernel.cct={}
|
||||
kernel.cct.peripheral={}
|
||||
local peripheral=kernel.cct.peripheral
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
@@ -1,252 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local peripheral=kernel.cct.peripheral
|
||||
|
||||
local colors = {
|
||||
[0xFFFFFF]=0x0001,
|
||||
[0xFF0000]=0x0002,
|
||||
[0x00FF00]=0x0004,
|
||||
[0x0000FF]=0x0008,
|
||||
[0x00FFFF]=0x0010,
|
||||
[0xFF00FF]=0x0020,
|
||||
[0xFFFF00]=0x0040,
|
||||
[0xFF6D00]=0x0080,
|
||||
[0x6DFF55]=0x0100,
|
||||
[0x24FFFF]=0x0200,
|
||||
[0x924900]=0x0400,
|
||||
[0x6D6D55]=0x0800,
|
||||
[0xDBDBAA]=0x1000,
|
||||
[0x6D00FF]=0x2000,
|
||||
[0xB6FF00]=0x4000,
|
||||
[0x000000]=0x8000
|
||||
}
|
||||
|
||||
local plt = {
|
||||
0xFFFFFF,
|
||||
0xFF0000,
|
||||
0x00FF00,
|
||||
0x0000FF,
|
||||
0x00FFFF,
|
||||
0xFF00FF,
|
||||
0xFFFF00,
|
||||
0xFF6D00,
|
||||
0x6DFF55,
|
||||
0x24FFFF,
|
||||
0x924900,
|
||||
0x6D6D55,
|
||||
0xDBDBAA,
|
||||
0x6D00FF,
|
||||
0xB6FF00,
|
||||
0x000000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
if k==target then return k end
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local function write(text, term)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
|
||||
kernel.devfs.data.tty={}
|
||||
kernel.cct.ctrl,kernel.cct.alt = false, false
|
||||
|
||||
local function serializeBool(bool)
|
||||
if bool then
|
||||
return "T"
|
||||
else
|
||||
return "F"
|
||||
end
|
||||
end
|
||||
|
||||
local function newtty(obj, id, ev)
|
||||
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "Terminal"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {ev()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
write(content, obj)
|
||||
end,
|
||||
size=function()
|
||||
local s={obj.getSize()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
clear=function()
|
||||
obj.clear()
|
||||
obj.setCursorPos(1,1)
|
||||
end,
|
||||
gpos=function()
|
||||
local s={obj.getCursorPos()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
spos=function(x,y)
|
||||
return obj.setCursorPos(x,y)
|
||||
end,
|
||||
sfgc=function(c)
|
||||
fg=c
|
||||
return obj.setTextColor(aprox(c))
|
||||
end,
|
||||
sbgc=function(c)
|
||||
bg=c
|
||||
return obj.setBackgroundColor(aprox(c))
|
||||
end,
|
||||
gfgc=function()
|
||||
return fg
|
||||
end,
|
||||
gbgc=function()
|
||||
return bg
|
||||
end,
|
||||
gctrl=function()
|
||||
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
|
||||
end,
|
||||
gplt=function()
|
||||
return plt
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fifo = kernel.newFifo()
|
||||
kernel.cct.fifo=fifo
|
||||
|
||||
newtty(kernel.apis.term, "1", fifo.pop)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
v.setTextScale(.5)
|
||||
v.write("Initializing...")
|
||||
newtty(v,tostring(i+1),function () end)
|
||||
end
|
||||
@@ -1,86 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local keys=kernel.apis.keys
|
||||
|
||||
kernel.processes.cctdeamon = function()
|
||||
local timeout = false
|
||||
while true do
|
||||
local event = {kernel.EFI:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
local ctrlKeyMap = {
|
||||
[keys.a]=1, [keys.b]=2, [keys.c]=3,
|
||||
[keys.d]=4, [keys.e]=5, [keys.f]=6,
|
||||
[keys.g]=7, [keys.h]=8, [keys.i]=9,
|
||||
[keys.j]=10, [keys.k]=11, [keys.l]=12,
|
||||
[keys.m]=13, [keys.n]=14, [keys.o]=15,
|
||||
[keys.p]=16, [keys.q]=17, [keys.r]=18,
|
||||
[keys.s]=19, [keys.t]=20, [keys.u]=21,
|
||||
[keys.v]=22, [keys.w]=23, [keys.x]=24,
|
||||
[keys.y]=25, [keys.z]=26,
|
||||
}
|
||||
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
|
||||
kernel.cct.ctrl = true
|
||||
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
|
||||
kernel.cct.alt = true
|
||||
end
|
||||
|
||||
if kernel.cct.ctrl then
|
||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||
if ctrlByte then
|
||||
if ctrlByte == 3 then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1)
|
||||
end
|
||||
else
|
||||
kernel.cct.fifo.push(string.char(ctrlByte))
|
||||
end
|
||||
end
|
||||
else
|
||||
local specialKeyMap = {
|
||||
[keys.up] = "[A",
|
||||
[keys.down] = "[B",
|
||||
[keys.right] = "[C",
|
||||
[keys.left] = "[D",
|
||||
[keys.home] = "[H",
|
||||
[keys["end"]] = "[F",
|
||||
[keys.pageUp] = "[5~",
|
||||
[keys.pageDown] = "[6~",
|
||||
[keys.delete] = "[3~",
|
||||
}
|
||||
local special = specialKeyMap[charOrKey]
|
||||
if special then kernel.cct.fifo.push(special) end
|
||||
end
|
||||
|
||||
elseif eventType == "keyReleased" then
|
||||
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
|
||||
kernel.cct.ctrl = false
|
||||
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
|
||||
kernel.cct.alt = false
|
||||
end
|
||||
|
||||
elseif eventType == "keyTyped" then
|
||||
if charOrKey then kernel.cct.fifo.push(charOrKey) end
|
||||
elseif eventType == "http_success" then
|
||||
kernel.cct.httpqueue[event[2]]=nil
|
||||
kernel.cct.httpresponse[event[2]]=event[3]
|
||||
elseif eventType == "http_failure" then
|
||||
kernel.cct.httpqueue[event[2]]=nil
|
||||
kernel.cct.httperror[event[2]]=event[3]
|
||||
end
|
||||
|
||||
timeout = false
|
||||
else
|
||||
timeout = true
|
||||
end
|
||||
|
||||
if timeout then
|
||||
sleep(0.05)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local rs=kernel.apis.rs
|
||||
local sides = {top=1, bottom=2, left=3, right=4, front=5, back=6}
|
||||
local function newGPIO(side)
|
||||
return function(mode, data)
|
||||
if mode=="w" then
|
||||
if type(data)~="boolean" then error("data: expected bool") end
|
||||
rs.setOutput(side, data)
|
||||
elseif mode=="wa" then
|
||||
if type(data)~="number" then error("data: expected bool") end
|
||||
rs.setAnalogOutput(side, data)
|
||||
elseif mode=="r" then
|
||||
return rs.getInput(side)
|
||||
elseif mode=="ra" then
|
||||
return rs.getAnalogInput(side)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for side, alt in pairs(sides) do
|
||||
local func=newGPIO(side)
|
||||
kernel.gpio[side]=func
|
||||
kernel.gpio[alt]=func
|
||||
end
|
||||
0
Src/Hyperion-installer/bin/install
Normal file
0
Src/Hyperion-installer/bin/install
Normal file
BIN
Src/Hyperion-kernel/boot/.meta
Normal file
BIN
Src/Hyperion-kernel/boot/.meta
Normal file
Binary file not shown.
@@ -4,9 +4,8 @@
|
||||
-- This file is auto-generated during the build process.
|
||||
-- DEFAULT BOOT CONFIGURATION FILE
|
||||
return {
|
||||
initPath = "/sbin/init",
|
||||
initPath = "/sbin/init.lua",
|
||||
maxOpenFiles = 128,
|
||||
maxFilesPerTask = 16,
|
||||
preempt=true,
|
||||
logTaskExit=true
|
||||
preempt=true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
--:Minify:--
|
||||
-- :Minify:--
|
||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
local term = term
|
||||
@@ -71,6 +71,7 @@ local ok, err = xpcall(function()
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
gcinfo = true,
|
||||
getfenv = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
__inext = true,
|
||||
@@ -84,6 +85,7 @@ local ok, err = xpcall(function()
|
||||
rawlen = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setfenv = true,
|
||||
setmetatable = true,
|
||||
string = true,
|
||||
table = true,
|
||||
@@ -152,106 +154,17 @@ local ok, err = xpcall(function()
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
if not apis.fs.exists("/nvram.dat") then
|
||||
local file = apis.fs.open("/nvram.dat", "w")
|
||||
file.write("Hello, World!")
|
||||
file.close()
|
||||
end
|
||||
|
||||
local eeprom
|
||||
if apis.fs.exists("/startup.lua") then
|
||||
eeprom="/startup.lua"
|
||||
elseif apis.fs.exists("/eeprom") then
|
||||
eeprom="/eeprom"
|
||||
end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
table.insert(eventQueue, {event, ...})
|
||||
end
|
||||
|
||||
local colors = {
|
||||
[0x000000]=0x0001,
|
||||
[0xFFFFFF]=0x0002,
|
||||
[0xFF0000]=0x0004,
|
||||
[0x00FF00]=0x0008,
|
||||
[0x0000FF]=0x0010,
|
||||
[0x00FFFF]=0x0020,
|
||||
[0xFF00FF]=0x0040,
|
||||
[0xFFFF00]=0x0080,
|
||||
[0xFF6D00]=0x0100,
|
||||
[0x6DFF55]=0x0200,
|
||||
[0x24FFFF]=0x0400,
|
||||
[0x924900]=0x0800,
|
||||
[0x6D6D55]=0x1000,
|
||||
[0xDBDBAA]=0x2000,
|
||||
[0x6D00FF]=0x4000,
|
||||
[0xB6FF00]=0x8000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local EFI = {
|
||||
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||
getUptime = function() return apis.os.clock() * 1000 end,
|
||||
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||
local computer = {
|
||||
time = function() return apis.os.epoch("utc") end,
|
||||
clock = function() return apis.os.clock() * 1000 end,
|
||||
shutdown = apis.os.shutdown,
|
||||
reboot = apis.os.reboot,
|
||||
getMachineEvent = function()
|
||||
if #eventQueue > 0 then
|
||||
return table.unpack(table.remove(eventQueue, 1))
|
||||
@@ -259,15 +172,61 @@ local ok, err = xpcall(function()
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
getEEPROM = function() return getFile(eeprom) end,
|
||||
getEEPROM = function() return getFile("/startup.lua") end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open(eeprom, "w")
|
||||
local h = apis.fs.open("/startup.lua", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
initfs=fs,
|
||||
disks=initFs,
|
||||
screenCtl={
|
||||
end
|
||||
}
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||
{
|
||||
print = function(_, text) write(text .. "\n") end,
|
||||
printInline = function(_, text) write(text) end,
|
||||
clear = function()
|
||||
@@ -280,51 +239,26 @@ local ok, err = xpcall(function()
|
||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||
getSize = function() return apis.term.getSize() end,
|
||||
setBackgroundColor = function(_, color)
|
||||
apis.term.setBackgroundColor(aprox(color))
|
||||
apis.term.setBackgroundColor(colors[color])
|
||||
end,
|
||||
setTextColor = function(_, color)
|
||||
apis.term.setTextColor(aprox(color))
|
||||
apis.term.setTextColor(colors[color])
|
||||
end,
|
||||
getBackgroundColor = function()
|
||||
return bg
|
||||
return icolors[apis.term.getBackgroundColor()]
|
||||
end,
|
||||
getTextColor = function()
|
||||
return fg
|
||||
end,
|
||||
enable=function() end,
|
||||
disable=function() end
|
||||
},
|
||||
architecture="cct",
|
||||
getNvram = function() return getFile("/nvram.dat") end,
|
||||
setNvram = function(_, text)
|
||||
local h = apis.fs.open("/nvram.dat", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
firmware=apis,
|
||||
reboot=false
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, EFI)
|
||||
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||
if err then
|
||||
apis.os.reboot()
|
||||
else
|
||||
apis.os.shutdown()
|
||||
end
|
||||
return icolors[apis.term.getTextColor()]
|
||||
end
|
||||
}, computer, fs, "$")
|
||||
if not ok then displaySuperBadError(err) end
|
||||
end)
|
||||
|
||||
-- time is in milliseconds
|
||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||
local startTime = EFI.getEpochMs()
|
||||
local startTime = computer.time()
|
||||
debug.sethook(co, function()
|
||||
if EFI.getEpochMs() > startTime + timeout then
|
||||
if computer.time() - startTime > timeout then
|
||||
return coroutine.yield("timeout")
|
||||
end
|
||||
end, "", 1000)
|
||||
@@ -360,28 +294,16 @@ local ok, err = xpcall(function()
|
||||
queueEvent("componentAdded", "disk")
|
||||
elseif event[1] == "disk_eject" then
|
||||
queueEvent("componentRemoved", "disk")
|
||||
elseif event[1] == "modem_message" then
|
||||
queueEvent("modem_message", table.unpack(event, 2))
|
||||
elseif event[1] == "rednet_message" then
|
||||
queueEvent("rednet_message", table.unpack(event, 2))
|
||||
elseif event[1] == "http_success" then
|
||||
queueEvent("http_success", table.unpack(event, 2))
|
||||
elseif event[1] == "http_failure" then
|
||||
queueEvent("http_failure", table.unpack(event, 2))
|
||||
elseif event[1] == "NoSleep" then
|
||||
exit = true
|
||||
end
|
||||
end
|
||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||
if EFI.reboot then
|
||||
apis.os.reboot()
|
||||
end
|
||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||
coroutine.yield("key")
|
||||
end
|
||||
initFs:refresh()
|
||||
end
|
||||
end, debug.traceback)
|
||||
|
||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||
while true do coroutine.yield("key") end
|
||||
while true do coroutine.yield() end
|
||||
@@ -116,4 +116,4 @@ if debug then
|
||||
end
|
||||
_G.peripheral = value or peripheral
|
||||
end
|
||||
end
|
||||
end
|
||||
155
Src/Hyperion-kernel/boot/cct/initdisks
Normal file
155
Src/Hyperion-kernel/boot/cct/initdisks
Normal file
@@ -0,0 +1,155 @@
|
||||
-- :Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getType(name)
|
||||
if native.isPresent(name) then return native.getType(name) end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and
|
||||
native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getTypeRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getNames()
|
||||
local names = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then table.insert(names, side) end
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local hubSides = native.call(side, "getConnectedSides")
|
||||
for _, hubSide in ipairs(hubSides) do
|
||||
table.insert(names, hubSide)
|
||||
end
|
||||
end
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
local disks = {}
|
||||
local internal = {}
|
||||
|
||||
local function norm(path)
|
||||
if not path or path == "" then return "/" end
|
||||
return fs.combine("/", path)
|
||||
end
|
||||
|
||||
local function createDisk(id, basePath, readonly, periph)
|
||||
basePath = norm(basePath)
|
||||
|
||||
local disk = {address = id, isReadOnly = function() return readonly end}
|
||||
|
||||
function disk:spaceUsed()
|
||||
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||
end
|
||||
|
||||
function disk:spaceTotal() return fs.getCapacity(basePath) end
|
||||
|
||||
function disk:list(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) or not fs.isDir(p) then
|
||||
return nil, "not directory"
|
||||
end
|
||||
return fs.list(p)
|
||||
end
|
||||
|
||||
function disk:fileExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and not fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:directoryExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:type(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) then
|
||||
return nil
|
||||
elseif fs.isDir(p) then
|
||||
return "directory"
|
||||
else
|
||||
return "file"
|
||||
end
|
||||
end
|
||||
|
||||
function disk:makeDirectory(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
fs.makeDir(p)
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:remove(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if fs.exists(p) then fs.delete(p) end
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:setLabel(label) periph.setLabel(label) end
|
||||
|
||||
function disk:getLabel(label) return periph.getLabel() end
|
||||
|
||||
function disk:attributes(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.attributes(p)
|
||||
end
|
||||
|
||||
function disk:open(path, mode)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.open(p, mode)
|
||||
end
|
||||
|
||||
return disk
|
||||
end
|
||||
|
||||
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
setLabel = function(label)
|
||||
local h = fs.open("/.label", "w")
|
||||
h.write(label)
|
||||
h.close()
|
||||
end,
|
||||
getLabel = function()
|
||||
local h = fs.open("/.label", "r")
|
||||
if not h then return "$" end
|
||||
local label = h.readAll()
|
||||
h.close()
|
||||
return label
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
for id, _ in pairs(disks) do
|
||||
if not peripheral.getType(id) then disks[id] = nil end
|
||||
end
|
||||
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.getType(name) == "disk" then
|
||||
if not disks[name] then
|
||||
local mount = disk.getMountPath(name)
|
||||
if mount then
|
||||
disks[name] = createDisk(name, mount, false, disk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function iter()
|
||||
refresh()
|
||||
local combined = {}
|
||||
|
||||
for id, obj in pairs(internal) do combined[id] = obj end
|
||||
for id, obj in pairs(disks) do combined[id] = obj end
|
||||
|
||||
return pairs(combined)
|
||||
end
|
||||
|
||||
return {refresh = refresh, list = iter}
|
||||
@@ -1,4 +1,3 @@
|
||||
U $;/
|
||||
U devfs0000;/dev/
|
||||
U tmpfs0000;/tmp/
|
||||
U procfs0000;/proc/
|
||||
U tmpfs0000;/tmp/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user