36 Commits

Author SHA1 Message Date
f7b64c11b7 added seperate input and working on http / sockets 2026-05-27 17:01:26 -04:00
59e09a5995 Update LICENSE 2026-03-26 10:34:00 -04:00
ab0a10105c formating better for spm packages 2026-03-26 08:12:37 -04:00
22b5021e9d fixed boot crash 2026-03-21 01:10:47 -04:00
462c74686a fixed the installer 2026-03-21 01:04:27 -04:00
0072547beb updated package list 2026-03-21 00:59:46 -04:00
7deefc83ca made newer installer that does not require tar stuff 2026-03-21 00:54:39 -04:00
df62414229 made system use full salt+pepper 2026-03-19 14:10:17 -04:00
055dd4e606 fixed again 2026-03-19 12:10:31 -04:00
5755dd9cbe fixed 2026-03-19 11:32:48 -04:00
8e11faf9ec updated again 2026-03-19 11:23:24 -04:00
0ea42f9454 Updated installer 2026-03-19 11:21:08 -04:00
d0f26a937f made installer 2026-03-19 11:16:05 -04:00
9b338328f0 made unified colors and stuff 2026-03-19 08:33:47 -04:00
4f9eebade2 update tar 2026-03-12 20:54:27 -04:00
d08935b68a remove gotos 2026-03-12 19:31:37 -04:00
45b46cf3c4 Merge pull request 'main' (#10) from main into 1.2-dev
Reviewed-on: #10
2026-03-12 19:27:44 -04:00
aeea68bc9b Update install/data/tarbad 2026-03-12 12:12:48 -04:00
f983b13d56 Update install/installcc.lua
make it use right stuff
2026-03-12 12:10:37 -04:00
03c5b106c4 Update Src/Hyperion-kernel/boot/kernel.lua 2026-03-12 11:59:35 -04:00
08323e00ff Update install/data/tarbad 2026-03-12 11:56:58 -04:00
5e3cdbe40c Update install/installcc.lua 2026-03-12 11:51:55 -04:00
8762b8f022 Merge pull request '1.2-dev' (#9) from 1.2-dev into main
Reviewed-on: #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: #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: #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: #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
135 changed files with 12807 additions and 2902 deletions

212
LICENSE
View File

@@ -1,18 +1,202 @@
MIT License
Copyright (c) 2025 Astronand Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial 1. Definitions.
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT "License" shall mean the terms and conditions for use, reproduction,
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO and distribution as defined by Sections 1 through 9 of this document.
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE "Licensor" shall mean the copyright owner or entity authorized by
USE OR OTHER DEALINGS IN THE SOFTWARE. the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,18 @@
--:Minify:--
return {
white=0xFFFFFF,
red=0xFF0000,
green=0x00FF00,
blue=0x0000FF,
cyan=0x00FFFF,
yellow=0xFFFF00,
purple=0xFF00FF,
black=0x000000,
gray=0x888888,
lightgrey=0xBBBBBB,
darkgrey=0x444444,
orange=0xFF8800,
mint=0x00FF88,
brown=0xa52a2a,
chocolate=0xd2691e
}

View File

@@ -0,0 +1,201 @@
--:Minify:--
local http = {}
local syscall = syscall
local function parseUrl(url)
local proto, host, path =
url:match(
"^(https?://)([^/]+)(/.*)$"
)
if not proto then
proto, host =
url:match(
"^(https?://)([^/]+)$"
)
path = "/"
end
if not proto then
return nil, "EINVAL"
end
return host, path
end
local function buildRequest(req)
local host, path =
parseUrl(req.url)
if not host then
return nil, path
end
local method =
req.method or "GET"
local body =
req.body or ""
local headers =
table.deepcopy(
req.headers or {}
)
headers.Host =
headers.Host or host
headers.Connection =
headers.Connection or "close"
if body ~= "" then
headers["Content-Length"] =
tostring(#body)
end
local out = {
method ..
" " ..
path ..
" HTTP/1.1"
}
for k, v in pairs(headers) do
out[#out + 1] =
tostring(k) ..
": " ..
tostring(v)
end
out[#out + 1] = ""
out[#out + 1] = body
return table.concat(out, "\r\n")
end
local function parseResponse(raw)
local headerEnd =
raw:find(
"\r\n\r\n",
1,
true
)
if not headerEnd then
return nil, "EBADMSG"
end
local headerPart =
raw:sub(1, headerEnd - 1)
local body =
raw:sub(headerEnd + 4)
local lines = {}
for line in headerPart:gmatch("[^\r\n]+") do
lines[#lines + 1] = line
end
local _, code, msg =
lines[1]:match(
"^(HTTP/%S+)%s+(%d+)%s*(.*)$"
)
local headers = {}
for i = 2, #lines do
local k, v =
lines[i]:match(
"^([^:]+):%s*(.*)$"
)
if k then
headers[k:lower()] = v
end
end
return {
code = tonumber(code),
message = msg,
headers = headers,
body = body
}
end
local function readAll(fd)
local out = ""
while true do
local chunk =
syscall.read(fd, 4096)
if not chunk or chunk == "" then
break
end
out = out .. chunk
end
return out
end
function http.request(req)
local raw, err =
buildRequest(req)
if not raw then
return nil, err
end
local fd =
syscall.socket(0, 0)
if not fd then
return nil, "ESOCKET"
end
local ok, err =
syscall.connect(fd, req.url)
if not ok then
syscall.close(fd)
return nil, err
end
local ok2, err2 =
syscall.write(fd, raw)
if not ok2 then
syscall.close(fd)
return nil, err2
end
local resp =
readAll(fd)
syscall.close(fd)
return parseResponse(resp)
end
function http.get(url, headers)
return http.request({
url = url,
method = "GET",
headers = headers
})
end
function http.post(url, body, headers)
return http.request({
url = url,
method = "POST",
body = body,
headers = headers
})
end
return http

View File

@@ -1,54 +1,39 @@
--:Minify:-- --:Minify:--
local BOOT_DRIVE_PATH = ({...})[1] or "/$" local args={...}
---@diagnostic disable-next-line: undefined-global local bootdrive=args[1]
local term = term local gpu=components:getFirst("gpu")
local os = os local screenTextBuffer=nil
local cursorX,cursorY=0,0
local screenSizeX,screenSizeY=128,25
if gpu then
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
for t,v in components:list() do
if t == "screen" then
gpu:assignBuffer(screenTextBuffer, v)
end
end
end
local function write(text) local function write(text)
local x, y = term.getCursorPos() cursorX, cursorY = screenTextBuffer:pasteText(cursorX, cursorY, "SCROLL_SPILL_CLEAR", text)
local w, h = term.getSize() cursorY = cursorY + 1
cursorX = 0
for i = 1, #text do if cursorY >= screenSizeY then
local c = text:sub(i, i) cursorY = screenSizeY-1
screenTextBuffer:newline()
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
if x > w then
x = 1
y = y + 1
end
if y - 1 >= h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end end
local function displaySuperBadError(err) local function displaySuperBadError(err)
gpu:freeAllBuffers()
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
for t,v in components:list() do
if t == "screen" then
gpu:assignBuffer(screenTextBuffer, v)
end
end
term.setBackgroundColor(0x1) term.setBackgroundColor(0x1)
term.setTextColor(0x4) term.setTextColor(0x4)
term.clear() term.clear()
@@ -59,9 +44,8 @@ local function displaySuperBadError(err)
while true do end while true do end
end end
term.setCursorBlink(false)
local ok, err = xpcall(function() local ok, err = xpcall(function()
local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH} local apis = {}
local lua = { local lua = {
coroutine = true, coroutine = true,
@@ -102,79 +86,125 @@ local ok, err = xpcall(function()
end end
end end
local acekeys={ local function string_file(file)
[apis.keys.enter]="\n", local str = file:read()
[apis.keys.tab]="\t", local buf = {str or ""}
[apis.keys.backspace]="\b", local pos = 1
[apis.keys.up]="\17", local closed = false
[apis.keys.down]="\18",
[apis.keys.left]="\19",
[apis.keys.right]="\20",
}
function sleep(time) local function content()
local stoptime = apis.os.clock() + (time) return table.concat(buf)
while stoptime > apis.os.clock() do end
end end
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000 local function set_content(s)
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF buf = {s}
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000 end
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF local function flush()
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF file.write(content())
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF end
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00 local file = {}
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF function file.read(n)
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900 assert(not closed, "file is closed")
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA local s = content()
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF local len = #s
apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00
if not n then
local out = s:sub(pos)
pos = len + 1
return out
end
local out = s:sub(pos, pos + n - 1)
pos = pos + #out
return out
end
function file.write(data)
assert(not closed, "file is closed")
local s = content()
local before = s:sub(1, pos - 1)
local after = s:sub(pos + #data)
set_content(before .. data .. after)
pos = pos + #data
return true
end
function file.seek(whence, offset)
assert(not closed, "file is closed")
local s = content()
local len = #s
whence = whence or "cur"
offset = offset or 0
if whence == "set" then
pos = offset + 1
elseif whence == "cur" then
pos = pos + offset
elseif whence == "end" then
pos = len + offset + 1
else
error("invalid whence")
end
if pos < 1 then pos = 1 end
if pos > len + 1 then pos = len + 1 end
return pos - 1
end
function file:close()
assert(not closed, "file is closed")
flush()
closed = true
end
function file:flush()
assert(not closed, "file is closed")
flush()
end
return file
end
local function getFile(path) local function getFile(path)
local file = apis.fs.open(path, "r") local file = bootdrive:open(path, "r")
if not file then if not file then
displaySuperBadError("Could not open file: " .. path) displaySuperBadError("Could not open file: " .. path)
end end
local content = file.readAll() local content = file:read()
file.close()
return content return content
end end
local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel") local Kernel = load(getFile("/boot/kernel.lua"),"@Kernel")
local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis) local initFs = load(getFile("/boot/ac/initdisks"),"@Init_disks")(apis)
local fs = load(getFile(BOOT_DRIVE_PATH .. "/boot/initfs"), "@InitFs")() local fs = load(getFile("/boot/initfs"), "@InitFs")()
if not Kernel then displaySuperBadError("Could not load kernel.") end if not Kernel then displaySuperBadError("Could not load kernel.") end
if not initFs then displaySuperBadError("Could not load initdisks.") end if not initFs then displaySuperBadError("Could not load initdisks.") end
if not fs then displaySuperBadError("Could not load initfs.") end if not fs then displaySuperBadError("Could not load initfs.") end
local eventQueue = {} local computercomp=apis.components:getFirst("computer")
local uefi=apis.components:getFirst("uefi")
local function queueEvent(event, ...)
table.insert(eventQueue, {event, ...})
end
local computer = { local computer = {
time = function() return apis.os.epoch("utc") end, time = function() return apis.os.epoch("utc") end,
clock = function() return apis.os.clock() * 1000 end, clock = function() return apis.os.clock() * 1000 end,
shutdown = apis.os.shutdown, shutdown = apis.os.shutdown,
reboot = apis.os.reboot, reboot = apis.os.reboot,
getMachineEvent = function() getMachineEvent = function()
if #eventQueue > 0 then return computercomp:getMachineEvent()
return table.unpack(table.remove(eventQueue, 1))
else
return nil
end
end, end,
getEEPROM = function() return getFile("/startup.lua") end, getEEPROM = function() return uefi.data end,
setEEPROM = function(_, text) setEEPROM = function(_, text)
local h = apis.fs.open("/startup.lua", "w") uefi.data=text
h.write(text)
h.close()
end end
} }

View File

@@ -1,155 +0,0 @@
-- :Minify:--
local apis = ({...})[1]
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getType(name)
if native.isPresent(name) then return native.getType(name) end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and
native.call(side, "isPresentRemote", name) then
return native.call(side, "getTypeRemote", name)
end
end
return nil
end
function peripheral.getNames()
local names = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then table.insert(names, side) end
if native.hasType(side, "peripheral_hub") then
local hubSides = native.call(side, "getConnectedSides")
for _, hubSide in ipairs(hubSides) do
table.insert(names, hubSide)
end
end
end
return names
end
local disks = {}
local internal = {}
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address = id, isReadOnly = function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal() return fs.getCapacity(basePath) end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then
return nil, "not directory"
end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:type(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then
return nil
elseif fs.isDir(p) then
return "directory"
else
return "file"
end
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label) periph.setLabel(label) end
function disk:getLabel(label) return periph.getLabel() end
function disk:attributes(path)
local p = fs.combine(basePath, path)
return fs.attributes(p)
end
function disk:open(path, mode)
local p = fs.combine(basePath, path)
return fs.open(p, mode)
end
return disk
end
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
setLabel = function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel = function()
local h = fs.open("/.label", "r")
if not h then return "$" end
local label = h.readAll()
h.close()
return label
end
})
local function refresh()
for id, _ in pairs(disks) do
if not peripheral.getType(id) then disks[id] = nil end
end
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "disk" then
if not disks[name] then
local mount = disk.getMountPath(name)
if mount then
disks[name] = createDisk(name, mount, false, disk)
end
end
end
end
end
local function iter()
refresh()
local combined = {}
for id, obj in pairs(internal) do combined[id] = obj end
for id, obj in pairs(disks) do combined[id] = obj end
return pairs(combined)
end
return {refresh = refresh, list = iter}

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,
@@ -154,17 +152,106 @@ local ok, err = xpcall(function()
if not initFs then displaySuperBadError("Could not load initdisks.") end if not initFs then displaySuperBadError("Could not load initdisks.") end
if not fs then displaySuperBadError("Could not load initfs.") end if not fs then displaySuperBadError("Could not load initfs.") end
if not apis.fs.exists("/nvram.dat") then
local file = apis.fs.open("/nvram.dat", "w")
file.write("Hello, World!")
file.close()
end
local eeprom
if apis.fs.exists("/startup.lua") then
eeprom="/startup.lua"
elseif apis.fs.exists("/eeprom") then
eeprom="/eeprom"
end
local eventQueue = {} local eventQueue = {}
local function queueEvent(event, ...) local function queueEvent(event, ...)
table.insert(eventQueue, {event, ...}) table.insert(eventQueue, {event, ...})
end end
local computer = { local colors = {
time = function() return apis.os.epoch("utc") end, [0x000000]=0x0001,
clock = function() return apis.os.clock() * 1000 end, [0xFFFFFF]=0x0002,
shutdown = apis.os.shutdown, [0xFF0000]=0x0004,
reboot = apis.os.reboot, [0x00FF00]=0x0008,
[0x0000FF]=0x0010,
[0x00FFFF]=0x0020,
[0xFF00FF]=0x0040,
[0xFFFF00]=0x0080,
[0xFF6D00]=0x0100,
[0x6DFF55]=0x0200,
[0x24FFFF]=0x0400,
[0x924900]=0x0800,
[0x6D6D55]=0x1000,
[0xDBDBAA]=0x2000,
[0x6D00FF]=0x4000,
[0xB6FF00]=0x8000
}
local fg,bg=0x6D6D55,0x000000
local l1f,l1d,l2,ops={},{},{},0
local function findClosest(tbl, target)
local closest = nil
local smallestDiff = math.huge
for k, _ in pairs(tbl) do
local diff = math.abs(k - target)
if diff < smallestDiff then
smallestDiff = diff
closest = k
end
end
return closest
end
local function aprox(c24)
ops = ops + 1
if ops % 1024 == 0 then
l1d = {}
l1f = {}
end
if ops % 8192 == 0 then
l2 = {}
end
if l2[c24] ~= nil then
return l2[c24]
end
if l1d[c24] ~= nil then
l1f[c24] = l1f[c24] + 1
if l1f[c24] >= 16 then
l2[c24] = l1d[c24]
l1d[c24] = nil
l1f[c24] = nil
return l2[c24]
end
return l1d[c24]
end
local closestKey = findClosest(colors, c24)
if not closestKey then return nil end
local value = colors[closestKey]
l1d[c24] = value
l1f[c24] = 1
return value
end
local EFI = {
getEpochMs = function() return apis.os.epoch("utc") end,
getUptime = function() return apis.os.clock() * 1000 end,
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
getMachineEvent = function() getMachineEvent = function()
if #eventQueue > 0 then if #eventQueue > 0 then
return table.unpack(table.remove(eventQueue, 1)) return table.unpack(table.remove(eventQueue, 1))
@@ -172,61 +259,15 @@ local ok, err = xpcall(function()
return nil return nil
end end
end, end,
getEEPROM = function() return getFile("/startup.lua") end, getEEPROM = function() return getFile(eeprom) end,
setEEPROM = function(_, text) setEEPROM = function(_, text)
local h = apis.fs.open("/startup.lua", "w") local h = apis.fs.open(eeprom, "w")
h.write(text) h.write(text)
h.close() h.close()
end end,
} initfs=fs,
disks=initFs,
local icolors = { screenCtl={
[0x1] = 1, -- #000000
[0x2] = 2, -- #FFFFFF
[0x4] = 3, -- #FF0000
[0x8] = 4, -- #00FF00
[0x10] = 5, -- #0000FF
[0x20] = 6, -- #00FFFF
[0x40] = 7, -- #FF00FF
[0x80] = 8, -- #FFFF00
[0x100] = 9, -- #FF6D00
[0x200] = 10, -- #6DFF55
[0x400] = 11, -- #24FFFF
[0x800] = 12, -- #924900
[0x1000] = 13, -- #6D6D55
[0x2000] = 14, -- #DBDBAA
[0x4000] = 15, -- #6D00FF
[0x8000] = 16 -- #B6FF00
}
local colors = {
0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
apis.term.setBackgroundColor(0x8000)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
local kernelCoro = coroutine.create(function()
---@diagnostic disable-next-line: param-type-mismatch
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
{
print = function(_, text) write(text .. "\n") end, print = function(_, text) write(text .. "\n") end,
printInline = function(_, text) write(text) end, printInline = function(_, text) write(text) end,
clear = function() clear = function()
@@ -239,25 +280,51 @@ local ok, err = xpcall(function()
getCursorPos = function() return apis.term.getCursorPos() end, getCursorPos = function() return apis.term.getCursorPos() end,
getSize = function() return apis.term.getSize() end, getSize = function() return apis.term.getSize() end,
setBackgroundColor = function(_, color) setBackgroundColor = function(_, color)
apis.term.setBackgroundColor(colors[color]) apis.term.setBackgroundColor(aprox(color))
end, end,
setTextColor = function(_, color) setTextColor = function(_, color)
apis.term.setTextColor(colors[color]) apis.term.setTextColor(aprox(color))
end, end,
getBackgroundColor = function() getBackgroundColor = function()
return icolors[apis.term.getBackgroundColor()] return bg
end, end,
getTextColor = function() getTextColor = function()
return icolors[apis.term.getTextColor()] return fg
end,
enable=function() end,
disable=function() end
},
architecture="cct",
getNvram = function() return getFile("/nvram.dat") end,
setNvram = function(_, text)
local h = apis.fs.open("/nvram.dat", "w")
h.write(text)
h.close()
end,
firmware=apis,
reboot=false
}
apis.term.setBackgroundColor(0x8000)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
local kernelCoro = coroutine.create(function()
---@diagnostic disable-next-line: param-type-mismatch
local ok, err = xpcall(Kernel, debug.traceback, EFI)
if not ok and not EFI.reboot then displaySuperBadError(err) end
if err then
apis.os.reboot()
else
apis.os.shutdown()
end end
}, computer, fs, "$")
if not ok then displaySuperBadError(err) end
end) end)
function coroutine.resumeWithTimeout(co, timeout, ...) function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time() local startTime = EFI.getEpochMs()
debug.sethook(co, function() debug.sethook(co, function()
if computer.time() > startTime + timeout then if EFI.getEpochMs() > startTime + timeout then
return coroutine.yield("timeout") return coroutine.yield("timeout")
end end
end, "", 1000) end, "", 1000)
@@ -306,11 +373,15 @@ local ok, err = xpcall(function()
end end
end end
if status == "error" or coroutine.status(kernelCoro) == "dead" then if status == "error" or coroutine.status(kernelCoro) == "dead" then
if EFI.reboot then
apis.os.reboot()
end
displaySuperBadError("Kernel error: " .. tostring(err)) displaySuperBadError("Kernel error: " .. tostring(err))
coroutine.yield("key") coroutine.yield("key")
end end
initFs:refresh()
end end
end, debug.traceback) end, debug.traceback)
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
while true do coroutine.yield() end while true do coroutine.yield("key") end

View File

@@ -0,0 +1,272 @@
--:Minify:--
local apis = ({...})[1]
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
local disks = {}
local internal = {}
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address = id, isReadOnly = function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal() return fs.getCapacity(basePath) end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then
return nil, "not directory"
end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:type(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then
return nil
elseif fs.isDir(p) then
return "directory"
else
return "file"
end
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label) periph.setLabel(label) end
function disk:getLabel(label) return periph.getLabel() end
function disk:attributes(path)
local p = fs.combine(basePath, path)
return fs.attributes(p)
end
function disk:open(path, mode)
local p = fs.combine(basePath, path)
return fs.open(p, mode)
end
return disk
end
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
setLabel = function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel = function()
local h = fs.open("/.label", "r")
if not h then return "$" end
local label = h.readAll()
h.close()
return label
end
})
internal["rom"] = createDisk("rom", "/rom", true, {
setLabel = function(label)
error("Device is read-only")
end,
getLabel = function()
return "cctrom"
end
})
local function refresh()
disks={}
for _, disk in ipairs({peripheral.find("drive")}) do
if disk.isDiskPresent() then
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
end
end
end
local function iter()
refresh()
local combined = {}
for id, obj in pairs(internal) do combined[id] = obj end
for id, obj in pairs(disks) do combined[id] = obj end
return pairs(combined)
end
return {refresh = refresh, list = iter}

View File

@@ -311,7 +311,7 @@ local fifo = kernel.newFifo()
kernel.processes.cctmond = function() kernel.processes.cctmond = function()
local timeout = false local timeout = false
while true do while true do
local event = {kernel.computer:getMachineEvent()} local event = {kernel.EFI:getMachineEvent()}
if event[1] then if event[1] then
local eventType = event[1] local eventType = event[1]

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,155 +0,0 @@
-- :Minify:--
local apis = ({...})[1]
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getType(name)
if native.isPresent(name) then return native.getType(name) end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and
native.call(side, "isPresentRemote", name) then
return native.call(side, "getTypeRemote", name)
end
end
return nil
end
function peripheral.getNames()
local names = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then table.insert(names, side) end
if native.hasType(side, "peripheral_hub") then
local hubSides = native.call(side, "getConnectedSides")
for _, hubSide in ipairs(hubSides) do
table.insert(names, hubSide)
end
end
end
return names
end
local disks = {}
local internal = {}
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address = id, isReadOnly = function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal() return fs.getCapacity(basePath) end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then
return nil, "not directory"
end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:type(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then
return nil
elseif fs.isDir(p) then
return "directory"
else
return "file"
end
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label) periph.setLabel(label) end
function disk:getLabel(label) return periph.getLabel() end
function disk:attributes(path)
local p = fs.combine(basePath, path)
return fs.attributes(p)
end
function disk:open(path, mode)
local p = fs.combine(basePath, path)
return fs.open(p, mode)
end
return disk
end
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
setLabel = function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel = function()
local h = fs.open("/.label", "r")
if not h then return "$" end
local label = h.readAll()
h.close()
return label
end
})
local function refresh()
for id, _ in pairs(disks) do
if not peripheral.getType(id) then disks[id] = nil end
end
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "disk" then
if not disks[name] then
local mount = disk.getMountPath(name)
if mount then
disks[name] = createDisk(name, mount, false, disk)
end
end
end
end
end
local function iter()
refresh()
local combined = {}
for id, obj in pairs(internal) do combined[id] = obj end
for id, obj in pairs(disks) do combined[id] = obj end
return pairs(combined)
end
return {refresh = refresh, list = iter}

View File

@@ -0,0 +1,560 @@
--:Minify:--
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
---@diagnostic disable-next-line: undefined-global
local lterm = term
local function write(text, term)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
if x > w then
x = 1
y = y + 1
end
if y - 1 >= h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
local function displaySuperBadError(err)
lterm.setBackgroundColor(0x1)
lterm.setTextColor(0x4)
lterm.clear()
lterm.setCursorPos(1, 1)
lterm.write("A critical error occurred while loading the system:")
lterm.setCursorPos(1, 3)
write(err, lterm)
while true do end
end
term.setCursorBlink(false)
local ok, err = xpcall(function()
local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH}
local lua = {
coroutine = true,
debug = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G = true
}
local debug = debug
for i, v in pairs(_G) do
if not lua[i] or lua[i] == nil then
apis[i] = v
_G[i] = nil
end
end
local acekeys={
[apis.keys.enter]="\n",
[apis.keys.tab]="\t",
[apis.keys.backspace]="\b",
[apis.keys.up]="\17",
[apis.keys.down]="\18",
[apis.keys.left]="\19",
[apis.keys.right]="\20",
}
function sleep(time)
local stoptime = apis.os.clock() + (time)
while stoptime > apis.os.clock() do end
end
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00
local function getFile(path)
local file = apis.fs.open(path, "r")
if not file then
displaySuperBadError("Could not open file: " .. path)
end
local content = file.readAll()
file.close()
return content
end
local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel")
local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis)
local fs = load(getFile(BOOT_DRIVE_PATH .. "/boot/initfs"), "@InitFs")()
if not Kernel then displaySuperBadError("Could not load kernel.") end
if not initFs then displaySuperBadError("Could not load initdisks.") end
if not fs then displaySuperBadError("Could not load initfs.") end
if not apis.fs.exists("/nvram.dat") then
local file = apis.fs.open("/nvram.dat", "w")
file.write("Hello, World!")
file.close()
end
local eeprom
if apis.fs.exists("/startup.lua") then
eeprom="/startup.lua"
elseif apis.fs.exists("/eeprom") then
eeprom="/eeprom"
end
local eventQueue = {}
local function queueEvent(event, ...)
table.insert(eventQueue, {event, ...})
end
local colors = {
[0xFFFFFF]=0x0001,
[0xFF0000]=0x0002,
[0x00FF00]=0x0004,
[0x0000FF]=0x0008,
[0x00FFFF]=0x0010,
[0xFF00FF]=0x0020,
[0xFFFF00]=0x0040,
[0xFF6D00]=0x0080,
[0x6DFF55]=0x0100,
[0x24FFFF]=0x0200,
[0x924900]=0x0400,
[0x6D6D55]=0x0800,
[0xDBDBAA]=0x1000,
[0x6D00FF]=0x2000,
[0xB6FF00]=0x4000,
[0x000000]=0x8000
}
local fg,bg=0x6D6D55,0x000000
local l1f,l1d,l2,ops={},{},{},0
local function findClosest(tbl, target)
local closest = nil
local smallestDiff = math.huge
for k, _ in pairs(tbl) do
if k==target then return k end
local diff = math.abs(k - target)
if diff < smallestDiff then
smallestDiff = diff
closest = k
end
end
return closest
end
local function aprox(c24)
ops = ops + 1
if ops % 1024 == 0 then
l1d = {}
l1f = {}
end
if ops % 8192 == 0 then
l2 = {}
end
if l2[c24] ~= nil then
return l2[c24]
end
if l1d[c24] ~= nil then
l1f[c24] = l1f[c24] + 1
if l1f[c24] >= 16 then
l2[c24] = l1d[c24]
l1d[c24] = nil
l1f[c24] = nil
return l2[c24]
end
return l1d[c24]
end
local closestKey = findClosest(colors, c24)
if not closestKey then return nil end
local value = colors[closestKey]
l1d[c24] = value
l1f[c24] = 1
return value
end
local peripheral={}
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
local allscreens = {peripheral.find("monitor")}
for i=1, #allscreens do
allscreens[i].setTextScale(.5)
allscreens[i].clear()
allscreens[i].setCursorPos(1,1)
end
allscreens[#allscreens+1] = apis.term
local EFI = {
getEpochMs = function() return apis.os.epoch("utc") end,
getUptime = function() return apis.os.clock() * 1000 end,
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
getMachineEvent = function()
if #eventQueue > 0 then
return table.unpack(table.remove(eventQueue, 1))
else
return nil
end
end,
getEEPROM = function() return getFile(eeprom) end,
setEEPROM = function(_, text)
local h = apis.fs.open(eeprom, "w")
h.write(text)
h.close()
end,
initfs=fs,
disks=initFs,
screenCtl={
print = function(_, text)
for i=1, #allscreens do
write(text.."\n", allscreens[i])
end
end,
printInline = function(_, text)
for i=1, #allscreens do
write(text, allscreens[i])
end
end,
clear = function()
for i=1, #allscreens do
allscreens[i].clear()
allscreens[i].setCursorPos(1, 1)
end
end,
resetCursor = function()
for i=1, #allscreens do
allscreens[i].setCursorPos(1, 1)
end
end,
setBackgroundColor = function(_, color)
bg=color
for i=1, #allscreens do
allscreens[i].setBackgroundColor(aprox(color))
end
end,
setTextColor = function(_, color)
fg=color
for i=1, #allscreens do
allscreens[i].setTextColor(aprox(color))
end
end,
getBackgroundColor = function()
return bg
end,
getTextColor = function()
return fg
end,
enable=function() end,
disable=function() end
},
architecture="cct",
getNvram = function() return getFile("/nvram.dat") end,
setNvram = function(_, text)
local h = apis.fs.open("/nvram.dat", "w")
h.write(text)
h.close()
end,
firmware=apis,
reboot=false,
beep=function() end
}
apis.term.setBackgroundColor(0x8000)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
local kernelCoro = coroutine.create(function()
---@diagnostic disable-next-line: param-type-mismatch
local ok, err = xpcall(Kernel, debug.traceback, EFI)
if not ok and not EFI.reboot then displaySuperBadError(err) end
if err then
apis.os.reboot()
else
apis.os.shutdown()
end
end)
function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = EFI.getEpochMs()
debug.sethook(co, function()
if EFI.getEpochMs() > startTime + timeout then
return coroutine.yield("timeout")
end
end, "", 1000)
local ret = {coroutine.resume(co, ...)}
if ret[1] and ret[2] == "timeout" then
return "timeout"
elseif ret[1] == false then
return "error", ret[2]
else
debug.sethook(co)
return "success", table.unpack(ret, 2)
end
end
EFI.screenCtl:print("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
while true do
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
apis.os.queueEvent("NoSleep")
local exit = false
while not exit do
local event = {coroutine.yield()}
if event[1] == "key" then
queueEvent("keyPressed", 1, event[2])
if acekeys[event[2]] then
queueEvent("keyTyped", 1, acekeys[event[2]])
end
elseif event[1] == "char" then
queueEvent("keyTyped", 1, event[2])
elseif event[1] == "key_up" then
queueEvent("keyReleased", 1, event[2])
elseif event[1] == "disk" then
queueEvent("componentAdded", "disk")
elseif event[1] == "disk_eject" then
queueEvent("componentRemoved", "disk")
elseif event[1] == "modem_message" then
queueEvent("modem_message", table.unpack(event, 2))
elseif event[1] == "rednet_message" then
queueEvent("rednet_message", table.unpack(event, 2))
elseif event[1] == "http_success" then
queueEvent("http_success", table.unpack(event, 2))
elseif event[1] == "http_failure" then
queueEvent("http_failure", table.unpack(event, 2))
elseif event[1] == "NoSleep" then
exit = true
end
end
if status == "error" or coroutine.status(kernelCoro) == "dead" then
if EFI.reboot then
apis.os.reboot()
end
displaySuperBadError("Kernel error: " .. tostring(err))
coroutine.yield("key")
end
initFs:refresh()
end
end, debug.traceback)
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
while true do coroutine.yield("key") end

View File

@@ -0,0 +1,272 @@
--:Minify:--
local apis = ({...})[1]
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
local disks = {}
local internal = {}
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address = id, isReadOnly = function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal() return fs.getCapacity(basePath) end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then
return nil, "not directory"
end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:type(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then
return nil
elseif fs.isDir(p) then
return "directory"
else
return "file"
end
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label) periph.setLabel(label) end
function disk:getLabel(label) return periph.getLabel() end
function disk:attributes(path)
local p = fs.combine(basePath, path)
return fs.attributes(p)
end
function disk:open(path, mode)
local p = fs.combine(basePath, path)
return fs.open(p, mode)
end
return disk
end
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
setLabel = function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel = function()
local h = fs.open("/.label", "r")
if not h then return "$" end
local label = h.readAll()
h.close()
return label
end
})
internal["rom"] = createDisk("rom", "/rom", true, {
setLabel = function(label)
error("Device is read-only")
end,
getLabel = function()
return "cctrom"
end
})
local function refresh()
disks={}
for _, disk in ipairs({peripheral.find("drive")}) do
if disk.isDiskPresent() then
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
end
end
end
local function iter()
refresh()
local combined = {}
for id, obj in pairs(internal) do combined[id] = obj end
for id, obj in pairs(disks) do combined[id] = obj end
return pairs(combined)
end
return {refresh = refresh, list = iter}

View File

@@ -0,0 +1,152 @@
--:Minify:--
-- CCT driver peripheral helper
local kernel=...
kernel.cct={}
kernel.cct.peripheral={}
local peripheral=kernel.cct.peripheral
local apis = kernel.apis
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end

View File

@@ -0,0 +1,306 @@
--:Minify:--
local kernel = ...
local handler = {}
local http = kernel.apis.http
kernel.cct.httpqueue = kernel.cct.httpqueue or {}
kernel.cct.httpresponse = kernel.cct.httpresponse or {}
kernel.cct.httperror = kernel.cct.httperror or {}
local function parseRawRequest(raw)
local sepLen = 4
local headerEnd =
raw:find("\r\n\r\n", 1, true)
if not headerEnd then
headerEnd =
raw:find("\n\n", 1, true)
sepLen = 2
end
local headerPart
local body = ""
if headerEnd then
headerPart = raw:sub(1, headerEnd - 1)
body = raw:sub(headerEnd + sepLen)
else
headerPart = raw
end
local lines = {}
for line in headerPart:gmatch("[^\r\n]+") do
lines[#lines + 1] = line
end
if #lines == 0 then
return nil, "EINVAL"
end
local method, path =
lines[1]:match("^(%S+)%s+(%S+)")
if not method or not path then
return nil, "EBADMSG"
end
local headers = {}
for i = 2, #lines do
local k, v =
lines[i]:match("^([^:]+):%s*(.*)$")
if k then
headers[k] = v
end
end
local host = headers.Host or headers.host
if not host and not path:match("^https?://") then
return nil, "EHOSTUNREACH"
end
local url
if path:match("^https?://") then
url = path
else
url = "http://" .. host .. path
end
local req = {
url = url,
method = method,
headers = headers
}
if body ~= "" then
req.body = body
end
return req
end
local function buildResponse(resp)
if not resp then
return nil, "EINVAL"
end
local code, msg = resp.getResponseCode()
local headers =
resp:getResponseHeaders()
local body =
resp:readAll() or ""
local out = {
"HTTP/1.1 " ..
tostring(code) ..
" " ..
tostring(msg)
}
local hasLength = false
for k, v in pairs(headers or {}) do
if k:lower() == "content-length" then
hasLength = true
end
out[#out + 1] =
tostring(k) ..
": " ..
tostring(v)
end
if not hasLength then
out[#out + 1] =
"Content-Length: " ..
tostring(#body)
end
out[#out + 1] = ""
out[#out + 1] = body
return table.concat(out, "\r\n")
end
function handler.connect(fd, address)
local fdo = kernel.currentTask.fd[fd]
if not fdo then
return nil, "EBADF"
end
fdo.socket.rbuf = ""
fdo.socket.closed = false
fdo.socket.httpid = nil
fdo.handle.write = function(raw)
local req, err =
parseRawRequest(raw)
if not req then
return nil, err
end
local id =
tostring(kernel.uuid())
fdo.socket.httpid = id
kernel.cct.httpqueue[id] = true
local ok, err =
http.request(req, id)
if not ok then
kernel.cct.httpqueue[id] = nil
return nil, err
end
return true
end
fdo.handle.read = function(count)
count = count or 4096
local sock = fdo.socket
if #sock.rbuf > 0 then
local out =
sock.rbuf:sub(1, count)
sock.rbuf =
sock.rbuf:sub(count + 1)
return out
end
local id = sock.httpid
if not id then
return ""
end
local function finish(resp)
sock.rbuf =
buildResponse(resp) or ""
local out =
sock.rbuf:sub(1, count)
sock.rbuf =
sock.rbuf:sub(count + 1)
return out
end
if kernel.cct.httpresponse[id] then
local resp =
kernel.cct.httpresponse[id]
kernel.cct.httpresponse[id] = nil
kernel.cct.httpqueue[id] = nil
return finish(resp)
end
if kernel.cct.httperror[id] then
local err =
kernel.cct.httperror[id]
kernel.cct.httperror[id] = nil
kernel.cct.httpqueue[id] = nil
return nil, err
end
kernel.currentTask.status = "D"
local coro
coro = function()
if kernel.cct.httpresponse[id] then
local resp =
kernel.cct.httpresponse[id]
kernel.cct.httpresponse[id] = nil
kernel.cct.httpqueue[id] = nil
kernel.asyncReturn(
finish(resp)
)
return
end
if kernel.cct.httperror[id] then
local err =
kernel.cct.httperror[id]
kernel.cct.httperror[id] = nil
kernel.cct.httpqueue[id] = nil
kernel.asyncReturn(
nil,
err
)
return
end
coroutine.yield()
end
kernel.currentTask.ksh =
coroutine.create(function()
local ok, err =
xpcall(
coro,
debug.traceback
)
if not ok then
kernel.asyncReturn(
nil,
err
)
end
end)
end
fdo.handle.close = function()
fdo.socket.closed = true
local id =
fdo.socket.httpid
if id then
kernel.cct.httpqueue[id] = nil
kernel.cct.httpresponse[id] = nil
kernel.cct.httperror[id] = nil
end
return true
end
return true
end
kernel.socket.registerProtocal(
"http://",
handler
)
kernel.socket.registerProtocal(
"https://",
handler
)

View File

@@ -0,0 +1,284 @@
--:Minify:--
local kernel = ...
local peripheral=kernel.cct.peripheral
local colors = {
[0xFFFFFF]=0x0001,
[0xFF0000]=0x0002,
[0x00FF00]=0x0004,
[0x0000FF]=0x0008,
[0x00FFFF]=0x0010,
[0xFF00FF]=0x0020,
[0xFFFF00]=0x0040,
[0xFF6D00]=0x0080,
[0x6DFF55]=0x0100,
[0x24FFFF]=0x0200,
[0x924900]=0x0400,
[0x6D6D55]=0x0800,
[0xDBDBAA]=0x1000,
[0x6D00FF]=0x2000,
[0xB6FF00]=0x4000,
[0x000000]=0x8000
}
local plt = {
0xFFFFFF,
0xFF0000,
0x00FF00,
0x0000FF,
0x00FFFF,
0xFF00FF,
0xFFFF00,
0xFF6D00,
0x6DFF55,
0x24FFFF,
0x924900,
0x6D6D55,
0xDBDBAA,
0x6D00FF,
0xB6FF00,
0x000000
}
local fg,bg=0x6D6D55,0x000000
local l1f,l1d,l2,ops={},{},{},0
local function findClosest(tbl, target)
local closest = nil
local smallestDiff = math.huge
for k, _ in pairs(tbl) do
if k==target then return k end
local diff = math.abs(k - target)
if diff < smallestDiff then
smallestDiff = diff
closest = k
end
end
return closest
end
local function aprox(c24)
ops = ops + 1
if ops % 1024 == 0 then
l1d = {}
l1f = {}
end
if ops % 8192 == 0 then
l2 = {}
end
if l2[c24] ~= nil then
return l2[c24]
end
if l1d[c24] ~= nil then
l1f[c24] = l1f[c24] + 1
if l1f[c24] >= 16 then
l2[c24] = l1d[c24]
l1d[c24] = nil
l1f[c24] = nil
return l2[c24]
end
return l1d[c24]
end
local closestKey = findClosest(colors, c24)
if not closestKey then return nil end
local value = colors[closestKey]
l1d[c24] = value
l1f[c24] = 1
return value
end
local function write(text, term)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
if x > w then
x = 1
y = y + 1
end
if y - 1 >= h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
kernel.devfs.data.tty={}
kernel.cct.ctrl,kernel.cct.alt = false, false
local function serializeBool(bool)
if bool then
return "T"
else
return "F"
end
end
local function newtty(obj, id, ev)
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
kernel.devfs.data["tty"][id] = function(op, mode)
if op=="type" then
return "Terminal"
elseif op=="open" then
local h = {
read=function(amount)
local rv=""
for i=1, amount or 1 do
local event = {ev()}
if event[1] then
rv=rv..event[1]
end
end
if rv=="" then rv=nil end
return rv
end,
write=function(content)
write(content, obj)
end,
size=function()
local s={obj.getSize()}
return table.concat(s,";")
end,
clear=function()
obj.clear()
obj.setCursorPos(1,1)
end,
gpos=function()
local s={obj.getCursorPos()}
return table.concat(s,";")
end,
spos=function(x,y)
return obj.setCursorPos(x,y)
end,
sfgc=function(c)
fg=c
return obj.setTextColor(aprox(c))
end,
sbgc=function(c)
bg=c
return obj.setBackgroundColor(aprox(c))
end,
gfgc=function()
return fg
end,
gbgc=function()
return bg
end,
gctrl=function()
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
end,
isvirt=function()
return false
end,
gplt=function()
return plt
end
}
if mode=="rw" then
return h
elseif mode=="r" then
h["write"]=nil
return h
elseif mode=="w" then
h["read"]=nil
return h
end
end
end
end
local fifo = kernel.newFifo()
kernel.cct.fifo=fifo
newtty(kernel.apis.term, "1", function() end)
for i,v in ipairs({peripheral.find("monitor")}) do
newtty(v,tostring(i+1),function () end)
end
kernel.devfs.data["input"]["keyboard1"] = function(op, mode)
if op=="type" then
return "Keyboard"
elseif op=="open" then
local h = {
read=function(amount)
local rv=""
for i=1, amount or 1 do
local event = {fifo.pop()}
if event[1] then
rv=rv..event[1]
end
end
if rv=="" then rv=nil end
return rv
end,
write=function(content)
end
}
if mode=="rw" then
return h
elseif mode=="r" then
h["write"]=nil
return h
elseif mode=="w" then
h["read"]=nil
return h
end
end
end

View File

@@ -0,0 +1,95 @@
--:Minify:--
local kernel=...
local keys=kernel.apis.keys
kernel.processes.cctdeamon = function()
local timeout = false
kernel.log("CCT deamon started")
while true do
local event = {kernel.EFI:getMachineEvent()}
if event[1] then
local eventType = event[1]
local charOrKey = event[3]
local ctrlKeyMap = {
[keys.a]=1, [keys.b]=2, [keys.c]=3,
[keys.d]=4, [keys.e]=5, [keys.f]=6,
[keys.g]=7, [keys.h]=8, [keys.i]=9,
[keys.j]=10, [keys.k]=11, [keys.l]=12,
[keys.m]=13, [keys.n]=14, [keys.o]=15,
[keys.p]=16, [keys.q]=17, [keys.r]=18,
[keys.s]=19, [keys.t]=20, [keys.u]=21,
[keys.v]=22, [keys.w]=23, [keys.x]=24,
[keys.y]=25, [keys.z]=26,
}
if eventType == "keyPressed" then
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
kernel.cct.ctrl = true
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
kernel.cct.alt = true
end
if kernel.cct.ctrl then
local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1)
end
else
kernel.cct.fifo.push(string.char(ctrlByte))
end
end
else
local specialKeyMap = {
[keys.up] = "",
[keys.down] = "",
[keys.right] = "",
[keys.left] = "",
[keys.home] = "",
[keys["end"]] = "",
[keys.pageUp] = "[5~",
[keys.pageDown] = "[6~",
[keys.delete] = "[3~",
}
local special = specialKeyMap[charOrKey]
if special then kernel.cct.fifo.push(special) end
end
elseif eventType == "keyReleased" then
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
kernel.cct.ctrl = false
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
kernel.cct.alt = false
end
elseif eventType == "keyTyped" then
if charOrKey then kernel.cct.fifo.push(charOrKey) end
elseif eventType == "mouse_scroll" then
if event[2] == 1 then
kernel.cct.fifo.push("S")
else
kernel.cct.fifo.push("T")
end
elseif eventType == "http_success" then
kernel.cct.httpqueue[event[2]]=nil
kernel.cct.httpresponse[event[2]]=event[3]
elseif eventType == "http_failure" then
kernel.cct.httpqueue[event[2]]=nil
kernel.cct.httperror[event[2]]=event[3]
end
timeout = false
else
timeout = true
end
if timeout then
sleep(0.05)
end
end
end
kernel.log("CCT deamon queued for execution")

View File

@@ -0,0 +1,25 @@
--:Minify:--
local kernel=...
local rs=kernel.apis.rs
local sides = {top=1, bottom=2, left=3, right=4, front=5, back=6}
local function newGPIO(side)
return function(mode, data)
if mode=="w" then
if type(data)~="boolean" then error("data: expected bool") end
rs.setOutput(side, data)
elseif mode=="wa" then
if type(data)~="number" then error("data: expected bool") end
rs.setAnalogOutput(side, data)
elseif mode=="r" then
return rs.getInput(side)
elseif mode=="ra" then
return rs.getAnalogInput(side)
end
end
end
for side, alt in pairs(sides) do
local func=newGPIO(side)
kernel.gpio[side]=func
kernel.gpio[alt]=func
end

View File

@@ -1,410 +0,0 @@
--:Minify:--
local kernel = ...
local apis = kernel.apis
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
local peripheral={}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
local icolors = {
[0x1] = 1, -- #000000
[0x2] = 2, -- #FFFFFF
[0x4] = 3, -- #FF0000
[0x8] = 4, -- #00FF00
[0x10] = 5, -- #0000FF
[0x20] = 6, -- #00FFFF
[0x40] = 7, -- #FF00FF
[0x80] = 8, -- #FFFF00
[0x100] = 9, -- #FF6D00
[0x200] = 10, -- #6DFF55
[0x400] = 11, -- #24FFFF
[0x800] = 12, -- #924900
[0x1000] = 13, -- #6D6D55
[0x2000] = 14, -- #DBDBAA
[0x4000] = 15, -- #6D00FF
[0x8000] = 16 -- #B6FF00
}
local colors = {
0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
local function write(text, term)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
if x > w then
x = 1
y = y + 1
end
if y - 1 >= h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
kernel.devfs.data.tty={}
local ctrl,alt = false, false
local function serializeBool(bool)
if bool then
return "T"
else
return "F"
end
end
local function newtty(obj, id, ev)
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
kernel.devfs.data["tty"][id] = function(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
local h = {
read=function(amount)
local rv=""
for i=1, amount or 1 do
local event = {ev()}
if event[1] then
rv=rv..event[1]
end
end
if rv=="" then rv=nil end
return rv
end,
write=function(content)
write(content, obj)
end,
size=function()
local s={obj.getSize()}
return table.concat(s,";")
end,
clear=function()
obj.clear()
obj.setCursorPos(1,1)
end,
gpos=function()
local s={obj.getCursorPos()}
return table.concat(s,";")
end,
spos=function(x,y)
return obj.setCursorPos(x,y)
end,
sfgc=function(c)
return obj.setTextColor(colors[c])
end,
sbgc=function(c)
return obj.setBackgroundColor(colors[c])
end,
gfgc=function()
return icolors[obj.getTextColor()]
end,
gbgc=function()
return icolors[obj.getBackgroundColor()]
end,
gctrl=function()
return serializeBool(ctrl)..";"..serializeBool(alt)
end
}
if mode=="rw" then
return h
elseif mode=="r" then
h["write"]=nil
return h
elseif mode=="w" then
h["read"]=nil
return h
end
end
end
end
local fifo = kernel.newFifo()
kernel.processes.cctmond = function()
local timeout = false
while true do
local event = {kernel.computer:getMachineEvent()}
if event[1] then
local eventType = event[1]
local charOrKey = event[3]
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 charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = true
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
alt = true
end
if ctrl then
local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1)
end
else
fifo.push(string.char(ctrlByte))
end
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
elseif eventType == "keyReleased" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = false
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
alt = false
end
elseif eventType == "keyTyped" then
if charOrKey then fifo.push(charOrKey) end
end
timeout = false
else
timeout = true
end
if timeout then
sleep(0.05)
end
end
end
newtty(apis.term, "1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5)
v.write("Initializing...")
newtty(v,tostring(i+1),function () end)
end

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,14 +1,14 @@
--:Minify:-- --:Minify:--
local args = {...} local EFI=...
local apis = args[1] EFI.beep(440, 500)
local disks = args[2] local screen=EFI.screenCtl
local arch = args[3] local ifs=EFI.initfs
local screen = args[5] local disks=EFI.disks
local computer = args[6] local arch=EFI.architecture
local ifs = args[7]
local kernel = {} local kernel = {}
kernel.LOG_Text="" kernel.LOG_Text=""
kernel.version="HyperionOS V1.2.3" kernel.version="HyperionOS V1.2.4-dev_5"
kernel.tag="1.2.4-dev_5"
kernel.process = "Kernel" kernel.process = "Kernel"
kernel.users={[0]="root",[1]="User"} kernel.users={[0]="root",[1]="User"}
kernel.hostname = "hyperion" kernel.hostname = "hyperion"
@@ -19,67 +19,73 @@ kernel.status = "start"
kernel.key = {} kernel.key = {}
kernel.cache = {} kernel.cache = {}
kernel.cache.preload = {} kernel.cache.preload = {}
kernel.unixSockets={}
kernel._G=_G kernel._G=_G
kernel.sleep=sleep kernel.sleep=sleep
_G.sleep=nil _G.sleep=nil
local windowsExp = false local windowsExp = false
function kernel.log(msg, level, c) function kernel.log(msg, level, c, nopanic)
c=c or 12 local f=function()
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" c=c or 0x6D6D6D
kernel.LOG_Text = kernel.LOG_Text..tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
if kernel.status == "start" then if kernel.status == "start" then
screen:setTextColor(c) screen:setTextColor(c)
screen:print(tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg) screen:print(tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
elseif kernel.status == "term" then elseif kernel.status == "term" then
kernel.standbyTask=kernel.currentTask kernel.standbyTask=kernel.currentTask
kernel.currentTask=kernel.kernelTask kernel.currentTask=kernel.kernelTask
kernel.vfs.devctl(1,"sfgc",c) local file=kernel.vfs.open("/dev/console", "w")
kernel.vfs.write(1,tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n") kernel.vfs.devctl(file,"sfgc",c)
kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
kernel.vfs.close(file)
kernel.currentTask=kernel.standbyTask kernel.currentTask=kernel.standbyTask
end end
end end
local ok,err = xpcall(f,debug.traceback)
if not ok and not nopanic then
kernel.panic(err)
end
end
function kernel.PANIC(msg) function kernel.PANIC(msg)
if kernel.status~="Panic" then if kernel.status~="Panic" then
kernel.log("PANIC: "..msg, "PANIC") kernel.log("PANIC: "..msg, "PANIC", 0xFF0000)
pcall(kernel["saveLog"]) pcall(kernel["saveLog"])
kernel.status="Panic" kernel.status="Panic"
kernel.reason=msg kernel.reason=msg
screen:setTextColor(2) screen:enable()
screen:setBackgroundColor(16) screen:setTextColor(0xFF0000)
screen:setBackgroundColor(0x000000)
screen:clear() screen:clear()
screen:setCursorPos(1,1) screen:resetCursor()
screen:print(kernel.LOG_Text) screen:print(kernel.LOG_Text)
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.") screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
screen:print("Press any key to continue...") screen:print("Press any key to continue...")
kernel.exitMain = true kernel.exitMain = true
end end
while true do while true do
local event={computer:getMachineEvent()} local event={EFI:getMachineEvent()}
if event[1]=="keyPressed" then if event[1]=="keyPressed" then
break break
end end
end end
computer:reboot() EFI.reboot=true
error("KERNEL PANIC")
end end
kernel.panic=kernel.PANIC kernel.panic=kernel.PANIC
if windowsExp then if windowsExp then
screen:setTextColor(1) screen:setTextColor(0xFFFFFF)
screen:setBackgroundColor(4) screen:setBackgroundColor(0x0000FF)
screen:clear() screen:clear()
local w,h = screen:getSize() screen:resetCursor()
screen:setCursorPos(3,5) screen:print("\n\n\n\n\n :(")
screen:print(":(") screen:print("\n Your PC ran into a problem and needs to restart. We're just collecting some error")
screen:setCursorPos(3,7)
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
screen:setCursorPos(3,8)
screen:print(" info, and then we'll restart for you.\n") screen:print(" info, and then we'll restart for you.\n")
screen:setCursorPos(3,h-5) screen:print("\n\n\n Stop code: average windows experience")
screen:print("Stop code: average windows experience")
screen:setCursorPos(1,h)
screen:print("Press any key to continue... jk reboot it yourself lazy")
while true do end while true do end
end end
@@ -113,7 +119,7 @@ local split = function(str, delim, maxResultCountOrNil)
end end
if not ifs.isFile("/boot/boot.cfg") then if not ifs.isFile("/boot/boot.cfg") then
kernel.log("First boot detected writing boot.cfg", "INFO", 3) kernel.log("First boot detected writing boot.cfg", "INFO", 0x00FF00)
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg")) ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
kernel.firstBoot=true kernel.firstBoot=true
end end
@@ -130,25 +136,35 @@ if not initCfgStatus then
end end
kernel.config = config kernel.config = config
local skip=false
for i,v in ipairs(split(fstab,"\n")) do for i,v in ipairs(split(fstab,"\n")) do
if v:sub(1,1)=="U" then if v:sub(1,1)=="U" then
local id="" local id=""
for i=3,#v do for i=3,#v do
if v:sub(i,i)==";" then if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN", 0xFF8800) skip = true break end
id=v:sub(3,i-1) id=v:sub(3,i-1)
end end
end end
if not skip then
local path=v:sub(#id+4) local path=v:sub(#id+4)
ifs.mount(id,path) ifs.mount(id,path)
::endline:: else
skip=false
end
end end
end end
kernel.log("Disks initialized") kernel.log("Disks initialized")
function kernel.saveLog() function kernel.saveLog()
if kernel.status=="running" then
local file = kernel.vfs.open("/var/log/syslog.log", "w")
kernel.vfs.write(file, kernel.LOG_Text)
kernel.vfs.close(file)
else
ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text) ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text)
end end
end
function kernel.newFifo() function kernel.newFifo()
local fifo = {} local fifo = {}
@@ -187,7 +203,7 @@ kernel.log("Gathering modules")
for _, i in ipairs(ifs.list("/lib/modules")) do for _, i in ipairs(ifs.list("/lib/modules")) do
local modlist = ifs.list("/lib/modules/"..i) local modlist = ifs.list("/lib/modules/"..i)
if not modlist then if not modlist then
kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8) kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 0xFF8800)
else else
for _,v in ipairs(modlist) do for _,v in ipairs(modlist) do
local prior=tonumber(v:sub(1,2)) local prior=tonumber(v:sub(1,2))
@@ -199,8 +215,8 @@ for _, i in ipairs(ifs.list("/lib/modules")) do
end end
kernel.ifs=ifs kernel.ifs=ifs
kernel.apis=apis kernel.apis=EFI.firmware
kernel.computer=computer kernel.EFI=EFI
kernel.arch=arch kernel.arch=arch
kernel.initdisks=disks kernel.initdisks=disks
kernel.screen=screen kernel.screen=screen
@@ -229,16 +245,29 @@ kernel.kernelTask = {
kernel.currentTask = kernel.kernelTask kernel.currentTask = kernel.kernelTask
function kernel.shutdown() function kernel.shutdown()
kernel.computer:shutdown() kernel.exitMain=true
kernel.status="shutdown"
end end
function kernel.reboot() function kernel.reboot()
kernel.computer:reboot() kernel.exitMain=true
kernel.status="reboot"
end end
kernel.syscalls["time"]=function() return kernel.computer:time() end function kernel.halt()
kernel.exitMain=true
kernel.status="halt"
end
function kernel.asyncReturn(...)
kernel.currentTask.syscallReturn = {...}
end
kernel.syscalls["time"]=function() return kernel.EFI:getEpochMs() end
kernel.syscalls["date"]=function() return kernel.EFI:date() end
kernel.syscalls["log"]=kernel.log kernel.syscalls["log"]=kernel.log
kernel.syscalls["getUptime"]=function() return kernel.computer:clock() end kernel.syscalls["tag"]=function() return kernel.tag end
kernel.syscalls["getUptime"]=function() return kernel.EFI:getUptime() end
kernel.syscalls["getUsername"]=function(uid) return kernel.users[uid or kernel.uid] end kernel.syscalls["getUsername"]=function(uid) return kernel.users[uid or kernel.uid] end
kernel.syscalls["getHostname"]=function() return kernel.hostname end kernel.syscalls["getHostname"]=function() return kernel.hostname end
kernel.syscalls["getHost"]=function() return kernel.apis._HOST end kernel.syscalls["getHost"]=function() return kernel.apis._HOST end
@@ -253,35 +282,53 @@ kernel.syscalls["sysdump"]=function()
return rv return rv
end end
kernel.syscalls["reboot"]=function() kernel.syscalls["reboot"]=function()
kernel.computer:reboot() if kernel.uid==0 or kernel.groups.wheel then
kernel.reboot()
end
end end
kernel.syscalls["shutdown"]=function() kernel.syscalls["shutdown"]=function()
kernel.computer:reboot() if kernel.uid==0 or kernel.groups.wheel then
kernel.shutdown()
end
end
kernel.syscalls["halt"]=function()
if kernel.uid==0 or kernel.groups.wheel then
kernel.halt()
end
end end
kernel.log("Running modules") kernel.log("Running modules")
for _,p in ipairs(modules) do for _,p in ipairs(modules) do
for _,v in ipairs(p) do for _,v in ipairs(p) do
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 5) end if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 0x00FFFF) end
local code=ifs.readAllText(v) local code=ifs.readAllText(v)
if not code then if not code then
kernel.log("ModuReadErr: "..v, "WARN", 8) kernel.panic("Failed to read module "..v)
goto skip
end end
local func,err=load(code,"@"..v) local func,err=load(code,"@"..v)
if not func then kernel.panic("ModuLoadErr: "..tostring(err)) goto skip end if not func then kernel.panic("ModuLoadErr: "..tostring(err)) end
local status, err = xpcall(func,debug.traceback, kernel) local status, err = xpcall(func,debug.traceback, kernel)
if not status then kernel.panic("ModuRunErr: "..tostring(err)) end if not status then kernel.panic("ModuRunErr: "..tostring(err)) end
if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 5) end if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 0x00FFFF) end
::skip::
end end
end end
kernel.log("Kernel initialized successfully.") kernel.log("Kernel initialized successfully.")
kernel.saveLog() kernel.saveLog()
kernel.status="running" kernel.status="running"
kernel.main() screen:disable()
local ok,err = xpcall(kernel.main, debug.traceback)
if not ok then
kernel.panic(err)
end
if kernel.status=="panic" then if kernel.status=="panic" then
kernel.panic() kernel.panic(kernel.reason)
end
if kernel.status=="reboot" then
EFI.reboot=true
return true
elseif kernel.status=="halt" then
kernel.log("System halted.")
kernel.saveLog()
while true do end
end end
kernel.PANIC("Execution complete")

View File

@@ -7,5 +7,5 @@ return {
initPath = "/sbin/init", initPath = "/sbin/init",
maxOpenFiles = 128, maxOpenFiles = 128,
maxFilesPerTask = 16, maxFilesPerTask = 16,
preempt=true preempt = true,
} }

View File

@@ -229,6 +229,23 @@ function toHex(num)
return string.format("%X", num) return string.format("%X", num)
end end
local st={
push=function(self,...) table.insert(self.contents,{...}) end,
pop=function(self) return table.unpack(table.remove(self.contents)) end,
}
local q={
push=function(self,...) table.insert(self.contents,{...}) end,
pop=function(self) return table.unpack(table.remove(self.contents,1)) end,
}
function stack()
return setmetatable({contents={}}, {__index=st})
end
function queue()
return setmetatable({contents={}}, {__index=q})
end
local function makeSyscallProxy() local function makeSyscallProxy()
local backing = {} local backing = {}
return setmetatable(backing, { return setmetatable(backing, {

View File

@@ -129,6 +129,8 @@ local function validateComponent(comp)
end end
if lower == ".meta" then if lower == ".meta" then
error("EINVAL: reserved path component: .meta", 3) error("EINVAL: reserved path component: .meta", 3)
elseif lower:sub(#lower) == "." then
error("Cannot have empty name extention: " .. comp, 3)
end end
end end
@@ -397,6 +399,10 @@ end
local function getFileMeta(path, noFollow) local function getFileMeta(path, noFollow)
local real = namei(path, noFollow) local real = namei(path, noFollow)
if kernel.unixSockets[real] then
return kernel.unixSockets[real].meta
end
if real == "/" then if real == "/" then
return { etype = 0x00, owner = 0, group = 0, perms = 62, cmeta = "" } return { etype = 0x00, owner = 0, group = 0, perms = 62, cmeta = "" }
end end
@@ -499,7 +505,7 @@ local function checkperms(meta, mode)
local bitmap = { local bitmap = {
r = {owner = 5, group = 3, everyone = 1}, r = {owner = 5, group = 3, everyone = 1},
w = {owner = 4, group = 2, everyone = 0}, w = {owner = 4, group = 2, everyone = 0},
a = {owner = 4, group = 2, everyone = 0}, a = {owner = 4, group = 2, everyone = 0}
} }
local m = bitmap[mode] local m = bitmap[mode]
if not m then error("EINVAL") end if not m then error("EINVAL") end
@@ -536,6 +542,7 @@ local function allocFD(task)
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
return fd return fd
end end
local function checkSystemLimit() local function checkSystemLimit()
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
end end
@@ -544,32 +551,93 @@ local function newFileObj(handle, mode, path, meta, ftype)
end end
function vfs.newfd(fdobj) function vfs.newfd(fdobj)
checkSystemLimit(); total = total + 1
local fd = allocFD(kernel.currentTask) local fd = allocFD(kernel.currentTask)
kernel.currentTask.fd[fd] = fdobj kernel.currentTask.fd[fd] = fdobj
return fd return fd
end end
function vfs.mount(target, diskOrId) function vfs.mount(target, diskOrId, bind)
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if _euid ~= 0 then error("EPERM") end if _euid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end if not target then error("EINVAL") end
target = normalizeMountPoint(target) target = normalizeMountPoint(target)
if not vfs.exists(target) then vfs.mkdir(target) end local drive, path = resolvePath(target)
if vfs.type(target) ~= "directory" then error("EINVAL") end if not drive:directoryExists(path) then
drive:makeDirectory(path)
end
if drive:type(path) ~= "directory" then
error("EINVAL")
end
local disk, id local disk, id
-- bind mount
if bind then
local src = normalizeMountPoint(diskOrId)
local srcDrive = resolvePath(src)
if not srcDrive then error("ENOENT") end
id = "bind:" .. src
disk = {
address = id,
isBind = true,
source = srcDrive,
exists = function(_, p)
return srcDrive:exists(p)
end,
type = function(_, p)
return srcDrive:type(p)
end,
list = function(_, p)
return srcDrive:list(p)
end,
open = function(_, ...)
return srcDrive:open(...)
end,
remove = function(_, p)
return srcDrive:remove(p)
end,
makeDirectory = function(_, p)
return srcDrive:makeDirectory(p)
end,
rename = function(_, a, b)
return srcDrive:rename(a, b)
end,
size = function(_, p)
return srcDrive:size(p)
end,
lastModified = function(_, p)
return srcDrive:lastModified(p)
end,
isReadOnly = function()
return srcDrive:isReadOnly()
end,
spaceTotal = function()
return srcDrive:spaceTotal()
end,
spaceUsed = function()
return srcDrive:spaceUsed()
end
}
vfs.disks[id] = disk
else
if type(diskOrId) == "string" then if type(diskOrId) == "string" then
disk = kernel.disks[diskOrId] disk = vfs.disks[diskOrId]
if not disk then error("ENODEV") end if not disk then error("ENODEV") end
checkDisk(disk); id = diskOrId checkDisk(disk)
id = diskOrId
elseif type(diskOrId) == "table" then elseif type(diskOrId) == "table" then
checkDisk(diskOrId); disk = diskOrId checkDisk(diskOrId)
id = disk.address; vfs.disks[id] = disk disk = diskOrId
else error("EINVAL") end id = disk.address
vfs.disks[id] = disk
else
error("EINVAL")
end
end
if vfs.mounts[id] then error("EBUSY") end if vfs.mounts[id] then error("EBUSY") end
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end for _, mp in pairs(vfs.mounts) do
if mp == target then
error("EBUSY")
end
end
vfs.mounts[id] = target vfs.mounts[id] = target
return true return true
end end
@@ -582,7 +650,11 @@ function vfs.umount(target)
for id, mp in pairs(vfs.mounts) do for id, mp in pairs(vfs.mounts) do
if mp == target then if mp == target then
if id == "$" then error("EBUSY") end if id == "$" then error("EBUSY") end
vfs.mounts[id] = nil; return true vfs.mounts[id] = nil
if vfs.disks[id] and vfs.disks[id].isBind then
vfs.disks[id] = nil
end
return true
end end
end end
error("EINVAL") error("EINVAL")
@@ -594,6 +666,21 @@ function vfs.open(path, mode)
local fd = allocFD(task) local fd = allocFD(task)
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if not disk then error("NODISK") end if not disk then error("NODISK") end
if (mode=="w" or mode=="a") and disk:isReadOnly() then error("ERDONLY") end
if kernel.unixSockets[namei(path)] then
local meta = kernel.unixSockets[namei(path)].meta
if kernel.uid~=0 or kernel.uid~=meta.owner then
local groups = (task and task.groups) or kernel.groups or {}
local access=false
for _, gid in ipairs(groups) do
if gid == meta.group then access=true end
end
if not access then error("EACCES") end
end
task[fd]=kernel.unixSockets[namei(path)]
return fd
end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath) local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
@@ -695,10 +782,17 @@ function vfs.sendfile(outfd, infd, count)
end end
function vfs.stat(path) function vfs.stat(path)
local attrs
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path) local meta = getFileMeta(path)
local ok, attrs = pcall(disk.attributes, disk, diskPath) if meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 }
else
local ok
ok, attrs = pcall(disk.attributes, disk, diskPath)
if not ok then attrs = { size=0, modified=0, created=0 } end if not ok then attrs = { size=0, modified=0, created=0 } end
end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -713,15 +807,15 @@ end
function vfs.lstat(path) function vfs.lstat(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
local attrs local attrs
if meta.etype == 0x01 then if meta.etype == 0x01 or meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 } attrs = { size=0, modified=0, created=0 }
else else
local disk, diskPath = resolvePath(path, true) local disk, diskPath = resolvePath(path, true)
local ok, a = pcall(disk.attributes, disk, diskPath) local ok, a = pcall(disk.attributes, disk, diskPath)
attrs = ok and a or { size=0, modified=0, created=0 } attrs = ok and a or { size=0, modified=0, created=0 }
end end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -738,8 +832,15 @@ end
function vfs.fstat(fd) function vfs.fstat(fd)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
local disk, diskPath = resolvePath(file.path) local attrs
local attrs = disk:attributes(diskPath) if file.meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 }
else
local disk, diskPath = resolvePath(file.path, true)
local ok, a = pcall(disk.attributes, disk, diskPath)
attrs = ok and a or { size=0, modified=0, created=0 }
end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -768,6 +869,14 @@ function vfs.listdir(path)
end end
end end
for k,v in pairs(kernel.unixSockets) do
local p=normalizePath(path)
if k:match("^(.*)/[^/]+$")==p then
seen[v.name]=true
table.insert(out, v.name)
end
end
local mp local mp
if diskPath == "/" then if diskPath == "/" then
mp = ".meta" mp = ".meta"
@@ -796,6 +905,7 @@ function vfs.mkdir(path)
local parentMeta = getFileMeta(parent) local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w") checkperms(parentMeta, "w")
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if disk:isReadOnly() then error("ERDONLY") end
disk:makeDirectory(diskPath) disk:makeDirectory(diskPath)
local task = kernel.currentTask local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid local euid = (task and (task.euid or task.uid)) or kernel.uid
@@ -817,8 +927,13 @@ function vfs.remove(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if kernel.unixSockets and kernel.unixSockets[path] then if kernel.unixSockets[namei(path)] then
kernel.unixSockets[path] = nil if kernel.uid ~= 0 then
if kernel.unixSockets[namei(path)].meta.owner~=kernel.uid then
error("EACCES")
end
end
kernel.unixSockets[namei(path)] = nil
end end
if meta.etype == 0x01 then if meta.etype == 0x01 then
@@ -827,6 +942,7 @@ function vfs.remove(path)
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
local disk, parentDiskPath = resolveMount(parent) local disk, parentDiskPath = resolveMount(parent)
if disk:isReadOnly() then error("ERDONLY") end
local mp local mp
if parentDiskPath == "/" then mp = ".meta" if parentDiskPath == "/" then mp = ".meta"
else mp = parentDiskPath:gsub("^/+", "") .. "/.meta" end else mp = parentDiskPath:gsub("^/+", "") .. "/.meta" end
@@ -843,6 +959,7 @@ function vfs.remove(path)
if f2.close then f2.close() end if f2.close then f2.close() end
else else
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if disk:isReadOnly() then error("ERDONLY") end
disk:remove(diskPath) disk:remove(diskPath)
end end
end end
@@ -851,6 +968,8 @@ function vfs.symlink(target, linkPath)
if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end
local norm = normalizePath(linkPath) local norm = normalizePath(linkPath)
local parent = norm:match("^(.*)/[^/]+$") or "/" local parent = norm:match("^(.*)/[^/]+$") or "/"
local disk = resolveMount(linkPath)
if disk:isReadOnly() then error("ERDONLY") end
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
if not name then error("EINVAL") end if not name then error("EINVAL") end
@@ -880,7 +999,13 @@ function vfs.readlink(path)
end end
function vfs.access(path, mode) function vfs.access(path, mode)
local meta = getFileMeta(path) local meta
if kernel.unixSockets[normalizePath(path)] then
meta = kernel.unixSockets[normalizePath(path)].meta
else
meta = getFileMeta(path)
end
for i = 1, #mode do for i = 1, #mode do
checkperms(meta, mode:sub(i,i)) checkperms(meta, mode:sub(i,i))
end end
@@ -891,6 +1016,8 @@ local function updateMeta(path, fn, noFollow)
local real = namei(path, noFollow) local real = namei(path, noFollow)
local norm = real local norm = real
local parent = norm:match("^(.*)/[^/]+$") or "/" local parent = norm:match("^(.*)/[^/]+$") or "/"
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
if not name then error("EINVAL") end if not name then error("EINVAL") end
@@ -917,6 +1044,9 @@ local function updateMeta(path, fn, noFollow)
end end
function vfs.chmod(path, perms) function vfs.chmod(path, perms)
if kernel.unixSockets[namei(path)] then error("EINVAL") end
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if euid ~= 0 and euid ~= meta.owner then error("EACCES") end if euid ~= 0 and euid ~= meta.owner then error("EACCES") end
@@ -926,10 +1056,14 @@ end
function vfs.fchmod(fd, perms) function vfs.fchmod(fd, perms)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
if file.meta.etype==0x02 then error("EINVAL") end
vfs.chmod(file.path, perms) vfs.chmod(file.path, perms)
end end
function vfs.chown(path, uid, gid) function vfs.chown(path, uid, gid)
if kernel.unixSockets[namei(path)] then error("EINVAL") end
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if _euid ~= 0 then error("EPERM") end if _euid ~= 0 then error("EPERM") end
updateMeta(path, function(e) e.owner = uid; e.group = gid end) updateMeta(path, function(e) e.owner = uid; e.group = gid end)
@@ -938,20 +1072,29 @@ end
function vfs.fchown(fd, uid, gid) function vfs.fchown(fd, uid, gid)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
if file.meta.etype==0x02 then error("EINVAL") end
vfs.chown(file.path, uid, gid) vfs.chown(file.path, uid, gid)
end end
function vfs.exists(path) function vfs.exists(path)
if kernel.unixSockets[namei(path)] then return true end
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if meta.etype == 0x01 then return true end if meta.etype == 0x01 or meta.etype == 0x02 then return true end
local ok, disk, diskPath = pcall(resolvePath, path) local ok, disk, diskPath = pcall(resolvePath, path)
if not ok then return false end if not ok then return false end
return disk:fileExists(diskPath) if disk:type(diskPath) then
return true
else
return false
end
end end
function vfs.type(path) function vfs.type(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if meta.etype == 0x01 then return "symlink" end if meta.etype == 0x01 then return "symlink" end
if kernel.unixSockets[namei(path)] then
return "socket"
end
local ok, disk, diskPath = pcall(resolvePath, path) local ok, disk, diskPath = pcall(resolvePath, path)
if not ok then return nil end if not ok then return nil end
return disk:type(diskPath) return disk:type(diskPath)

View File

@@ -0,0 +1,63 @@
--:Minify:--
local kernel = ...
local cache = {}
kernel.searchpaths = {
"?", "?.lua", "/lib/?", "/lib/?.lua", "/usr/lib/?", "/usr/lib/?.lua",
"/usr/local/lib/?", "/usr/local/lib/?.lua"
}
kernel.reqcache = cache
local function require(module, ...)
if cache[module] then return cache[module].ret end
kernel.currentTask.status = "D"
local args = {...}
kernel.currentTask.ksh = coroutine.create(function()
local coro = function()
for _, path in ipairs(kernel.searchpaths) do
local filepath = path:gsub("?", module)
if kernel.vfs.exists(filepath) then
if kernel.vfs.type(filepath) == "directory" then
filepath = filepath .. "/init.lua"
if kernel.vfs.type(filepath) == "directory" then
kernel.asyncReturn(false, "Module not found: "..module)
return
end
end
local fd = kernel.vfs.open(filepath, "r")
local chunks = {}
while true do
local chunk = kernel.vfs.read(fd, 4096)
if not chunk or chunk == "" then break end
chunks[#chunks + 1] = chunk
coroutine.yield()
end
kernel.vfs.close(fd)
local data = table.concat(chunks)
local func, err = load(data, "@"..module, "t", kernel._U)
if not func then
kernel.asyncReturn(false, "Error loading module "..module..": "..tostring(err))
return
end
local ok, ret = xpcall(func, debug.traceback, table.unpack(args))
if not ok then
kernel.asyncReturn(false, "Error running module "..module..": "..tostring(ret))
return
end
cache[module] = {ret = ret, expires = kernel.EFI:getEpochMs() + 120000}
kernel.asyncReturn(true, ret)
return
end
coroutine.yield()
end
kernel.asyncReturn(false, "Module not found: "..module)
end
local status, err = xpcall(coro, debug.traceback)
if not status then
kernel.asyncReturn(false, "Error in require coroutine for module "..module..": "..tostring(err))
end
end)
end
kernel.syscalls["require"] = require
_G.require = function(...) return syscall.require(...) end

View File

@@ -0,0 +1,301 @@
--: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
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 "file"
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
data[".meta"]=strFile(buildMeta({
stdin="/proc/self/fd/0",
stdout="/proc/self/fd/1",
log="/var/log/syslog.log"
}))
if kernel.EFI:getEEPROM() then
function data.eeprom(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
local ptr,eeprom=1,kernel.EFI:getEEPROM()
return {
read=function(amount)
ptr=ptr+amount
return eeprom:sub(ptr-amount, ptr)
end
}
elseif mode=="w" then
if kernel.uid~=0 then error("EACCES") end
local firstwrite=true
return {
write=function(data)
if firstwrite then
kernel.EFI:setEEPROM(data)
else
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
end
end
}
elseif mode=="a" then
if kernel.uid~=0 then error("EACCES") end
return {
write=function(data)
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
end
}
else error("EACCES")
end
end
end
end
if kernel.EFI:getNvram() then
function data.nvram(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
local ptr,nvram=1,kernel.EFI:getNvram()
return {
read=function(amount)
ptr=ptr+amount
return nvram:sub(ptr-amount, ptr)
end
}
elseif mode=="w" then
if kernel.uid~=0 then error("EACCES") end
local firstwrite=true
return {
write=function(data)
if firstwrite then
kernel.EFI:setNvram(data)
else
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
end
end
}
elseif mode=="a" then
if kernel.uid~=0 then error("EACCES") end
return {
write=function(data)
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
end
}
else error("EACCES")
end
end
end
end
data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}}
data["input"]={}
kernel.devfs={}
kernel.devfs.data=data
kernel.devfs.proxy=proxy
kernel.disks["devfs0000"]=proxy

View File

@@ -6,7 +6,7 @@ local data = {}
proxy.address = "procfs0000" proxy.address = "procfs0000"
proxy.isvirt = true proxy.isvirt = true
proxy.isReadOnly = function() return false end proxy.isReadOnly = function() return true end
proxy.spaceUsed = function() return 0 end proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("EACCES") end proxy.makeDirectory = function() error("EACCES") end
@@ -114,7 +114,8 @@ local function newtaskproxy(task)
}, },
children={ children={
[".meta"]=strFile(buildMeta(children)) [".meta"]=strFile(buildMeta(children))
} },
pid=strFile(task.pid)
} }
end end
@@ -132,6 +133,17 @@ function proxy:open(path, mode)
if type(step[steps[#steps]]) == "function" then if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode) return step[steps[#steps]]("open", mode)
end 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 else
for i=1, #steps-1 do for i=1, #steps-1 do
local dat = step[steps[i]] local dat = step[steps[i]]
@@ -166,6 +178,21 @@ function proxy:type(path, mode)
if type(step[steps[#steps]]) == "table" then if type(step[steps[#steps]]) == "table" then
return "directory" return "directory"
end 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 else
for i=1, #steps-1 do for i=1, #steps-1 do
local dat = step[steps[i]] local dat = step[steps[i]]
@@ -186,7 +213,7 @@ function proxy:list(path)
local steps = kernel.vfs.splitPath(path) local steps = kernel.vfs.splitPath(path)
local step = data local step = data
if #steps == 0 then if #steps == 0 then
return table.merge(table.keys(data),table.keys(kernel.tasks)) return table.merge(table.keys(data),table.keys(kernel.tasks),{"self"})
end end
if tonumber(steps[1]) then if tonumber(steps[1]) then
local task=kernel.tasks[steps[1]] local task=kernel.tasks[steps[1]]
@@ -200,6 +227,18 @@ function proxy:list(path)
if type(step[steps[#steps]]) == "table" then if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]]) return table.keys(step[steps[#steps]])
end 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 else
for i=1, #steps-1 do for i=1, #steps-1 do
local dat = step[steps[i]] local dat = step[steps[i]]
@@ -220,7 +259,7 @@ function proxy:fileExists(path)
return ok return ok
end end
data.uptime=simpleFile(function()return tostring(kernel.computer:getUptime())end,function()error("EACCES")end) data.uptime=simpleFile(function()return tostring(kernel.EFI:getUptime())end,function()error("EACCES")end)
kernel.procfs={} kernel.procfs={}
kernel.procfs.data=data kernel.procfs.data=data
kernel.procfs.proxy=proxy kernel.procfs.proxy=proxy

View File

@@ -0,0 +1,141 @@
--:Minify:--
local kernel = ...
kernel.vfs.remove("/tmp")
kernel.vfs.mkdir("/tmp")
if not kernel.config.tmpToDisk then
local proxy = {}
local data = {}
proxy.address = "tmpfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
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
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,
close = function() end,
}
elseif mode == "w" then
step[filename] = ""
local buf = {}
return {
write = function(str)
buf[#buf + 1] = str
end,
close = function()
step[filename] = table.concat(buf)
end,
}
elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end
return {
write = function(str)
step[filename] = step[filename] .. str
end,
close = function() end,
}
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)
local t = self:type(path)
return t == "file" or t == "directory"
end
kernel.disks["tmpfs0000"] = proxy
else
kernel.log("tmpToDisk enabled, skipping tmpfs module")
kernel.log("tmpfs0000 will passthrough to /tmp on the disk")
end

View File

@@ -21,14 +21,15 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
end end
if not semicolon_pos or semicolon_pos == 3 then if not semicolon_pos or semicolon_pos == 3 then
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8) kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 0xFF8800)
else else
local id = line:sub(3, semicolon_pos - 1) local id = line:sub(3, semicolon_pos - 1)
local path = trim(line:sub(semicolon_pos + 1)) local path = trim(line:sub(semicolon_pos + 1))
kernel.log("Mounted "..id.." to "..path) kernel.log("Mounting '"..id.."' to '"..path.."'")
if id ~= "$" then if id ~= "$" then
kernel.vfs.mount(path, id) kernel.vfs.mount(path, id)
end end
kernel.log("Mounted "..id.." to "..path)
end end
end end
end end

View File

@@ -12,13 +12,24 @@ function signal.sigsend(pid, sig)
end end
function signal.sigcatch(handler) function signal.sigcatch(handler)
kernel.currentTask.sigh=handler local task=kernel.currentTask
if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end task.sigh=handler
if not task.sigq then task.sigq={} end
local handle={
error="",
active=true
}
if task.sigd then task.sigd.active=false; end
task.sigd=handle
return handle
end end
function signal.sigignore() function signal.sigignore()
kernel.currentTask.sigh=nil local task=kernel.currentTask
kernel.currentTask.sigq=nil task.sigh=nil
task.sigq=nil
if task.sigd then task.sigd.active=false end
task.sigd=nil
end end
local s=kernel.syscalls local s=kernel.syscalls

View File

@@ -0,0 +1,194 @@
--:Minify:--
local kernel = ...
local socket = {}
socket.handlers = {}
kernel.socket = socket
local P = kernel.vfs.P
local sys = kernel.syscalls
function socket.registerProtocal(proto, handler)
socket.handlers[proto] = handler
end
local function getHandler(address)
for proto, handler in pairs(socket.handlers) do
if string.hasPrefix(address, proto) then
return handler
end
end
end
function socket.socket(domain, socktype)
local fd = kernel.vfs.newfd({
handle = {},
type = "socket",
refcount = 1,
socket = {
connected = false,
protocol = nil,
address = nil
},
meta = {
owner = kernel.currentTask.uid,
group = kernel.currentTask.uid,
etype = 2,
perms =
P.OWNER_R +
P.OWNER_W +
P.GROUP_R +
P.GROUP_W
},
isvirt = true
})
local fdo = kernel.currentTask.fd[fd]
fdo.handle.read = function()
return ""
end
fdo.handle.write = function()
return nil, "ENOTCONN"
end
fdo.handle.close = function()
fdo.socket.connected = false
return true
end
return fd
end
function socket.connect(fd, address)
local fdo = kernel.currentTask.fd[fd]
if not fdo then
return nil, "EBADF"
end
if fdo.type ~= "socket" then
return nil, "ENOTSOCK"
end
local handler =
getHandler(address)
if not handler then
return nil, "EPROTONOSUPPORT"
end
fdo.socket.protocol = handler
fdo.socket.address = address
local ok, err =
handler.connect(fd, address)
if not ok then
return nil, err
end
fdo.socket.connected = true
return true
end
function socket.listen(fd, backlog)
local fdo =
kernel.currentTask.fd[fd]
if not fdo then
return nil, "EBADF"
end
if not fdo.handle.listen then
return nil, "EOPNOTSUPP"
end
return fdo.handle.listen(backlog)
end
function socket.send(fd, data)
local fdo =
kernel.currentTask.fd[fd]
if not fdo then
return nil, "EBADF"
end
if fdo.type ~= "socket" then
return nil, "ENOTSOCK"
end
if not fdo.socket.connected then
return nil, "ENOTCONN"
end
if not fdo.handle.write then
return nil, "EOPNOTSUPP"
end
return fdo.handle.write(data)
end
function socket.recv(fd, amount)
local fdo =
kernel.currentTask.fd[fd]
if not fdo then
return nil, "EBADF"
end
if fdo.type ~= "socket" then
return nil, "ENOTSOCK"
end
if not fdo.socket.connected then
return nil, "ENOTCONN"
end
if not fdo.handle.read then
return nil, "EOPNOTSUPP"
end
return fdo.handle.read(amount)
end
function socket.sockshutdown(fd)
local fdo =
kernel.currentTask.fd[fd]
if not fdo then
return nil, "EBADF"
end
if fdo.type ~= "socket" then
return nil, "ENOTSOCK"
end
if fdo.handle.close then
return fdo.handle.close()
end
fdo.socket.connected = false
return true
end
sys.socket = socket.socket
sys.connect = socket.connect
sys.listen = socket.listen
sys.send = socket.send
sys.recv = socket.recv
sys.sockshutdown = socket.sockshutdown
kernel.log("Loaded socket module")

View File

@@ -0,0 +1,4 @@
--:Minify:--
local kernel=...
kernel.unixSockets={}
local socket=kernel.socket

View File

@@ -0,0 +1,129 @@
--:Minify:--
local kernel = ...
local vterms = {}
local function createVt(id, width, height)
local vt = {
id = id,
width = width,
height = height,
buffer = {},
cursorX = 1,
cursorY = 1,
fgColor = 0xFFFFFF,
bgColor = 0x000000,
obj = {}
}
for y = 1, height do
vt.buffer[y] = {}
for x = 1, width do
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
end
local function scroll(lines)
for _ = 1, lines do
table.remove(vt.buffer, 1)
vt.buffer[vt.height] = {}
for x = 1, vt.width do
vt.buffer[vt.height][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
end
end
function vt.obj:write(content)
local x, y = vt.cursorX, vt.cursorY
if x>vt.width then return end
if y>vt.height then return end
for i = 1, #content do
local c = content:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
for _ = 1, spaces do
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
x = x + 1
if x > vt.width then
x = 1
y = y + 1
end
end
elseif c == "\b" then
if x > 1 then
x = x - 1
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
else
if x <= vt.width and y <= vt.height then
vt.buffer[y][x] = {char = c, fgColor = vt.fgColor, bgColor = vt.bgColor}
x = x + 1
end
end
if x > vt.width then
x = 1
y = y + 1
end
if y > vt.height then
scroll(1)
y = vt.height
end
end
vt.cursorX, vt.cursorY = x, y
end
function vt.obj:spos(x, y)
vt.cursorX = tonumber(x)
vt.cursorY = tonumber(y)
end
function vt.obj:gpos()
return tostring(vt.cursorX)..";"..tostring(vt.cursorY)
end
function vt.obj:sfgc(color)
vt.fgColor = tonumber(color)
end
function vt.obj:sbgc(color)
vt.bgColor = tonumber(color)
end
function vt.obj:gfgc()
return vt.fgColor
end
function vt.obj:gbgc()
return vt.bgColor
end
function vt.obj:gplt()
return 24
end
function vt.obj:clear()
for y = 1, vt.height do
for x = 1, vt.width do
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
end
vt.cursorX, vt.cursorY = 1, 1
end
function vt.obj:isvirt()
return true
end
function vt.obj:gctrl()
return serializeBool(vt.ctrl)..";"..serializeBool(vt.alt)
end
return vt
end

View File

@@ -139,7 +139,7 @@ end
if not blake2s then error("Failed to load blake2s") end if not blake2s then error("Failed to load blake2s") end
if not kernel.vfs.exists("/etc/pam.d/secret") then if not kernel.vfs.exists("/etc/pam.d/secret") then
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT") kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT", "WARN", 0xFF8800)
local key = "" local key = ""
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
local handle = kernel.vfs.open("/etc/pam.d/secret", "w") local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
@@ -150,16 +150,11 @@ end
local pepper = getFile("/etc/pam.d/secret") local pepper = getFile("/etc/pam.d/secret")
local function genSalt() local function genSalt()
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" return toHex(math.random(0,2^32))
local s = ""
for i = 1, 16 do
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
end
return s
end end
local function hashPassword(password, salt) local function hashPassword(password, salt)
local key = (pepper .. salt):sub(1, 32) local key = (pepper .. salt)
return blake2s(password, key) return blake2s(password, key)
end end
@@ -236,19 +231,18 @@ local function nextUID()
return max + 1 return max + 1
end end
function auth.login(username, password) function auth.login(uid, password)
if type(username) ~= "string" or type(password) ~= "string" then if type(uid) ~= "number" or type(password) ~= "string" then
return nil, "Authentication failure" return nil, "Authentication failure"
end end
local entry = getPasswdByUsername(username) local entry = getPasswdByUID(uid)
if not entry then if not entry then
-- timing attack resistance -- timing attack resistance
hashPassword(password, "aaaaaaaaaaaaaaaa") hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure" return nil, "Authentication failure"
end end
local uid = tonumber(entry[1])
local sEntry = getShadowByUID(uid) local sEntry = getShadowByUID(uid)
if not sEntry then if not sEntry then
hashPassword(password, "aaaaaaaaaaaaaaaa") hashPassword(password, "aaaaaaaaaaaaaaaa")
@@ -273,7 +267,7 @@ function auth.login(username, password)
_task.egid = tonumber(entry[2]) or uid _task.egid = tonumber(entry[2]) or uid
end end
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")") kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. getPasswdByUID(uid)[3] .. ")")
return true return true
end end

View File

@@ -21,7 +21,7 @@ local function loadExecutable(path)
local env = kernel.freshUserEnv() local env = kernel.freshUserEnv()
local func, err = load(data, "@" .. path, "t", env) local func, err = load(data, "@" .. path, "t", env)
if not func then error("ENOEXEC: " .. tostring(err)) end if not func then kernel.log("Failed to load executable: " .. tostring(err).. " : ".. tostring(path)); error("ENOEXEC: " .. tostring(err)) end
local meta = kernel.vfs.lstat(path) local meta = kernel.vfs.lstat(path)
local suid_set = bit_is_set(meta.perms, 6) local suid_set = bit_is_set(meta.perms, 6)
@@ -41,11 +41,11 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if not ok then if not ok then
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 2) kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 0xFF0000)
elseif err then elseif err then
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "INFO") kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "DBUG", 0x00FFFF)
else else
kernel.log("Task " .. tostring(id) .. " exited without code", "INFO") kernel.log("Task " .. tostring(id) .. " exited without code", "DBUG", 0x00FFFF)
end end
end end
@@ -110,7 +110,7 @@ function sys.execspawn(path, name, envars, args, tgid)
kernel.log( kernel.log(
"execspawn: suid exec '" .. path .. "execspawn: suid exec '" .. path ..
"' caller_uid=" .. tostring(real_uid) .. "' caller_uid=" .. tostring(real_uid) ..
" -> euid=" .. tostring(euid), "INFO" " -> euid=" .. tostring(euid)
) )
end end
@@ -136,9 +136,9 @@ function sys.exec(path, args, envars)
local ok, err = xpcall(func, debug.traceback, table.unpack(task.args)) local ok, err = xpcall(func, debug.traceback, table.unpack(task.args))
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if not ok then if not ok then
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 2) kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 0xFF0000)
else else
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "INFO") kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "DBUG", 0x00FFFF)
end end
end end
if type(err) == "number" then tasks[tostring(task.pid)].exit = err end if type(err) == "number" then tasks[tostring(task.pid)].exit = err end
@@ -155,8 +155,7 @@ end
function sys.sleep(s) function sys.sleep(s)
kernel.currentTask.status = "S" kernel.currentTask.status = "S"
kernel.currentTask.sleep = kernel.computer:time() + s * 1000 kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
coroutine.yield()
end end
function sys.getTask(pid) function sys.getTask(pid)
@@ -204,12 +203,16 @@ function sys.collect(pid)
end end
end end
function sys.kill(pid) function sys.kill(pid, force)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status == "Z" then elseif task.status == "Z" then
return false, "Task is already dead" return false, "Task is already dead"
elseif task.status == "D" and not force then
return false, "Cannot kill task waiting for IO"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
end end
local caller = kernel.currentTask local caller = kernel.currentTask
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
@@ -224,10 +227,16 @@ function sys.stop(pid)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "R" then elseif task.status ~= "R" and task.status ~= "S" then
return false, "Cannot stop non-running task" return false, "Cannot stop non-running task"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else
if task.status == "S" then
task.status = "ST"
else else
task.status = "T" task.status = "T"
end
return true return true
end end
end end
@@ -236,10 +245,16 @@ function sys.continue(pid)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "T" then elseif task.status ~= "T" and task.status ~= "ST" then
return false, "Task is not stopped" return false, "Task is not stopped"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else
if task.status == "ST" then
task.status = "S"
else else
task.status = "R" task.status = "R"
end
return true return true
end end
end end
@@ -260,9 +275,9 @@ function sys.exit(code)
local task = kernel.currentTask local task = kernel.currentTask
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if code then if code then
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "INFO") kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "DBUG", 0x00FFFF)
else else
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "INFO") kernel.log("Task " .. tostring(task.pid) .. " exited without code", "DBUG", 0x00FFFF)
end end
end end
tasks[tostring(task.pid)].status = "Z" tasks[tostring(task.pid)].status = "Z"
@@ -290,6 +305,7 @@ function sys.getuid() return kernel.currentTask.uid end
local function reapDeadTasks() local function reapDeadTasks()
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then if task.status == "Z" and not task.reapTime then
if task.pid == 1 then kernel.panic("Attempted to gc init!") end
task.coro = nil task.coro = nil
task.ivs = nil task.ivs = nil
task.vs = nil task.vs = nil
@@ -303,9 +319,9 @@ local function reapDeadTasks()
task.syscallReturn = nil task.syscallReturn = nil
task.sleep = nil task.sleep = nil
task.fd = nil task.fd = nil
task.reapTime = kernel.computer:time() + 30000 task.reapTime = kernel.EFI:getEpochMs() + 30000
elseif task.reapTime and kernel.computer:time() > task.reapTime elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
and task.status == "Z" then and task.status == "Z" then
for _, child in ipairs(task.children) do for _, child in ipairs(task.children) do
child.parent = tasks["1"] child.parent = tasks["1"]
@@ -335,7 +351,23 @@ local k_max = 0.5
local B = 0.01 local B = 0.01
function kernel.main() function kernel.main()
kernel.log("Starting main loop...")
kernel.saveLog()
local stopLog=5
local logTasks=100
while not kernel.exitMain do while not kernel.exitMain do
if kernel.config.logTasks then
if logTasks<0 then
kernel.log("Active Tasks:")
for i,v in pairs(tasks) do
kernel.log(v.name.." : "..v.status.." : "..tostring(v.pid).." : "..tostring(v.exit))
end
kernel.log("[END BLOCK]")
logTasks=100
end
logTasks=logTasks-1
end
local N = 0 local N = 0
local Tmin_hit = 0 local Tmin_hit = 0
local Tmax_hit = 0 local Tmax_hit = 0
@@ -343,55 +375,94 @@ function kernel.main()
local taskTimes = {} local taskTimes = {}
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "S" and kernel.computer:time() >= task.sleep then if kernel.exitMain then break end
kernel.currentTask = task
kernel.uid = task.euid or task.uid
kernel.process = task.name
if task.status == "S" and kernel.EFI:getEpochMs() >= task.sleep then
task.status = "R" task.status = "R"
task.sleep = 0 task.sleep = 0
end end
if task.status == "R" then if task.status == "ST" and kernel.EFI:getEpochMs() >= task.sleep then
kernel.currentTask = task task.status = "T"
task.sleep = 0
end
kernel.uid = task.euid or task.uid if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
kernel.process = task.name task.status = "D"
task.sleep = 0
end
if task.status == "D" then
if task.ksh then
coroutine.resume(task.ksh)
if coroutine.status(task.ksh) == "dead" then
task.ksh = nil
if task.status == "D" then
task.status = "R"
end
if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " IO wait completed", "DBUG", 0x00FFFF)
local sysret = task.syscallReturn
for i = 2, #sysret do
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
end
end
end
end
end
if task.status == "R" then
N = N + 1 N = N + 1
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha))) task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
if task.sigq and #task.sigq ~= 0 and task.sigh then if task.sigq and #task.sigq ~= 0 and task.sigh then
local coro = coroutine.create(task.sigh) local coro = coroutine.create(task.sigh)
local sigret = { coroutine.resume(coro, table.remove(task.sigq, 1)) } local status,err
while coroutine.status(coro) ~= "dead" do if kernel.config.preempt then
if sigret[1] == false then break end status,err=coroutine.resumeWithTimeout(coro, 100, table.remove(task.sigq, 1))
if sigret[2] == "syscall" then
local scname = sigret[3]
local sysret
if kernel.syscalls[scname] then
sysret = { xpcall(kernel.syscalls[scname], debug.traceback, table.unpack(sigret, 4)) }
else else
sysret = { false, "Unknown syscall: " .. tostring(scname) } status,err=coroutine.resume(coro, table.remove(task.sigq, 1))
end end
if not sysret[1] then if status=="error" or status==false then
sigret = { coroutine.resume(coro, false, sysret[2]) } task.sigd.error=err or "Unknown"
else task.sigd.active=false
sigret = { coroutine.resume(coro, true, table.unpack(sysret, 2)) } task.sigh=nil
end task.sigq=nil
else task.sigd=nil
sigret = { coroutine.resume(coro) } elseif status=="success" or status==true then
if err=="syscall" then
task.sigd.error="Cannot execute syscalls from signals"
task.sigd.active=false
task.sigh=nil
task.sigq=nil
task.sigd=nil
end end
end end
end end
if task.status == "R" then if task.status == "R" then
local startTime = kernel.computer:time() local startTime = kernel.EFI:getEpochMs()
local ret local ret
if stopLog > 0 then
kernel.log("Running task " .. tostring(task.pid) .. " (" .. task.name .. ") with time slice " .. string.format("%.3f", task.timeSlice) .. "s", "DBUG", 0x00FFFF)
end
if kernel.config.preempt then if kernel.config.preempt then
if not task.debugger then
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
else else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) } ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end end
else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end
local elapsed = kernel.computer:time() - startTime local elapsed = kernel.EFI:getEpochMs() - startTime
task.lastTime = elapsed task.lastTime = elapsed
task.totalTime = (task.totalTime or 0) + elapsed task.totalTime = (task.totalTime or 0) + elapsed
task.numRuns = (task.numRuns or 0) + 1 task.numRuns = (task.numRuns or 0) + 1
@@ -403,7 +474,7 @@ function kernel.main()
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
if ret[1] == "error" or ret[1] == false then if ret[1] == "error" or ret[1] == false then
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 2) kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 0xFF0000)
task.status = "Z" task.status = "Z"
task.exit = "processHandlerException: " .. tostring(ret[2]) task.exit = "processHandlerException: " .. tostring(ret[2])
@@ -418,9 +489,9 @@ function kernel.main()
local scname = ret[3] local scname = ret[3]
if kernel.syscalls[scname] then if kernel.syscalls[scname] then
if kernel.config.debugSyscalls then if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 5) kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 0x00FFFF)
for i = 4, #ret do for i = 4, #ret do
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 5) kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 0x00FFFF)
end end
end end
@@ -428,12 +499,12 @@ function kernel.main()
if kernel.config.debugSyscalls then if kernel.config.debugSyscalls then
if not sysret[1] then if not sysret[1] then
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 2) kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 0xFF0000)
else else
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 5) kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 0x00FFFF)
for i = 2, #sysret do for i = 2, #sysret do
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i]) local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 5) kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
end end
end end
end end
@@ -466,6 +537,11 @@ function kernel.main()
end end
reapDeadTasks() reapDeadTasks()
if stopLog > 0 then
kernel.log("Executed " .. N .. " tasks, avg time: " .. string.format("%.2f", T_prev_avg) .. "ms, var: " .. string.format("%.2f", T_prev_var) .. ", B: " .. string.format("%.5f", B))
stopLog = stopLog - 1
kernel.saveLog()
end
end end
end end

View File

@@ -9,8 +9,20 @@ sysc["gpio_write"]=function(pin, data)
end end
end end
sysc["gpio_writeAnalog"]=function(pin, data)
if kernel.gpio[pin] then
return kernel.gpio[pin]("wa", data)
end
end
sysc["gpio_read"]=function(pin) sysc["gpio_read"]=function(pin)
if kernel.gpio[pin] then if kernel.gpio[pin] then
return kernel.gpio[pin]("r") return kernel.gpio[pin]("r")
end end
end end
sysc["gpio_readAnalog"]=function(pin)
if kernel.gpio[pin] then
return kernel.gpio[pin]("ra")
end
end

View File

@@ -9,11 +9,13 @@ if not initOk then
end end
local handle = kernel.vfs.open(kernel.config.initPath, "r") local handle = kernel.vfs.open(kernel.config.initPath, "r")
if not handle then kernel.panic("Failed to open "..kernel.config.initPath) end
local data = kernel.vfs.read(handle, 1024 * 1024 * 4) local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
if not handle then kernel.panic("Failed to read "..kernel.config.initPath) end
kernel.vfs.close(handle) kernel.vfs.close(handle)
local initFunc, err = load(data, "@sysinit", "t", kernel._U) local initFunc, err = load(data, "@sysinit", "t", kernel._U)
if not initFunc then error("Failed to load init system: " .. err) end if not initFunc then kernel.PANIC("Failed to load init system: " .. err) end
kernel.tasks["1"] = { kernel.tasks["1"] = {
coro = coroutine.create(function() coro = coroutine.create(function()

View File

@@ -0,0 +1,11 @@
local kernel = ...
kernel.processes.kgc = function()
while true do
for i,v in pairs(kernel.reqcache) do
if v.expires and kernel.EFI:getEpochMs() > v.expires then
kernel.reqcache[i] = nil
end
end
kernel.sleep(5000)
end
end

View File

@@ -1,9 +1,10 @@
--:Minify:-- --:Minify:--
local kernel = ... local kernel = ...
if not kernel.vfs.exists("/root") then kernel.vfs.mkdir("/root") end
kernel.processes.login = function() kernel.processes.login = function()
local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login") local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login")
if not ok then if not ok then
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2) kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 0xFF0000)
end end
end end

View File

@@ -0,0 +1,121 @@
--:Minify:--
local kernel = ...
if kernel.firstBoot then
local P = kernel.vfs.P
local PERM = kernel.vfs.PERM
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
+ P.GROUP_R + P.GROUP_X
+ P.WORLD_R + P.WORLD_X
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755
local META_VERSION = 0x02
local rootDisk = kernel.disks["$"]
local function makeEntry(name, etype, owner, group, perms, cmeta)
cmeta = cmeta or ""
local plo = perms % 256
local phi = math.floor(perms / 256) % 256
local olo = (owner or 0) % 256
local ohi = math.floor((owner or 0) / 256) % 256
local glo = (group or 0) % 256
local ghi = math.floor((group or 0) / 256) % 256
return string.char(#name) .. name
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
.. string.char(#cmeta) .. cmeta
end
local REG = 0x00
local function mergeMeta(dir, entries)
local diskDir = dir
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
local existing = {}
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
if rok and rf then
local raw = rf.read(65535)
if rf.close then rf.close() end
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
end
for _, e in ipairs(entries) do
local name = e[1]
local etype = e[2] or REG
local owner = e[3] or 0
local group = e[4] or 0
local perms = e[5] or RWX_RX_RX
local cmeta = e[6] or ""
existing[name] = {
etype = etype,
owner = owner,
group = group,
perms = perms,
cmeta = cmeta,
}
end
local data = string.char(META_VERSION)
for name, m in pairs(existing) do
data = data .. makeEntry(
name,
m.etype or REG,
m.owner or 0,
m.group or 0,
m.perms or RWX_RX_RX,
m.cmeta or ""
)
end
local ok, err = pcall(function()
local f = rootDisk:open(metaPath, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 0xFF8800)
end
end
kernel.log("Seeding filesystem permissions...")
mergeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX},
{"dev", REG, 0, 0, RWXRWXRWX},
{"etc", REG, 0, 0, RWX_RX_RX},
{"home", REG, 0, 0, RWX_RX_RX},
{"lib", REG, 0, 0, RWX_RX_RX},
{"proc", REG, 0, 0, RWXRWXRWX},
{"root", REG, 0, 0, RW____ },
{"sbin", REG, 0, 0, RWX_RX_RX},
{"tmp", REG, 0, 0, RWXRWXRWX},
{"usr", REG, 0, 0, RWX_RX_RX},
{"var", REG, 0, 0, RWXRWXRWX},
{"opt", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/bin", {
{"login", REG, 0, 0, SUID_755 },
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
})
mergeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R },
{"shadow", REG, 0, 0, RW____ },
{"pam.d", REG, 0, 0, RW____ },
})
mergeMeta("/etc/pam.d", {
{"secret", REG, 0, 0, RW____},
})
kernel.log("Filesystem permissions seeded.")
end

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,134 +0,0 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "tmpfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
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
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,
close = function() end,
}
elseif mode == "w" then
step[filename] = ""
local buf = {}
return {
write = function(str)
buf[#buf + 1] = str
end,
close = function()
step[filename] = table.concat(buf)
end,
}
elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end
return {
write = function(str)
step[filename] = step[filename] .. str
end,
close = function() end,
}
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)
local t = self:type(path)
return t == "file" or t == "directory"
end
kernel.disks["tmpfs0000"] = proxy

View File

@@ -1,556 +0,0 @@
--:Minify:--
-- Supports:
-- AF_UNIX - local IPC via /var/run/*.sock paths
-- AF_INET - network sockets with three backends:
-- rednet://0.0.B.C or rednet+PROTO://0.0.B.C -> CC rednet (computer B*256+C)
-- modem://0.0.B.C -> raw CC modem frames
-- http://host/path or https://... -> HTTP via CC http API
-- A.B.C.D (dotted quad, non-zero A) -> HTTP
--
-- Socket lifecycle:
-- fd = syscall.socket(domain, socktype) -- "unix"/"inet", "stream"/"dgram"
-- syscall.bind(fd, address) -- server: claim address
-- syscall.listen(fd, backlog) -- server: mark as listening
-- cfd = syscall.accept(fd) -- server: get connected client fd (blocking poll)
-- syscall.connect(fd, address) -- client: connect to server
-- syscall.send(fd, data) -- send bytes
-- syscall.recv(fd, len) -- receive bytes (blocking poll, returns "" on nothing)
-- syscall.sockshutdown(fd) -- half-close send side
-- -- normal vfs.close(fd) closes the socket
local kernel = ...
local sockets = {}
local unixSocks = {}
local nextSockId = 1
local function allocSockId()
local id = nextSockId
nextSockId = nextSockId + 1
return id
end
local function parseAddress(addr)
if not addr then error("EINVAL") end
if addr:sub(1,1) == "/" or addr:sub(1,5) == "unix:" then
local path = addr:sub(1,5) == "unix:" and addr:sub(6) or addr
return { backend="unix", path=path }
end
local rproto, raddr = addr:match("^rednet%+?([^:/]*)://(.+)$")
if raddr then
local a,b,c,d = raddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
if not a then error("EINVAL: bad rednet address " .. raddr) end
local compId = tonumber(c)*256 + tonumber(d)
return { backend="rednet", compId=compId,
protocol=(rproto ~= "" and rproto or "hyperion") }
end
local maddr = addr:match("^modem://(.+)$")
if maddr then
local a,b,c,d = maddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
if not a then error("EINVAL: bad modem address " .. maddr) end
local compId = tonumber(c)*256 + tonumber(d)
local port = tonumber(maddr:match(":(%d+)$")) or 0
return { backend="modem", compId=compId, port=port }
end
local scheme, rest = addr:match("^(https?)://(.+)$")
if scheme then
return { backend=scheme, url=addr }
end
local a,b,c,d = addr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)")
if a and tonumber(a) ~= 0 then
return { backend="http", url="http://" .. addr }
end
error("EINVAL: unrecognised address format: " .. tostring(addr))
end
local rednetOpen = false
local function ensureRednet()
if rednetOpen then return end
local rn = kernel.apis and kernel.apis.rednet
if not rn then error("ENODEV: no rednet API available") end
local peripheral = kernel.apis.peripheral
if peripheral then
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
if peripheral.getType(name) == "modem" then
pcall(rn.open, name)
end
end
end
rednetOpen = true
end
local function getModem()
local peripheral = kernel.apis and kernel.apis.peripheral
if not peripheral then error("ENODEV") end
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
if peripheral.getType(name) == "modem" then
local m = peripheral.wrap(name)
if m then return m, name end
end
end
error("ENODEV: no modem peripheral found")
end
local function pumpEvents()
local ev = kernel.computer:getMachineEvent()
while ev do
if ev == "rednet_message" then
for _, sock in pairs(sockets) do
if sock.backend == "rednet" and sock.bound then
if sock.address.protocol == tostring(select(4, table.unpack({ev}))) or
sock.address.protocol == "hyperion" then
end
end
end
end
ev = kernel.computer:getMachineEvent()
end
end
local function pollEvent()
local results = table.pack(kernel.computer:getMachineEvent())
if results.n == 0 or results[1] == nil then return nil end
return results
end
local function dispatchEvent(ev)
if not ev then return end
local evtype = ev[1]
if evtype == "rednet_message" then
local senderId = ev[2]
local message = ev[3]
local protocol = ev[4] or "hyperion"
for _, sock in pairs(sockets) do
if sock.backend == "rednet" and (sock.listening or sock.connected) then
if sock.address and sock.address.protocol == protocol then
table.insert(sock.rxbuf, { from=senderId, data=message })
end
end
end
elseif evtype == "modem_message" then
local channel = ev[3]
local msg = ev[5]
local fromCh = ev[4]
for _, sock in pairs(sockets) do
if sock.backend == "modem" and sock.modemChannel == channel then
table.insert(sock.rxbuf, { from=fromCh, data=msg })
end
end
elseif evtype == "http_success" then
local url = ev[2]
local handle = ev[3]
for _, sock in pairs(sockets) do
if sock.backend == "http" or sock.backend == "https" then
if sock.pendingUrl == url then
local body = handle.readAll and handle.readAll() or ""
handle.close()
table.insert(sock.rxbuf, { data=body, done=true })
sock.pendingUrl = nil
sock.connected = true
end
end
end
elseif evtype == "http_failure" then
local url = ev[2]
local err = ev[3]
for _, sock in pairs(sockets) do
if (sock.backend == "http" or sock.backend == "https") and
sock.pendingUrl == url then
sock.error = err
sock.pendingUrl = nil
end
end
end
end
local function pumpAll()
local ev = pollEvent()
while ev do
dispatchEvent(ev)
ev = pollEvent()
end
end
local function newSocket(domain, socktype)
local sock = {
id = allocSockId(),
domain = domain, -- "unix" | "inet"
socktype = socktype, -- "stream" | "dgram"
backend = nil,
state = "idle", -- idle | bound | listening | connected | closed
rxbuf = {},
txbuf = {},
backlog = {},
address = nil,
peer = nil,
modemChannel = nil,
modem = nil,
pendingUrl = nil,
bound = false,
listening = false,
connected = false,
error = nil,
}
sockets[sock.id] = sock
return sock
end
local sockSend, sockClose
local function socketToFd(sock)
return {
isSocket = true,
sockId = sock.id,
mode = "rw",
meta = { etype=0, owner=0, group=0, perms=0x1FF, cmeta="" },
type = "socket",
refcount = 1,
handle = {
read = function(count)
pumpAll()
if #sock.rxbuf == 0 then return "" end
local item = table.remove(sock.rxbuf, 1)
local data = type(item) == "table" and (item.data or "") or tostring(item)
if count and #data > count then
table.insert(sock.rxbuf, 1, { data=data:sub(count+1), from=item.from })
data = data:sub(1, count)
end
return data
end,
write = function(data)
if sock.state == "closed" then error("EBADF") end
return sockSend(sock, data)
end,
close = function()
sockClose(sock)
end,
}
}
end
sockSend = function(sock, data)
if sock.backend == "unix" then
local peer = sock.peer
if not peer then error("ENOTCONN") end
table.insert(peer.rxbuf, { data=data })
return #data
elseif sock.backend == "rednet" then
ensureRednet()
local rn = kernel.apis.rednet
rn.send(sock.address.compId, data, sock.address.protocol)
return #data
elseif sock.backend == "modem" then
local modem = sock.modem
if not modem then error("ENOTCONN") end
modem.transmit(sock.address.port, sock.modemChannel or 0, data)
return #data
elseif sock.backend == "http" or sock.backend == "https" then
local http = kernel.apis and kernel.apis.http
if not http then error("ENODEV: no http API") end
local url = sock.address.url
local ok, err = pcall(http.request, url, data, {
["Content-Type"] = "application/octet-stream"
})
if not ok then error("ENETDOWN: " .. tostring(err)) end
sock.pendingUrl = url
return #data
end
error("EPROTONOSUPPORT")
end
sockClose = function(sock)
if sock.state == "closed" then return end
sock.state = "closed"
if sock.backend == "unix" then
if sock.peer then
sock.peer.peer = nil
sock.peer.state = "closed"
end
if sock.bound and sock.address and sock.address.path then
unixSocks[sock.address.path] = nil
end
elseif sock.backend == "modem" and sock.modem and sock.modemChannel then
pcall(sock.modem.close, sock.modemChannel)
elseif sock.backend == "rednet" then
end
sockets[sock.id] = nil
end
kernel.syscalls["socket"] = function(domain, socktype)
domain = domain or "inet"
socktype = socktype or "stream"
if domain ~= "unix" and domain ~= "inet" then error("EAFNOSUPPORT") end
if socktype ~= "stream" and socktype ~= "dgram" then error("EPROTOTYPE") end
local sock = newSocket(domain, socktype)
local fdobj = socketToFd(sock)
local fd = kernel.vfs.newfd(fdobj)
return fd
end
kernel.syscalls["bind"] = function(fd, address)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if sock.bound then error("EINVAL") end
local parsed = parseAddress(address)
if parsed.backend == "unix" then
local existing = unixSocks[parsed.path]
if existing then
if existing.state == "closed" then
unixSocks[parsed.path] = nil
else
error("EADDRINUSE")
end
end
sock.backend = "unix"
sock.address = parsed
sock.bound = true
sock.state = "bound"
unixSocks[parsed.path] = sock
elseif parsed.backend == "rednet" then
ensureRednet()
sock.backend = "rednet"
sock.address = parsed
sock.bound = true
sock.state = "bound"
elseif parsed.backend == "modem" then
local modem, side = getModem()
sock.backend = "modem"
sock.address = parsed
sock.modem = modem
sock.modemChannel = parsed.port
sock.bound = true
sock.state = "bound"
modem.open(parsed.port)
else
error("EOPNOTSUPP: cannot bind to " .. parsed.backend .. " address")
end
end
kernel.syscalls["listen"] = function(fd, backlog)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if not sock.bound then error("EDESTADDRREQ") end
sock.listening = true
sock.state = "listening"
sock.maxBacklog = backlog or 5
end
kernel.syscalls["accept"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if not sock.listening then error("EINVAL") end
local deadline = kernel.computer:time() + 30000
while #sock.backlog == 0 do
pumpAll()
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
coroutine.yield()
end
local clientSock = table.remove(sock.backlog, 1)
local cfdobj = socketToFd(clientSock)
local newfd = kernel.vfs.newfd(cfdobj)
return newfd
end
kernel.syscalls["connect"] = function(fd, address)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if sock.connected then error("EISCONN") end
local parsed = parseAddress(address)
sock.address = parsed
sock.backend = parsed.backend
if parsed.backend == "unix" then
local server = unixSocks[parsed.path]
if not server then error("ECONNREFUSED") end
if not server.listening then error("ECONNREFUSED") end
if #server.backlog >= (server.maxBacklog or 5) then error("ECONNREFUSED") end
local serverPeer = newSocket("unix", sock.socktype)
serverPeer.backend = "unix"
serverPeer.connected = true
serverPeer.state = "connected"
serverPeer.peer = sock
sock.peer = serverPeer
sock.connected = true
sock.state = "connected"
table.insert(server.backlog, serverPeer)
elseif parsed.backend == "rednet" then
ensureRednet()
sock.connected = true
sock.state = "connected"
elseif parsed.backend == "modem" then
local modem, side = getModem()
local replyChannel = math.random(1024, 65534)
sock.modem = modem
sock.modemChannel = replyChannel
sock.connected = true
sock.state = "connected"
modem.open(replyChannel)
elseif parsed.backend == "http" or parsed.backend == "https" then
sock.connected = true
sock.state = "connected"
else
error("EAFNOSUPPORT")
end
end
kernel.syscalls["send"] = function(fd, data)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
return sockSend(sock, data)
end
kernel.syscalls["recv"] = function(fd, maxlen, timeout_ms)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
local deadline = kernel.computer:time() + (timeout_ms or 10000)
while #sock.rxbuf == 0 do
pumpAll()
if #sock.rxbuf > 0 then break end
if sock.state == "closed" or sock.error then
if sock.error then error("ECONNRESET: " .. tostring(sock.error)) end
return ""
end
if kernel.computer:time() > deadline then return "" end
coroutine.yield()
end
local item = table.remove(sock.rxbuf, 1)
local data = type(item) == "table" and (item.data or "") or tostring(item)
if maxlen and #data > maxlen then
table.insert(sock.rxbuf, 1, { data=data:sub(maxlen+1), from=item and item.from })
data = data:sub(1, maxlen)
end
return data
end
kernel.syscalls["sockshutdown"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if sock then sockClose(sock) end
end
kernel.syscalls["getpeername"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock or not sock.connected then error("ENOTCONN") end
if sock.address then return sock.address end
return nil
end
kernel.syscalls["getsockname"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
return sock.address
end
kernel.syscalls["httpget"] = function(url, headers)
local http = kernel.apis and kernel.apis.http
if not http then error("ENODEV: no http API") end
local ok, err = pcall(http.request, url, nil, headers)
if not ok then error("ENETDOWN: " .. tostring(err)) end
local deadline = kernel.computer:time() + 15000
while true do
local ev = pollEvent()
if ev then
if ev[1] == "http_success" and ev[2] == url then
local handle = ev[3]
local body = handle.readAll and handle.readAll() or ""
handle.close()
return body
elseif ev[1] == "http_failure" and ev[2] == url then
error("ECONNREFUSED: " .. tostring(ev[3]))
else
dispatchEvent(ev)
end
end
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
coroutine.yield()
end
end
kernel.syscalls["resolve"] = function(hostname)
if hostname:match("^%d+%.%d+%.%d+%.%d+$") then return hostname end
local a,b,c,d = hostname:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
if a and tonumber(a) == 0 and tonumber(b) == 0 then
return hostname
end
local http = kernel.apis and kernel.apis.http
if not http then error("ENODEV: no http API for DNS") end
local url = "https://cloudflare-dns.com/dns-query?name=" .. hostname .. "&type=A"
local body = kernel.syscalls["httpget"](url, {
["Accept"] = "application/dns-json"
})
local ip = body:match('"type":1[^}]*"data":"([%d%.]+)"')
if not ip then error("ENOENT: could not resolve " .. hostname) end
return ip
end
kernel.sockets = sockets
kernel.unixSockets = unixSocks
kernel.log("Loaded socket module")

View File

@@ -1,120 +0,0 @@
--:Minify:--
local kernel = ...
local P = kernel.vfs.P
local PERM = kernel.vfs.PERM
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
+ P.GROUP_R + P.GROUP_X
+ P.WORLD_R + P.WORLD_X
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755
local META_VERSION = 0x02
local rootDisk = kernel.disks["$"]
local function makeEntry(name, etype, owner, group, perms, cmeta)
cmeta = cmeta or ""
local plo = perms % 256
local phi = math.floor(perms / 256) % 256
local olo = (owner or 0) % 256
local ohi = math.floor((owner or 0) / 256) % 256
local glo = (group or 0) % 256
local ghi = math.floor((group or 0) / 256) % 256
return string.char(#name) .. name
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
.. string.char(#cmeta) .. cmeta
end
local REG = 0x00
local function mergeMeta(dir, entries)
local diskDir = dir
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
local existing = {}
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
if rok and rf then
local raw = rf.read(65535)
if rf.close then rf.close() end
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
end
for _, e in ipairs(entries) do
local name = e[1]
local etype = e[2] or REG
local owner = e[3] or 0
local group = e[4] or 0
local perms = e[5] or RWX_RX_RX
local cmeta = e[6] or ""
existing[name] = {
etype = etype,
owner = owner,
group = group,
perms = perms,
cmeta = cmeta,
}
end
local data = string.char(META_VERSION)
for name, m in pairs(existing) do
data = data .. makeEntry(
name,
m.etype or REG,
m.owner or 0,
m.group or 0,
m.perms or RWX_RX_RX,
m.cmeta or ""
)
end
local ok, err = pcall(function()
local f = rootDisk:open(metaPath, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
end
end
if kernel.firstBoot then
kernel.log("Seeding filesystem permissions...")
mergeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX},
{"dev", REG, 0, 0, RWXRWXRWX},
{"etc", REG, 0, 0, RWX_RX_RX},
{"home", REG, 0, 0, RWX_RX_RX},
{"lib", REG, 0, 0, RWX_RX_RX},
{"root", REG, 0, 0, RW____ },
{"sbin", REG, 0, 0, RWX_RX_RX},
{"tmp", REG, 0, 0, RWXRWXRWX},
{"usr", REG, 0, 0, RWX_RX_RX},
{"var", REG, 0, 0, RWX_RX_RX},
{"opt", REG, 0, 0, RWXRWXRWX},
})
mergeMeta("/bin", {
{"login", REG, 0, 0, SUID_755 },
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
})
mergeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R },
{"shadow", REG, 0, 0, RW____ },
{"pam.d", REG, 0, 0, RW____ },
})
mergeMeta("/etc/pam.d", {
{"secret", REG, 0, 0, RW____},
})
kernel.log("Filesystem permissions seeded.")
end

View File

@@ -1,5 +1,5 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/tty/1", "r") --stdin (fd 0) syscall.open("/dev/input/keyboard1", "r") --stdin (fd 0)
syscall.open("/dev/tty/1", "w") --stdout (fd 1) syscall.open("/dev/tty/1", "w") --stdout (fd 1)
syscall.open("/dev/null", "w") --stderr (fd 2) syscall.open("/dev/null", "w") --stderr (fd 2)
@@ -37,9 +37,9 @@ local function firstBoot()
syscall.devctl(1, "clear") syscall.devctl(1, "clear")
syscall.devctl(1, "spos", 1, 1) syscall.devctl(1, "spos", 1, 1)
syscall.devctl(1, "sfgc", 3) syscall.devctl(1, "sfgc", 0x00FF00)
syscall.write(1, "HyperionOS First Boot Setup\n") syscall.write(1, "HyperionOS First Boot Setup\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
syscall.write(1, "No root password is set. Please create one now.\n\n") syscall.write(1, "No root password is set. Please create one now.\n\n")
while true do while true do
@@ -49,25 +49,25 @@ local function firstBoot()
local pw2 = readLine("*") local pw2 = readLine("*")
if pw1 ~= pw2 then if pw1 ~= pw2 then
syscall.devctl(1, "sfgc", 2) syscall.devctl(1, "sfgc", 0xFF0000)
syscall.write(1, "Passwords do not match. Try again.\n\n") syscall.write(1, "Passwords do not match. Try again.\n\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
elseif #pw1 < 6 then elseif #pw1 < 6 then
syscall.devctl(1, "sfgc", 2) syscall.devctl(1, "sfgc", 0xFF0000)
syscall.write(1, "Password too short (minimum 6 characters).\n\n") syscall.write(1, "Password too short (minimum 6 characters).\n\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
else else
local ok, err = syscall.setpassword(0, pw1) local ok, err = syscall.setpassword(0, pw1)
if ok then if ok then
syscall.devctl(1, "sfgc", 3) syscall.devctl(1, "sfgc", 0x00FF00)
syscall.write(1, "Root password set.\n\n") syscall.write(1, "Root password set.\n\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
sleep(0.5) sleep(0.5)
break break
else else
syscall.devctl(1, "sfgc", 2) syscall.devctl(1, "sfgc", 0xFF0000)
syscall.write(1, "Error: " .. tostring(err) .. "\n") syscall.write(1, "Error: " .. tostring(err) .. "\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
end end
end end
end end
@@ -112,8 +112,8 @@ end
local function doLogin() local function doLogin()
syscall.devctl(1, "clear") syscall.devctl(1, "clear")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
syscall.devctl(1, "sbgc", 16) syscall.devctl(1, "sbgc", 0x000000)
syscall.devctl(1, "spos", 1, 1) syscall.devctl(1, "spos", 1, 1)
local hostname = syscall.getHostname() or "hyperion" local hostname = syscall.getHostname() or "hyperion"
@@ -122,16 +122,17 @@ local function doLogin()
local attempts = 0 local attempts = 0
while attempts < MAX_ATTEMPTS do while attempts < MAX_ATTEMPTS do
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
syscall.write(1, "Username: ") syscall.write(1, "Username: ")
local username = readLine(nil) local username = readLine(nil)
if username == "" then goto continue end if username ~= "" then
syscall.write(1, "Password: ") syscall.write(1, "Password: ")
local password = readLine("*") local password = readLine("*")
local uid = syscall.getuidbyname(username)
local ok, err = syscall.login(username, password) local ok, err = syscall.login(uid, password)
if ok then if ok then
local uid = syscall.getuid() local uid = syscall.getuid()
local pwent = syscall.getpasswd(uid) local pwent = syscall.getpasswd(uid)
@@ -139,9 +140,9 @@ local function doLogin()
local shell = (pwent and pwent.shell) or "/bin/hysh" local shell = (pwent and pwent.shell) or "/bin/hysh"
local homedir = (pwent and pwent.homedir) or "/" local homedir = (pwent and pwent.homedir) or "/"
syscall.devctl(1, "sfgc", 3) syscall.devctl(1, "sfgc", 0x00FF00)
syscall.write(1, "\nWelcome, " .. username .. "!\n") syscall.write(1, "\nWelcome, " .. username .. "!\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
sleep(0.3) sleep(0.3)
spawnShell(username, uid, shell, homedir) spawnShell(username, uid, shell, homedir)
@@ -149,17 +150,16 @@ local function doLogin()
else else
attempts = attempts + 1 attempts = attempts + 1
sleep(1) sleep(1)
syscall.devctl(1, "sfgc", 2) syscall.devctl(1, "sfgc", 0xFF0000)
syscall.write(1, "Login incorrect.\n\n") syscall.write(1, "Login incorrect.\n\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
end
end
end end
::continue:: syscall.devctl(1, "sfgc", 0xFF0000)
end
syscall.devctl(1, "sfgc", 2)
syscall.write(1, "Maximum login attempts exceeded.\n") syscall.write(1, "Maximum login attempts exceeded.\n")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
sleep(5) sleep(5)
end end

View File

@@ -5,15 +5,15 @@ if not users or #users == 0 then
return return
end end
syscall.devctl(1,"sfgc",13) syscall.devctl(1,"sfgc",0xDBDBDB)
print(string.format("%-6s %-6s %-16s %-20s %s", "UID", "GID", "Username", "Home", "Shell")) print(string.format("%-6s %-6s %-16s %-20s %s", "UID", "GID", "Username", "Home", "Shell"))
print(string.rep("-", 65)) print(string.rep("-", 65))
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
for _, u in ipairs(users) do for _, u in ipairs(users) do
local lock_marker = u.locked and " [locked]" or "" local lock_marker = u.locked and " [locked]" or ""
if u.locked then syscall.devctl(1,"sfgc",2) end if u.locked then syscall.devctl(1,"sfgc",0xFF0000) end
print(string.format("%-6d %-6d %-16s %-20s %s%s", print(string.format("%-6d %-6d %-16s %-20s %s%s",
u.uid, u.gid, u.username, u.homedir, u.shell, lock_marker)) u.uid, u.gid, u.username, u.homedir, u.shell, lock_marker))
if u.locked then syscall.devctl(1,"sfgc",1) end if u.locked then syscall.devctl(1,"sfgc",0xFFFFFF) end
end end

View File

@@ -69,17 +69,17 @@ if #args == 0 and not opts.o then
end end
if next(loDevs) == nil then if next(loDevs) == nil then
syscall.devctl(1, "sfgc", 14) syscall.devctl(1, "sfgc", 0xFF00FF)
print("(no loop devices attached)") print("(no loop devices attached)")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end
for id, info in pairs(loDevs) do for id, info in pairs(loDevs) do
local colour = info.mode == "image" and 5 or 4 local colour = info.mode == "image" and 0x00FFFF or 0x0000FF
syscall.devctl(1, "sfgc", colour) syscall.devctl(1, "sfgc", colour)
printInline(info.mode.." "..id) printInline(info.mode.." "..id)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
print(" on "..info.path) print(" on "..info.path)
end end
return return
@@ -119,9 +119,9 @@ if opts.o and opts.o:lower() == "loop" then
print(name..": mount: "..msg); syscall.exit(1); return print(name..": mount: "..msg); syscall.exit(1); return
end end
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": "..loopId.." mounted at "..dest) print(name..": "..loopId.." mounted at "..dest)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end
@@ -141,9 +141,9 @@ if #args == 2 then
print(name..": "..msg); syscall.exit(1); return print(name..": "..msg); syscall.exit(1); return
end end
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": "..loopId.." mounted at "..dest) print(name..": "..loopId.." mounted at "..dest)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end

View File

@@ -9,7 +9,7 @@ local currentUid = syscall.getuid()
local targetUid local targetUid
if targetName then if targetName then
targetUid = syscall.getuid(targetName) targetUid = syscall.getuid()
if not targetUid then if not targetUid then
print("passwd: user '" .. targetName .. "' does not exist") print("passwd: user '" .. targetName .. "' does not exist")
syscall.exit(1); return syscall.exit(1); return
@@ -36,7 +36,7 @@ if currentUid ~= 0 then
if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end
else cur=cur..ch; syscall.write(1,"*") end else cur=cur..ch; syscall.write(1,"*") end
end end
local ok, err = syscall.elevate(targetName, cur) local ok, err = syscall.login(targetUid, cur)
if not ok then if not ok then
sleep(1) sleep(1)
print("passwd: authentication failure") print("passwd: authentication failure")

View File

@@ -1,7 +1,15 @@
--:Minify:-- --:Minify:--
local targetUser = ({ ... })[1] or "root" local targetUser = ({ ... })[1]
local currentUid = syscall.getuid() local currentUid = syscall.getuid()
local targetUid = syscall.getuidbyname(targetUser) if syscall.geteuid()~=0 then
syscall.exec("/bin/su", {...})
end
local targetUid
if targetUser then
targetUid = syscall.getuidbyname(targetUser)
else
targetUid = 0
end
if not targetUid then if not targetUid then
print("su: user '" .. targetUser .. "' does not exist") print("su: user '" .. targetUser .. "' does not exist")
@@ -25,20 +33,21 @@ if currentUid ~= 0 then
end end
end end
local ok, err = syscall.elevate(targetUser, pw) local ok, err = syscall.login(targetUid, pw)
if not ok then if not ok then
sleep(1) sleep(1)
print("su: Authentication failure") print("su: Authentication failure")
syscall.exit(1) syscall.exit(1)
return return
end end
end else
syscall.setuid(targetUid) syscall.setuid(targetUid)
end
local pwent = syscall.getpasswd(targetUid) local pwent = syscall.getpasswd(targetUid)
local shell = (pwent and pwent.shell) or "/bin/hysh" local shell = (pwent and pwent.shell) or "/bin/hysh"
local homedir = (pwent and pwent.homedir) or "/" local homedir = (pwent and pwent.homedir) or "/"
local username= (pwent and pwent.username)or "Unknown"
local ok_cd, err_cd = pcall(syscall.chdir, homedir) local ok_cd, err_cd = pcall(syscall.chdir, homedir)
if not ok_cd then if not ok_cd then
@@ -46,7 +55,7 @@ if not ok_cd then
syscall.chdir(homedir) syscall.chdir(homedir)
end end
syscall.setEnviron("HOME", homedir) syscall.setEnviron("HOME", homedir)
syscall.setEnviron("USER", targetUser) syscall.setEnviron("USER", username)
syscall.setEnviron("SHELL", shell) syscall.setEnviron("SHELL", shell)
local ok, err = pcall(syscall.exec, shell) local ok, err = pcall(syscall.exec, shell)

View File

@@ -55,7 +55,7 @@ if currentUid ~= 0 then
end end
end end
local ok, err = syscall.elevate("root", pw) local ok, err = syscall.login(0, pw)
if not ok then if not ok then
sleep(1) sleep(1)
print("sudo: Authentication failure") print("sudo: Authentication failure")

View File

@@ -59,9 +59,9 @@ if opts.l then
end end
print(name..": "..msg); syscall.exit(1); return print(name..": "..msg); syscall.exit(1); return
end end
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": detached "..id) print(name..": detached "..id)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end
@@ -95,17 +95,17 @@ if not ok then
print(name..": "..msg); syscall.exit(1); return print(name..": "..msg); syscall.exit(1); return
end end
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": unmounted "..mpt) print(name..": unmounted "..mpt)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
if loopIdToDetach then if loopIdToDetach then
for _, id in ipairs(loopIdToDetach) do for _, id in ipairs(loopIdToDetach) do
local dok = pcall(syscall.lodetach, id) local dok = pcall(syscall.lodetach, id)
if dok then if dok then
syscall.devctl(1, "sfgc", 14) syscall.devctl(1, "sfgc", 0xFF00FF)
print(name..": auto-detached "..id) print(name..": auto-detached "..id)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
end end
end end
end end

View File

@@ -0,0 +1,3 @@
{
"/bin/sh": "/bin/hysh"
}

View File

@@ -107,6 +107,7 @@ local COMMANDS = {
{ name="whoami", usage="whoami", desc="Print the current username.", flags={} }, { name="whoami", usage="whoami", desc="Print the current username.", flags={} },
{ name="id", usage="id [username]", desc="Print user identity (uid, gid).", flags={} }, { name="id", usage="id [username]", desc="Print user identity (uid, gid).", flags={} },
{ name="ps", usage="ps", desc="List running tasks with pid, user, name, and status.", flags={} }, { name="ps", usage="ps", desc="List running tasks with pid, user, name, and status.", flags={} },
{ name="hyperctl", usage="hyperctl <command> [service]", desc="Control system services (start, stop, restart, status, list, enable, disable).", flags={} },
{ name="hostname", usage="hostname [NAME]", desc="Print or set the system hostname.", flags={} }, { name="hostname", usage="hostname [NAME]", desc="Print or set the system hostname.", flags={} },
{ name="uname", usage="uname [-asnrm]", desc="Print system information (OS name, hostname, release, machine).", flags={ { name="uname", usage="uname [-asnrm]", desc="Print system information (OS name, hostname, release, machine).", flags={
{"-a","Print all fields"}, {"-a","Print all fields"},
@@ -261,16 +262,16 @@ local function render()
syscall.write(1, "\n") syscall.write(1, "\n")
end end
end end
syscall.devctl(1, "sfgc", 16) syscall.devctl(1, "sfgc", 0x000000)
syscall.devctl(1, "sbgc", 13) syscall.devctl(1, "sbgc", 0xDBDBDB)
local pct = math.floor(math.min(100, (scroll + pageSize) / totalLines * 100)) 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] ", local status = string.format(" help -- line %d/%d (%d%%) [up/down: scroll q: quit] ",
scroll + 1, totalLines, pct) scroll + 1, totalLines, pct)
if #status > screenW then status = status:sub(1, screenW) end if #status > screenW then status = status:sub(1, screenW) end
syscall.devctl(1, "spos", 1, screenH) syscall.devctl(1, "spos", 1, screenH)
syscall.write(1, status .. string.rep(" ", screenW - #status)) syscall.write(1, status .. string.rep(" ", screenW - #status))
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
syscall.devctl(1, "sbgc", 16) syscall.devctl(1, "sbgc", 0x000000)
dirty = false dirty = false
end end
@@ -281,7 +282,7 @@ if totalLines <= pageSize then
syscall.devctl(1, "sfgc", line[2]) syscall.devctl(1, "sfgc", line[2])
syscall.write(1, line[1] .. "\n") syscall.write(1, line[1] .. "\n")
end end
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end
@@ -305,5 +306,5 @@ end
syscall.devctl(1, "clear") syscall.devctl(1, "clear")
syscall.devctl(1, "spos", 1, 1) syscall.devctl(1, "spos", 1, 1)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
syscall.devctl(1, "sbgc", 16) syscall.devctl(1, "sbgc", 0x000000)

View File

@@ -3,11 +3,11 @@
-- 1=white, 2=red, 3=green, 4=blue, 5=cyan, 6=magenta, 7=yellow -- 1=white, 2=red, 3=green, 4=blue, 5=cyan, 6=magenta, 7=yellow
-- 8=orange, 9=lime, 10=lightcyan, 11=brown, 12=darkgrey, 13=lightgrey, 14=purple, 15=chartreuse, 16=black -- 8=orange, 9=lime, 10=lightcyan, 11=brown, 12=darkgrey, 13=lightgrey, 14=purple, 15=chartreuse, 16=black
local C_LOGO = 5 -- cyan local C_LOGO = 0x00FFFF -- cyan
local C_WHITE = 1 -- white local C_WHITE = 0xFFFFFF -- white
local C_LABEL = 13 -- light grey (key names) local C_LABEL = 0xDBDBDB -- light grey (key names)
local C_SEP = 12 -- dark grey (---- separator) local C_SEP = 0x6D6D6D -- dark grey (---- separator)
local C_USER = 3 -- green (user@host) local C_USER = 0x00FF00 -- green (user@host)
local function c(col) syscall.devctl(1, "sfgc", col) end local function c(col) syscall.devctl(1, "sfgc", col) end

View File

@@ -1,14 +1,14 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/tty/1","r") --stdin (Device 0) local success, errorMsg = xpcall(function()
syscall.open("/dev/input/keyboard1","r") --stdin (Device 0)
syscall.open("/dev/tty/1","w") --stdout (Device 1) syscall.open("/dev/tty/1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2) syscall.open("/dev/null","w") --stderr (device 2)
local success, errorMsg = xpcall(function()
local fs = require("fs") local fs = require("fs")
syscall.devctl(1,"clear") syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.devctl(1,"spos",1,1) syscall.devctl(1,"spos",1,1)
print("HyperionOS hysh Shell") print("HyperionOS hysh Shell")
@@ -26,8 +26,27 @@ else
end end
local oldWD = "" local oldWD = ""
local colors = {
0xFFFFFF,
0xFF0000,
0x00FF00,
0x0000FF,
0x00FFFF,
0xFF00FF,
0xFFFF00,
0xFF6D00,
0x6DFF55,
0x24FFFF,
0x924900,
0x6D6D55,
0xDBDBAA,
0x6D00FF,
0xB6FF00,
0x000000
}
for i = 1, 16 do for i = 1, 16 do
syscall.devctl(1,"sbgc",i); printInline(" ") syscall.devctl(1,"sbgc",colors[i]); printInline(" ")
end end
print("\n") print("\n")
@@ -221,7 +240,7 @@ end
builtinCmds.clear = function() builtinCmds.clear = function()
syscall.devctl(1,"clear") syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.devctl(1,"spos",1,1) syscall.devctl(1,"spos",1,1)
end end
@@ -429,7 +448,7 @@ builtinCmds.head = function(...)
local multi = #files > 1 local multi = #files > 1
local function dohead(text, label) local function dohead(text, label)
if multi then if multi then
syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0x0000FF); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",0xFFFFFF)
end end
local count = 0 local count = 0
for line in (text.."\n"):gmatch("([^\n]*)\n") do for line in (text.."\n"):gmatch("([^\n]*)\n") do
@@ -468,7 +487,7 @@ builtinCmds.tail = function(...)
local multi = #files > 1 local multi = #files > 1
local function dotail(text, label) local function dotail(text, label)
if multi then if multi then
syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0x0000FF); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",0xFFFFFF)
end end
local lines = splitlines(text) local lines = splitlines(text)
local start = math.max(1, #lines - n + 1) local start = math.max(1, #lines - n + 1)
@@ -808,6 +827,20 @@ builtinCmds.df = function(...)
end end
end end
builtinCmds.ops = function()
for i,v in pairs(math) do
print(i)
end
end
builtinCmds.reboot = function ()
syscall.reboot()
end
builtinCmds.shutdown = function ()
syscall.shutdown()
end
local function listDir(dir, prefix) local function listDir(dir, prefix)
local ok, entries = pcall(syscall.listdir, dir) local ok, entries = pcall(syscall.listdir, dir)
if not ok or not entries then return {} end if not ok or not entries then return {} end
@@ -962,14 +995,15 @@ local function doTabComplete(input, cursorPos)
end end
end end
local lastghost=""
local function getUserInput() local function getUserInput()
syscall.devctl(1,"sfgc",3) syscall.devctl(1,"sfgc",0x00FF00)
syscall.write(1, userhost) syscall.write(1, userhost)
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.write(1, ":") syscall.write(1, ":")
syscall.devctl(1,"sfgc",10) syscall.devctl(1,"sfgc",0x24FFFF)
syscall.write(1, syscall.getcwd()) syscall.write(1, syscall.getcwd())
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.write(1, "$ ") syscall.write(1, "$ ")
local curOffsetStr = syscall.devctl(1, "gpos") local curOffsetStr = syscall.devctl(1, "gpos")
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1)) local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
@@ -1020,21 +1054,21 @@ local function getUserInput()
syscall.devctl(1,"spos",curOffsetX,curOffsetY) syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, string.sub(input, 1, cursorPos-1)) syscall.write(1, string.sub(input, 1, cursorPos-1))
if blinkState then if blinkState then
syscall.devctl(1,"sfgc",16); syscall.devctl(1,"sbgc",1) syscall.devctl(1,"sfgc",0x000000); syscall.devctl(1,"sbgc",0xFFFFFF)
end end
if cursorPos > #input then if cursorPos > #input then
syscall.write(1, " ") syscall.write(1, " ")
else else
syscall.write(1, string.sub(input, cursorPos, cursorPos)) syscall.write(1, string.sub(input, cursorPos, cursorPos))
end end
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16) syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",0x000000)
local after = string.sub(input, cursorPos+1) local after = string.sub(input, cursorPos+1)
syscall.write(1, after) syscall.write(1, after)
local ghost = getGhostSuffix() local ghost = getGhostSuffix()
if #ghost > 0 then if #ghost > 0 then
syscall.devctl(1,"sfgc",14) syscall.devctl(1,"sfgc",0x6D00FF)
syscall.write(1, ghost) syscall.write(1, ghost)
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.write(1, " ") syscall.write(1, " ")
else else
syscall.write(1, " ") syscall.write(1, " ")
@@ -1082,10 +1116,10 @@ local function getUserInput()
syscall.write(1, string.rep(" ", tw)) syscall.write(1, string.rep(" ", tw))
syscall.devctl(1,"spos",1,py) syscall.devctl(1,"spos",1,py)
end end
syscall.devctl(1,"sfgc",3); syscall.write(1, userhost) syscall.devctl(1,"sfgc",0x00FF00); syscall.write(1, userhost)
syscall.devctl(1,"sfgc",1); syscall.write(1, ":") syscall.devctl(1,"sfgc",0xFFFFFF); syscall.write(1, ":")
syscall.devctl(1,"sfgc",10); syscall.write(1, syscall.getcwd()) syscall.devctl(1,"sfgc",0x00FFFF); syscall.write(1, syscall.getcwd())
syscall.devctl(1,"sfgc",1); syscall.write(1, "$ ") syscall.devctl(1,"sfgc",0xFFFFFF); syscall.write(1, "$ ")
posStr = syscall.devctl(1, "gpos") posStr = syscall.devctl(1, "gpos")
sep = posStr:find(";") sep = posStr:find(";")
curOffsetX = tonumber(posStr:sub(1, sep-1)) curOffsetX = tonumber(posStr:sub(1, sep-1))
@@ -1098,7 +1132,7 @@ local function getUserInput()
cursorPos=cursorPos-1;dirty=true cursorPos=cursorPos-1;dirty=true
end end
elseif key=="\n" then elseif key=="\n" then
syscall.devctl(1,"sfgc",1);syscall.devctl(1,"sbgc",16) syscall.devctl(1,"sfgc",0xFFFFFF);syscall.devctl(1,"sbgc",0x000000)
syscall.devctl(1,"spos",curOffsetX,curOffsetY) syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, input.." \n") syscall.write(1, input.." \n")
return input return input
@@ -1114,7 +1148,7 @@ local function getUserInput()
end end
local function printError(progName, msg) local function printError(progName, msg)
syscall.devctl(1,"sfgc",2) syscall.devctl(1,"sfgc",0xFF0000)
local s = tostring(msg) local s = tostring(msg)
local line, rest = s:match("%]:(%d+): (.+)$") local line, rest = s:match("%]:(%d+): (.+)$")
if not line then line, rest = s:match(":(%d+): (.+)$") end if not line then line, rest = s:match(":(%d+): (.+)$") end
@@ -1123,12 +1157,12 @@ local function printError(progName, msg)
else else
print(progName..": "..s) print(progName..": "..s)
end end
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
end end
local function runCommand(command) local function runCommand(command)
do do
local func = load("return "..command, "@equation", "t", {}) local func = load("return "..command, "@equation", "t", math)
if func then if func then
local ok, result = pcall(func) local ok, result = pcall(func)
if ok and type(result)=="number" then print(result); return end if ok and type(result)=="number" then print(result); return end
@@ -1171,13 +1205,13 @@ local function runCommand(command)
local xok, xerr = pcall(syscall.access, cmdPath, "x") local xok, xerr = pcall(syscall.access, cmdPath, "x")
if not xok then if not xok then
syscall.devctl(1,"sfgc",2); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFF0000); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",0xFFFFFF)
return return
end end
local proc = syscall.spawn(function() local proc = syscall.spawn(function()
-- Open standard fds so programs that don't do it themselves work correctly. -- Open standard fds so programs that don't do it themselves work correctly.
syscall.open("/dev/tty/1", "r") -- fd 0 stdin syscall.open("/dev/input/keyboard1", "r") -- fd 0 stdin
syscall.open("/dev/tty/1", "w") -- fd 1 stdout syscall.open("/dev/tty/1", "w") -- fd 1 stdout
syscall.open("/dev/null", "w") -- fd 2 stderr syscall.open("/dev/null", "w") -- fd 2 stderr
-- exec replaces this coroutine's code with a fresh isolated environment -- exec replaces this coroutine's code with a fresh isolated environment
@@ -1195,8 +1229,8 @@ local function runCommand(command)
if terminate then if terminate then
local ok2 = syscall.kill(proc) local ok2 = syscall.kill(proc)
if ok2 then if ok2 then
syscall.devctl(1,"sbgc",16); syscall.devctl(1,"sfgc",2) syscall.devctl(1,"sbgc",16); syscall.devctl(1,"sfgc",0xFF0000)
print("\nProgram Terminated."); syscall.devctl(1,"sfgc",1) print("\nProgram Terminated."); syscall.devctl(1,"sfgc",0xFFFFFF)
end end
terminate = false; break terminate = false; break
end end
@@ -1218,8 +1252,8 @@ end, debug.traceback)
if not success then if not success then
syscall.log("Error running shell: "..errorMsg, "ERROR") syscall.log("Error running shell: "..errorMsg, "ERROR")
syscall.devctl(1,"sfgc",2) syscall.devctl(1,"sfgc",0xFF0000)
syscall.devctl(1,"sbgc",16) syscall.devctl(1,"sbgc",0x000000)
print() print()
print("Error running shell: ") print("Error running shell: ")
print(errorMsg) print(errorMsg)

View File

@@ -110,9 +110,9 @@ if opts.x then
pcall(syscall.umount, tmpMnt) pcall(syscall.umount, tmpMnt)
pcall(syscall.lodetach, loopId) pcall(syscall.lodetach, loopId)
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": extracted "..count.." file(s) to "..destPath) print(name..": extracted "..count.." file(s) to "..destPath)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end
@@ -150,8 +150,8 @@ local lineCount = 0
for _ in imgStr:gmatch("\n") do lineCount = lineCount + 1 end for _ in imgStr:gmatch("\n") do lineCount = lineCount + 1 end
local byteCount = #imgStr local byteCount = #imgStr
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": image written to "..imgPath) print(name..": image written to "..imgPath)
syscall.devctl(1, "sfgc", 14) syscall.devctl(1, "sfgc", 0x6D00FF)
print(string.format(" %d records, %d bytes", lineCount - 1, byteCount)) print(string.format(" %d records, %d bytes", lineCount - 1, byteCount))
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)

View File

@@ -60,18 +60,18 @@ if opts.l then
local info = devs[id] local info = devs[id]
local mode = (type(info) == "table" and info.mode) or "bind" local mode = (type(info) == "table" and info.mode) or "bind"
local path = (type(info) == "table" and info.path) or tostring(info) local path = (type(info) == "table" and info.path) or tostring(info)
local colour = mode == "image" and 5 or 4 local colour = mode == "image" and 0x00FFFF or 0x0000FF
syscall.devctl(1, "sfgc", 3) syscall.devctl(1, "sfgc", 0x00FF00)
printInline(string.format("%-10s", id)) printInline(string.format("%-10s", id))
syscall.devctl(1, "sfgc", colour) syscall.devctl(1, "sfgc", colour)
printInline(string.format("%-7s", "["..mode.."]")) printInline(string.format("%-7s", "["..mode.."]"))
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
print(" "..path) print(" "..path)
end end
if not any then if not any then
syscall.devctl(1, "sfgc", 14) syscall.devctl(1, "sfgc", 0x6D00FF)
print(name..": no loop devices attached") print(name..": no loop devices attached")
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
end end
return return
end end
@@ -92,9 +92,9 @@ if opts.d then
end end
print(name..": "..msg); syscall.exit(1); return print(name..": "..msg); syscall.exit(1); return
end end
syscall.devctl(1, "sfgc", 10) syscall.devctl(1, "sfgc", 0x00FFFF)
print(name..": detached "..id) print(name..": detached "..id)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
return return
end end

View File

@@ -95,6 +95,8 @@ local screenSizeStr = syscall.devctl(1, "size")
local sizeX = tonumber(screenSizeStr:match("^(%d+)")) or 80 local sizeX = tonumber(screenSizeStr:match("^(%d+)")) or 80
local list = fs.list(dir) local list = fs.list(dir)
list[#list+1] = "."
list[#list+1] = ".."
if not cloptions.a then if not cloptions.a then
for i = #list, 1, -1 do for i = #list, 1, -1 do
if list[i]:sub(1, 1) == "." then table.remove(list, i) end if list[i]:sub(1, 1) == "." then table.remove(list, i) end
@@ -110,10 +112,12 @@ if cloptions.l then
local stat = syscall.lstat and syscall.lstat(fullPath) or syscall.stat(fullPath) local stat = syscall.lstat and syscall.lstat(fullPath) or syscall.stat(fullPath)
local isDir = fs.isDir(fullPath) local isDir = fs.isDir(fullPath)
local isSym = stat and stat.etype == 0x01 local isSym = stat and stat.etype == 0x01
local isSock = stat and stat.etype == 0x02
local typeChar local typeChar
if isSym then typeChar = "l" if isSym then typeChar = "l"
elseif isDir then typeChar = "d" elseif isDir then typeChar = "d"
elseif isSock then typeChar = "s"
else typeChar = "-" end else typeChar = "-" end
local pstr local pstr
@@ -136,26 +140,30 @@ if cloptions.l then
printInline(tostring(mtime) .. " ") printInline(tostring(mtime) .. " ")
if isSym then if isSym then
syscall.devctl(1, "sfgc", 6) syscall.devctl(1, "sfgc", 0x00FFFF)
printInline(v) printInline(v)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
local ok, target = pcall(syscall.readlink, fullPath) local ok, target = pcall(syscall.readlink, fullPath)
if ok then if ok then
printInline(" -> ") printInline(" -> ")
local targetExists = pcall(syscall.stat, fullPath) local targetExists = pcall(syscall.stat, fullPath)
syscall.devctl(1, "sfgc", targetExists and 6 or 2) syscall.devctl(1, "sfgc", targetExists and 0x00FFFF or 0xFF0000)
printInline(target) printInline(target)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
end end
elseif isDir then elseif isDir then
syscall.devctl(1, "sfgc", 14) syscall.devctl(1, "sfgc", 0x6D00FF)
printInline(v) printInline(v)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
elseif isSock then
syscall.devctl(1, "sfgc", 0xFF00FF)
printInline(v)
syscall.devctl(1, "sfgc", 0xFFFFFF)
else else
local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1) local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
syscall.devctl(1, "sfgc", isExec and 3 or 1) syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
printInline(v) printInline(v)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
end end
print("") print("")
end end
@@ -175,16 +183,18 @@ for i, v in ipairs(list) do
local isSym = stat and stat.etype == 0x01 local isSym = stat and stat.etype == 0x01
if isSym then if isSym then
syscall.devctl(1, "sfgc", 6) syscall.devctl(1, "sfgc", 0x00FFFF)
elseif isDir then elseif isDir then
syscall.devctl(1, "sfgc", 14) syscall.devctl(1, "sfgc", 0x6D00FF)
elseif isSock then
syscall.devctl(1, "sfgc", 0xFF00FF)
else else
local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1) local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
syscall.devctl(1, "sfgc", isExec and 3 or 1) syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
end end
printInline(v) printInline(v)
syscall.devctl(1, "sfgc", 1) syscall.devctl(1, "sfgc", 0xFFFFFF)
printInline((" "):rep(colWidth - #v)) printInline((" "):rep(colWidth - #v))
if i % numCols == 0 then print("") end if i % numCols == 0 then print("") end

View File

@@ -139,12 +139,9 @@ local function parseCommands(src)
local c = src:sub(pos, pos) local c = src:sub(pos, pos)
if c == "\n" or c == ";" then if c == "\n" or c == ";" then
pos = pos + 1 pos = pos + 1
goto continue elseif c == "#" then
end
if c == "#" then
while pos <= len and src:sub(pos,pos) ~= "\n" do pos = pos + 1 end while pos <= len and src:sub(pos,pos) ~= "\n" do pos = pos + 1 end
goto continue else
end
local addr1, addr2 local addr1, addr2
addr1, pos = parseAddr(src, pos) addr1, pos = parseAddr(src, pos)
@@ -203,8 +200,7 @@ local function parseCommands(src)
elseif cmd == "\n" or cmd == ";" then elseif cmd == "\n" or cmd == ";" then
else else
end end
end
::continue::
end end
return cmds return cmds

113
Src/hysh/data/bin/tree Normal file
View File

@@ -0,0 +1,113 @@
--:Minify:--
local cloptions = {
a = false,
h = false,
l = false,
help = false,
}
local inpArgs = { ... }
local args = {}
local name = syscall.getTask(syscall.getpid()).name
for _, v in pairs(inpArgs) do
if v:sub(1, 2) == "--" then
local opt = v:sub(3)
if cloptions[opt] == nil then
print(name .. ": unrecognized option '" .. v .. "'.")
print("try '" .. name .. " --help' for more information.")
return
end
cloptions[opt] = true
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 .. "'.")
print("try '" .. name .. " --help' for more information.")
return
end
cloptions[opt] = true
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("Display directory tree structure.")
print("")
print("Options:")
print(" -a do not ignore entries starting with .")
print(" -h with -l, print sizes in human readable format (e.g., 1K 234M 2G)")
print(" -l use a long listing format (show file sizes)")
print(" --help display this help and exit")
return
end
local fs = require("fs")
local dir = args[1] or ""
if dir:sub(1, 1) ~= "/" then
dir = syscall.getcwd() .. "/" .. dir
end
if dir:sub(-1) ~= "/" then dir = dir .. "/" end
if not fs.isDir(dir) then
print(name .. ": cannot access '" .. (args[1] or dir) .. "': no such directory")
return
end
local function format_size(size)
if not cloptions.h then return tostring(size) end
if size < 1024 then return tostring(size) .. "B"
elseif size < 1024*1024 then return string.format("%.1fK", size/1024)
elseif size < 1024*1024*1024 then return string.format("%.1fM", size/1024/1024)
else return string.format("%.1fG", size/1024/1024/1024) end
end
local function pdir(current_dir, level, prefix)
prefix = prefix or ""
local list = fs.list(current_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
table.sort(list)
if current_dir:sub(-1) ~= "/" then current_dir = current_dir .. "/" end
for i, entry in ipairs(list) do
local full_path = current_dir .. entry
local is_last = (i == #list)
local branch = is_last and "`--" or "|--"
local indent = prefix .. branch
local suffix = ""
local info = ""
if fs.isDir(full_path) then
suffix = "/"
end
if cloptions.l then
local stat = fs.stat and fs.stat(full_path)
if stat then
local size = stat.size or 0
info = " " .. format_size(size)
end
end
print(indent .. entry .. suffix .. info)
if fs.isDir(full_path) then
local new_prefix = prefix .. (is_last and " " or "| ")
pcall(pdir, full_path, level + 1, new_prefix)
end
end
end
-- Print root directory
local root_name = dir:sub(1, -2) -- remove trailing /
if root_name == "" then root_name = "/" end
print(root_name)
pdir(dir, 0, "")

View File

@@ -1,3 +1,4 @@
--:Minify:--
local ini = {} local ini = {}
function ini.parse(str) function ini.parse(str)

12
Src/install.json Normal file
View File

@@ -0,0 +1,12 @@
[
"Hyperion-kernel",
"Hyperion-core",
"hysh",
"lua",
"micro",
"sysinit",
"bit32",
"json",
"muxzcat",
"spm"
]

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