69 Commits

Author SHA1 Message Date
8762b8f022 Merge pull request '1.2-dev' (#9) from 1.2-dev into main
Reviewed-on: Hyperion/HyperionOS#9
2026-03-11 10:37:44 -04:00
677b2cccec user login fixed 2026-03-11 10:37:13 -04:00
a5e8624368 forgot to edit login 2026-03-11 08:54:47 -04:00
bbda3b3937 fixed elevate VULN 2026-03-11 08:52:41 -04:00
585d39bec2 Merge pull request '1.2-dev' (#8) from 1.2-dev into main
Reviewed-on: Hyperion/HyperionOS#8
2026-03-11 08:23:21 -04:00
b08b14763a i am a we bit stupid (fixed cct bug) 2026-03-11 08:22:53 -04:00
de6696003b fix capitaization part 2 2026-03-11 07:26:38 -04:00
9220281365 fix capitalization part 1 2026-03-11 07:25:52 -04:00
813ddabd9d fixed disk detection i think 2026-03-10 21:13:52 -04:00
5177639d71 Merge pull request 'update build.tar' (#7) from 1.2-dev into main
Reviewed-on: Hyperion/HyperionOS#7
i was dumb
2026-03-10 20:05:26 -04:00
528b4f31bd update build.tar 2026-03-10 20:04:53 -04:00
60162c7c57 Merge pull request '1.2.4' (#6) from 1.2-dev into main
Reviewed-on: Hyperion/HyperionOS#6
2026-03-10 20:02:20 -04:00
18f5c454bb fixed bug where cct drivers are inaccessable 2026-03-10 20:00:21 -04:00
849ecb7dd6 made procfs self 2026-03-10 19:41:54 -04:00
e41bd6bee7 Merge pull request '1.2-dev merge' (#5) from 1.2-dev into main
Reviewed-on: Hyperion/HyperionOS#5
2026-03-10 12:27:08 -04:00
359198c1ea Merge branch '1.2-dev' of https://git.astronand.dev/Hyperion/HyperionOS into 1.2-dev 2026-03-10 10:57:27 -04:00
beebf01223 remove tar during install 2026-03-10 10:57:10 -04:00
2d4ea1bbf4 Update README.md 2026-03-10 10:39:01 -04:00
ea3a7e99a7 Update install/installcc.lua 2026-03-10 10:34:36 -04:00
be0fe5dc5a install works (i think) 2026-03-10 10:32:32 -04:00
12669d9f82 adde /proc fs and working on install 2026-03-10 09:17:21 -04:00
f12159bfb9 fixed build script 2026-03-09 11:31:31 -04:00
1590e1f3f7 more reorganizeing and $PKGCONFIG.ini files added B to ls -lh for "Bytes" 2026-03-09 11:28:09 -04:00
a69f945b91 did some reorganizing 2026-03-06 09:57:45 -05:00
7da67899db finished hyperion manifest :D 2026-03-05 10:36:43 -05:00
62e032e4c5 made sysinit in /usr/lib/sysinit and sped up micro added kernel.firstBoot in kernel table 2026-03-05 09:53:30 -05:00
6fefa2d9ff cp should not copy perms 2026-03-03 12:25:25 -05:00
bb354cc706 mv syscallautofill 2026-03-03 11:45:05 -05:00
fabc061731 made lua not clear screen 2026-03-03 10:06:04 -05:00
82c3e2b346 fix ll 2026-03-03 08:57:45 -05:00
e2e1d5b8a5 making descriptions for syscalls 2026-03-03 08:52:55 -05:00
9342b9b2b3 Merge branch '1.2-dev' of https://git.astronand.dev/Hyperion/HyperionOS into 1.2-dev 2026-03-03 08:02:09 -05:00
9a7db6c243 syscalls now autocomplete is vsc 2026-03-03 08:02:05 -05:00
c7545e6947 Update build.py 2026-03-03 07:31:59 -05:00
b7f52dd17b working on syscall manifest and fixed anther exploit 2026-03-02 22:25:37 -05:00
eb5bed0f09 fixed asyncsyscall5 2026-03-02 21:58:51 -05:00
1827a463eb added ll for ghxx 2026-03-02 21:56:05 -05:00
b532a63fc6 fixed stupid dumbass mistake i made me dumb 2026-03-02 21:32:56 -05:00
4e5a4172bf hopfully fixed it omfg 2026-03-02 21:29:46 -05:00
31ce894fda fixed build script 2026-03-02 21:26:50 -05:00
16c900de84 fixed ls links, modules writeable 2026-03-02 21:23:35 -05:00
413afd96de Merge branch '1.2-dev' of https://git.astronand.dev/Hyperion/HyperionOS into 1.2-dev 2026-03-02 07:27:46 -05:00
a0a0ac69d4 remove old shell files 2026-03-02 07:27:45 -05:00
17453983ad Path traversal fixes, 26_tty removal, ctrl+key fixes 2026-03-01 00:21:02 -06:00
a6550aa069 fixed minify header and build script 2026-02-25 11:41:41 -05:00
02e7b3897c fixed tty naming 2026-02-25 09:03:32 -05:00
34b89a8e34 fixed build script 2026-02-25 08:04:53 -05:00
0eabfebd0f fixed new ghxx exploit 2026-02-25 08:01:28 -05:00
5b2e5eac65 added more libs and fixed build script 2026-02-24 17:54:09 -05:00
415064480a Patch the AsyncSyscall v4 exploit from working 2026-02-24 02:00:37 -06:00
f00453f703 Merge pull request '1.2-dev merge' (#5) from 1.2-dev into main
Reviewed-on: Hyperion/HyperionOS#5
2026-02-24 02:12:36 -05:00
ab1e847d1c Fix exploit with trailing whitespace 2026-02-24 00:48:20 -06:00
62a03bfe6b Potential windows case insensitive filesystem issue fix 2026-02-24 00:34:59 -06:00
e77a8b3636 AsyncSyscall3 exploit fix 2026-02-24 00:01:39 -06:00
6bb7f03a3e file permissions fixes 2026-02-23 23:50:37 -06:00
8798a2f4fe 2 potential vulnerability fixes 2026-02-23 23:26:21 -06:00
a6d2f6dca7 /home/user owned by user, user starts in cwd /home/user 2026-02-23 23:05:13 -06:00
b015d5880a load vuln fixed, sudo fixed 2026-02-23 22:43:12 -06:00
6694711423 Update contributors.md
Added developer names
2026-02-22 23:05:52 -05:00
40c97ca000 Hyperion v1.2.0 2026-02-22 21:53:02 -06:00
dd2437d4af fix astro's syscall redefiniton so files use it 2026-02-21 14:12:05 -06:00
d026cfbb03 cleaned syscalls 2026-02-21 15:06:54 -05:00
aad7efd055 Merge pull request 'New build system + hysh functionality' (#4) from spsf/HyperionOS:main into main
Reviewed-on: Hyperion/HyperionOS#4
2026-02-21 14:54:01 -05:00
93c3bab263 allow hypervisor to kill non-owned processes 2026-02-21 14:09:12 -05:00
5bd16b8fd4 su kill error handling 2026-02-21 14:07:35 -05:00
3cc1459769 New build system + hysh functionality 2026-02-21 12:49:57 -06:00
0655f2a39e added users (spsf untangled by astronand) 2026-02-20 23:32:05 -05:00
57b1d46837 Merge branch 'main' of https://git.astronand.dev/Hyperion/HyperionOS 2026-02-20 23:08:23 -05:00
875759022a fixed printInline & printf 2026-02-20 23:07:31 -05:00
194 changed files with 9892 additions and 9365 deletions

View File

@@ -5,6 +5,7 @@
"syscall", "syscall",
"printf", "printf",
"printInline", "printInline",
"toHex" "toHex",
"loadcstr"
] ]
} }

BIN
Build.tar

Binary file not shown.

BIN
Build.zip

Binary file not shown.

20
Makefile Normal file
View File

@@ -0,0 +1,20 @@
PYTHON := python3
ARCH_FLAG := $(if $(ARCH),--arch $(ARCH),)
MODE_FLAG := $(if $(DEV),--dev,--release)
.PHONY: build build-mini build-test build-mini-test clean
build:
$(PYTHON) build.py build $(ARCH_FLAG) $(MODE_FLAG)
build-mini:
$(PYTHON) build.py build-mini $(ARCH_FLAG) $(MODE_FLAG)
build-test:
$(PYTHON) build.py build-test $(ARCH_FLAG) $(MODE_FLAG)
build-mini-test:
$(PYTHON) build.py build-mini-test $(ARCH_FLAG) $(MODE_FLAG)
clean:
$(PYTHON) build.py clean

View File

@@ -1,9 +1,16 @@
[![Download on PineStore](https://raster.shields.io/badge/dynamic/json?url=https%3A%2F%2Fpinestore.cc%2Fapi%2Fproject%2F225&query=%24.project.downloads&suffix=%20downloads&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNzYuOTA0IiBoZWlnaHQ9Ijg5LjI5NSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDc2OS4wNCA4OTIuOTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI%2BCiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTQuNzQgLTQuNjgyNikiIGZpbGw9IiM5YWIyZjIiPgogIDxwYXRoIGQ9Im00MTAgODUxYzAtMTIgMjYtMjEgNTgtMjEgMTUgMCAyMiA0IDE3IDktMTQgMTItNzUgMjItNzUgMTJ6Ii8%2BCiAgPHBhdGggZD0ibTU4NSA3NDJjLTEtNDkgNC03MiAxNi04NSAyMi0yNCAzMC02OCAxNi04Ni0xMi0xNC0yNy0zOS00OC03OC0xMC0xOS05LTI2IDQtNDEgMjItMjQgMjEtNjctMi0xNDQtMjEtNjktMzktMTQ0LTQ4LTE5NS00LTI2LTItMzMgMTEtMzMgMzEgMCAxMTIgMzMgMTQxIDU4IDI4IDIzIDgxIDkyIDcxIDkyLTIgMCA1IDI2IDE2IDU3IDI4IDc5IDI5IDIyNCAzIDMwOC0xMCAzMy0xOSA2Mi0xOSA2NS00IDI2LTEzMiAxNTAtMTU1IDE1MC0zIDAtNi0zMC02LTY4eiIvPgogIDxwYXRoIGQ9Im02OCA2NzNjLTcyLTEwOS03MS0yNzggMy00MjMgMzYtNzEgNjItMTAwIDEyOC0xNDAgNDMtMjcgNjUtMzQgMTE4LTM2IDEwMC00IDk4IDExLTE5IDEzNi0zNCAzNy03OCA4OC05NiAxMTMtMjggMzktMzEgNDgtMjEgNjUgMTEgMTcgNiAyNy0zMyA3OS00MCA1My00NCA2Mi0zMiA3OCAxNyAyMyAxOCA1NyAyIDczLTYgNi0xNCAzMS0xNyA1NC02IDQyLTYgNDItMzMgMXoiLz4KIDwvZz4KIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNC43NCAtNC42ODI2KSIgZmlsbD0iIzU5YTY0ZiI%2BCiAgPHBhdGggZD0ibTM2NSA4MTNjLTUzLTYtMTM5LTMzLTE5Mi02MS02OC0zNS04My02Ny01OC0xMjIgMjYtNTkgNDAtNjcgNzgtNDkgNjggMzMgMTY3IDU4IDI2NiA2OSA1OCA1IDEwNiAxMiAxMDkgMTQgMiAzIDYgMzIgOSA2NSA4IDg1IDAgOTEtMTAxIDkwLTQ0LTEtOTQtNC0xMTEtNnoiLz4KICA8cGF0aCBkPSJtNDEwIDQ1OWMtNjctNy0xNjAtMjktMTk5LTQ4LTI3LTE0LTM0LTM2LTIwLTYzIDIxLTM4IDk3LTEzNiAxNTAtMTkzIDI1LTI3IDU4LTcxIDczLTk3IDI1LTQzIDMxLTQ3IDU0LTQyIDQwIDEwIDQyIDEyIDQyIDUyIDAgMjAgNiA1NyAxNCA4MiAyNCA3MyA1NCAxOTIgNjIgMjM2IDUgMzUgMyA0NS0xNSA2My0yMyAyMy0zNiAyNC0xNjEgMTB6Ii8%2BCiA8L2c%2BCiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTQuNzQgLTQuNjgyNikiIGZpbGw9IiM3ZWNiMjUiPgogIDxwYXRoIGQ9Im01NTggNjc0Yy0yLTItNTEtOS0xMDktMTQtMTAyLTExLTIwNC0zNy0yNjQtNjktMTYtOC0zMi0xNC0zNC0xMi00IDMtMzEtNDgtMzEtNjEgMC01IDIxLTMxIDQ2LTU4IDUxLTU0IDcxLTYwIDEzMC0zNSAxOSA4IDgzIDE5IDE0MiAyNSA1OCA2IDEwNyAxMiAxMDcgMTNzMTUgMjYgMzMgNTZjMjcgNDMgMzIgNjMgMzAgOTktMiAzNS04IDQ3LTI1IDUzLTExIDQtMjMgNi0yNSAzeiIvPgogPC9nPgogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE0Ljc0IC00LjY4MjYpIiBmaWxsPSIjZWNlZGVmIj4KICA8cGF0aCBkPSJtMjYwIDg5MGMtMzQtOC03MC00MS03MC02NSAwLTYtOS0yMC0yMC0zMHMtMjAtMjItMjAtMjctMTMtMjEtMzAtMzVjLTM1LTI5LTQxLTgzLTEzLTEyMiAxNS0yMiAxNS0yNi0xLTU2LTE4LTMzLTE4LTMzIDI3LTkxIDI4LTM2IDQyLTYzIDM2LTY4LTIzLTI1IDktNzggMTIwLTE5NyAzNi0zOCA3Mi04MSA4Mi05NiAxMC0xNCAyNS0zMCAzMy0zNSAzNi0yMCA3IDMyLTUzIDk3LTQ4IDUxLTEyNiAxNTAtMTQ5IDE4OS0xMCAxOC05IDI0IDEwIDQwIDIzIDE5IDIzIDE5LTI5IDcxLTUzIDUyLTUzIDUyLTM4IDgyIDE0IDI4IDE0IDMzLTEwIDc2LTMyIDU3LTIzIDgxIDQ2IDEyMCAzNCAxOSA0OSAzMyA0NSA0Mi0xNCAzNyAzNiA3NSA5OCA3NSAyNSAwIDQwLTcgNTQtMjUgMTgtMjMgMjctMjUgOTUtMjUgOTQgMCAxMDItOCA5My04OS02LTUzLTUtNTkgMTQtNjQgMzItOCAyNi02NC0xNS0xMzItMzUtNTgtMzUtNTgtOS04MiAyMS0xOSAyNC0yOSAxOS01Ni0xMC00Ny00NC0xNzUtNjEtMjI3LTgtMjUtMTQtNjItMTQtODMgMC0yNy01LTM5LTE3LTQzLTEwLTMtMjUtOC0zMy0xMC0xMi00LTEyLTYtMS0xNCAyNy0xNiA1NiA1IDY5IDUxIDM1IDExNyA0MyAxNDggNDYgMTcwIDIgMTMgMTEgNTEgMjEgODQgMjEgNzEgMjEgMTIxIDAgMTQ1LTE0IDE1LTEzIDE5IDUgNDMgMTEgMTQgMjAgMzAgMjAgMzVzNyAxNSAxNSAyMmMyMSAxNyAxNiA3NS0xMCAxMDItMTggMTktMjAgMzItMTcgNzkgNCA1MCAyIDU4LTE5IDcyLTEyIDktNTAgMTktODMgMjMtNDUgNS02NSAxMy04MyAzMi0yNiAyOC05MiAzOC0xNTMgMjJ6Ii8%2BCiA8L2c%2BCiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTQuNzQgLTQuNjgyNikiIGZpbGw9IiM3ZTY3NGQiPgogIDxwYXRoIGQ9Im0yNDggODU0Yy0zMC0xNi00Ny01OS0zMC03NiA4LTggMjMtNyA1NCAyIDI0IDcgNjEgMTQgODMgMTcgNTQgNyA1OSAxNSAzNSA0Ni0xOCAyMy0yOSAyNy02OCAyNy0yNi0xLTU5LTctNzQtMTZ6Ii8%2BCiA8L2c%2BCjwvc3ZnPgo%3D&label=PineStore)](https://pinestore.cc/projects/225/hyperionos)
# HyperionOS # HyperionOS
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services. HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
--- ---
## Building
See `building.md`.
---
## Features ## Features
- Functionality is split into kernel modules (`.kmod`) - Functionality is split into kernel modules (`.kmod`)

Binary file not shown.

View File

@@ -1,295 +0,0 @@
--:Minify:--
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2)
local success, errorMsg = xpcall(function()
local fs = require("sys.fs")
syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"spos",1,1)
print("HyperionOS Bash Shell")
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
local commandHistory = {}
local terminate = false
syscall.setEnviron("SHELL","rtbash")
syscall.setEnviron("PATH","/bin/")
syscall.chdir("/")
local oldWD = ""
for i = 1, 16 do
syscall.devctl(1,"sbgc",i)
printInline(" ");
end
print("\n")
syscall.sigcatch(function(sig)
if sig == 1 then
terminate = true
end
end)
local builtinCmds = {}
builtinCmds.cd = function(path)
local cwd = syscall.getcwd()
local dirIn = (path or "")
if dirIn == "-" then
if oldWD == "" then
print("bash-cd: No previous working directory set.")
else
print(oldWD)
syscall.chdir(oldWD)
oldWD = cwd
end
return
end
local dirInMod = dirIn
if dirIn:sub(1, 1) ~= "/" then dirInMod = cwd .. "/" .. dirIn end
local parts = {}
for part in dirInMod:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
table.insert(parts, part)
end
end
local normDir = "/" .. table.concat(parts, "/")
if normDir:sub(#normDir, #normDir) ~= "/" then normDir = normDir .. "/" end
if not fs.isDir(normDir) then
print("bash-cd: "..dirIn..": No such directory.")
return
end
oldWD = cwd
syscall.chdir(normDir)
end
local function getUserInput()
syscall.devctl(1,"sfgc",3)
printInline(userhost)
syscall.devctl(1,"sfgc",1)
printInline(":")
syscall.devctl(1,"sfgc",10)
printInline(syscall.getcwd())
syscall.devctl(1,"sfgc",1)
printInline("$ ")
local curOffsetStr = syscall.devctl(1, "gpos")
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
local curOffsetY = tonumber(curOffsetStr:sub(curOffsetStr:find(";")+1))
local input = ""
local blinkState = false
local cursorPos = 1
local history = 0
while true do
local key=syscall.read(0)
if key then
if key == "\19" then --TODO: REPLACE WITH LEFT ARROW
if cursorPos > 1 then
cursorPos = cursorPos - 1
end
elseif key == "\20" then --TODO: REPLACE WITH RIGHT ARROW
if cursorPos <= #input then
cursorPos = cursorPos + 1
end
elseif key == "\17" then --TODO: REPLACE WITH UP ARROW
if history < #commandHistory then
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
printInline((" "):rep(#input + 1))
history = history + 1
input = commandHistory[#commandHistory - history + 1]
cursorPos = #input + 1
end
elseif key == "\18" then --TODO: REPLACE WITH DOWN ARROW
if history > 1 then
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
printInline((" "):rep(#input + 1))
history = history - 1
input = commandHistory[#commandHistory - history + 1]
cursorPos = #input + 1
elseif history == 1 then
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
printInline((" "):rep(#input + 1))
history = 0
input = ""
cursorPos = 1
end
elseif key == "\b" then
if cursorPos > 1 then
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
printInline((" "):rep(#input + 1))
input = string.sub(input, 1, cursorPos-2)..string.sub(input, cursorPos)
cursorPos = cursorPos - 1
end
elseif key == "\n" then
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
print(input.." ")
return input
else
input = string.sub(input, 1, cursorPos-1)..key..string.sub(input, cursorPos)
cursorPos = cursorPos + 1
end
local screenSizeStr = syscall.devctl(1, "size")
local sizeX = tonumber(screenSizeStr:sub(1, screenSizeStr:find(";")-1))
local sizeY = tonumber(screenSizeStr:sub(screenSizeStr:find(";")+1))
local totalChars = sizeX * sizeY
local eocCharNum = ((curOffsetY - 1) * sizeX) + curOffsetX + #input
if eocCharNum >= totalChars then
syscall.devctl(1,"spos",sizeX,sizeY)
printInline(" ")
curOffsetY = curOffsetY - 1
end
end
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
printInline(string.sub(input, 1, cursorPos-1))
if blinkState then
syscall.devctl(1,"sfgc",16)
syscall.devctl(1,"sbgc",1)
end
if cursorPos > #input then
printInline(" ")
else
printInline(string.sub(input, cursorPos, cursorPos))
end
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"sbgc",16)
printInline(string.sub(input, cursorPos+1))
if cursorPos <= #input then
printInline(" ")
end
local curBlink = ((math.floor(syscall.getUptime() / 500) % 2) == 0)
if curBlink ~= blinkState then
blinkState = curBlink
end
end
end
local function runCommand(command)
do
local func = load("return " .. command, "@equation", "t", {})
if func then
local success, result = pcall(func)
if success and type(result) == "number" then
print(result)
return
end
end
end
terminate = false
local args = string.split(command, " ")
if builtinCmds[args[1]] then
local success, msg = pcall(builtinCmds[args[1]], table.unpack(args, 2))
if not success then
local errSL = string.sub(msg, string.find(msg, "]") + 2)
syscall.devctl(1,"sfgc",2)
printInline(args[1]..": Program runtime error on line ")
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
syscall.devctl(1,"sfgc",1)
print(string.sub(errSL, string.find(errSL, ":") + 1))
end
return
end
local cmdPath = ""
if string.find(args[1], "/") then
if fs.exists(args[1]) then
cmdPath = args[1]
end
else
local paths = string.split(syscall.getEnviron("PATH"), ":")
for _, path in pairs(paths) do
if fs.exists(path..args[1]) then
cmdPath = path..args[1]
break
end
end
end
if cmdPath == "" then
print(args[1]..": Command not found")
return
end
local progName = string.sub(cmdPath, #cmdPath - string.find(string.reverse(cmdPath), "/") + 2)
local text = fs.readAllText(cmdPath)
local program, err = load(text, progName)
if not program then
local errSL = string.sub(err, string.find(err, ":") + 1)
syscall.devctl(1,"sfgc",2)
printInline(progName..": Program load error on line ")
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
syscall.devctl(1,"sfgc",1)
print(string.sub(errSL, string.find(errSL, ":") + 1))
return
end
local proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","w")
local success, msg = pcall(program, ...)
if not success then
local errSL = string.sub(msg, string.find(msg, ":") + 1)
syscall.devctl(1,"sfgc",2)
printInline(progName..": Program runtime error on line ")
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
syscall.devctl(1,"sfgc",1)
print(string.sub(errSL, string.find(errSL, ":") + 1))
end
end, progName, nil, {table.unpack(args, 2)})
while true do
local exited, code = syscall.collect(proc)
if exited then
if code then
print("\nTask exited with code:\n"..tostring(code))
end
return
end
if terminate then
local success, err = syscall.kill(proc)
if success then
syscall.devctl(1,"sbgc",16)
syscall.devctl(1,"sfgc",2)
print("\nProgram Terminated.")
syscall.devctl(1,"sfgc",1)
end
terminate = false
break
end
sleep(0.05)
end
end
while true do
local command = getUserInput()
if command ~= "" then
if command ~= commandHistory[#commandHistory] then
table.insert(commandHistory, command)
end
runCommand(command)
end
end
--ERROR HANDLING
end, debug.traceback)
if not success then
syscall.log("Error running shell: "..errorMsg, "ERROR")
syscall.devctl(1,"sfgc",2)
syscall.devctl(1,"sbgc",16)
print()
print("Error running shell: ")
print(errorMsg)
end

View File

@@ -1,94 +0,0 @@
--:Minify:--
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","r")
syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"spos",1,1)
print("HyperionOS Bash Shell")
local str=""
local stopInput=false
local proc=0
local fs=require("sys.fs")
local timeout=false
syscall.setEnviron("SHELL","simpleshell")
printInline("> ")
syscall.sigcatch(function(sig)
if sig==1 then
syscall.kill(proc)
print("Terminated")
printInline("> ")
stopInput=false
end
end)
while true do
if not stopInput then
local input=syscall.read(0)
if input then
if input=="\b" then
if #str>0 then
str=str:sub(1,#str-1)
printInline("\b")
end
elseif input=="\n" then
print("")
stopInput=true
if str == "" then
printInline("> ")
stopInput=false
else
local path=nil
local split=string.split(str, " ")
if fs.exists("/bin/"..split[1]) then
path="/bin/"..split[1]
elseif fs.exists("/bin/"..split[1]..".lua") then
path="/bin/"..split[1]..".lua"
end
if not path then
print("Program not found")
printInline("> ")
stopInput=false
else
local text = fs.readAllText(path)
local program, err = load(text, path)
if not program then
print(err)
printInline("> ")
end
proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","w")
program(...)
end, path, nil, {table.unpack(split, 2)})
end
str=""
end
else
str=str..input
printInline(input)
end
timeout=false
else
timeout=true
end
else
local exited, code = syscall.collect(proc)
if exited then
if code then
print("\nTask exited with code:\n"..tostring(code))
end
printInline("> ")
stopInput=false
end
timeout=true
end
if timeout then
if stopInput then
sleep(.5)
else
sleep(.05)
end
end
end

View File

@@ -1,24 +0,0 @@
local args = {...}
local name = syscall.getTask(syscall.getpid()).name
local fs = require("sys.fs")
local filePath = (args[1] or "")
if filePath:sub(1, 1) ~= "/" then
filePath = syscall.getcwd().."/"..filePath
end
if not fs.exists(filePath) and args[1] then
print(name..": Cannot access '"..args[1].."': No such file.")
return
end
local file = 0
if args[1] then
file = syscall.open(filePath, "r")
end
local content=""
while content~=nil or file == 0 do
content=syscall.read(file, 1024)
printInline(content)
end
syscall.close(file)
print("")

View File

@@ -1 +0,0 @@
syscall.devctl(1,"clear")

View File

@@ -1,2 +0,0 @@
local args = {...}
print(table.concat(args, " "))

View File

@@ -1,292 +0,0 @@
--:Minify:--
-- help: display command reference with paged scrolling
local COMMANDS = {
-- {name, usage, description, {flags...}}
-- flags: {flag, desc}
{
name = "cat",
usage = "cat [file]",
desc = "Print file contents to stdout. Reads from stdin if no file given.",
flags = {}
},
{
name = "cd",
usage = "cd [dir]",
desc = "Change working directory. Use '-' to return to previous directory.",
flags = {}
},
{
name = "clear",
usage = "clear",
desc = "Clear the terminal screen.",
flags = {}
},
{
name = "echo",
usage = "echo [text...]",
desc = "Print arguments to stdout.",
flags = {}
},
{
name = "hfetch",
usage = "hfetch",
desc = "Display system information in a neofetch-style layout.",
flags = {}
},
{
name = "id",
usage = "id [username]",
desc = "Print user identity (uid, gid). Defaults to current user.",
flags = {}
},
{
name = "login",
usage = "login",
desc = "System login prompt. Launched automatically at boot.",
flags = {}
},
{
name = "ls",
usage = "ls [-alh] [dir]",
desc = "List directory contents.",
flags = {
{"-a", "Show hidden files (starting with .)"},
{"-l", "Long format: permissions, owner, size"},
{"-h", "Human-readable file sizes"},
}
},
{
name = "lsusers",
usage = "lsusers",
desc = "List all user accounts with uid, gid, home, and shell.",
flags = {}
},
{
name = "lua",
usage = "lua",
desc = "Interactive Lua REPL prompt.",
flags = {}
},
{
name = "mkdir",
usage = "mkdir <dir>",
desc = "Create a directory.",
flags = {}
},
{
name = "passwd",
usage = "passwd [username]",
desc = "Change a user password. Non-root must verify current password first.",
flags = {}
},
{
name = "ps",
usage = "ps",
desc = "List running tasks with pid, user, name, and status.",
flags = {}
},
{
name = "pwd",
usage = "pwd",
desc = "Print current working directory.",
flags = {}
},
{
name = "su",
usage = "su [username]",
desc = "Switch user. Defaults to root. Root can switch without a password.",
flags = {}
},
{
name = "sudo",
usage = "sudo [-u user] <command> [args...]",
desc = "Run a command as another user (default root). Authenticates as current user.",
flags = {
{"-u user", "Run as the specified user (name or uid)"},
}
},
{
name = "sysdump",
usage = "sysdump",
desc = "List all registered kernel syscalls.",
flags = {}
},
{
name = "useradd",
usage = "useradd [-p pw] [-g gid] [-d home] [-s shell] [-M] <username>",
desc = "Create a new user account.",
flags = {
{"-p pw", "Set password (prompted interactively if omitted)"},
{"-g gid", "Set primary group id"},
{"-d home", "Set home directory (default: /home/username)"},
{"-s shell", "Set login shell (default: /bin/hysh)"},
{"-M", "Do not create home directory"},
}
},
{
name = "userdel",
usage = "userdel [-r] <username>",
desc = "Delete a user account.",
flags = {
{"-r", "Also recursively remove the user's home directory"},
}
},
{
name = "usermod",
usage = "usermod [-l name] [-p pw] [-g gid] [-d home] [-s shell] [-L] [-U] <username>",
desc = "Modify an existing user account.",
flags = {
{"-l name", "Rename the user"},
{"-p pw", "Set new password"},
{"-g gid", "Change primary group id"},
{"-d home", "Change home directory"},
{"-s shell", "Change login shell"},
{"-L", "Lock the account (disable login)"},
{"-U", "Unlock the account"},
}
},
{
name = "whoami",
usage = "whoami",
desc = "Print the current username.",
flags = {}
},
{
name = "yes",
usage = "yes [text]",
desc = "Repeatedly print 'y' (or given text) until interrupted with Ctrl+C.",
flags = {}
},
}
-- Build lines to display
local C_HEAD = 7 -- yellow (section headers)
local C_CMD = 5 -- cyan (command name)
local C_USAGE = 1 -- white (usage line)
local C_DESC = 13 -- grey (description)
local C_FLAG = 3 -- green (flag name)
local C_DIM = 12 -- dark grey
-- Each entry is {text, color}
local lines = {}
local function push(text, col) lines[#lines+1] = {text, col or 1} end
push("HyperionOS Command Reference", C_HEAD)
push(string.rep("=", 50), C_DIM)
push("", 1)
local args = {...}
local filter = args[1] -- optional: help <command>
local function addCmd(cmd)
push(cmd.name, C_CMD)
push(" Usage: " .. cmd.usage, C_USAGE)
push(" " .. cmd.desc, C_DESC)
if #cmd.flags > 0 then
for _, f in ipairs(cmd.flags) do
push(" " .. f[1], C_FLAG)
push(" " .. f[2], C_DESC)
end
end
push("", 1)
end
if filter then
local found = false
for _, cmd in ipairs(COMMANDS) do
if cmd.name == filter then addCmd(cmd); found = true; break end
end
if not found then
push("help: unknown command '" .. filter .. "'", 2)
push("Run 'help' with no arguments for the full list.", C_DESC)
end
else
push("Run 'help <command>' for details on a specific command.", C_DESC)
push("", 1)
for _, cmd in ipairs(COMMANDS) do addCmd(cmd) end
end
-- Pager
local sizeStr = syscall.devctl(1, "size")
local screenW = tonumber(sizeStr:sub(1, sizeStr:find(";")-1)) or 51
local screenH = tonumber(sizeStr:sub(sizeStr:find(";")+1)) or 19
local pageSize = screenH - 2 -- reserve bottom row for status bar
local scroll = 0
local totalLines = #lines
local dirty = true
local function render()
syscall.devctl(1, "clear")
syscall.devctl(1, "spos", 1, 1)
for row = 1, pageSize do
local li = scroll + row
if li <= totalLines then
local text, col = lines[li][1], lines[li][2]
syscall.devctl(1, "sfgc", col)
-- truncate to screen width
if #text > screenW then text = text:sub(1, screenW) end
syscall.write(1, text .. "\n")
else
syscall.write(1, "\n")
end
end
-- status bar
syscall.devctl(1, "sfgc", 16)
syscall.devctl(1, "sbgc", 13)
local pct = math.floor(math.min(100, (scroll + pageSize) / totalLines * 100))
local status = string.format(" help -- line %d/%d (%d%%) [up/down: scroll q: quit] ",
scroll + 1, totalLines, pct)
if #status > screenW then status = status:sub(1, screenW) end
syscall.devctl(1, "spos", 1, screenH)
syscall.write(1, status .. string.rep(" ", screenW - #status))
syscall.devctl(1, "sfgc", 1)
syscall.devctl(1, "sbgc", 16)
dirty = false
end
-- If output fits on one screen, just print without pager
if totalLines <= pageSize then
syscall.devctl(1, "clear")
syscall.devctl(1, "spos", 1, 1)
for _, line in ipairs(lines) do
syscall.devctl(1, "sfgc", line[2])
syscall.write(1, line[1] .. "\n")
end
syscall.devctl(1, "sfgc", 1)
return
end
render()
while true do
local ch = syscall.read(0)
if not ch or ch == "" then
-- idle
elseif ch == "q" or ch == "Q" then
break
elseif ch == "\17" then -- up arrow
if scroll > 0 then
scroll = scroll - 1
dirty = true
end
elseif ch == "\18" then -- down arrow
if scroll + pageSize < totalLines then
scroll = scroll + 1
dirty = true
end
elseif ch == "\19" then -- left / page up (reuse left arrow)
scroll = math.max(0, scroll - pageSize)
dirty = true
elseif ch == "\20" then -- right / page down
scroll = math.min(totalLines - pageSize, scroll + pageSize)
dirty = true
end
if dirty then render() end
end
-- Restore screen
syscall.devctl(1, "clear")
syscall.devctl(1, "spos", 1, 1)
syscall.devctl(1, "sfgc", 1)
syscall.devctl(1, "sbgc", 16)

View File

@@ -1,299 +0,0 @@
--:Minify:--
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2)
local success, errorMsg = xpcall(function()
local fs = require("sys.fs")
syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"spos",1,1)
print("HyperionOS hysh Shell")
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
local commandHistory = {}
local terminate = false
syscall.setEnviron("SHELL","rtbash")
syscall.setEnviron("PATH","/bin/")
syscall.chdir("/")
local oldWD = ""
for i = 1, 16 do
syscall.devctl(1,"sbgc",i)
printInline(" ");
end
print("\n")
syscall.sigcatch(function(sig)
if sig == 1 then
terminate = true
end
end)
local builtinCmds = {}
builtinCmds.cd = function(path)
local cwd = syscall.getcwd()
local dirIn = (path or "")
if dirIn == "-" then
if oldWD == "" then
print("hysh-cd: No previous working directory set.")
else
print(oldWD)
syscall.chdir(oldWD)
oldWD = cwd
end
return
end
local dirInMod = dirIn
if dirIn:sub(1, 1) ~= "/" then dirInMod = cwd .. "/" .. dirIn end
local parts = {}
for part in dirInMod:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
table.insert(parts, part)
end
end
local normDir = "/" .. table.concat(parts, "/")
if normDir:sub(#normDir, #normDir) ~= "/" then normDir = normDir .. "/" end
if not fs.isDir(normDir) then
print("hysh-cd: "..dirIn..": No such directory.")
return
end
oldWD = cwd
syscall.chdir(normDir)
end
local function getUserInput()
syscall.devctl(1,"sfgc",3)
syscall.write(1, userhost)
syscall.devctl(1,"sfgc",1)
syscall.write(1, ":")
syscall.devctl(1,"sfgc",10)
syscall.write(1, syscall.getcwd())
syscall.devctl(1,"sfgc",1)
syscall.write(1, "$ ")
local curOffsetStr = syscall.devctl(1, "gpos")
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
local curOffsetY = tonumber(curOffsetStr:sub(curOffsetStr:find(";")+1))
local input = ""
local blinkState = false
local cursorPos = 1
local history = 0
local dirty = true -- redraw on first iteration
local function redraw()
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
-- text before cursor
syscall.write(1, string.sub(input, 1, cursorPos-1))
-- cursor character (inverted if blinking on)
if blinkState then
syscall.devctl(1,"sfgc",16)
syscall.devctl(1,"sbgc",1)
end
if cursorPos > #input then
syscall.write(1, " ")
else
syscall.write(1, string.sub(input, cursorPos, cursorPos))
end
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"sbgc",16)
-- text after cursor + trailing space to erase old chars
syscall.write(1, string.sub(input, cursorPos+1) .. " ")
end
while true do
local key = syscall.read(0)
if key and key ~= "" then
if key == "\19" then -- left arrow
if cursorPos > 1 then
cursorPos = cursorPos - 1
dirty = true
end
elseif key == "\20" then -- right arrow
if cursorPos <= #input then
cursorPos = cursorPos + 1
dirty = true
end
elseif key == "\17" then -- up arrow
if history < #commandHistory then
history = history + 1
input = commandHistory[#commandHistory - history + 1]
cursorPos = #input + 1
dirty = true
end
elseif key == "\18" then -- down arrow
if history > 1 then
history = history - 1
input = commandHistory[#commandHistory - history + 1]
cursorPos = #input + 1
dirty = true
elseif history == 1 then
history = 0
input = ""
cursorPos = 1
dirty = true
end
elseif key == "\b" then
if cursorPos > 1 then
input = string.sub(input, 1, cursorPos-2) .. string.sub(input, cursorPos)
cursorPos = cursorPos - 1
dirty = true
end
elseif key == "\n" then
-- redraw cleanly with no cursor highlight before committing
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"sbgc",16)
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, input .. " \n")
return input
else
input = string.sub(input, 1, cursorPos-1) .. key .. string.sub(input, cursorPos)
cursorPos = cursorPos + 1
dirty = true
end
end
-- cursor blink
local curBlink = ((math.floor(syscall.getUptime() / 500) % 2) == 0)
if curBlink ~= blinkState then
blinkState = curBlink
dirty = true
end
if dirty then
redraw()
dirty = false
end
end
end
local function runCommand(command)
do
local func = load("return " .. command, "@equation", "t", {})
if func then
local success, result = pcall(func)
if success and type(result) == "number" then
print(result)
return
end
end
end
terminate = false
local args = string.split(command, " ")
if builtinCmds[args[1]] then
local success, msg = pcall(builtinCmds[args[1]], table.unpack(args, 2))
if not success then
local errSL = string.sub(msg, string.find(msg, "]") + 2)
syscall.devctl(1,"sfgc",2)
printInline(args[1]..": Program runtime error on line ")
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
syscall.devctl(1,"sfgc",1)
print(string.sub(errSL, string.find(errSL, ":") + 1))
end
return
end
local cmdPath = ""
if string.find(args[1], "/") then
if fs.exists(args[1]) then
cmdPath = args[1]
end
else
local paths = string.split(syscall.getEnviron("PATH"), ":")
for _, path in pairs(paths) do
if fs.exists(path..args[1]) then
cmdPath = path..args[1]
break
end
end
end
if cmdPath == "" then
print(args[1]..": Command not found")
return
end
local progName = string.sub(cmdPath, #cmdPath - string.find(string.reverse(cmdPath), "/") + 2)
local text = fs.readAllText(cmdPath)
local program, err = load(text, progName)
if not program then
local errSL = string.sub(err, string.find(err, ":") + 1)
syscall.devctl(1,"sfgc",2)
printInline(progName..": Program load error on line ")
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
syscall.devctl(1,"sfgc",1)
print(string.sub(errSL, string.find(errSL, ":") + 1))
return
end
local proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","w")
local success, msg = pcall(program, ...)
if not success then
local errSL = string.sub(msg, string.find(msg, ":") + 1)
syscall.devctl(1,"sfgc",2)
printInline(progName..": Program runtime error on line ")
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
syscall.devctl(1,"sfgc",1)
print(string.sub(errSL, string.find(errSL, ":") + 1))
end
end, progName, nil, {table.unpack(args, 2)})
while true do
local exited, code = syscall.collect(proc)
if exited then
if code then
print("\nTask exited with code:\n"..tostring(code))
end
return
end
if terminate then
local success, err = syscall.kill(proc)
if success then
syscall.devctl(1,"sbgc",16)
syscall.devctl(1,"sfgc",2)
print("\nProgram Terminated.")
syscall.devctl(1,"sfgc",1)
end
terminate = false
break
end
sleep(0.05)
end
end
while true do
local command = getUserInput()
if command ~= "" then
if command ~= commandHistory[#commandHistory] then
table.insert(commandHistory, command)
end
runCommand(command)
end
end
--ERROR HANDLING
end, debug.traceback)
if not success then
syscall.log("Error running shell: "..errorMsg, "ERROR")
syscall.devctl(1,"sfgc",2)
syscall.devctl(1,"sbgc",16)
print()
print("Error running shell: ")
print(errorMsg)
end

View File

@@ -1,94 +0,0 @@
--:Minify:--
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","r")
syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"spos",1,1)
print("HyperionOS hysh Shell")
local str=""
local stopInput=false
local proc=0
local fs=require("sys.fs")
local timeout=false
syscall.setEnviron("SHELL","simpleshell")
printInline("> ")
syscall.sigcatch(function(sig)
if sig==1 then
syscall.kill(proc)
print("Terminated")
printInline("> ")
stopInput=false
end
end)
while true do
if not stopInput then
local input=syscall.read(0)
if input then
if input=="\b" then
if #str>0 then
str=str:sub(1,#str-1)
printInline("\b")
end
elseif input=="\n" then
print("")
stopInput=true
if str == "" then
printInline("> ")
stopInput=false
else
local path=nil
local split=string.split(str, " ")
if fs.exists("/bin/"..split[1]) then
path="/bin/"..split[1]
elseif fs.exists("/bin/"..split[1]..".lua") then
path="/bin/"..split[1]..".lua"
end
if not path then
print("Program not found")
printInline("> ")
stopInput=false
else
local text = fs.readAllText(path)
local program, err = load(text, path)
if not program then
print(err)
printInline("> ")
end
proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","w")
program(...)
end, path, nil, {table.unpack(split, 2)})
end
str=""
end
else
str=str..input
printInline(input)
end
timeout=false
else
timeout=true
end
else
local exited, code = syscall.collect(proc)
if exited then
if code then
print("\nTask exited with code:\n"..tostring(code))
end
printInline("> ")
stopInput=false
end
timeout=true
end
if timeout then
if stopInput then
sleep(.5)
else
sleep(.05)
end
end
end

View File

@@ -1,145 +0,0 @@
local cloptions = {
a = false,
h = false,
l = false,
help = false,
}
local inpArgs = {...}
local args = {}
local name = syscall.getTask(syscall.getpid()).name
local optToSet = false
for _, v in pairs(inpArgs) do
if optToSet then
cloptions[optToSet] = v
optToSet = false
elseif v:sub(1, 2) == "--" then
local opt = v:sub(3)
if cloptions[opt] == nil then
print(name..": unrecognized option '"..v.."'.")
if cloptions.help ~= nil then
print("try '"..name.." --help' for more information.")
end
return
elseif cloptions[opt] == false then
cloptions[opt] = true
else
optToSet = opt
end
elseif v:sub(1, 1) == "-" then
for i = 2, #v do
local opt = v:sub(i, i)
if cloptions[opt] == nil then
print(name..": invalid option '-"..opt.."'.")
if cloptions.help ~= nil then
print("try '"..name.." --help' for more information.")
end
return
else
cloptions[opt] = true
end
end
else
table.insert(args, v)
end
end
if cloptions.help then
print("Usage: "..name.." [OPTION]... [DIR]")
print("List all entries in the specified DIRectory, or cwd if not specified")
print("\nOptions:")
print(" -a Do not ignore entries starting with .")
print(" -h with -l, print sizes in a human readble format")
print(" -l Use a long listing format")
print(" --help Display this help and exit")
return
end
local fs = require("sys.fs")
local dir = (args[1] or "")
if dir:sub(1, 1) ~= "/" then
dir = syscall.getcwd().."/"..dir
end
if dir:sub(#dir, #dir) ~= "/" then
dir = dir.."/"
end
if not fs.isDir(dir) then
print(name..": Cannot access '"..args[1].."': No such directory.")
return
end
local screenSizeStr = syscall.devctl(1, "size")
local sizeX = tonumber(screenSizeStr:sub(1, screenSizeStr:find(";")-1))
local sizeY = tonumber(screenSizeStr:sub(screenSizeStr:find(";")+1))
local list = fs.list(dir)
if not cloptions.a then
for i = #list, 1, -1 do
if list[i]:sub(1, 1) == "." then
table.remove(list, i)
end
end
end
local colWidth = 0
local numCols = 1
if not cloptions.l then
for _, item in pairs(list) do
if #item + 2 > colWidth then
colWidth = #item + 2
end
end
numCols = math.floor(sizeX / colWidth)
end
local sizePrefixes = {"K", "M", "G"}
for i,v in ipairs(list) do
local fileStats = syscall.stat(dir..v)
local isDir = fs.isDir(dir..v)
if cloptions.l then
if isDir then
printInline("d")
else
printInline("-")
end
printInline("------ ")
printInline(fileStats.owner.." ")
printInline(fileStats.group.." ")
local size = fileStats.size
if cloptions.h then
local scale = 0
while size > 1024 do
size = size / 1024
scale = scale + 1
end
if scale > 0 then
if size < 10 then
size = math.floor(size).."."..math.floor((size * 10) % 10)..sizePrefixes[scale]
else
size = math.floor(size)..sizePrefixes[scale]
end
end
end
printInline(size.." ")
printInline(math.floor(fileStats.modified / 1000).." ")
end
if isDir then
syscall.devctl(1,"sfgc",4)
else
syscall.devctl(1,"sfgc",1)
end
printInline(v)
printInline((" "):rep(colWidth - #v))
syscall.devctl(1,"sfgc",1)
if i % numCols == 0 then
print("")
end
end
if #list % numCols ~= 0 then
print("")
end

View File

@@ -1,50 +0,0 @@
--:Minify:--
print("HyperionOS lua")
local str=""
local stopInput=false
local timeout=false
local luaEnv=setmetatable({},{__index=_ENV})
printInline("> ")
while true do
local input=syscall.read(0)
if input then
if input=="\b" then
if #str>0 then
str=str:sub(1,#str-1)
printInline("\b")
end
elseif input=="\n" then
print("")
stopInput=true
if str == "" then
printInline("> ")
stopInput=false
elseif str == "exit()" then
break
else
local func=load(str,"@Lua","t",luaEnv)
local ok,err = xpcall(func, debug.traceback)
if not ok then
print(err)
end
printInline("\n> ")
str=""
end
str=""
else
str=str..input
printInline(input)
end
timeout=false
else
timeout=true
end
if timeout then
if stopInput then
sleep(.5)
else
sleep(.05)
end
end
end

View File

@@ -1,23 +0,0 @@
local args = {...}
local name = syscall.getTask(syscall.getpid()).name
if #args == 0 then
print(name..": Missing operand.")
return
end
local fs = require("sys.fs")
local newDir = args[1]
if newDir:sub(1, 1) ~= "/" then
newDir = syscall.getcwd().."/"..newDir
end
if newDir:sub(#newDir, #newDir) ~= "/" then
newDir = newDir.."/"
end
if fs.isDir(newDir) then
print(name..": Cannot create directory '"..args[1].."': Directory already exists.")
return
end
fs.mkdir(newDir)

View File

@@ -1,18 +0,0 @@
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
print(".. *. .. | "..userhost)
print(" *= +@* +* | "..string.rep("-",#userhost))
print(" .@#. -@@@= :#@. | OS: "..(syscall.version() or "Unknown"))
print(" =@@+ *@@@# +@@= | Host: "..(syscall.getHost() or "Unknown"))
print(" %@@%: *@@@# -%@@% | Uptime: "..(syscall.getUptime() or "Unknown"))
print(" :@@@@+ *@@@# .*@@@@: | Tasks: "..tostring((#syscall.getTasks() or "Unknown")))
print(" :*@@@%- *@@@# -@@@@*: | Packages: ".."Unknown")
print(" =%@@#. *@@@# .#@@%= | Shell: "..(syscall.getEnviron("SHELL") or "Unknown"))
print(" :=. :*@@= *@@@# =@@+: .=: | ")
print(" %@#=..*# +@@@# #*..=#@# | ")
print(" .@@@@+=# .%@%: #=+@@@@. | ")
print(" .....=# -@= *+...:. | ")
print(" -*%*-@= - =@-*%*- | ")
print(" -@*. -@%. :%@- :*@- | ")
print(" .#@#@* | ")
print(" -#- | ")
print(" | ")

View File

@@ -1 +0,0 @@
print(syscall.getcwd())

Binary file not shown.

View File

@@ -1,4 +0,0 @@
local fs = require("sys.fs")
local bashStr = fs.readAllText("/bin/bash")
local bashFun = load(bashStr)
syscall.spawn(bashFun, "bash")

View File

@@ -1 +0,0 @@
syscall.chown("/bin", 0, 0)

View File

@@ -1,5 +0,0 @@
local syscalls=syscall.sysdump()
for i=1, #syscalls do
print(syscalls[i])
end
print("Total # of syscalls: "..tostring(#syscalls))

View File

@@ -1 +0,0 @@
print((syscall.auth_whoami() or "Unknown"))

Binary file not shown.

View File

@@ -1,5 +1,5 @@
local io = {} local io = {}
local fs = require("sys.fs") local fs = require("fs")
function io.open(path, mode) function io.open(path, mode)
return fs.open(path, mode) return fs.open(path, mode)

View File

@@ -1,629 +0,0 @@
-- :Minify:--
local kernel = ...
local vfs = {}
kernel.vfs = vfs
vfs.mounts = {["$"] = "/"}
vfs.disks = kernel.disks
-- Path normalization
local function normalizePath(path)
local task = kernel.currentTask
local cwd = task.cwd or "/"
if path:sub(1, 1) ~= "/" then path = cwd .. "/" .. path end
local parts = {}
for part in path:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
table.insert(parts, part)
end
end
return "/" .. table.concat(parts, "/")
end
function vfs.splitPath(path)
local rv=string.split(path,"/")
while table.indexOf(rv, "") ~= -1 do
table.remove(rv, table.indexOf(rv, ""))
end
return rv
end
-- Resolve mount and disk path
local function resolvePath(path)
path = normalizePath(path)
local mountPoint = nil
local mountId = nil
for id, mp in pairs(vfs.mounts) do
if path == mp or (mp == "/" and path:sub(1, 1) == "/") or path:sub(1, #mp + 1) == mp .. "/" then
if not mountPoint or #mp > #mountPoint then
mountPoint = mp
mountId = id
end
end
end
if not mountId then
error("ENODEV")
end
local diskPath = path:sub(#mountPoint + 1)
if diskPath == "" then
diskPath = "/"
end
if kernel.config.logPathResolution then
kernel.log("Path '"..path.."' resolved to disk '"..mountId.."' and path '"..diskPath.."'")
end
return vfs.disks[mountId], diskPath
end
-- Allocate file descriptor for current task
local function allocFD(task)
local fd = 0
while task.fd[fd] do fd = fd + 1 end
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
return fd
end
-- System-wide open file limit
local total = 0
local function checkSystemLimit()
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
end
-- File object constructor
local function newFileObj(handle, mode, path, meta, type)
return {
handle = handle,
mode = mode,
path = path,
meta = meta,
type = type,
refcount = 1
}
end
function vfs.newfd(fdobj)
checkSystemLimit()
total=total+1
local fd = allocFD(kernel.currentTask)
kernel.currentTask.fd[fd]=fdobj
end
-- Parse metafile
local function parseMetafile(file)
if not file or file == "" then return {} end
local ret = {}
local pointer = 1
while pointer <= #file do
local namelen = file:byte(pointer)
pointer = pointer + 1
local name = file:sub(pointer, pointer + namelen - 1)
pointer = pointer + namelen
local owner = file:byte(pointer)
local group = file:byte(pointer + 1)
local perms = file:byte(pointer + 2)
pointer = pointer + 3
local cmetalen = file:byte(pointer)
pointer = pointer + 1
local cmeta = ""
if cmetalen > 0 then
cmeta = file:sub(pointer, pointer + cmetalen - 1)
pointer = pointer + cmetalen
end
ret[name] = {owner = owner, group = group, perms = perms, cmeta = cmeta}
end
return ret
end
-- Build metafile
local function makeMetafile(meta)
local file = ""
for name, m in pairs(meta) do
local entry = ""
entry = entry .. string.char(#name) .. name
entry = entry .. string.char(m.owner, m.group, m.perms)
entry = entry .. string.char(#m.cmeta) .. m.cmeta
file = file .. entry
end
return file
end
-- Get file metadata object
local function getFileMeta(path)
local disk, fullPath = resolvePath(path)
fullPath = normalizePath(fullPath)
local parts = {}
for p in fullPath:gmatch("[^/]+") do table.insert(parts, p) end
-- default fallback
local default = {owner = 0, group = 0, perms = 63, cmeta = ""}
-- walk from deepest parent upward
for i = #parts, 1, -1 do
local parent = "/" .. table.concat(parts, "/", 1, i - 1)
if parent ~= "/" then parent = parent .. "/" end
local target = parts[i]
if target == ".meta" then error("Cannot open metafile") end
local metaPath = parent .. ".meta"
if disk:fileExists(metaPath) then
local f = disk:open(metaPath, "r")
local text = f.read(65535)
f.close()
local parsed = parseMetafile(text)
if parsed[target] then return parsed[target] end
end
end
return default
end
local function ensureParentMeta(path)
local disk, fullPath = resolvePath(path)
fullPath = normalizePath(fullPath)
-- split parent + name
local parent, name = fullPath:match("^(.*)/([^/]+)$")
if not parent then
parent = "/"
name = fullPath:gsub("^/", "")
end
if name == ".meta" then error("Cannot open metafile") end
if parent ~= "/" and parent:sub(-1) ~= "/" then parent = parent .. "/" end
local metaPath = parent .. ".meta"
if not disk:fileExists(metaPath) then
local f = disk:open(metaPath, "w")
f.write("")
f.close()
end
return metaPath, name
end
-- Permission checking
local function checkperms(meta, mode)
local modes = {
r = {owner = 5, group = 3, everyone = 1},
w = {owner = 4, group = 2, everyone = 0},
a = {owner = 4, group = 2, everyone = 0}
}
local bits = meta.perms
local function bit_is_set(num, bit)
return math.floor(num / (2 ^ bit)) % 2 == 1
end
if kernel.uid == 0 then return true end
if kernel.uid == meta.owner and bit_is_set(bits, modes[mode].owner) then
return true
end
if meta.group and kernel.groups then
for _, gid in ipairs(kernel.groups) do
if gid == meta.group and bit_is_set(bits, modes[mode].group) then
return true
end
end
end
if bit_is_set(bits, modes[mode].everyone) then return true end
error("EACCES")
end
-- mounts
local function normalizeMountPoint(path)
path = normalizePath(path)
if path ~= "/" and path:sub(-1) == "/" then path = path:sub(1, -2) end
return path
end
local required = {
"open",
"type",
"list",
"attributes",
"fileExists",
"makeDirectory",
"remove"
}
local function check(disk)
for _, name in ipairs(required) do
if type(disk[name]) ~= "function" then
error("Invalid disk: missing method '" .. name .. "'")
end
end
end
function vfs.mount(target, diskOrId)
if kernel.uid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end
target = normalizeMountPoint(target)
if not vfs.exists(target) then vfs.mkdir(target) end
if vfs.type(target) ~= "directory" then error("EINVAL") end
local disk
local id
if type(diskOrId) == "string" then
disk = kernel.disks[diskOrId]
if not disk then error("ENODEV") end
check(disk)
id = diskOrId
elseif type(diskOrId) == "table" then
check(disk)
disk = diskOrId
id = disk.address
vfs.disks[id] = disk
else
error("EINVAL")
end
-- Prevent shadowing an existing mount
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
vfs.mounts[id] = target
return true
end
function vfs.umount(target)
if kernel.uid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end
target = normalizeMountPoint(target)
for id, mp in pairs(vfs.mounts) do
if mp == target then
if id == "$" then error("EBUSY") end -- root fs
vfs.mounts[id] = nil
return true
end
end
error("EINVAL")
end
-- Open file
function vfs.open(path, mode)
checkSystemLimit()
local task = kernel.currentTask
local fd = allocFD(task)
local disk, diskPath = resolvePath(path)
if not disk then error("NODISK") end
local meta = getFileMeta(path)
checkperms(meta, mode)
local handle
if disk:type(diskPath)~="directory" then
handle = disk:open(diskPath, mode)
if type(handle)~="table" then error("ENFILE") end
end
task.fd[fd] = newFileObj(handle, mode, path, meta, disk:type(diskPath))
if not disk.isvirt then
total = total + 1
end
return fd
end
-- Read
function vfs.read(fd, count)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.read then error("EBADF") end
return file.handle.read(count or 1) or ""
end
-- Write
function vfs.write(fd, content)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.write then error("EBADF") end
return file.handle.write(content)
end
-- Pread / Pwrite
function vfs.pread(fd, count, offset)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.read then error("EBADF") end
if not file.handle.seek then error("EBADF") end
file.handle.seek("set", offset)
return file.handle.read(count or 1) or ""
end
function vfs.pwrite(fd, content, offset)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.write then error("EBADF") end
if not file.handle.seek then error("EBADF") end
file.handle.seek("set", offset)
return file.handle.write(content)
end
-- Seek
function vfs.lseek(fd, offset, whence)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.seek then error("EBADF") end
return file.handle.seek(whence or "set", offset)
end
-- Fsync
function vfs.fsync(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.flush then error("EBADF") end
if file.mode ~= "w" and file.mode ~= "a" then error("EBADF") end
file.handle.flush()
end
-- Close
function vfs.close(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
task.fd[fd] = nil
total = total - 1
file.refcount = file.refcount - 1
if file.refcount <= 0 then
if file.handle.close then
file.handle.close()
end
end
end
-- Sendfile
function vfs.sendfile(outfd, infd, count)
local task = kernel.currentTask
local inFile = task.fd[infd]
local outFile = task.fd[outfd]
if not inFile or not outFile then error("EBADF") end
if not inFile.handle.read then error("EBADF") end
if not outFile.handle.write then error("EBADF") end
local data = inFile.handle.read(count or 1024)
if not data or data == "" then return end
return outFile.handle.write(data)
end
-- Stat / Fstat
function vfs.stat(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
local attrs = disk:attributes(diskPath)
return {
size = attrs.size,
modified = attrs.modified,
created = attrs.created,
owner = meta.owner,
group = meta.group,
xattr = meta.cmeta
}
end
function vfs.fstat(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
local disk, path = resolvePath(file.path)
local attrs = disk:attributes(path)
return {
size = attrs.size,
modified = attrs.modified,
created = attrs.created,
owner = file.meta.owner,
group = file.meta.group,
xattr = file.meta.cmeta
}
end
-- Directory operations
function vfs.listdir(path)
local disk, diskPath = resolvePath(path)
if disk:type(diskPath) ~= "directory" then error("ENOENT") end
local meta = getFileMeta(path)
checkperms(meta, "r")
local list = disk:list(diskPath)
if table.indexOf(list, ".meta") ~= -1 then
table.remove(list, table.indexOf(list, ".meta"))
end
return list
end
function vfs.mkdir(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "w")
disk:makeDirectory(diskPath)
end
function vfs.remove(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "w")
disk:remove(diskPath)
end
-- Permission functions
function vfs.chmod(path, perms)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
meta.perms = perms
local mpath, target = ensureParentMeta(path)
local mf = disk:open(mpath, "r")
local text = mf.read(65535)
mf.close()
local parsed = parseMetafile(text)
parsed[target] = meta
local f = disk:open(mpath, "w")
f.write(makeMetafile(parsed))
f.close()
end
function vfs.fchmod(fd, perms)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
vfs.chmod(file.path, perms)
end
function vfs.chown(path, uid, gid)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
meta.owner = uid
meta.group = gid
local mpath, target = ensureParentMeta(path)
local mf = disk:open(mpath, "r")
local text = mf.read(65535)
mf.close()
local parsed = parseMetafile(text)
parsed[target] = meta
local f = disk:open(mpath, "w")
f.write(makeMetafile(parsed))
f.close()
end
function vfs.fchown(fd, uid, gid)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
vfs.chown(file.path, uid, gid)
end
function vfs.exists(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "r")
return disk:fileExists(diskPath)
end
function vfs.type(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "r")
return disk:type(diskPath)
end
function vfs.getcwd() return kernel.currentTask.cwd end
function vfs.chdir(path) kernel.currentTask.cwd = path end
function vfs.dup(oldfd)
local task = kernel.currentTask
local file = task.fd[oldfd]
if not file then error("EBADF") end
checkSystemLimit()
local newfd = allocFD(task)
file.refcount = file.refcount + 1
task.fd[newfd] = file
total = total + 1
return newfd
end
function vfs.dup2(oldfd, newfd)
local task = kernel.currentTask
local file = task.fd[oldfd]
if not file then error("EBADF") end
if newfd < 0 or newfd >= kernel.config.maxFilesPerTask then
error("EBADF")
end
if oldfd == newfd then
return newfd
end
if task.fd[newfd] then
vfs.close(newfd)
end
checkSystemLimit()
file.refcount = file.refcount + 1
task.fd[newfd] = file
total = total + 1
return newfd
end
function vfs.devctl(fd, method, ...)
if not kernel.currentTask.fd[fd] then error("EBADF") end
if not kernel.currentTask.fd[fd].handle[method] then error("EINVAL") end
return kernel.currentTask.fd[fd].handle[method](...)
end
-- Export syscalls
local sys = kernel.syscalls
sys["open"] = vfs.open
sys["close"] = vfs.close
sys["read"] = vfs.read
sys["write"] = vfs.write
sys["pread"] = vfs.pread
sys["pwrite"] = vfs.pwrite
sys["lseek"] = vfs.lseek
sys["fsync"] = vfs.fsync
sys["sendfile"] = vfs.sendfile
sys["stat"] = vfs.stat
sys["fstat"] = vfs.fstat
sys["mkdir"] = vfs.mkdir
sys["remove"] = vfs.remove
sys["listdir"] = vfs.listdir
sys["chmod"] = vfs.chmod
sys["fchmod"] = vfs.fchmod
sys["chown"] = vfs.chown
sys["fchown"] = vfs.fchown
sys["exists"] = vfs.exists
sys["type"] = vfs.type
sys["mount"] = vfs.mount
sys["umount"] = vfs.umount
sys["getcwd"] = vfs.getcwd
sys["chdir"] = vfs.chdir
sys["dup"] = vfs.dup
sys["dup2"] = vfs.dup2
sys["devctl"] = vfs.devctl
kernel.log("VFS module loaded")

View File

@@ -1,40 +0,0 @@
-- :Minify:--
local kernel = ...
local cache = {}
kernel.searchpaths = {
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
}
function require(module, ...)
if cache[module] then return cache[module] end
local modpath = module:gsub("%.", "/")
local failed = {}
for _, path in ipairs(kernel.searchpaths) do
local full_path = string.replace(path, "?", modpath)
if full_path:sub(1, 1) ~= "/" then
full_path = kernel.currentTask.cwd .. full_path
end
if kernel.vfs.exists(full_path) then
if kernel.vfs.type(full_path) == "directory" then
full_path = full_path .. "/init"
end
if kernel.vfs.exists(full_path) then
local handle = kernel.vfs.open(full_path, "r")
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
return
assert(load(file_content, full_path, "t", kernel._U))(...)
else
table.insert(failed, full_path)
end
else
table.insert(failed, full_path)
end
end
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
end

View File

@@ -1,147 +0,0 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "devfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("EACCES") end
proxy.remove = function() error("EACCES") end
proxy.setLabel = function() error("EACCES") end
proxy.getLabel = function() return "devfs" end
proxy.attributes = function(path) return {
size = 0,
modified = 0,
created = 0,
} end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
error("ENFILE")
end
function proxy:type(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return "directory"
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
error("ENOENT")
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return table.keys(data)
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
error("ENOENT")
end
function proxy:fileExists(path)
local ok = pcall(function()
return self:type(path)
end)
return ok
end
function data.random(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(math.random(0, 255))
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.null(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount) end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.zero(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(0)
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
data["disk"]={}
kernel.devfs={}
kernel.devfs.data=data
kernel.devfs.proxy=proxy
kernel.disks["devfs0000"]=proxy

View File

@@ -1,22 +0,0 @@
---- :Minify:--
--local kernel = ...
--
--local timeout = false
--kernel.processes.keventd = function()
-- while true do
-- local event = {kernel.computer:getMachineEvent()}
-- if event[1] then
-- if event[1] == "keyTyped" then
-- if event[3] == "\x1b^s" then
-- kernel.shutdown()
-- elseif event[3] == "\x1b^r" then
-- kernel.reboot()
-- end
-- end
-- timeout = false
-- else
-- timeout = true
-- end
-- if timeout then sleep(.05) end
-- end
--end

View File

@@ -1,14 +0,0 @@
--:Minify:--
local kernel = ...
local socket = {}
function socket.socket()
end
function socket.bind()
end
kernel.socket=socket
kernel.log("Loaded socket module")

View File

@@ -1,6 +0,0 @@
--:Minify:--
local kernel=...
kernel.vfs.open("/dev/null", "r")
kernel.vfs.open("/dev/tty/TTY1", "w")
kernel.vfs.open("/dev/null", "w")
kernel.status="term"

View File

@@ -1,625 +0,0 @@
--:Minify:--
local kernel = ...
local auth = {}
kernel.auth = auth
-- PASSWD FILE FORMAT: uid:gid:username:homedir:shell
-- SHADOW FILE FORMAT: uid:salt:hash
local function getFile(path)
local file = kernel.vfs.open(path, "r")
if not file then error("Failed to open file: " .. path) end
local content = kernel.vfs.read(file, 1024000)
kernel.vfs.close(file)
return content
end
local function writeFile(path, content)
local file = kernel.vfs.open(path, "w")
if not file then error("Failed to open file for writing: " .. path) end
kernel.vfs.write(file, content)
kernel.vfs.close(file)
end
local blake2s
do
local MOD32 = 2^32
local function norm(x) return x % MOD32 end
local function tobits(x)
x = norm(x)
local t = {}
for i = 0, 31 do local b = x % 2; t[i] = b; x = (x - b) / 2 end
return t
end
local function frombits(t)
local x, p = 0, 1
for i = 0, 31 do if t[i] == 1 then x = x + p end; p = p * 2 end
return norm(x)
end
local function bor(...)
local args = {...}
if #args == 0 then return 0 end
local bits = tobits(args[1])
for i = 2, #args do
local b = tobits(args[i])
for j = 0, 31 do bits[j] = (bits[j] == 1 or b[j] == 1) and 1 or 0 end
end
return frombits(bits)
end
local function bxor(...)
local args = {...}
if #args == 0 then return 0 end
local bits = tobits(args[1])
for i = 2, #args do
local b = tobits(args[i])
for j = 0, 31 do bits[j] = (bits[j] ~= b[j]) and 1 or 0 end
end
return frombits(bits)
end
local function lshift(x, n) return norm(norm(x) * 2^n) end
local function rshift(x, n) return math.floor(norm(x) / 2^n) end
local function rotr(x, n) return bor(rshift(x, n), lshift(x, 32 - n)) end
local IV = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
}
local SIGMA = {
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
{14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3},
{11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4},
{7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8},
{9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13},
{2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9},
{12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11},
{13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10},
{6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5},
{10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0}
}
local function G(v, a, b, c, d, x, y)
v[a] = (v[a] + v[b] + x) % MOD32
v[d] = rotr(bxor(v[d], v[a]), 16)
v[c] = (v[c] + v[d]) % MOD32
v[b] = rotr(bxor(v[b], v[c]), 12)
v[a] = (v[a] + v[b] + y) % MOD32
v[d] = rotr(bxor(v[d], v[a]), 8)
v[c] = (v[c] + v[d]) % MOD32
v[b] = rotr(bxor(v[b], v[c]), 7)
end
local function compress(h, block, t, last)
local v = {}
for i = 1, 8 do v[i] = h[i] end
for i = 1, 8 do v[i + 8] = IV[i] end
v[13] = bxor(v[13], t)
if last then v[15] = bxor(v[15], 0xFFFFFFFF) end
local m = {}
for i = 0, 15 do
local p = i * 4 + 1
m[i] = (block:byte(p) or 0)
+ ((block:byte(p+1) or 0) * 0x100)
+ ((block:byte(p+2) or 0) * 0x10000)
+ ((block:byte(p+3) or 0) * 0x1000000)
end
for r = 1, 10 do
local s = SIGMA[r]
G(v,1,5,9,13, m[s[1]], m[s[2]])
G(v,2,6,10,14, m[s[3]], m[s[4]])
G(v,3,7,11,15, m[s[5]], m[s[6]])
G(v,4,8,12,16, m[s[7]], m[s[8]])
G(v,1,6,11,16, m[s[9]], m[s[10]])
G(v,2,7,12,13, m[s[11]], m[s[12]])
G(v,3,8,9,14, m[s[13]], m[s[14]])
G(v,4,5,10,15, m[s[15]], m[s[16]])
end
for i = 1, 8 do h[i] = bxor(h[i], v[i], v[i+8]) end
end
function blake2s(msg, key)
key = key or ""
local h = {}
for i = 1, 8 do h[i] = IV[i] end
local outlen = 32
h[1] = bxor(h[1], 0x01010000 + lshift(#key, 8) + outlen)
local t = 0
if #key > 0 then
local block = key .. string.rep("\0", 64 - #key)
t = #key
compress(h, block, t, false)
end
for i = 1, #msg, 64 do
local block = msg:sub(i, i + 63)
if #block < 64 then block = block .. string.rep("\0", 64 - #block) end
t = t + math.min(64, #msg - i + 1)
compress(h, block, t, i + 64 > #msg)
end
local out = ""
for i = 1, 8 do out = out .. string.format("%08x", h[i]) end
return out
end
end
if not blake2s then error("Failed to load blake2s") end
if not kernel.vfs.exists("/etc/pam.d/secret") then
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT")
local key = ""
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
kernel.vfs.write(handle, key)
kernel.vfs.close(handle)
end
local pepper = getFile("/etc/pam.d/secret")
local function genSalt()
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
local s = ""
for i = 1, 16 do
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
end
return s
end
local function hashPassword(password, salt)
local key = (pepper .. salt):sub(1, 32)
return blake2s(password, key)
end
local passwdFile = getFile("/etc/passwd")
local shadowFile = getFile("/etc/shadow")
local passwdLines = string.split(passwdFile, "\n")
local shadowLines = string.split(shadowFile, "\n")
local passwd, shadow = {}, {}
for _, v in ipairs(passwdLines) do
local fields = string.split(v, ":")
if fields[1] and fields[1] ~= "" then
passwd[#passwd + 1] = fields
end
end
for _, v in ipairs(shadowLines) do
local fields = string.split(v, ":")
if fields[1] and fields[1] ~= "" then
shadow[#shadow + 1] = fields
end
end
for _, v in ipairs(passwd) do
local uid = tonumber(v[1])
if uid then kernel.users[uid] = v[3] end
end
kernel.passwd = passwd
local function flushPasswd()
local lines = {}
for _, v in ipairs(passwd) do
lines[#lines + 1] = table.concat(v, ":")
end
writeFile("/etc/passwd", table.concat(lines, "\n"))
end
local function flushShadow()
local lines = {}
for _, v in ipairs(shadow) do
lines[#lines + 1] = table.concat(v, ":")
end
writeFile("/etc/shadow", table.concat(lines, "\n"))
end
local function getPasswdByUID(uid)
for _, v in ipairs(passwd) do
if tonumber(v[1]) == uid then return v end
end
return nil
end
local function getShadowByUID(uid)
for _, v in ipairs(shadow) do
if tonumber(v[1]) == uid then return v end
end
return nil
end
local function getPasswdByUsername(username)
for _, v in ipairs(passwd) do
if v[3] == username then return v end
end
return nil
end
local function nextUID()
local max = 999
for _, v in ipairs(passwd) do
local uid = tonumber(v[1])
if uid and uid >= 1000 and uid > max then max = uid end
end
return max + 1
end
function auth.login(username, password)
if type(username) ~= "string" or type(password) ~= "string" then
return nil, "Authentication failure"
end
local entry = getPasswdByUsername(username)
if not entry then
-- timing attack resistance
hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure"
end
local uid = tonumber(entry[1])
local sEntry = getShadowByUID(uid)
if not sEntry then
hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure"
end
local salt = sEntry[2]
local storedHash = sEntry[3]
local computed = hashPassword(password, salt)
if computed ~= storedHash then
return nil, "Authentication failure"
end
kernel.currentUID = uid
if kernel.currentProcess then
kernel.currentProcess.uid = uid
kernel.currentProcess.euid = uid
kernel.currentProcess.gid = tonumber(entry[2]) or uid
kernel.currentProcess.egid = tonumber(entry[2]) or uid
end
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
return true
end
function auth.setPassword(uid, newPassword)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID or 0
if callerUID ~= 0 and callerUID ~= uid then
return nil, "Permission denied"
end
if type(newPassword) ~= "string" or #newPassword == 0 then
return nil, "Password may not be empty"
end
if #newPassword < 6 then
return nil, "Password is too short (minimum 6 characters)"
end
local salt = genSalt()
local hash = hashPassword(newPassword, salt)
local sEntry = getShadowByUID(uid)
if sEntry then
sEntry[2] = salt
sEntry[3] = hash
else
shadow[#shadow + 1] = { tostring(uid), salt, hash }
end
flushShadow()
kernel.log("AUTH: password changed for uid=" .. tostring(uid))
return true
end
function auth.setUsername(uid, newUsername)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID or 0
if callerUID ~= 0 then
return nil, "Permission denied (root only)"
end
if type(newUsername) ~= "string" or #newUsername == 0 then
return nil, "Invalid username"
end
if not newUsername:match("^[a-z_][a-z0-9_%-]*$") or #newUsername > 32 then
return nil, "Invalid username format"
end
if getPasswdByUsername(newUsername) then
return nil, "Username already taken"
end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
local oldName = entry[3]
entry[3] = newUsername
kernel.users[uid] = newUsername
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " renamed '" .. oldName .. "' → '" .. newUsername .. "'")
return true
end
function auth.newUser(username, password, gid, homedir, shell)
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID or 0
if callerUID ~= 0 then
return nil, "Permission denied (root only)"
end
if type(username) ~= "string" or #username == 0 then
return nil, "Invalid username"
end
if not username:match("^[a-z_][a-z0-9_%-]*$") or #username > 32 then
return nil, "Invalid username format"
end
if getPasswdByUsername(username) then
return nil, "Username already exists"
end
if type(password) ~= "string" or #password < 6 then
return nil, "Password is too short (minimum 6 characters)"
end
local uid = nextUID()
gid = tonumber(gid) or uid
homedir = homedir or ("/home/" .. username)
shell = shell or "/bin/sh"
passwd[#passwd + 1] = {
tostring(uid),
tostring(gid),
username,
homedir,
shell
}
kernel.users[uid] = username
local salt = genSalt()
local hash = hashPassword(password, salt)
shadow[#shadow + 1] = { tostring(uid), salt, hash }
flushPasswd()
flushShadow()
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
kernel.vfs.mkdir(homedir)
end
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
return uid
end
function auth.whoami()
local uid = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID
if not uid then return nil, "Not logged in" end
return kernel.users[uid] or ("uid=" .. tostring(uid))
end
function auth.getUID(username)
local entry = getPasswdByUsername(username)
if entry then return tonumber(entry[1]) end
return nil
end
function auth.getPasswd(uid)
uid = tonumber(uid)
local entry = getPasswdByUID(uid)
if not entry then return nil end
return {
uid = tonumber(entry[1]),
gid = tonumber(entry[2]),
username = entry[3],
homedir = entry[4],
shell = entry[5],
}
end
function auth.deleteUser(uid)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
if uid == 0 then return nil, "Cannot delete root" end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
local username = entry[3]
-- Remove from passwd
for i, v in ipairs(passwd) do
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
end
-- Remove from shadow
for i, v in ipairs(shadow) do
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
end
kernel.users[uid] = nil
flushPasswd()
flushShadow()
kernel.log("AUTH: deleted user '" .. username .. "' uid=" .. tostring(uid))
return true
end
function auth.lockUser(uid)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
if uid == 0 then return nil, "Cannot lock root" end
local sEntry = getShadowByUID(uid)
if not sEntry then return nil, "No shadow entry for uid" end
-- Prefix hash with ! to lock (standard Linux convention)
if sEntry[3]:sub(1,1) ~= "!" then
sEntry[3] = "!" .. sEntry[3]
end
flushShadow()
kernel.log("AUTH: locked uid=" .. tostring(uid))
return true
end
function auth.unlockUser(uid)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
local sEntry = getShadowByUID(uid)
if not sEntry then return nil, "No shadow entry for uid" end
if sEntry[3]:sub(1,1) == "!" then
sEntry[3] = sEntry[3]:sub(2)
end
flushShadow()
kernel.log("AUTH: unlocked uid=" .. tostring(uid))
return true
end
function auth.listUsers()
local result = {}
for _, v in ipairs(passwd) do
local uid = tonumber(v[1])
local sEntry = getShadowByUID(uid)
local locked = sEntry and sEntry[3]:sub(1,1) == "!"
result[#result+1] = {
uid = uid,
gid = tonumber(v[2]),
username = v[3],
homedir = v[4],
shell = v[5],
locked = locked or false,
}
end
return result
end
function auth.setShell(uid, shell)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 and callerUID ~= uid then
return nil, "Permission denied"
end
if type(shell) ~= "string" or #shell == 0 then
return nil, "Invalid shell"
end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
entry[5] = shell
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " shell -> " .. shell)
return true
end
function auth.setHomedir(uid, homedir)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
if type(homedir) ~= "string" or #homedir == 0 then
return nil, "Invalid homedir"
end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
entry[4] = homedir
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " homedir -> " .. homedir)
return true
end
function auth.setGID(uid, gid)
uid = tonumber(uid)
gid = tonumber(gid)
if not uid or not gid then return nil, "Invalid uid or gid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
entry[2] = tostring(gid)
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " gid -> " .. tostring(gid))
return true
end
-- Elevate the calling task to targetUid after verifying targetUsername's password.
-- This is the kernel-side primitive for su/sudo — it bypasses the kernel.uid==0
-- check in sys.setuid because the auth module itself is trusted kernel code.
function auth.elevate(targetUsername, password)
if type(targetUsername) ~= "string" or type(password) ~= "string" then
return nil, "Authentication failure"
end
local entry = getPasswdByUsername(targetUsername)
if not entry then
hashPassword(password, "aaaaaaaaaaaaaaaa") -- timing resistance
return nil, "Authentication failure"
end
local uid = tonumber(entry[1])
local sEntry = getShadowByUID(uid)
if not sEntry then
hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure"
end
local computed = hashPassword(password, sEntry[2])
if computed ~= sEntry[3] then
return nil, "Authentication failure"
end
-- Directly set the calling task's uid — trusted kernel path
local task = kernel.currentTask
local prevUid = task.uid
task.uid = uid
task.euid = uid
task.gid = tonumber(entry[2]) or uid
task.egid = tonumber(entry[2]) or uid
kernel.uid = uid
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")")
return true, uid
end
if kernel.syscalls then
kernel.syscalls["auth_login"] = auth.login
kernel.syscalls["auth_setpassword"] = auth.setPassword
kernel.syscalls["auth_setusername"] = auth.setUsername
kernel.syscalls["auth_newuser"] = auth.newUser
kernel.syscalls["auth_whoami"] = auth.whoami
kernel.syscalls["auth_getuid"] = auth.getUID
kernel.syscalls["auth_getpasswd"] = auth.getPasswd
kernel.syscalls["auth_elevate"] = auth.elevate
kernel.syscalls["auth_deleteuser"] = auth.deleteUser
kernel.syscalls["auth_lockuser"] = auth.lockUser
kernel.syscalls["auth_unlockuser"] = auth.unlockUser
kernel.syscalls["auth_listusers"] = auth.listUsers
kernel.syscalls["auth_setshell"] = auth.setShell
kernel.syscalls["auth_sethomedir"] = auth.setHomedir
kernel.syscalls["auth_setgid"] = auth.setGID
end

View File

@@ -1,447 +0,0 @@
-- :Minify:--
local kernel = ...
local tasks = {}
local sys = {}
local nextpid = 2
kernel.exitMain = false
function sys.spawn(func, name, envars, args, tgid)
local id = nextpid
nextpid = nextpid + 1
tasks[tostring(id)] = {
coro = coroutine.create(function()
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
if not ok then
if kernel.config.logTaskExit then
kernel.log(
"Task " .. tostring(id) .. " exited with err: " ..
tostring(err), "ERROR", 2)
end
if type(err) == "number" then
tasks[tostring(id)].exit = err
end
else
if kernel.config.logTaskExit then
if err then
kernel.log("Task " .. tostring(id) ..
" exited with code: " .. tostring(err),
"INFO")
else
kernel.log("Task " .. tostring(id) ..
" exited without code", "INFO")
end
end
if type(err) == "number" then
tasks[tostring(id)].exit = err
end
end
for v, _ in ipairs(tasks[tostring(id)].fd) do pcall(kernel.vfs.close,v) end
tasks[tostring(id)].status = "Z"
end),
name = name or ("task" .. tostring(id)),
envars = envars or kernel.currentTask.envars,
args = args or {},
status = "R",
pid = id,
tgid = tgid or kernel.currentTask.tgid,
uid = kernel.uid,
fd = {},
sleep = 0,
ivs = 0,
vs = 0,
children = {},
parent = kernel.currentTask,
siblings = kernel.currentTask.children,
syscallReturn = {},
cwd = kernel.currentTask.cwd,
timeSlice = 0,
lastTime = 0,
totalTime = 0,
numRuns = 0
}
table.insert(kernel.currentTask.children, tasks[tostring(id)])
return id
end
function sys.sleep(s)
kernel.currentTask.status = "S"
kernel.currentTask.sleep = kernel.computer:time() + s * 1000
coroutine.yield()
end
function sys.getTask(pid)
if tasks[tostring(pid)] then
local task = tasks[tostring(pid)]
local children = {}
local siblings = {}
for i, v in ipairs(task.children) do children[i] = v.pid end
for i, v in ipairs(task.siblings) do siblings[i] = v.pid end
return {
name = task.name,
status = task.status,
pid = task.pid,
tgid = task.tgid,
username = kernel.users[task.uid],
uid = task.uid,
exit = task.exit,
sleep = task.sleep,
ivs = task.ivs,
vs = task.vs,
children = children,
siblings = siblings,
parent = task.parent.pid,
cwd = task.cwd,
term = task.term
}
end
end
function sys.collect(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status ~= "Z" then
return false, "Task must exit to collect status"
else
tasks[tostring(pid)].reapTime = 0
return true, tasks[tostring(pid)].exit
end
end
function sys.kill(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status == "Z" then
return false, "Task is already dead"
else
tasks[tostring(pid)].status = "Z"
return true
end
end
function sys.stop(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status ~= "R" then
return false, "Cannot stop non running task"
else
tasks[tostring(pid)].status = "T"
return true
end
end
function sys.continue(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status ~= "T" then
return false, "Task is not stopped"
else
tasks[tostring(pid)].status = "R"
return true
end
end
function sys.getpid() return kernel.currentTask.pid end
function sys.getppid() return kernel.currentTask.parent.pid end
function sys.getTasks()
local ret = {}
for i, v in pairs(tasks) do ret[#ret + 1] = v.pid end
return ret
end
function sys.getEnviron(key) return kernel.currentTask.envars[key] end
function sys.setEnviron(key, value) kernel.currentTask.envars[key] = value end
function sys.exit(code)
if kernel.config.logTaskExit then
if code then
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited with code: " .. tostring(code), "INFO")
else
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited without code", "INFO")
end
end
tasks[tostring(kernel.currentTask.pid)].status = "Z"
if type(code) == "number" then
tasks[tostring(kernel.currentTask.pid)].exit = code
end
end
function sys.setuid(uid)
if kernel.uid ~= 0 then error("EACCES") end
kernel.currentTask.uid = uid
end
function sys.getuid() return kernel.currentTask.uid end
local sysc = kernel.syscalls
sysc["spawn"] = sys.spawn
sysc["sleep"] = sys.sleep
sysc["getTask"] = sys.getTask
sysc["collect"] = sys.collect
sysc["kill"] = sys.kill
sysc["stop"] = sys.stop
sysc["continue"] = sys.continue
sysc["getpid"] = sys.getpid
sysc["getppid"] = sys.getppid
sysc["getTasks"] = sys.getTasks
sysc["setEnviron"] = sys.setEnviron
sysc["getEnviron"] = sys.getEnviron
sysc["exit"] = sys.exit
sysc["setuid"] = sys.setuid
sysc["getuid"] = sys.getuid
kernel._G.sleep = function(...) coroutine.yield("syscall", "sleep", ...) end
local function reapDeadTasks()
for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then
kernel.currentTask = task
kernel.uid = task.uid
kernel.process = task.name
task.coro = nil
task.ivs = nil
task.vs = nil
task.args = nil
task.envars = nil
task.cwd = nil
task.numRuns = nil
task.totalTime = nil
task.lastTime = nil
task.timeSlice = nil
task.syscallReturn = nil
task.sleep = nil
task.fd = nil
task.reapTime = kernel.computer:time() + 30000
elseif task.reapTime and kernel.computer:time() > task.reapTime and
task.status == "Z" then
for _, child in ipairs(task.children) do
child.parent = tasks["1"]
child.siblings = tasks["1"].children
table.insert(tasks["1"].children, child)
end
for i, sibling in ipairs(task.siblings) do
if sibling.pid == task.pid then
table.remove(task.siblings, i)
break
end
end
tasks[pid] = nil
end
end
end
local alpha = 0.85
local C_target = 0.01
local Tmin = 0.0005
local Tmax = 0.5
local lambda_budget = 0.08
local lambda_clamp = 0.03
local lambda_var = 0.02
local k_min = 0.5
local k_max = 0.5
local B = 0.01
function kernel.main()
while not kernel.exitMain do
local N = 0
local Tmin_hit = 0
local Tmax_hit = 0
local totalTaskTime = 0
local taskTimes = {}
for pid, task in pairs(tasks) do
if task.status == "S" then
if kernel.computer:time() >= task.sleep then
task.status = "R"
task.sleep = 0
end
end
if task.status == "R" then
kernel.currentTask = task
kernel.uid = task.uid
kernel.process = task.name
N = N + 1
-- assign adaptive time slice
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
if task.sigq and #task.sigq~=0 and task.sigh then
local coro = coroutine.create(task.sigh)
if kernel.config.preempt then
coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
else
coroutine.resume(coro, table.remove(task.sigq, 1))
end
end
-- check for exit/stop
if task.status=="R" then
-- measure execution time
local startTime = kernel.computer:time()
local ret
if kernel.config.preempt then
ret = {
coroutine.resumeWithTimeout(
task.coro,
task.timeSlice,
table.unpack(task.syscallReturn)
)
}
else
ret = {
coroutine.resume(
task.coro,
table.unpack(task.syscallReturn)
)
}
end
local elapsed = kernel.computer:time() - startTime
task.lastTime = elapsed
task.totalTime = (task.totalTime or 0) + elapsed
task.numRuns = (task.numRuns or 0) + 1
taskTimes[#taskTimes + 1] = elapsed
totalTaskTime = totalTaskTime + elapsed
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
-- handle task results
if ret[1] == "error" or ret[1] == false then
kernel.log("processHandlerException: " .. ret[2], "ERROR", 2)
task.status = "Z"
task.exit = "processHandlerException: " .. ret[2]
elseif ret[1] == "timeout" then
task.ivs = task.ivs + 1
task.syscallReturn = {}
elseif ret[1] == "success" or ret[1] == true then
task.vs = task.vs + 1
if ret[2] == "syscall" then
if kernel.syscalls[ret[3]] then
if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " invoking syscall: " .. ret[3], "DBUG", 5)
for i = 4, #ret do
kernel.log(" inval[" .. tostring(i - 3) .. "] = " .. tostring(ret[i]), "DBUG", 5)
end
end
local sysret = {
xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))
}
if kernel.config.debugSyscalls then
if not sysret[1] then
kernel.log(
"Task " .. task.pid .. " syscall " .. ret[3] .. " failed: " .. tostring(sysret[2]), "ERROR", 2
)
else
kernel.log(
"Task " .. task.pid .. " syscall " .. ret[3] .. " completed returning " .. tostring(#sysret - 1) .. " values", "DBUG", 5
)
for i = 2, #sysret do
if type(sysret[i]) == "table" then
kernel.log(
" retval[" .. tostring(i - 1) .. "] = " .. table.serialize(sysret[i]),"DBUG", 5
)
else
kernel.log(
" retval[" .. tostring(i - 1) .. "] = " .. tostring(sysret[i]), "DBUG", 5
)
end
end
end
end
if not sysret[1] then
task.syscallReturn = {false, sysret[2]}
else
task.syscallReturn = {
true, table.unpack(sysret, 2)
}
end
else
task.syscallReturn = {
false, "Unknown syscall: " .. tostring(ret[3])
}
end
end
end
end
end
end
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
local T_prev_var = 0
for _, t in ipairs(taskTimes) do
T_prev_var = T_prev_var + (t - T_prev_avg) ^ 2
end
if N > 0 then T_prev_var = T_prev_var / N end
if N > 0 then
local f_clamp = k_min * (Tmin_hit / N) - k_max * (Tmax_hit / N)
local B_budget = (C_target * (N ^ (alpha - 1))) /
math.max(T_prev_avg, 1e-8)
B = B + lambda_budget * (B_budget - B) + lambda_clamp * f_clamp -
lambda_var * T_prev_var
end
-- clean up dead tasks
reapDeadTasks()
end
end
kernel.tasks = tasks
kernel.hpv = sys

View File

@@ -1,24 +0,0 @@
-- :Minify:--
local kernel = ...
function print(...)
local args = {...}
local output = ""
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
output = output:sub(1, -2)
syscall.write(1, output.."\n")
end
function printf(fmt, ...)
coroutine.yield()
local output = string.format(fmt, ...)
syscall.write(1, output.."\n")
end
function printInline(...)
coroutine.yield()
local args = {...}
local output = ""
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
output = output:sub(1, -2)
syscall.write(1, output)
end

View File

@@ -1,16 +0,0 @@
--:Minify:--
local kernel = ...
-- It runs at uid 0 so it can call setuid() to drop privileges to the logged in user
kernel.processes.login = function()
local handle = kernel.vfs.open("/bin/login", "r")
local text = kernel.vfs.read(handle, 1024 * 1024)
kernel.vfs.close(handle)
local fn, err = load(text, "@/bin/login", "t", kernel._U)
if not fn then
kernel.log("Failed to load /bin/login: " .. tostring(err), "ERROR", 2)
return
end
fn()
end

View File

@@ -1,165 +0,0 @@
--:Minify:--
local kernel = ...
local bit32 = require("bit32")
local bor = bit32.bor
local lshift = bit32.lshift
-- bit 0 = everyone-write, bit 1 = everyone-read
-- bit 2 = group-write, bit 3 = group-read
-- bit 4 = owner-write, bit 5 = owner-read
-- bit 6 = suid
local P_OWNER_R = lshift(1, 5)
local P_OWNER_W = lshift(1, 4)
local P_GROUP_R = lshift(1, 3)
local P_GROUP_W = lshift(1, 2)
local P_WORLD_R = lshift(1, 1)
local P_WORLD_W = lshift(1, 0)
local P_SUID = lshift(1, 6)
local RW_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 644 / rw-r--r--
local RWX_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 755 / rwxr--r--
local RW_R__ = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R) -- 640 / rw-r-----
local RW____ = bor(P_OWNER_R, P_OWNER_W) -- 600 / rw-------
local SUID_755 = bor(P_SUID, P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 4755
local function metaEntry(name, owner, group, perms)
return string.char(#name) .. name
.. string.char(owner, group, perms)
.. string.char(0)
end
local rootDisk = kernel.disks["$"]
local function writeMeta(dir, entries)
local diskDir = dir == "/" and "/" or dir
local path = (diskDir:sub(-1) == "/" and diskDir or diskDir .. "/") .. ".meta"
if path:sub(1,1) == "/" then path = path:sub(2) end
if path == "" then path = ".meta" end
local data = ""
for _, e in ipairs(entries) do
data = data .. metaEntry(e[1], e[2], e[3], e[4])
end
local ok, err = pcall(function()
local f = rootDisk:open(path, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write /" .. path .. ": " .. tostring(err), "WARN", 8)
end
end
if rootDisk:fileExists(".meta") then
kernel.log("Permissions already seeded, skipping.", "INFO")
else
kernel.log("Seeding filesystem permissions...", "INFO")
writeMeta("/", {
{"bin", 0, 0, RWX_R_R},
{"boot", 0, 0, RWX_R_R},
{"dev", 0, 0, RWX_R_R},
{"etc", 0, 0, RWX_R_R},
{"home", 0, 0, RWX_R_R},
{"lib", 0, 0, RWX_R_R},
{"root", 0, 0, RW____ },
{"sbin", 0, 0, RWX_R_R},
{"tmp", 0, 0, bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_GROUP_W, P_WORLD_R, P_WORLD_W)},
{"usr", 0, 0, RWX_R_R},
{"var", 0, 0, RWX_R_R},
})
writeMeta("/bin", {
{"cat", 0, 0, RWX_R_R},
{"clear", 0, 0, RWX_R_R},
{"echo", 0, 0, RWX_R_R},
{"hfetch", 0, 0, RWX_R_R},
{"hysh", 0, 0, RWX_R_R},
{"hyshex", 0, 0, RWX_R_R},
{"install", 0, 0, RWX_R_R},
{"login", 0, 0, SUID_755},
{"ls", 0, 0, RWX_R_R},
{"lua", 0, 0, RWX_R_R},
{"luaold", 0, 0, RWX_R_R},
{"mkdir", 0, 0, RWX_R_R},
{"ps", 0, 0, RWX_R_R},
{"pwd", 0, 0, RWX_R_R},
{"spm", 0, 0, RWX_R_R},
{"su", 0, 0, SUID_755},
{"sudo", 0, 0, SUID_755},
{"sysdump", 0, 0, RWX_R_R},
{"whoami", 0, 0, RWX_R_R},
{"yes", 0, 0, RWX_R_R},
{"startup", 0, 0, RWX_R_R},
})
writeMeta("/bin/startup", {
{"test.lua", 0, 0, RWX_R_R},
})
writeMeta("/etc", {
{"passwd", 0, 0, RW_R_R},
{"shadow", 0, 0, RW____ },
{"pam.d", 0, 0, RWX_R_R},
})
writeMeta("/etc/pam.d", {
{"secret", 0, 0, RW____},
})
writeMeta("/sbin", {
{"init.lua", 0, 0, RWX_R_R},
})
writeMeta("/boot", {
{"kernel.lua", 0, 0, RW_R_R},
{"boot.cfg", 0, 0, RW_R_R},
{"safeboot.cfg", 0, 0, RW_R_R},
{"fstab", 0, 0, RW_R_R},
{"initfs", 0, 0, RW_R_R},
{"cct", 0, 0, RWX_R_R},
{"oc", 0, 0, RWX_R_R},
})
writeMeta("/lib", {
{"sys", 0, 0, RWX_R_R},
{"modules", 0, 0, RWX_R_R},
{"crypto", 0, 0, RWX_R_R},
{"store", 0, 0, RWX_R_R},
{"snip", 0, 0, RW_R_R},
{"io", 0, 0, RW_R_R},
{"bit32", 0, 0, RW_R_R},
})
kernel.log("Filesystem permissions seeded.", "INFO")
end
-- TODO: move this to vfs.kmod
local _orig_open = kernel.vfs.open
kernel.vfs.open = function(path, mode)
local fd = _orig_open(path, mode)
if mode == "r" then
local task = kernel.currentTask
local fobj = task.fd[fd]
if fobj and fobj.meta then
local suid_set = bit32.extract(fobj.meta.perms, 6) == 1
if suid_set then
fobj.suid_owner = fobj.meta.owner
end
end
end
return fd
end
kernel.syscalls["fget_suid"] = function(fd)
local task = kernel.currentTask
local fobj = task and task.fd[fd]
if fobj and fobj.suid_owner then
return fobj.suid_owner
end
return nil
end
kernel.log("Permission module loaded.", "INFO")

View File

@@ -1,6 +0,0 @@
local sys = {}
local fs = require("sys.fs")
return sys

View File

@@ -1,5 +0,0 @@
local sys = {}
sys.fs = require("sys.fs")
sys.hpv = require("sys.hpv")
sys.ipc = require("sys.ipc")
return sys

View File

@@ -1,3 +0,0 @@
local ipc = {}
return ipc

View File

@@ -1,71 +0,0 @@
local term = {}
function term.clear()
coroutine.yield("VFS_write", 1, "\27C\25")
end
function term.setCursorPos(x, y)
coroutine.yield("VFS_write", 1, "\27cs"..tostring(y)..";"..tostring(x).."\25")
end
function term.size()
coroutine.yield("VFS_write", 1, "\27ts\25")
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
if not ok then error("Failed to get terminal size") end
local x, y = string.match(data, "%R(%d+);(%d+)\25")
return tonumber(x), tonumber(y)
end
function term.getCursorPos()
coroutine.yield("VFS_write", 1, "\27gc\25")
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
if not ok then error("Failed to get cursor position") end
local y, x = string.match(data, "%R(%d+);(%d+)\25")
return tonumber(x), tonumber(y)
end
function term.write(data)
coroutine.yield("VFS_write", 1, data)
end
function term.setTextColor(color)
local ok, err = coroutine.yield("VFS_type", 1)
if not ok then error(err) end
if ok ~= "tty" then return end
coroutine.yield("VFS_write", 1, "\27f"..tostring(color).."\25")
end
function term.setBackgroundColor(color)
local ok, err = coroutine.yield("VFS_type", 1)
if not ok then error(err) end
if ok ~= "tty" then return end
coroutine.yield("VFS_write", 1, "\27b"..tostring(color).."\25")
end
function term.isColor()
local ok, err = coroutine.yield("VFS_type", 1)
if not ok then error(err) end
return ok == "tty"
end
function term.scroll(n)
coroutine.yield("VFS_write", 1, "\27S"..tostring(n).."\25")
end
function term.setDefault(color, layer)
if layer then
coroutine.yield("VFS_write", 1, "\27F"..tostring(color).."\25")
else
coroutine.yield("VFS_write", 1, "\27B"..tostring(color).."\25")
end
end
function term.showCursor(show)
if show then
coroutine.yield("VFS_write", 1, "\27sc\25")
else
coroutine.yield("VFS_write", 1, "\27hc\25")
end
end
return term

Binary file not shown.

View File

@@ -254,11 +254,10 @@ local ok, err = xpcall(function()
if not ok then displaySuperBadError(err) end if not ok then displaySuperBadError(err) end
end) end)
-- time is in milliseconds
function coroutine.resumeWithTimeout(co, timeout, ...) function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time() local startTime = computer.time()
debug.sethook(co, function() debug.sethook(co, function()
if computer.time() - startTime > timeout then if computer.time() > startTime + timeout then
return coroutine.yield("timeout") return coroutine.yield("timeout")
end end
end, "", 1000) end, "", 1000)
@@ -294,6 +293,14 @@ local ok, err = xpcall(function()
queueEvent("componentAdded", "disk") queueEvent("componentAdded", "disk")
elseif event[1] == "disk_eject" then elseif event[1] == "disk_eject" then
queueEvent("componentRemoved", "disk") queueEvent("componentRemoved", "disk")
elseif event[1] == "modem_message" then
queueEvent("modem_message", table.unpack(event, 2))
elseif event[1] == "rednet_message" then
queueEvent("rednet_message", table.unpack(event, 2))
elseif event[1] == "http_success" then
queueEvent("http_success", table.unpack(event, 2))
elseif event[1] == "http_failure" then
queueEvent("http_failure", table.unpack(event, 2))
elseif event[1] == "NoSleep" then elseif event[1] == "NoSleep" then
exit = true exit = true
end end

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local apis = kernel.apis local apis = kernel.apis
local native = apis.peripheral local native = apis.peripheral
@@ -37,7 +37,7 @@ function peripheral.isPresent(name)
end end
function peripheral.getType(peripheral) function peripheral.getType(peripheral)
if type(peripheral) == "string" then -- Peripheral name passed if type(peripheral) == "string" then
if native.isPresent(peripheral) then if native.isPresent(peripheral) then
return native.getType(peripheral) return native.getType(peripheral)
end end
@@ -58,7 +58,7 @@ function peripheral.getType(peripheral)
end end
function peripheral.hasType(peripheral, peripheral_type) function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then -- Peripheral name passed if type(peripheral) == "string" then
if native.isPresent(peripheral) then if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type) return native.hasType(peripheral, peripheral_type)
end end
@@ -317,7 +317,18 @@ kernel.processes.cctmond = function()
local eventType = event[1] local eventType = event[1]
local charOrKey = event[3] local charOrKey = event[3]
-- Update modifier keys local ctrlKeyMap = {
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
[apis.keys.y]=25, [apis.keys.z]=26,
}
if eventType == "keyPressed" then if eventType == "keyPressed" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = true ctrl = true
@@ -325,11 +336,31 @@ kernel.processes.cctmond = function()
alt = true alt = true
end end
-- Handle Ctrl+C if ctrl then
if ctrl and charOrKey == apis.keys.c then local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1) -- SIGINT syscall.sigsend(task, 1)
end end
else
fifo.push(string.char(ctrlByte))
end
end
else
local specialKeyMap = {
[apis.keys.up] = "",
[apis.keys.down] = "",
[apis.keys.right] = "",
[apis.keys.left] = "",
[apis.keys.home] = "",
[apis.keys["end"]] = "",
[apis.keys.pageUp] = "[5~",
[apis.keys.pageDown] = "[6~",
[apis.keys.delete] = "[3~",
}
local special = specialKeyMap[charOrKey]
if special then fifo.push(special) end
end end
elseif eventType == "keyReleased" then elseif eventType == "keyReleased" then
@@ -354,10 +385,10 @@ kernel.processes.cctmond = function()
end end
end end
newtty(apis.term, "TTY1", fifo.pop) newtty(apis.term, "1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5) v.setTextScale(.5)
v.write("Initializing...") v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end) newtty(v,tostring(i+1),function () end)
end end

View File

@@ -71,7 +71,6 @@ local ok, err = xpcall(function()
collectgarbage = true, collectgarbage = true,
error = true, error = true,
gcinfo = true, gcinfo = true,
getfenv = true,
getmetatable = true, getmetatable = true,
ipairs = true, ipairs = true,
__inext = true, __inext = true,
@@ -85,7 +84,6 @@ local ok, err = xpcall(function()
rawlen = true, rawlen = true,
rawset = true, rawset = true,
select = true, select = true,
setfenv = true,
setmetatable = true, setmetatable = true,
string = true, string = true,
table = true, table = true,
@@ -254,7 +252,6 @@ local ok, err = xpcall(function()
if not ok then displaySuperBadError(err) end if not ok then displaySuperBadError(err) end
end) end)
-- time is in milliseconds
function coroutine.resumeWithTimeout(co, timeout, ...) function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time() local startTime = computer.time()
debug.sethook(co, function() debug.sethook(co, function()
@@ -294,6 +291,14 @@ local ok, err = xpcall(function()
queueEvent("componentAdded", "disk") queueEvent("componentAdded", "disk")
elseif event[1] == "disk_eject" then elseif event[1] == "disk_eject" then
queueEvent("componentRemoved", "disk") queueEvent("componentRemoved", "disk")
elseif event[1] == "modem_message" then
queueEvent("modem_message", table.unpack(event, 2))
elseif event[1] == "rednet_message" then
queueEvent("rednet_message", table.unpack(event, 2))
elseif event[1] == "http_success" then
queueEvent("http_success", table.unpack(event, 2))
elseif event[1] == "http_failure" then
queueEvent("http_failure", table.unpack(event, 2))
elseif event[1] == "NoSleep" then elseif event[1] == "NoSleep" then
exit = true exit = true
end end

View File

@@ -1,5 +1,4 @@
--:Minify:-- --:Minify:--
sleep(1)
local BOOT_DRIVE_PATH=({...})[1] or "/$" local BOOT_DRIVE_PATH=({...})[1] or "/$"
-- UnBIOS by JackMacWindows -- UnBIOS by JackMacWindows
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable -- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable

View File

@@ -6,31 +6,147 @@ local native = apis.peripheral
local peripheral = {} local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"} local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getType(name) function peripheral.getNames()
if native.isPresent(name) then return native.getType(name) end local results = {}
for n = 1, #sides do for n = 1, #sides do
local side = sides[n] local side = sides[n]
if native.hasType(side, "peripheral_hub") and if native.isPresent(side) then
native.call(side, "isPresentRemote", name) then table.insert(results, side)
return native.call(side, "getTypeRemote", name) if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end end
end end
return nil return nil
end end
function peripheral.getNames() function peripheral.getName(peripheral)
local names = {} local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do for n = 1, #sides do
local side = sides[n] local side = sides[n]
if native.isPresent(side) then table.insert(names, side) end if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
if native.hasType(side, "peripheral_hub") then return native.call(side, "callRemote", name, method, ...)
local hubSides = native.call(side, "getConnectedSides") end
for _, hubSide in ipairs(hubSides) do end
table.insert(names, hubSide) return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end end
end end
end end
return names return table.unpack(results)
end end
local disks = {} local disks = {}
@@ -130,14 +246,9 @@ local function refresh()
if not peripheral.getType(id) then disks[id] = nil end if not peripheral.getType(id) then disks[id] = nil end
end end
for _, name in ipairs(peripheral.getNames()) do for _, disk in ipairs({peripheral.find("drive")}) do
if peripheral.getType(name) == "disk" then if disk.isDiskPresent() then
if not disks[name] then disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
local mount = disk.getMountPath(name)
if mount then
disks[name] = createDisk(name, mount, false, disk)
end
end
end end
end end
end end

View File

@@ -1,26 +0,0 @@
local args={...}
local kernel=args[1]
local driver={}
driver.name="CCT Term Module"
driver.version="0.1.0"
driver.type="gpio"
driver.description="CCT redstone Module Kernel Module"
driver.arch="cct"
driver.author="HyperionOS Dev Team"
driver.license="MIT"
driver.api={}
function driver.load()
-- will
end
function driver.unload()
-- Nothing to unload
end
function driver.main()
-- Nothing to run
end
-- kernel.drivers.register(driver)

View File

@@ -1,4 +1,5 @@
-- :Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local apis = kernel.apis local apis = kernel.apis
local native = apis.peripheral local native = apis.peripheral
@@ -37,7 +38,7 @@ function peripheral.isPresent(name)
end end
function peripheral.getType(peripheral) function peripheral.getType(peripheral)
if type(peripheral) == "string" then -- Peripheral name passed if type(peripheral) == "string" then
if native.isPresent(peripheral) then if native.isPresent(peripheral) then
return native.getType(peripheral) return native.getType(peripheral)
end end
@@ -58,7 +59,7 @@ function peripheral.getType(peripheral)
end end
function peripheral.hasType(peripheral, peripheral_type) function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then -- Peripheral name passed if type(peripheral) == "string" then
if native.isPresent(peripheral) then if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type) return native.hasType(peripheral, peripheral_type)
end end
@@ -243,6 +244,22 @@ local function serializeBool(bool)
end end
local function newtty(obj, id, ev) local function newtty(obj, id, ev)
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
kernel.devfs.data["tty"][id] = function(op, mode) kernel.devfs.data["tty"][id] = function(op, mode)
if op=="type" then if op=="type" then
return "character device" return "character device"
@@ -317,7 +334,18 @@ kernel.processes.cctmond = function()
local eventType = event[1] local eventType = event[1]
local charOrKey = event[3] local charOrKey = event[3]
-- Update modifier keys local ctrlKeyMap = {
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
[apis.keys.y]=25, [apis.keys.z]=26,
}
if eventType == "keyPressed" then if eventType == "keyPressed" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = true ctrl = true
@@ -325,11 +353,31 @@ kernel.processes.cctmond = function()
alt = true alt = true
end end
-- Handle Ctrl+C if ctrl then
if ctrl and charOrKey == apis.keys.c then local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1) -- SIGINT syscall.sigsend(task, 1)
end end
else
fifo.push(string.char(ctrlByte))
end
end
else
local specialKeyMap = {
[apis.keys.up] = "",
[apis.keys.down] = "",
[apis.keys.right] = "",
[apis.keys.left] = "",
[apis.keys.home] = "",
[apis.keys["end"]] = "",
[apis.keys.pageUp] = "[5~",
[apis.keys.pageDown] = "[6~",
[apis.keys.delete] = "[3~",
}
local special = specialKeyMap[charOrKey]
if special then fifo.push(special) end
end end
elseif eventType == "keyReleased" then elseif eventType == "keyReleased" then
@@ -354,10 +402,10 @@ kernel.processes.cctmond = function()
end end
end end
newtty(apis.term, "TTY1", fifo.pop) newtty(apis.term, "1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5) v.setTextScale(.5)
v.write("Initializing...") v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end) newtty(v,tostring(i+1),function () end)
end end

Binary file not shown.

View File

@@ -1,11 +0,0 @@
-- DO NOT EDIT THIS FILE IF YOU DO NOT KNOW WHAT YOU ARE DOING!
-- DOING SO MAY RENDER YOUR SYSTEM UNBOOTABLE!
-- This file is auto-generated during the build process.
-- DEFAULT BOOT CONFIGURATION FILE
return {
initPath = "/sbin/init.lua",
maxOpenFiles = 128,
maxFilesPerTask = 16,
preempt=true
}

View File

@@ -1,3 +1,4 @@
U $;/ U $;/
U devfs0000;/dev/ U devfs0000;/dev/
U tmpfs0000;/tmp/ U tmpfs0000;/tmp/
U procfs0000;/proc/

View File

@@ -7,9 +7,8 @@ local screen = args[5]
local computer = args[6] local computer = args[6]
local ifs = args[7] local ifs = args[7]
local kernel = {} local kernel = {}
kernel.LOG_Text="" kernel.LOG_Text=""
kernel.version="HyperionOS V1.0.0" kernel.version="HyperionOS V1.2.3"
kernel.process = "Kernel" kernel.process = "Kernel"
kernel.users={[0]="root",[1]="User"} kernel.users={[0]="root",[1]="User"}
kernel.hostname = "hyperion" kernel.hostname = "hyperion"
@@ -28,15 +27,15 @@ local windowsExp = false
function kernel.log(msg, level, c) function kernel.log(msg, level, c)
c=c or 12 c=c or 12
kernel.LOG_Text = kernel.LOG_Text..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n" kernel.LOG_Text = kernel.LOG_Text..string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
if kernel.status == "start" then if kernel.status == "start" then
screen:setTextColor(c) screen:setTextColor(c)
screen:print(string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg) screen:print(tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
elseif kernel.status == "term" then elseif kernel.status == "term" then
kernel.standbyTask=kernel.currentTask kernel.standbyTask=kernel.currentTask
kernel.currentTask=kernel.kernelTask kernel.currentTask=kernel.kernelTask
kernel.vfs.devctl(1,"sfgc",c) kernel.vfs.devctl(1,"sfgc",c)
kernel.vfs.write(1,string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n") kernel.vfs.write(1,tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
kernel.currentTask=kernel.standbyTask kernel.currentTask=kernel.standbyTask
end end
end end
@@ -114,8 +113,9 @@ local split = function(str, delim, maxResultCountOrNil)
end end
if not ifs.isFile("/boot/boot.cfg") then if not ifs.isFile("/boot/boot.cfg") then
kernel.log("boot.cfg missing or corrupted!, Attempting to write recovery boot.cfg", "ERROR", 2) kernel.log("First boot detected writing boot.cfg", "INFO", 3)
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg")) ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
kernel.firstBoot=true
end end
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg") local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
@@ -185,10 +185,17 @@ end
kernel.log("Gathering modules") kernel.log("Gathering modules")
for _, i in ipairs(ifs.list("/lib/modules")) do for _, i in ipairs(ifs.list("/lib/modules")) do
for _,v in ipairs(ifs.list("/lib/modules/"..i)) do local modlist = ifs.list("/lib/modules/"..i)
if not modlist then
kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8)
else
for _,v in ipairs(modlist) do
local prior=tonumber(v:sub(1,2)) local prior=tonumber(v:sub(1,2))
if prior then
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
end end
end
end
end end
kernel.ifs=ifs kernel.ifs=ifs
@@ -245,7 +252,12 @@ kernel.syscalls["sysdump"]=function()
end end
return rv return rv
end end
kernel.syscalls["test"]=function() return true end kernel.syscalls["reboot"]=function()
kernel.computer:reboot()
end
kernel.syscalls["shutdown"]=function()
kernel.computer:reboot()
end
kernel.log("Running modules") kernel.log("Running modules")
for _,p in ipairs(modules) do for _,p in ipairs(modules) do
@@ -266,6 +278,7 @@ for _,p in ipairs(modules) do
end end
kernel.log("Kernel initialized successfully.") kernel.log("Kernel initialized successfully.")
kernel.saveLog()
kernel.status="running" kernel.status="running"
kernel.main() kernel.main()
if kernel.status=="panic" then if kernel.status=="panic" then

View File

@@ -1,41 +0,0 @@
local lua = {
coroutine = true,
debug = true,
_HOST = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getfenv = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setfenv = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G=true
}
local apis={}
for i,v in pairs(_G) do
if not lua[i] or lua[i]==nil then
apis[i]=v
_G[i]=nil
end
end

View File

@@ -1,18 +0,0 @@
checkArg=nil
local oldcomputer=computer
_G.computer=nil
local os=os
_G.os=nil
function component.wrap(address)
local methods=oldcomponent.methods(address)
local object={}
for _,method in ipairs(methods) do
object[method]=function(_,...)
return oldcomponent.invoke(address,method,...)
end
end
return object
end
local

View File

@@ -1 +0,0 @@
local fs={}

View File

@@ -4,7 +4,7 @@
-- This file is auto-generated during the build process. -- This file is auto-generated during the build process.
-- DEFAULT BOOT CONFIGURATION FILE -- DEFAULT BOOT CONFIGURATION FILE
return { return {
initPath = "/sbin/init.lua", initPath = "/sbin/init",
maxOpenFiles = 128, maxOpenFiles = 128,
maxFilesPerTask = 16, maxFilesPerTask = 16,
preempt=true preempt=true

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,2 +1 @@
0:0:root:/root:/bin/hysh 0:0:root:/root:/bin/hysh
1000:1000:testuser:/home/testuser:/bin/hysh

View File

@@ -1,2 +0,0 @@
0:bcdefghijklmnopqrstuvwxyzABCDEzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789lmnopqrstuvwxyzABCDEOPQRSTUVWXYBCDEFGHIJKLMNOPQRSTUVWXYZ01qrstuvwxyzABCDEFGHIJKLMNOPQRSklmnopqrstuvwxyzABCDKLMNOPQRSTUVWXYZ012345CDEFGHIJKLMNOPQRSTUVWXYZ012345:ae6dedb263f6d68c01a49a2bb6f2512c3ea2854dbac9d786fba7c774b47b601d
1000:hijklmnopqrstuvwxyzABCDDEFGHIJKLMNOPQRSTUdefghijklmnopqrstuvwxyzABCDEFGHIJKLLMNOPklmnopqrstuvwxyzABCDEFGHIJKLMvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZYZ0123456BCDEFGHIJKLMNOPQRSTUV:27b25b4cc851e668a4ac14f453e1906b6a07c50a175dd636632d8036a1e91485

View File

@@ -1,226 +0,0 @@
-- :Minify:--
function string.hasSuffix(str, suffix)
return string.sub(str, #suffix + 1) == suffix
end
function string.hasPrefix(str, prefix)
return string.sub(str, 1, #prefix) == prefix
end
function string.getSuffix(str, prefix) return string.sub(str, #prefix + 1) end
function string.getPrefix(str, suffix) return string.sub(str, 1, #suffix) end
function string.join(str, ...) return table.concat(table.pack(str, ...)) end
function string.delim(str, ...) return table.concat(table.pack(...), str) end
function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
if not str then return false end
maxResultCountOrNil = (maxResultCountOrNil or 0) - 1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str, i, i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf .. c
end
end
table.insert(rv, buf)
return rv
end
function table.deepcopy(orig, copies)
copies = copies or {}
if type(orig) ~= 'table' then
return orig
elseif copies[orig] then
return copies[orig]
end
local copy = {}
copies[orig] = copy
for k, v in next, orig, nil do
local copied_key = table.deepcopy(k, copies)
local copied_val = table.deepcopy(v, copies)
copy[copied_key] = copied_val
end
return copy
end
function table.hasKey(tabl, query)
for i, v in pairs(tabl) do if i == query then return v end end
return false
end
function table.hasVal(tabl, query)
for i, v in pairs(tabl) do if v == query then return i end end
return false
end
local function serialize(tbl, seen)
seen = seen or {}
-- If we've seen this table before, return a placeholder to prevent infinite loops
if seen[tbl] then return '"[Circular Reference]"' end
-- Mark this table as seen
seen[tbl] = true
local output = "{"
local first = true
for i, v in pairs(tbl) do
-- Handle comma placement more cleanly
if not first then output = output .. "," end
first = false
-- Serialize Key
if type(i) == "string" then
output = output .. "[\"" .. i .. "\"]="
elseif type(i) == "number" then
output = output .. "[" .. tostring(i) .. "]="
end
-- Serialize Value
if type(v) == "table" then
-- Pass the 'seen' table down to the recursive call
output = output .. serialize(v, seen)
elseif type(v) == "string" then
output = output .. "[=[" .. v .. "]=]"
elseif type(v) == "number" or type(v) == "boolean" then
output = output .. tostring(v)
elseif type(v) == "function" then
output = output .. "\"" .. tostring(v) .. "\""
elseif type(v) == "thread" then
output = output .. "\"" .. tostring(v) .. "\""
else
error("serialization of type \"" .. type(v) .. "\" is not supported")
end
end
seen[tbl] = nil
output = output .. "}"
return output
end
local oldtype = type
local oldgetmetatable = getmetatable
function type(object, trueType)
if trueType then return oldtype(object) end
if oldtype(object) ~= "table" then
return oldtype(object)
else
if oldtype(oldgetmetatable(object)) == "table" then
local metatable = oldgetmetatable(object)
---@diagnostic disable-next-line: need-check-nil
if metatable.__type then return metatable.__type end
else
return "table"
end
end
end
function getmetatable(object)
if oldtype(object) ~= "table" then return end
if oldtype(oldgetmetatable(object)) == "table" then
if oldgetmetatable(object).__isuserdata then
if oldtype(oldgetmetatable(object).__usermeta) == "function" then
return oldgetmetatable(object).__usermeta()
else
return oldgetmetatable(object).__usermeta
end
else
return oldgetmetatable(object)
end
else
return oldgetmetatable(object)
end
end
function isEqualToAny(a, ...)
local args = {...}
for i = 0, #args do if a == args[i] then return true end end
return false
end
function isEqualToAll(a, ...)
local args = {...}
for i = 0, #args do if a ~= args[i] then return false end end
return true
end
function table.keys(t)
local a = {}
for n in pairs(t) do table.insert(a, n) end
return a
end
function table.values(t)
local a = {}
for _, n in pairs(t) do table.insert(a, n) end
return a
end
function table.indexOf(t, value)
for i, v in ipairs(t) do if v == value then return i end end
return -1
end
function string.replace(s, target, repl)
local result = {}
local i = 1
local n = #s
local t_len = #target
while i <= n do
local match = true
if i + t_len - 1 <= n then
for j = 1, t_len do
if s:sub(i + j - 1, i + j - 1) ~= target:sub(j, j) then
match = false
break
end
end
else
match = false
end
if match then
table.insert(result, repl)
i = i + t_len
else
table.insert(result, s:sub(i, i))
i = i + 1
end
end
return table.concat(result)
end
function toHex(num)
return string.format("%X", num)
end
syscall = setmetatable({}, {
__index = function(self, name)
return function(...)
local res = table.pack(coroutine.yield("syscall", name, ...))
if res[1] then
return table.unpack(res, 2, res.n)
else
error(res[2], 2)
end
end
end
})
table.serialize = serialize

View File

@@ -1,629 +0,0 @@
-- :Minify:--
local kernel = ...
local vfs = {}
kernel.vfs = vfs
vfs.mounts = {["$"] = "/"}
vfs.disks = kernel.disks
-- Path normalization
local function normalizePath(path)
local task = kernel.currentTask
local cwd = task.cwd or "/"
if path:sub(1, 1) ~= "/" then path = cwd .. "/" .. path end
local parts = {}
for part in path:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
table.insert(parts, part)
end
end
return "/" .. table.concat(parts, "/")
end
function vfs.splitPath(path)
local rv=string.split(path,"/")
while table.indexOf(rv, "") ~= -1 do
table.remove(rv, table.indexOf(rv, ""))
end
return rv
end
-- Resolve mount and disk path
local function resolvePath(path)
path = normalizePath(path)
local mountPoint = nil
local mountId = nil
for id, mp in pairs(vfs.mounts) do
if path == mp or (mp == "/" and path:sub(1, 1) == "/") or path:sub(1, #mp + 1) == mp .. "/" then
if not mountPoint or #mp > #mountPoint then
mountPoint = mp
mountId = id
end
end
end
if not mountId then
error("ENODEV")
end
local diskPath = path:sub(#mountPoint + 1)
if diskPath == "" then
diskPath = "/"
end
if kernel.config.logPathResolution then
kernel.log("Path '"..path.."' resolved to disk '"..mountId.."' and path '"..diskPath.."'")
end
return vfs.disks[mountId], diskPath
end
-- Allocate file descriptor for current task
local function allocFD(task)
local fd = 0
while task.fd[fd] do fd = fd + 1 end
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
return fd
end
-- System-wide open file limit
local total = 0
local function checkSystemLimit()
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
end
-- File object constructor
local function newFileObj(handle, mode, path, meta, type)
return {
handle = handle,
mode = mode,
path = path,
meta = meta,
type = type,
refcount = 1
}
end
function vfs.newfd(fdobj)
checkSystemLimit()
total=total+1
local fd = allocFD(kernel.currentTask)
kernel.currentTask.fd[fd]=fdobj
end
-- Parse metafile
local function parseMetafile(file)
if not file or file == "" then return {} end
local ret = {}
local pointer = 1
while pointer <= #file do
local namelen = file:byte(pointer)
pointer = pointer + 1
local name = file:sub(pointer, pointer + namelen - 1)
pointer = pointer + namelen
local owner = file:byte(pointer)
local group = file:byte(pointer + 1)
local perms = file:byte(pointer + 2)
pointer = pointer + 3
local cmetalen = file:byte(pointer)
pointer = pointer + 1
local cmeta = ""
if cmetalen > 0 then
cmeta = file:sub(pointer, pointer + cmetalen - 1)
pointer = pointer + cmetalen
end
ret[name] = {owner = owner, group = group, perms = perms, cmeta = cmeta}
end
return ret
end
-- Build metafile
local function makeMetafile(meta)
local file = ""
for name, m in pairs(meta) do
local entry = ""
entry = entry .. string.char(#name) .. name
entry = entry .. string.char(m.owner, m.group, m.perms)
entry = entry .. string.char(#m.cmeta) .. m.cmeta
file = file .. entry
end
return file
end
-- Get file metadata object
local function getFileMeta(path)
local disk, fullPath = resolvePath(path)
fullPath = normalizePath(fullPath)
local parts = {}
for p in fullPath:gmatch("[^/]+") do table.insert(parts, p) end
-- default fallback
local default = {owner = 0, group = 0, perms = 63, cmeta = ""}
-- walk from deepest parent upward
for i = #parts, 1, -1 do
local parent = "/" .. table.concat(parts, "/", 1, i - 1)
if parent ~= "/" then parent = parent .. "/" end
local target = parts[i]
if target == ".meta" then error("Cannot open metafile") end
local metaPath = parent .. ".meta"
if disk:fileExists(metaPath) then
local f = disk:open(metaPath, "r")
local text = f.read(65535)
f.close()
local parsed = parseMetafile(text)
if parsed[target] then return parsed[target] end
end
end
return default
end
local function ensureParentMeta(path)
local disk, fullPath = resolvePath(path)
fullPath = normalizePath(fullPath)
-- split parent + name
local parent, name = fullPath:match("^(.*)/([^/]+)$")
if not parent then
parent = "/"
name = fullPath:gsub("^/", "")
end
if name == ".meta" then error("Cannot open metafile") end
if parent ~= "/" and parent:sub(-1) ~= "/" then parent = parent .. "/" end
local metaPath = parent .. ".meta"
if not disk:fileExists(metaPath) then
local f = disk:open(metaPath, "w")
f.write("")
f.close()
end
return metaPath, name
end
-- Permission checking
local function checkperms(meta, mode)
local modes = {
r = {owner = 5, group = 3, everyone = 1},
w = {owner = 4, group = 2, everyone = 0},
a = {owner = 4, group = 2, everyone = 0}
}
local bits = meta.perms
local function bit_is_set(num, bit)
return math.floor(num / (2 ^ bit)) % 2 == 1
end
if kernel.uid == 0 then return true end
if kernel.uid == meta.owner and bit_is_set(bits, modes[mode].owner) then
return true
end
if meta.group and kernel.groups then
for _, gid in ipairs(kernel.groups) do
if gid == meta.group and bit_is_set(bits, modes[mode].group) then
return true
end
end
end
if bit_is_set(bits, modes[mode].everyone) then return true end
error("EACCES")
end
-- mounts
local function normalizeMountPoint(path)
path = normalizePath(path)
if path ~= "/" and path:sub(-1) == "/" then path = path:sub(1, -2) end
return path
end
local required = {
"open",
"type",
"list",
"attributes",
"fileExists",
"makeDirectory",
"remove"
}
local function check(disk)
for _, name in ipairs(required) do
if type(disk[name]) ~= "function" then
error("Invalid disk: missing method '" .. name .. "'")
end
end
end
function vfs.mount(target, diskOrId)
if kernel.uid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end
target = normalizeMountPoint(target)
if not vfs.exists(target) then vfs.mkdir(target) end
if vfs.type(target) ~= "directory" then error("EINVAL") end
local disk
local id
if type(diskOrId) == "string" then
disk = kernel.disks[diskOrId]
if not disk then error("ENODEV") end
check(disk)
id = diskOrId
elseif type(diskOrId) == "table" then
check(disk)
disk = diskOrId
id = disk.address
vfs.disks[id] = disk
else
error("EINVAL")
end
-- Prevent shadowing an existing mount
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
vfs.mounts[id] = target
return true
end
function vfs.umount(target)
if kernel.uid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end
target = normalizeMountPoint(target)
for id, mp in pairs(vfs.mounts) do
if mp == target then
if id == "$" then error("EBUSY") end -- root fs
vfs.mounts[id] = nil
return true
end
end
error("EINVAL")
end
-- Open file
function vfs.open(path, mode)
checkSystemLimit()
local task = kernel.currentTask
local fd = allocFD(task)
local disk, diskPath = resolvePath(path)
if not disk then error("NODISK") end
local meta = getFileMeta(path)
checkperms(meta, mode)
local handle
if disk:type(diskPath)~="directory" then
handle = disk:open(diskPath, mode)
if type(handle)~="table" then error("ENFILE") end
end
task.fd[fd] = newFileObj(handle, mode, path, meta, disk:type(diskPath))
if not disk.isvirt then
total = total + 1
end
return fd
end
-- Read
function vfs.read(fd, count)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.read then error("EBADF") end
return file.handle.read(count or 1) or ""
end
-- Write
function vfs.write(fd, content)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.write then error("EBADF") end
return file.handle.write(content)
end
-- Pread / Pwrite
function vfs.pread(fd, count, offset)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.read then error("EBADF") end
if not file.handle.seek then error("EBADF") end
file.handle.seek("set", offset)
return file.handle.read(count or 1) or ""
end
function vfs.pwrite(fd, content, offset)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.write then error("EBADF") end
if not file.handle.seek then error("EBADF") end
file.handle.seek("set", offset)
return file.handle.write(content)
end
-- Seek
function vfs.lseek(fd, offset, whence)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.seek then error("EBADF") end
return file.handle.seek(whence or "set", offset)
end
-- Fsync
function vfs.fsync(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
if not file.handle.flush then error("EBADF") end
if file.mode ~= "w" and file.mode ~= "a" then error("EBADF") end
file.handle.flush()
end
-- Close
function vfs.close(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
task.fd[fd] = nil
total = total - 1
file.refcount = file.refcount - 1
if file.refcount <= 0 then
if file.handle.close then
file.handle.close()
end
end
end
-- Sendfile
function vfs.sendfile(outfd, infd, count)
local task = kernel.currentTask
local inFile = task.fd[infd]
local outFile = task.fd[outfd]
if not inFile or not outFile then error("EBADF") end
if not inFile.handle.read then error("EBADF") end
if not outFile.handle.write then error("EBADF") end
local data = inFile.handle.read(count or 1024)
if not data or data == "" then return end
return outFile.handle.write(data)
end
-- Stat / Fstat
function vfs.stat(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
local attrs = disk:attributes(diskPath)
return {
size = attrs.size,
modified = attrs.modified,
created = attrs.created,
owner = meta.owner,
group = meta.group,
xattr = meta.cmeta
}
end
function vfs.fstat(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
local disk, path = resolvePath(file.path)
local attrs = disk:attributes(path)
return {
size = attrs.size,
modified = attrs.modified,
created = attrs.created,
owner = file.meta.owner,
group = file.meta.group,
xattr = file.meta.cmeta
}
end
-- Directory operations
function vfs.listdir(path)
local disk, diskPath = resolvePath(path)
if disk:type(diskPath) ~= "directory" then error("ENOENT") end
local meta = getFileMeta(path)
checkperms(meta, "r")
local list = disk:list(diskPath)
if table.indexOf(list, ".meta") ~= -1 then
table.remove(list, table.indexOf(list, ".meta"))
end
return list
end
function vfs.mkdir(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "w")
disk:makeDirectory(diskPath)
end
function vfs.remove(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "w")
disk:remove(diskPath)
end
-- Permission functions
function vfs.chmod(path, perms)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
meta.perms = perms
local mpath, target = ensureParentMeta(path)
local mf = disk:open(mpath, "r")
local text = mf.read(65535)
mf.close()
local parsed = parseMetafile(text)
parsed[target] = meta
local f = disk:open(mpath, "w")
f.write(makeMetafile(parsed))
f.close()
end
function vfs.fchmod(fd, perms)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
vfs.chmod(file.path, perms)
end
function vfs.chown(path, uid, gid)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
if meta.owner ~= kernel.currentTask.uid then error("EACCES") end
meta.owner = uid
meta.group = gid
local mpath, target = ensureParentMeta(path)
local mf = disk:open(mpath, "r")
local text = mf.read(65535)
mf.close()
local parsed = parseMetafile(text)
parsed[target] = meta
local f = disk:open(mpath, "w")
f.write(makeMetafile(parsed))
f.close()
end
function vfs.fchown(fd, uid, gid)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
vfs.chown(file.path, uid, gid)
end
function vfs.exists(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "r")
return disk:fileExists(diskPath)
end
function vfs.type(path)
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "r")
return disk:type(diskPath)
end
function vfs.getcwd() return kernel.currentTask.cwd end
function vfs.chdir(path) kernel.currentTask.cwd = path end
function vfs.dup(oldfd)
local task = kernel.currentTask
local file = task.fd[oldfd]
if not file then error("EBADF") end
checkSystemLimit()
local newfd = allocFD(task)
file.refcount = file.refcount + 1
task.fd[newfd] = file
total = total + 1
return newfd
end
function vfs.dup2(oldfd, newfd)
local task = kernel.currentTask
local file = task.fd[oldfd]
if not file then error("EBADF") end
if newfd < 0 or newfd >= kernel.config.maxFilesPerTask then
error("EBADF")
end
if oldfd == newfd then
return newfd
end
if task.fd[newfd] then
vfs.close(newfd)
end
checkSystemLimit()
file.refcount = file.refcount + 1
task.fd[newfd] = file
total = total + 1
return newfd
end
function vfs.devctl(fd, method, ...)
if not kernel.currentTask.fd[fd] then error("EBADF") end
if not kernel.currentTask.fd[fd].handle[method] then error("EINVAL") end
return kernel.currentTask.fd[fd].handle[method](...)
end
-- Export syscalls
local sys = kernel.syscalls
sys["open"] = vfs.open
sys["close"] = vfs.close
sys["read"] = vfs.read
sys["write"] = vfs.write
sys["pread"] = vfs.pread
sys["pwrite"] = vfs.pwrite
sys["lseek"] = vfs.lseek
sys["fsync"] = vfs.fsync
sys["sendfile"] = vfs.sendfile
sys["stat"] = vfs.stat
sys["fstat"] = vfs.fstat
sys["mkdir"] = vfs.mkdir
sys["remove"] = vfs.remove
sys["listdir"] = vfs.listdir
sys["chmod"] = vfs.chmod
sys["fchmod"] = vfs.fchmod
sys["chown"] = vfs.chown
sys["fchown"] = vfs.fchown
sys["exists"] = vfs.exists
sys["type"] = vfs.type
sys["mount"] = vfs.mount
sys["umount"] = vfs.umount
sys["getcwd"] = vfs.getcwd
sys["chdir"] = vfs.chdir
sys["dup"] = vfs.dup
sys["dup2"] = vfs.dup2
sys["devctl"] = vfs.devctl
kernel.log("VFS module loaded")

View File

@@ -1,40 +0,0 @@
-- :Minify:--
local kernel = ...
local cache = {}
kernel.searchpaths = {
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
}
function require(module, ...)
if cache[module] then return cache[module] end
local modpath = module:gsub("%.", "/")
local failed = {}
for _, path in ipairs(kernel.searchpaths) do
local full_path = string.replace(path, "?", modpath)
if full_path:sub(1, 1) ~= "/" then
full_path = kernel.currentTask.cwd .. full_path
end
if kernel.vfs.exists(full_path) then
if kernel.vfs.type(full_path) == "directory" then
full_path = full_path .. "/init"
end
if kernel.vfs.exists(full_path) then
local handle = kernel.vfs.open(full_path, "r")
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
return
assert(load(file_content, full_path, "t", kernel._U))(...)
else
table.insert(failed, full_path)
end
else
table.insert(failed, full_path)
end
end
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
end

View File

@@ -1,129 +0,0 @@
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "tmpfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
-- Space functions (just placeholders)
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
-- Writable operations
proxy.makeDirectory = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
if not step[steps[i]] then
step[steps[i]] = {}
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
end
proxy.remove = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
step = step[steps[i]]
if not step then error("ENOENT") end
end
step[steps[#steps]] = nil
end
proxy.setLabel = function(_, label) end
proxy.getLabel = function() return "tmpfs" end
proxy.attributes = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
return {
size = type(step) == "string" and #step or 0,
modified = 0,
created = 0,
}
end
-- Open files
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
if not step[steps[i]] then
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
local filename = steps[#steps]
if mode == "r" then
if type(step[filename]) ~= "string" then error("ENOENT") end
local content = step[filename]
local pos = 1
return {
read = function(amount)
amount = amount or #content
local chunk = content:sub(pos, pos+amount-1)
pos = pos + #chunk
return chunk
end
}
elseif mode == "w" then
step[filename] = ""
return {
write = function(str)
step[filename] = str
end
}
elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end
return {
write = function(str)
step[filename] = step[filename] .. str
end
}
else
error("EACCES")
end
end
function proxy:type(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then return "directory" end
for i=1,#steps do
step = step[steps[i]]
if not step then return false end
end
if type(step) == "table" then return "directory" end
if type(step) == "string" then return "file" end
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
if type(step) ~= "table" then error("ENOTDIR") end
local keys = {}
for k,_ in pairs(step) do table.insert(keys, k) end
return keys
end
function proxy:fileExists(path)
return pcall(function() return self:type(path) end)
end
kernel.disks["tmpfs0000"] = proxy

View File

@@ -1,22 +0,0 @@
---- :Minify:--
--local kernel = ...
--
--local timeout = false
--kernel.processes.keventd = function()
-- while true do
-- local event = {kernel.computer:getMachineEvent()}
-- if event[1] then
-- if event[1] == "keyTyped" then
-- if event[3] == "\x1b^s" then
-- kernel.shutdown()
-- elseif event[3] == "\x1b^r" then
-- kernel.reboot()
-- end
-- end
-- timeout = false
-- else
-- timeout = true
-- end
-- if timeout then sleep(.05) end
-- end
--end

View File

@@ -1,34 +0,0 @@
--:Minify:--
local kernel = ...
local function trim(str)
local s, e = 1, #str
while s <= e and (str:sub(s,s) == " " or str:sub(s,s) == "\t") do s = s + 1 end
while e >= s and (str:sub(e,e) == " " or str:sub(e,e) == "\t" or str:sub(e,e) == "\n" or str:sub(e,e) == "\r") do e = e - 1 end
if s > e then return "" end
return str:sub(s,e)
end
for _, line in ipairs(string.split(kernel.fstab, "\n")) do
line = trim(line)
if line ~= "" and line:sub(1,1) == "U" then
local semicolon_pos
for i = 3, #line do
if line:sub(i,i) == ";" then
semicolon_pos = i
break
end
end
if not semicolon_pos or semicolon_pos == 3 then
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8)
else
local id = line:sub(3, semicolon_pos - 1)
local path = trim(line:sub(semicolon_pos + 1))
kernel.log("Mounted "..id.." to "..path)
if id ~= "$" then
kernel.vfs.mount(path, id)
end
end
end
end

View File

@@ -1,27 +0,0 @@
--:Minify:--
local kernel = ...
local signal = {}
kernel.signal=signal
function signal.sigsend(pid, sig)
if sig<0 or sig>256 then error("EINVAL") end
local task = kernel.tasks[tostring(pid)]
if not task then error("ENOENT") end
if not task.sigq then return end
task.sigq[#task.sigq+1] = sig
end
function signal.sigcatch(handler)
kernel.currentTask.sigh=handler
if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end
end
function signal.sigignore()
kernel.currentTask.sigh=nil
kernel.currentTask.sigq=nil
end
local s=kernel.syscalls
s["sigsend"] = signal.sigsend
s["sigcatch"] = signal.sigcatch
s["sigignore"] = signal.sigignore

View File

@@ -1,14 +0,0 @@
--:Minify:--
local kernel = ...
local socket = {}
function socket.socket()
end
function socket.bind()
end
kernel.socket=socket
kernel.log("Loaded socket module")

View File

@@ -1,6 +0,0 @@
--:Minify:--
local kernel=...
kernel.vfs.open("/dev/null", "r")
kernel.vfs.open("/dev/tty/TTY1", "w")
kernel.vfs.open("/dev/null", "w")
kernel.status="term"

View File

@@ -1,56 +0,0 @@
-- :Minify:--
local args = {...}
local kernel = args[1]
kernel._G = _G
local function readonly(tbl)
return setmetatable({}, {
__index = function(_, key)
local value = tbl[key]
if type(value) == "table" then return readonly(value) end
return value
end,
__newindex = function(t, k, v)
if kernel.config.allowGlobalOverwrites or
kernel.allowGlobalOverwrites then
rawset(tbl, k, v)
return
end
error("Attempt to modify global variable '" .. k .. "'", 2)
end,
__pairs = function()
local function iter(_, key)
local nextKey, value = next(tbl, key)
if type(value) == "table" then
value = readonly(value)
end
return nextKey, value
end
return iter, tbl, nil
end,
__ipairs = function()
local i = 0
return function()
i = i + 1
local value = tbl[i]
if value == nil then return end
if type(value) == "table" then
value = readonly(value)
end
return i, value
end
end,
__len = function() return #tbl end,
__metatable = false
})
end
kernel._U = readonly(kernel._G)
kernel.allowGlobalOverwrites = true
kernel._U._G = kernel._U
kernel.allowGlobalOverwrites = false

View File

@@ -1,625 +0,0 @@
--:Minify:--
local kernel = ...
local auth = {}
kernel.auth = auth
-- PASSWD FILE FORMAT: uid:gid:username:homedir:shell
-- SHADOW FILE FORMAT: uid:salt:hash
local function getFile(path)
local file = kernel.vfs.open(path, "r")
if not file then error("Failed to open file: " .. path) end
local content = kernel.vfs.read(file, 1024000)
kernel.vfs.close(file)
return content
end
local function writeFile(path, content)
local file = kernel.vfs.open(path, "w")
if not file then error("Failed to open file for writing: " .. path) end
kernel.vfs.write(file, content)
kernel.vfs.close(file)
end
local blake2s
do
local MOD32 = 2^32
local function norm(x) return x % MOD32 end
local function tobits(x)
x = norm(x)
local t = {}
for i = 0, 31 do local b = x % 2; t[i] = b; x = (x - b) / 2 end
return t
end
local function frombits(t)
local x, p = 0, 1
for i = 0, 31 do if t[i] == 1 then x = x + p end; p = p * 2 end
return norm(x)
end
local function bor(...)
local args = {...}
if #args == 0 then return 0 end
local bits = tobits(args[1])
for i = 2, #args do
local b = tobits(args[i])
for j = 0, 31 do bits[j] = (bits[j] == 1 or b[j] == 1) and 1 or 0 end
end
return frombits(bits)
end
local function bxor(...)
local args = {...}
if #args == 0 then return 0 end
local bits = tobits(args[1])
for i = 2, #args do
local b = tobits(args[i])
for j = 0, 31 do bits[j] = (bits[j] ~= b[j]) and 1 or 0 end
end
return frombits(bits)
end
local function lshift(x, n) return norm(norm(x) * 2^n) end
local function rshift(x, n) return math.floor(norm(x) / 2^n) end
local function rotr(x, n) return bor(rshift(x, n), lshift(x, 32 - n)) end
local IV = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
}
local SIGMA = {
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
{14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3},
{11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4},
{7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8},
{9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13},
{2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9},
{12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11},
{13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10},
{6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5},
{10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0}
}
local function G(v, a, b, c, d, x, y)
v[a] = (v[a] + v[b] + x) % MOD32
v[d] = rotr(bxor(v[d], v[a]), 16)
v[c] = (v[c] + v[d]) % MOD32
v[b] = rotr(bxor(v[b], v[c]), 12)
v[a] = (v[a] + v[b] + y) % MOD32
v[d] = rotr(bxor(v[d], v[a]), 8)
v[c] = (v[c] + v[d]) % MOD32
v[b] = rotr(bxor(v[b], v[c]), 7)
end
local function compress(h, block, t, last)
local v = {}
for i = 1, 8 do v[i] = h[i] end
for i = 1, 8 do v[i + 8] = IV[i] end
v[13] = bxor(v[13], t)
if last then v[15] = bxor(v[15], 0xFFFFFFFF) end
local m = {}
for i = 0, 15 do
local p = i * 4 + 1
m[i] = (block:byte(p) or 0)
+ ((block:byte(p+1) or 0) * 0x100)
+ ((block:byte(p+2) or 0) * 0x10000)
+ ((block:byte(p+3) or 0) * 0x1000000)
end
for r = 1, 10 do
local s = SIGMA[r]
G(v,1,5,9,13, m[s[1]], m[s[2]])
G(v,2,6,10,14, m[s[3]], m[s[4]])
G(v,3,7,11,15, m[s[5]], m[s[6]])
G(v,4,8,12,16, m[s[7]], m[s[8]])
G(v,1,6,11,16, m[s[9]], m[s[10]])
G(v,2,7,12,13, m[s[11]], m[s[12]])
G(v,3,8,9,14, m[s[13]], m[s[14]])
G(v,4,5,10,15, m[s[15]], m[s[16]])
end
for i = 1, 8 do h[i] = bxor(h[i], v[i], v[i+8]) end
end
function blake2s(msg, key)
key = key or ""
local h = {}
for i = 1, 8 do h[i] = IV[i] end
local outlen = 32
h[1] = bxor(h[1], 0x01010000 + lshift(#key, 8) + outlen)
local t = 0
if #key > 0 then
local block = key .. string.rep("\0", 64 - #key)
t = #key
compress(h, block, t, false)
end
for i = 1, #msg, 64 do
local block = msg:sub(i, i + 63)
if #block < 64 then block = block .. string.rep("\0", 64 - #block) end
t = t + math.min(64, #msg - i + 1)
compress(h, block, t, i + 64 > #msg)
end
local out = ""
for i = 1, 8 do out = out .. string.format("%08x", h[i]) end
return out
end
end
if not blake2s then error("Failed to load blake2s") end
if not kernel.vfs.exists("/etc/pam.d/secret") then
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT")
local key = ""
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
kernel.vfs.write(handle, key)
kernel.vfs.close(handle)
end
local pepper = getFile("/etc/pam.d/secret")
local function genSalt()
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
local s = ""
for i = 1, 16 do
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
end
return s
end
local function hashPassword(password, salt)
local key = (pepper .. salt):sub(1, 32)
return blake2s(password, key)
end
local passwdFile = getFile("/etc/passwd")
local shadowFile = getFile("/etc/shadow")
local passwdLines = string.split(passwdFile, "\n")
local shadowLines = string.split(shadowFile, "\n")
local passwd, shadow = {}, {}
for _, v in ipairs(passwdLines) do
local fields = string.split(v, ":")
if fields[1] and fields[1] ~= "" then
passwd[#passwd + 1] = fields
end
end
for _, v in ipairs(shadowLines) do
local fields = string.split(v, ":")
if fields[1] and fields[1] ~= "" then
shadow[#shadow + 1] = fields
end
end
for _, v in ipairs(passwd) do
local uid = tonumber(v[1])
if uid then kernel.users[uid] = v[3] end
end
kernel.passwd = passwd
local function flushPasswd()
local lines = {}
for _, v in ipairs(passwd) do
lines[#lines + 1] = table.concat(v, ":")
end
writeFile("/etc/passwd", table.concat(lines, "\n"))
end
local function flushShadow()
local lines = {}
for _, v in ipairs(shadow) do
lines[#lines + 1] = table.concat(v, ":")
end
writeFile("/etc/shadow", table.concat(lines, "\n"))
end
local function getPasswdByUID(uid)
for _, v in ipairs(passwd) do
if tonumber(v[1]) == uid then return v end
end
return nil
end
local function getShadowByUID(uid)
for _, v in ipairs(shadow) do
if tonumber(v[1]) == uid then return v end
end
return nil
end
local function getPasswdByUsername(username)
for _, v in ipairs(passwd) do
if v[3] == username then return v end
end
return nil
end
local function nextUID()
local max = 999
for _, v in ipairs(passwd) do
local uid = tonumber(v[1])
if uid and uid >= 1000 and uid > max then max = uid end
end
return max + 1
end
function auth.login(username, password)
if type(username) ~= "string" or type(password) ~= "string" then
return nil, "Authentication failure"
end
local entry = getPasswdByUsername(username)
if not entry then
-- timing attack resistance
hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure"
end
local uid = tonumber(entry[1])
local sEntry = getShadowByUID(uid)
if not sEntry then
hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure"
end
local salt = sEntry[2]
local storedHash = sEntry[3]
local computed = hashPassword(password, salt)
if computed ~= storedHash then
return nil, "Authentication failure"
end
kernel.currentUID = uid
if kernel.currentProcess then
kernel.currentProcess.uid = uid
kernel.currentProcess.euid = uid
kernel.currentProcess.gid = tonumber(entry[2]) or uid
kernel.currentProcess.egid = tonumber(entry[2]) or uid
end
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")")
return true
end
function auth.setPassword(uid, newPassword)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID or 0
if callerUID ~= 0 and callerUID ~= uid then
return nil, "Permission denied"
end
if type(newPassword) ~= "string" or #newPassword == 0 then
return nil, "Password may not be empty"
end
if #newPassword < 6 then
return nil, "Password is too short (minimum 6 characters)"
end
local salt = genSalt()
local hash = hashPassword(newPassword, salt)
local sEntry = getShadowByUID(uid)
if sEntry then
sEntry[2] = salt
sEntry[3] = hash
else
shadow[#shadow + 1] = { tostring(uid), salt, hash }
end
flushShadow()
kernel.log("AUTH: password changed for uid=" .. tostring(uid))
return true
end
function auth.setUsername(uid, newUsername)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID or 0
if callerUID ~= 0 then
return nil, "Permission denied (root only)"
end
if type(newUsername) ~= "string" or #newUsername == 0 then
return nil, "Invalid username"
end
if not newUsername:match("^[a-z_][a-z0-9_%-]*$") or #newUsername > 32 then
return nil, "Invalid username format"
end
if getPasswdByUsername(newUsername) then
return nil, "Username already taken"
end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
local oldName = entry[3]
entry[3] = newUsername
kernel.users[uid] = newUsername
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " renamed '" .. oldName .. "' → '" .. newUsername .. "'")
return true
end
function auth.newUser(username, password, gid, homedir, shell)
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID or 0
if callerUID ~= 0 then
return nil, "Permission denied (root only)"
end
if type(username) ~= "string" or #username == 0 then
return nil, "Invalid username"
end
if not username:match("^[a-z_][a-z0-9_%-]*$") or #username > 32 then
return nil, "Invalid username format"
end
if getPasswdByUsername(username) then
return nil, "Username already exists"
end
if type(password) ~= "string" or #password < 6 then
return nil, "Password is too short (minimum 6 characters)"
end
local uid = nextUID()
gid = tonumber(gid) or uid
homedir = homedir or ("/home/" .. username)
shell = shell or "/bin/sh"
passwd[#passwd + 1] = {
tostring(uid),
tostring(gid),
username,
homedir,
shell
}
kernel.users[uid] = username
local salt = genSalt()
local hash = hashPassword(password, salt)
shadow[#shadow + 1] = { tostring(uid), salt, hash }
flushPasswd()
flushShadow()
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
kernel.vfs.mkdir(homedir)
end
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
return uid
end
function auth.whoami()
local uid = (kernel.currentProcess and kernel.currentProcess.euid)
or kernel.currentUID
if not uid then return nil, "Not logged in" end
return kernel.users[uid] or ("uid=" .. tostring(uid))
end
function auth.getUID(username)
local entry = getPasswdByUsername(username)
if entry then return tonumber(entry[1]) end
return nil
end
function auth.getPasswd(uid)
uid = tonumber(uid)
local entry = getPasswdByUID(uid)
if not entry then return nil end
return {
uid = tonumber(entry[1]),
gid = tonumber(entry[2]),
username = entry[3],
homedir = entry[4],
shell = entry[5],
}
end
function auth.deleteUser(uid)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
if uid == 0 then return nil, "Cannot delete root" end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
local username = entry[3]
-- Remove from passwd
for i, v in ipairs(passwd) do
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
end
-- Remove from shadow
for i, v in ipairs(shadow) do
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
end
kernel.users[uid] = nil
flushPasswd()
flushShadow()
kernel.log("AUTH: deleted user '" .. username .. "' uid=" .. tostring(uid))
return true
end
function auth.lockUser(uid)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
if uid == 0 then return nil, "Cannot lock root" end
local sEntry = getShadowByUID(uid)
if not sEntry then return nil, "No shadow entry for uid" end
-- Prefix hash with ! to lock (standard Linux convention)
if sEntry[3]:sub(1,1) ~= "!" then
sEntry[3] = "!" .. sEntry[3]
end
flushShadow()
kernel.log("AUTH: locked uid=" .. tostring(uid))
return true
end
function auth.unlockUser(uid)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
local sEntry = getShadowByUID(uid)
if not sEntry then return nil, "No shadow entry for uid" end
if sEntry[3]:sub(1,1) == "!" then
sEntry[3] = sEntry[3]:sub(2)
end
flushShadow()
kernel.log("AUTH: unlocked uid=" .. tostring(uid))
return true
end
function auth.listUsers()
local result = {}
for _, v in ipairs(passwd) do
local uid = tonumber(v[1])
local sEntry = getShadowByUID(uid)
local locked = sEntry and sEntry[3]:sub(1,1) == "!"
result[#result+1] = {
uid = uid,
gid = tonumber(v[2]),
username = v[3],
homedir = v[4],
shell = v[5],
locked = locked or false,
}
end
return result
end
function auth.setShell(uid, shell)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 and callerUID ~= uid then
return nil, "Permission denied"
end
if type(shell) ~= "string" or #shell == 0 then
return nil, "Invalid shell"
end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
entry[5] = shell
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " shell -> " .. shell)
return true
end
function auth.setHomedir(uid, homedir)
uid = tonumber(uid)
if not uid then return nil, "Invalid uid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
if type(homedir) ~= "string" or #homedir == 0 then
return nil, "Invalid homedir"
end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
entry[4] = homedir
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " homedir -> " .. homedir)
return true
end
function auth.setGID(uid, gid)
uid = tonumber(uid)
gid = tonumber(gid)
if not uid or not gid then return nil, "Invalid uid or gid" end
local callerUID = kernel.uid or 0
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
local entry = getPasswdByUID(uid)
if not entry then return nil, "No such user" end
entry[2] = tostring(gid)
flushPasswd()
kernel.log("AUTH: uid=" .. tostring(uid) .. " gid -> " .. tostring(gid))
return true
end
-- Elevate the calling task to targetUid after verifying targetUsername's password.
-- This is the kernel-side primitive for su/sudo — it bypasses the kernel.uid==0
-- check in sys.setuid because the auth module itself is trusted kernel code.
function auth.elevate(targetUsername, password)
if type(targetUsername) ~= "string" or type(password) ~= "string" then
return nil, "Authentication failure"
end
local entry = getPasswdByUsername(targetUsername)
if not entry then
hashPassword(password, "aaaaaaaaaaaaaaaa") -- timing resistance
return nil, "Authentication failure"
end
local uid = tonumber(entry[1])
local sEntry = getShadowByUID(uid)
if not sEntry then
hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure"
end
local computed = hashPassword(password, sEntry[2])
if computed ~= sEntry[3] then
return nil, "Authentication failure"
end
-- Directly set the calling task's uid — trusted kernel path
local task = kernel.currentTask
local prevUid = task.uid
task.uid = uid
task.euid = uid
task.gid = tonumber(entry[2]) or uid
task.egid = tonumber(entry[2]) or uid
kernel.uid = uid
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")")
return true, uid
end
if kernel.syscalls then
kernel.syscalls["auth_login"] = auth.login
kernel.syscalls["auth_setpassword"] = auth.setPassword
kernel.syscalls["auth_setusername"] = auth.setUsername
kernel.syscalls["auth_newuser"] = auth.newUser
kernel.syscalls["auth_whoami"] = auth.whoami
kernel.syscalls["auth_getuid"] = auth.getUID
kernel.syscalls["auth_getpasswd"] = auth.getPasswd
kernel.syscalls["auth_elevate"] = auth.elevate
kernel.syscalls["auth_deleteuser"] = auth.deleteUser
kernel.syscalls["auth_lockuser"] = auth.lockUser
kernel.syscalls["auth_unlockuser"] = auth.unlockUser
kernel.syscalls["auth_listusers"] = auth.listUsers
kernel.syscalls["auth_setshell"] = auth.setShell
kernel.syscalls["auth_sethomedir"] = auth.setHomedir
kernel.syscalls["auth_setgid"] = auth.setGID
end

View File

@@ -1,447 +0,0 @@
-- :Minify:--
local kernel = ...
local tasks = {}
local sys = {}
local nextpid = 2
kernel.exitMain = false
function sys.spawn(func, name, envars, args, tgid)
local id = nextpid
nextpid = nextpid + 1
tasks[tostring(id)] = {
coro = coroutine.create(function()
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
if not ok then
if kernel.config.logTaskExit then
kernel.log(
"Task " .. tostring(id) .. " exited with err: " ..
tostring(err), "ERROR", 2)
end
if type(err) == "number" then
tasks[tostring(id)].exit = err
end
else
if kernel.config.logTaskExit then
if err then
kernel.log("Task " .. tostring(id) ..
" exited with code: " .. tostring(err),
"INFO")
else
kernel.log("Task " .. tostring(id) ..
" exited without code", "INFO")
end
end
if type(err) == "number" then
tasks[tostring(id)].exit = err
end
end
for v, _ in ipairs(tasks[tostring(id)].fd) do pcall(kernel.vfs.close,v) end
tasks[tostring(id)].status = "Z"
end),
name = name or ("task" .. tostring(id)),
envars = envars or kernel.currentTask.envars,
args = args or {},
status = "R",
pid = id,
tgid = tgid or kernel.currentTask.tgid,
uid = kernel.uid,
fd = {},
sleep = 0,
ivs = 0,
vs = 0,
children = {},
parent = kernel.currentTask,
siblings = kernel.currentTask.children,
syscallReturn = {},
cwd = kernel.currentTask.cwd,
timeSlice = 0,
lastTime = 0,
totalTime = 0,
numRuns = 0
}
table.insert(kernel.currentTask.children, tasks[tostring(id)])
return id
end
function sys.sleep(s)
kernel.currentTask.status = "S"
kernel.currentTask.sleep = kernel.computer:time() + s * 1000
coroutine.yield()
end
function sys.getTask(pid)
if tasks[tostring(pid)] then
local task = tasks[tostring(pid)]
local children = {}
local siblings = {}
for i, v in ipairs(task.children) do children[i] = v.pid end
for i, v in ipairs(task.siblings) do siblings[i] = v.pid end
return {
name = task.name,
status = task.status,
pid = task.pid,
tgid = task.tgid,
username = kernel.users[task.uid],
uid = task.uid,
exit = task.exit,
sleep = task.sleep,
ivs = task.ivs,
vs = task.vs,
children = children,
siblings = siblings,
parent = task.parent.pid,
cwd = task.cwd,
term = task.term
}
end
end
function sys.collect(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status ~= "Z" then
return false, "Task must exit to collect status"
else
tasks[tostring(pid)].reapTime = 0
return true, tasks[tostring(pid)].exit
end
end
function sys.kill(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status == "Z" then
return false, "Task is already dead"
else
tasks[tostring(pid)].status = "Z"
return true
end
end
function sys.stop(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status ~= "R" then
return false, "Cannot stop non running task"
else
tasks[tostring(pid)].status = "T"
return true
end
end
function sys.continue(pid)
local children = {}
for i, v in ipairs(kernel.currentTask.children) do children[i] = v.pid end
if not tasks[tostring(pid)] then
return false, "Task does not exist"
elseif not isEqualToAny(tasks[tostring(pid)].pid, table.unpack(children)) and kernel.uid ~= 0 then
return false, "You do not own this task"
elseif tasks[tostring(pid)].status ~= "T" then
return false, "Task is not stopped"
else
tasks[tostring(pid)].status = "R"
return true
end
end
function sys.getpid() return kernel.currentTask.pid end
function sys.getppid() return kernel.currentTask.parent.pid end
function sys.getTasks()
local ret = {}
for i, v in pairs(tasks) do ret[#ret + 1] = v.pid end
return ret
end
function sys.getEnviron(key) return kernel.currentTask.envars[key] end
function sys.setEnviron(key, value) kernel.currentTask.envars[key] = value end
function sys.exit(code)
if kernel.config.logTaskExit then
if code then
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited with code: " .. tostring(code), "INFO")
else
kernel.log("Task " .. tostring(kernel.currentTask.pid) .. " exited without code", "INFO")
end
end
tasks[tostring(kernel.currentTask.pid)].status = "Z"
if type(code) == "number" then
tasks[tostring(kernel.currentTask.pid)].exit = code
end
end
function sys.setuid(uid)
if kernel.uid ~= 0 then error("EACCES") end
kernel.currentTask.uid = uid
end
function sys.getuid() return kernel.currentTask.uid end
local sysc = kernel.syscalls
sysc["spawn"] = sys.spawn
sysc["sleep"] = sys.sleep
sysc["getTask"] = sys.getTask
sysc["collect"] = sys.collect
sysc["kill"] = sys.kill
sysc["stop"] = sys.stop
sysc["continue"] = sys.continue
sysc["getpid"] = sys.getpid
sysc["getppid"] = sys.getppid
sysc["getTasks"] = sys.getTasks
sysc["setEnviron"] = sys.setEnviron
sysc["getEnviron"] = sys.getEnviron
sysc["exit"] = sys.exit
sysc["setuid"] = sys.setuid
sysc["getuid"] = sys.getuid
kernel._G.sleep = function(...) coroutine.yield("syscall", "sleep", ...) end
local function reapDeadTasks()
for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then
kernel.currentTask = task
kernel.uid = task.uid
kernel.process = task.name
task.coro = nil
task.ivs = nil
task.vs = nil
task.args = nil
task.envars = nil
task.cwd = nil
task.numRuns = nil
task.totalTime = nil
task.lastTime = nil
task.timeSlice = nil
task.syscallReturn = nil
task.sleep = nil
task.fd = nil
task.reapTime = kernel.computer:time() + 30000
elseif task.reapTime and kernel.computer:time() > task.reapTime and
task.status == "Z" then
for _, child in ipairs(task.children) do
child.parent = tasks["1"]
child.siblings = tasks["1"].children
table.insert(tasks["1"].children, child)
end
for i, sibling in ipairs(task.siblings) do
if sibling.pid == task.pid then
table.remove(task.siblings, i)
break
end
end
tasks[pid] = nil
end
end
end
local alpha = 0.85
local C_target = 0.01
local Tmin = 0.0005
local Tmax = 0.5
local lambda_budget = 0.08
local lambda_clamp = 0.03
local lambda_var = 0.02
local k_min = 0.5
local k_max = 0.5
local B = 0.01
function kernel.main()
while not kernel.exitMain do
local N = 0
local Tmin_hit = 0
local Tmax_hit = 0
local totalTaskTime = 0
local taskTimes = {}
for pid, task in pairs(tasks) do
if task.status == "S" then
if kernel.computer:time() >= task.sleep then
task.status = "R"
task.sleep = 0
end
end
if task.status == "R" then
kernel.currentTask = task
kernel.uid = task.uid
kernel.process = task.name
N = N + 1
-- assign adaptive time slice
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
if task.sigq and #task.sigq~=0 and task.sigh then
local coro = coroutine.create(task.sigh)
if kernel.config.preempt then
coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
else
coroutine.resume(coro, table.remove(task.sigq, 1))
end
end
-- check for exit/stop
if task.status=="R" then
-- measure execution time
local startTime = kernel.computer:time()
local ret
if kernel.config.preempt then
ret = {
coroutine.resumeWithTimeout(
task.coro,
task.timeSlice,
table.unpack(task.syscallReturn)
)
}
else
ret = {
coroutine.resume(
task.coro,
table.unpack(task.syscallReturn)
)
}
end
local elapsed = kernel.computer:time() - startTime
task.lastTime = elapsed
task.totalTime = (task.totalTime or 0) + elapsed
task.numRuns = (task.numRuns or 0) + 1
taskTimes[#taskTimes + 1] = elapsed
totalTaskTime = totalTaskTime + elapsed
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
-- handle task results
if ret[1] == "error" or ret[1] == false then
kernel.log("processHandlerException: " .. ret[2], "ERROR", 2)
task.status = "Z"
task.exit = "processHandlerException: " .. ret[2]
elseif ret[1] == "timeout" then
task.ivs = task.ivs + 1
task.syscallReturn = {}
elseif ret[1] == "success" or ret[1] == true then
task.vs = task.vs + 1
if ret[2] == "syscall" then
if kernel.syscalls[ret[3]] then
if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " invoking syscall: " .. ret[3], "DBUG", 5)
for i = 4, #ret do
kernel.log(" inval[" .. tostring(i - 3) .. "] = " .. tostring(ret[i]), "DBUG", 5)
end
end
local sysret = {
xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))
}
if kernel.config.debugSyscalls then
if not sysret[1] then
kernel.log(
"Task " .. task.pid .. " syscall " .. ret[3] .. " failed: " .. tostring(sysret[2]), "ERROR", 2
)
else
kernel.log(
"Task " .. task.pid .. " syscall " .. ret[3] .. " completed returning " .. tostring(#sysret - 1) .. " values", "DBUG", 5
)
for i = 2, #sysret do
if type(sysret[i]) == "table" then
kernel.log(
" retval[" .. tostring(i - 1) .. "] = " .. table.serialize(sysret[i]),"DBUG", 5
)
else
kernel.log(
" retval[" .. tostring(i - 1) .. "] = " .. tostring(sysret[i]), "DBUG", 5
)
end
end
end
end
if not sysret[1] then
task.syscallReturn = {false, sysret[2]}
else
task.syscallReturn = {
true, table.unpack(sysret, 2)
}
end
else
task.syscallReturn = {
false, "Unknown syscall: " .. tostring(ret[3])
}
end
end
end
end
end
end
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
local T_prev_var = 0
for _, t in ipairs(taskTimes) do
T_prev_var = T_prev_var + (t - T_prev_avg) ^ 2
end
if N > 0 then T_prev_var = T_prev_var / N end
if N > 0 then
local f_clamp = k_min * (Tmin_hit / N) - k_max * (Tmax_hit / N)
local B_budget = (C_target * (N ^ (alpha - 1))) /
math.max(T_prev_avg, 1e-8)
B = B + lambda_budget * (B_budget - B) + lambda_clamp * f_clamp -
lambda_var * T_prev_var
end
-- clean up dead tasks
reapDeadTasks()
end
end
kernel.tasks = tasks
kernel.hpv = sys

View File

@@ -1,7 +0,0 @@
--:Minify:--
local kernel=...
local debug=debug
kernel._G.debug={
getinfo=debug.getinfo,
traceback=debug.traceback
}

View File

@@ -1,15 +0,0 @@
local kernel=...
local sysc=kernel.syscalls
kernel.gpio={}
sysc["gpio_write"]=function(pin, data)
if kernel.gpio[pin] then
return kernel.gpio[pin]("w", data)
end
end
sysc["gpio_read"]=function(pin)
if kernel.gpio[pin] then
return kernel.gpio[pin]("r")
end
end

View File

@@ -1,47 +0,0 @@
-- :Minify:--
local kernel = ...
kernel.log("Loading init system...")
kernel.log("InitPath: " .. kernel.config.initPath)
local handle = kernel.vfs.open(kernel.config.initPath, "r")
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
if not initFunc then error("Failed to load init system: " .. err) end
kernel.tasks["1"] = {
coro = coroutine.create(function()
local ok, err = xpcall(initFunc, debug.traceback, kernel)
if not ok then
kernel.panic("Init system crashed: " .. tostring(err))
else
kernel.panic("Init system exited: " .. tostring(err))
end
end),
name = "sysinit",
status = "R",
pid = 1,
tgid = 1,
uid = 0,
fd = {},
envars = {},
args = {},
exit = "",
sleep = 0,
ivs = 0,
vs = 0,
parent = kernel.kernelTask,
siblings = kernel.kernelTask.children,
children = {},
syscallReturn = {},
cwd = "/",
timeSlice = 0,
lastTime = 0,
totalTime = 0,
numRuns = 0
}
kernel.log("created init task with PID 1")
kernel.log("Initializing init system...")

View File

@@ -1,16 +0,0 @@
--:Minify:--
local kernel = ...
-- It runs at uid 0 so it can call setuid() to drop privileges to the logged in user
kernel.processes.login = function()
local handle = kernel.vfs.open("/bin/login", "r")
local text = kernel.vfs.read(handle, 1024 * 1024)
kernel.vfs.close(handle)
local fn, err = load(text, "@/bin/login", "t", kernel._U)
if not fn then
kernel.log("Failed to load /bin/login: " .. tostring(err), "ERROR", 2)
return
end
fn()
end

View File

@@ -1,165 +0,0 @@
--:Minify:--
local kernel = ...
local bit32 = require("bit32")
local bor = bit32.bor
local lshift = bit32.lshift
-- bit 0 = everyone-write, bit 1 = everyone-read
-- bit 2 = group-write, bit 3 = group-read
-- bit 4 = owner-write, bit 5 = owner-read
-- bit 6 = suid
local P_OWNER_R = lshift(1, 5)
local P_OWNER_W = lshift(1, 4)
local P_GROUP_R = lshift(1, 3)
local P_GROUP_W = lshift(1, 2)
local P_WORLD_R = lshift(1, 1)
local P_WORLD_W = lshift(1, 0)
local P_SUID = lshift(1, 6)
local RW_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 644 / rw-r--r--
local RWX_R_R = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 755 / rwxr--r--
local RW_R__ = bor(P_OWNER_R, P_OWNER_W, P_GROUP_R) -- 640 / rw-r-----
local RW____ = bor(P_OWNER_R, P_OWNER_W) -- 600 / rw-------
local SUID_755 = bor(P_SUID, P_OWNER_R, P_OWNER_W, P_GROUP_R, P_WORLD_R) -- 4755
local function metaEntry(name, owner, group, perms)
return string.char(#name) .. name
.. string.char(owner, group, perms)
.. string.char(0)
end
local rootDisk = kernel.disks["$"]
local function writeMeta(dir, entries)
local diskDir = dir == "/" and "/" or dir
local path = (diskDir:sub(-1) == "/" and diskDir or diskDir .. "/") .. ".meta"
if path:sub(1,1) == "/" then path = path:sub(2) end
if path == "" then path = ".meta" end
local data = ""
for _, e in ipairs(entries) do
data = data .. metaEntry(e[1], e[2], e[3], e[4])
end
local ok, err = pcall(function()
local f = rootDisk:open(path, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write /" .. path .. ": " .. tostring(err), "WARN", 8)
end
end
if rootDisk:fileExists(".meta") then
kernel.log("Permissions already seeded, skipping.", "INFO")
else
kernel.log("Seeding filesystem permissions...", "INFO")
writeMeta("/", {
{"bin", 0, 0, RWX_R_R},
{"boot", 0, 0, RWX_R_R},
{"dev", 0, 0, RWX_R_R},
{"etc", 0, 0, RWX_R_R},
{"home", 0, 0, RWX_R_R},
{"lib", 0, 0, RWX_R_R},
{"root", 0, 0, RW____ },
{"sbin", 0, 0, RWX_R_R},
{"tmp", 0, 0, bor(P_OWNER_R, P_OWNER_W, P_GROUP_R, P_GROUP_W, P_WORLD_R, P_WORLD_W)},
{"usr", 0, 0, RWX_R_R},
{"var", 0, 0, RWX_R_R},
})
writeMeta("/bin", {
{"cat", 0, 0, RWX_R_R},
{"clear", 0, 0, RWX_R_R},
{"echo", 0, 0, RWX_R_R},
{"hfetch", 0, 0, RWX_R_R},
{"hysh", 0, 0, RWX_R_R},
{"hyshex", 0, 0, RWX_R_R},
{"install", 0, 0, RWX_R_R},
{"login", 0, 0, SUID_755},
{"ls", 0, 0, RWX_R_R},
{"lua", 0, 0, RWX_R_R},
{"luaold", 0, 0, RWX_R_R},
{"mkdir", 0, 0, RWX_R_R},
{"ps", 0, 0, RWX_R_R},
{"pwd", 0, 0, RWX_R_R},
{"spm", 0, 0, RWX_R_R},
{"su", 0, 0, SUID_755},
{"sudo", 0, 0, SUID_755},
{"sysdump", 0, 0, RWX_R_R},
{"whoami", 0, 0, RWX_R_R},
{"yes", 0, 0, RWX_R_R},
{"startup", 0, 0, RWX_R_R},
})
writeMeta("/bin/startup", {
{"test.lua", 0, 0, RWX_R_R},
})
writeMeta("/etc", {
{"passwd", 0, 0, RW_R_R},
{"shadow", 0, 0, RW____ },
{"pam.d", 0, 0, RWX_R_R},
})
writeMeta("/etc/pam.d", {
{"secret", 0, 0, RW____},
})
writeMeta("/sbin", {
{"init.lua", 0, 0, RWX_R_R},
})
writeMeta("/boot", {
{"kernel.lua", 0, 0, RW_R_R},
{"boot.cfg", 0, 0, RW_R_R},
{"safeboot.cfg", 0, 0, RW_R_R},
{"fstab", 0, 0, RW_R_R},
{"initfs", 0, 0, RW_R_R},
{"cct", 0, 0, RWX_R_R},
{"oc", 0, 0, RWX_R_R},
})
writeMeta("/lib", {
{"sys", 0, 0, RWX_R_R},
{"modules", 0, 0, RWX_R_R},
{"crypto", 0, 0, RWX_R_R},
{"store", 0, 0, RWX_R_R},
{"snip", 0, 0, RW_R_R},
{"io", 0, 0, RW_R_R},
{"bit32", 0, 0, RW_R_R},
})
kernel.log("Filesystem permissions seeded.", "INFO")
end
-- TODO: move this to vfs.kmod
local _orig_open = kernel.vfs.open
kernel.vfs.open = function(path, mode)
local fd = _orig_open(path, mode)
if mode == "r" then
local task = kernel.currentTask
local fobj = task.fd[fd]
if fobj and fobj.meta then
local suid_set = bit32.extract(fobj.meta.perms, 6) == 1
if suid_set then
fobj.suid_owner = fobj.meta.owner
end
end
end
return fd
end
kernel.syscalls["fget_suid"] = function(fd)
local task = kernel.currentTask
local fobj = task and task.fd[fd]
if fobj and fobj.suid_owner then
return fobj.suid_owner
end
return nil
end
kernel.log("Permission module loaded.", "INFO")

View File

@@ -1,4 +1,8 @@
-- :Minify:-- --:Minify:--
--- @diagnostic disable: duplicate-set-field
local kernel = ...
kernel.allowGlobalOverwrites = true
function string.hasSuffix(str, suffix) function string.hasSuffix(str, suffix)
return string.sub(str, #suffix + 1) == suffix return string.sub(str, #suffix + 1) == suffix
end end
@@ -68,30 +72,24 @@ end
local function serialize(tbl, seen) local function serialize(tbl, seen)
seen = seen or {} seen = seen or {}
-- If we've seen this table before, return a placeholder to prevent infinite loops
if seen[tbl] then return '"[Circular Reference]"' end if seen[tbl] then return '"[Circular Reference]"' end
-- Mark this table as seen
seen[tbl] = true seen[tbl] = true
local output = "{" local output = "{"
local first = true local first = true
for i, v in pairs(tbl) do for i, v in pairs(tbl) do
-- Handle comma placement more cleanly
if not first then output = output .. "," end if not first then output = output .. "," end
first = false first = false
-- Serialize Key
if type(i) == "string" then if type(i) == "string" then
output = output .. "[\"" .. i .. "\"]=" output = output .. "[\"" .. i .. "\"]="
elseif type(i) == "number" then elseif type(i) == "number" then
output = output .. "[" .. tostring(i) .. "]=" output = output .. "[" .. tostring(i) .. "]="
end end
-- Serialize Value
if type(v) == "table" then if type(v) == "table" then
-- Pass the 'seen' table down to the recursive call
output = output .. serialize(v, seen) output = output .. serialize(v, seen)
elseif type(v) == "string" then elseif type(v) == "string" then
output = output .. "[=[" .. v .. "]=]" output = output .. "[=[" .. v .. "]=]"
@@ -175,6 +173,27 @@ function table.indexOf(t, value)
return -1 return -1
end end
function table.merge(...)
local args={...}
local out = {}
local outi = {}
for _,t in ipairs(args) do
for i,v in pairs(t) do
out[i]=v
end
for i,v in ipairs(t) do
outi[#outi+1]=v
end
end
for i,v in ipairs(outi) do
out[i]=v
end
return out
end
function string.replace(s, target, repl) function string.replace(s, target, repl)
local result = {} local result = {}
local i = 1 local i = 1
@@ -210,8 +229,12 @@ function toHex(num)
return string.format("%X", num) return string.format("%X", num)
end end
syscall = setmetatable({}, { local function makeSyscallProxy()
local backing = {}
return setmetatable(backing, {
__index = function(self, name) __index = function(self, name)
local raw = rawget(self, name)
if raw ~= nil then return raw end
return function(...) return function(...)
local res = table.pack(coroutine.yield("syscall", name, ...)) local res = table.pack(coroutine.yield("syscall", name, ...))
if res[1] then if res[1] then
@@ -220,7 +243,16 @@ syscall = setmetatable({}, {
error(res[2], 2) error(res[2], 2)
end end
end end
end end,
}) __newindex = function(self, k, v)
rawset(self, k, v)
end,
__metatable=false
})
end
syscall = makeSyscallProxy()
_makeSyscallProxy = makeSyscallProxy
table.serialize = serialize table.serialize = serialize

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local cache = {} local cache = {}
kernel.searchpaths = { kernel.searchpaths = {

View File

@@ -16,7 +16,7 @@ proxy.getLabel = function() return "devfs" end
proxy.attributes = function(path) return { proxy.attributes = function(path) return {
size = 0, size = 0,
modified = 0, modified = 0,
created = 0, created = 0
} end } end
function proxy:open(path, mode) function proxy:open(path, mode)

View File

@@ -0,0 +1,265 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "procfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("EACCES") end
proxy.remove = function() error("EACCES") end
proxy.setLabel = function() error("EACCES") end
proxy.getLabel = function() return "procfs" end
proxy.attributes = function(path) return {
size = 0,
modified = 0,
created = 0
} end
local function buildMeta(entries, opts)
opts = opts or {}
local uid = opts.uid or 0
local gid = opts.gid or 0
local perms = opts.perms or 0x3F -- default read/write for owner/group/world
local chunks = {}
table.insert(chunks, string.char(0x02)) -- version header
for path, target in pairs(entries) do
local name = path
local nameLen = #name
if nameLen > 255 then
error("Filename too long (>255 bytes): "..name)
end
-- Determine entry type: 0x01 = symlink if target ~= nil and target ~= ""
local entryType = 0x00
local cmeta = ""
if target and target ~= "" then
entryType = 0x01
cmeta = target
end
local cmetaLen = #cmeta
if cmetaLen > 255 then
error("cmeta too long (>255 bytes) for "..name)
end
-- Build entry as bytes
table.insert(chunks, string.char(nameLen)) -- name length
table.insert(chunks, name) -- name
table.insert(chunks, string.char(entryType)) -- entry type
table.insert(chunks, string.char(uid % 256, math.floor(uid/256) % 256)) -- uid
table.insert(chunks, string.char(gid % 256, math.floor(gid/256) % 256)) -- gid
table.insert(chunks, string.char(perms % 256, math.floor(perms/256) % 256)) -- perms
table.insert(chunks, string.char(cmetaLen)) -- cmeta length
if cmetaLen > 0 then
table.insert(chunks, cmeta)
end
end
return table.concat(chunks)
end
local function simpleFile(r,w)
return function(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=r
}
elseif mode=="w" then
return {
write=w
}
end
end
end
end
local function strFile(str)
local dat=tostring(str)
local pos=1
return simpleFile(function(amount)
pos=pos+amount
return dat:sub(pos-amount, pos)
end,function() error("EACCES") end)
end
local function newtaskproxy(task)
local files,siblings,children={},{},{}
if task.fd[0] then files["0"]=task.fd[0].path end
for i,v in ipairs(task.fd) do
files[tostring(i)]=tostring(v.path)
end
for i,v in ipairs(task.siblings) do
siblings[tostring(v.pid)]="/proc/"..tostring(v.pid)
end
for i,v in ipairs(task.children) do
children[tostring(v.pid)]="/proc/"..tostring(v.pid)
end
return {
[".meta"]=strFile(buildMeta({cwd=task.cwd,parent="/proc/"..tostring(task.parent.pid)})),
uid=strFile(task.uid),
comm=strFile(task.name),
fd={
[".meta"]=strFile(buildMeta(files))
},
siblings={
[".meta"]=strFile(buildMeta(siblings))
},
children={
[".meta"]=strFile(buildMeta(children))
}
}
end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
if tonumber(steps[1]) then
local task=kernel.tasks[tostring(steps[1])]
local step = newtaskproxy(task)
for i=2, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
elseif tostring(steps[1])=="self" then
local task=kernel.currentTask
local step = newtaskproxy(task)
for i=2, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
else
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
end
error("ENFILE")
end
function proxy:type(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return "directory"
end
if tonumber(steps[1]) then
local task=kernel.tasks[steps[1]]
if #steps==1 then return "directory" end
local step = newtaskproxy(task)
for i=2, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
elseif tostring(steps[1])=="self" then
local task=kernel.currentTask
if #steps==1 then return "directory" end
local step = newtaskproxy(task)
for i=2, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
else
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
end
error("ENOENT")
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return table.merge(table.keys(data),table.keys(kernel.tasks),{"self"})
end
if tonumber(steps[1]) then
local task=kernel.tasks[steps[1]]
local step = newtaskproxy(task)
if #steps==1 then return table.keys(step) end
for i=2, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
elseif tostring(steps[1])=="self" then
local task=kernel.currentTask
local step = newtaskproxy(task)
if #steps==1 then return table.keys(step) end
for i=2, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
else
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
end
error("ENOENT")
end
function proxy:fileExists(path)
local ok = pcall(function()
return self:type(path)
end)
return ok
end
data.uptime=simpleFile(function()return tostring(kernel.computer:getUptime())end,function()error("EACCES")end)
kernel.procfs={}
kernel.procfs.data=data
kernel.procfs.proxy=proxy
kernel.disks["procfs0000"]=proxy

View File

@@ -1,3 +1,4 @@
--:Minify:--
local kernel = ... local kernel = ...
local proxy = {} local proxy = {}
@@ -7,11 +8,9 @@ proxy.address = "tmpfs0000"
proxy.isvirt = true proxy.isvirt = true
proxy.isReadOnly = function() return false end proxy.isReadOnly = function() return false end
-- Space functions (just placeholders)
proxy.spaceUsed = function() return 0 end proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end proxy.spaceTotal = function() return 0 end
-- Writable operations
proxy.makeDirectory = function(_, path) proxy.makeDirectory = function(_, path)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
@@ -52,7 +51,6 @@ proxy.attributes = function(_, path)
} }
end end
-- Open files
function proxy:open(path, mode) function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
@@ -76,21 +74,27 @@ function proxy:open(path, mode)
local chunk = content:sub(pos, pos+amount-1) local chunk = content:sub(pos, pos+amount-1)
pos = pos + #chunk pos = pos + #chunk
return chunk return chunk
end end,
close = function() end,
} }
elseif mode == "w" then elseif mode == "w" then
step[filename] = "" step[filename] = ""
local buf = {}
return { return {
write = function(str) write = function(str)
step[filename] = str buf[#buf + 1] = str
end end,
close = function()
step[filename] = table.concat(buf)
end,
} }
elseif mode == "a" then elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end if type(step[filename]) ~= "string" then step[filename] = "" end
return { return {
write = function(str) write = function(str)
step[filename] = step[filename] .. str step[filename] = step[filename] .. str
end end,
close = function() end,
} }
else else
error("EACCES") error("EACCES")
@@ -123,7 +127,8 @@ function proxy:list(path)
end end
function proxy:fileExists(path) function proxy:fileExists(path)
return pcall(function() return self:type(path) end) local t = self:type(path)
return t == "file" or t == "directory"
end end
kernel.disks["tmpfs0000"] = proxy kernel.disks["tmpfs0000"] = proxy

Some files were not shown because too many files have changed in this diff Show More