5 Commits

127 changed files with 1021 additions and 9927 deletions

212
LICENSE
View File

@@ -1,202 +1,18 @@
MIT License
Apache License Copyright (c) 2025 Astronand
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
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:
1. Definitions. The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
"License" shall mean the terms and conditions for use, reproduction, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
and distribution as defined by Sections 1 through 9 of this document. LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
"Licensor" shall mean the copyright owner or entity authorized by IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
the copyright owner that is granting the License. USE OR OTHER DEALINGS IN THE SOFTWARE.
"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

@@ -1,201 +0,0 @@
--: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

@@ -106,7 +106,7 @@ local ok, err = xpcall(function()
local file = {} local file = {}
function file.read(n) function file:read(n)
assert(not closed, "file is closed") assert(not closed, "file is closed")
local s = content() local s = content()
@@ -123,7 +123,7 @@ local ok, err = xpcall(function()
return out return out
end end
function file.write(data) function file:write(data)
assert(not closed, "file is closed") assert(not closed, "file is closed")
local s = content() local s = content()
@@ -136,7 +136,7 @@ local ok, err = xpcall(function()
return true return true
end end
function file.seek(whence, offset) function file:seek(whence, offset)
assert(not closed, "file is closed") assert(not closed, "file is closed")
local s = content() local s = content()

View File

@@ -1,8 +1,9 @@
--:Minify:-- --:Minify:--
local BOOT_DRIVE_PATH = ({...})[1] or "/$" local BOOT_DRIVE_PATH = ({...})[1] or "/$"
---@diagnostic disable-next-line: undefined-global ---@diagnostic disable-next-line: undefined-global
local lterm = term local term = term
local function write(text, term) local os = os
local function write(text)
local x, y = term.getCursorPos() local x, y = term.getCursorPos()
local w, h = term.getSize() local w, h = term.getSize()
@@ -48,13 +49,13 @@ local function write(text, term)
end end
local function displaySuperBadError(err) local function displaySuperBadError(err)
lterm.setBackgroundColor(0x1) term.setBackgroundColor(0x1)
lterm.setTextColor(0x4) term.setTextColor(0x4)
lterm.clear() term.clear()
lterm.setCursorPos(1, 1) term.setCursorPos(1, 1)
lterm.write("A critical error occurred while loading the system:") term.write("A critical error occurred while loading the system:")
lterm.setCursorPos(1, 3) term.setCursorPos(1, 3)
write(err, lterm) write(err)
while true do end while true do end
end end
@@ -116,18 +117,18 @@ local ok, err = xpcall(function()
while stoptime > apis.os.clock() do end while stoptime > apis.os.clock() do end
end end
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000 apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000 apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00 apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00 apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00 apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55 apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900 apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55 apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
@@ -171,22 +172,22 @@ local ok, err = xpcall(function()
end end
local colors = { local colors = {
[0xFFFFFF]=0x0001, [0x000000]=0x0001,
[0xFF0000]=0x0002, [0xFFFFFF]=0x0002,
[0x00FF00]=0x0004, [0xFF0000]=0x0004,
[0x0000FF]=0x0008, [0x00FF00]=0x0008,
[0x00FFFF]=0x0010, [0x0000FF]=0x0010,
[0xFF00FF]=0x0020, [0x00FFFF]=0x0020,
[0xFFFF00]=0x0040, [0xFF00FF]=0x0040,
[0xFF6D00]=0x0080, [0xFFFF00]=0x0080,
[0x6DFF55]=0x0100, [0xFF6D00]=0x0100,
[0x24FFFF]=0x0200, [0x6DFF55]=0x0200,
[0x924900]=0x0400, [0x24FFFF]=0x0400,
[0x6D6D55]=0x0800, [0x924900]=0x0800,
[0xDBDBAA]=0x1000, [0x6D6D55]=0x1000,
[0x6D00FF]=0x2000, [0xDBDBAA]=0x2000,
[0xB6FF00]=0x4000, [0x6D00FF]=0x4000,
[0x000000]=0x8000 [0xB6FF00]=0x8000
} }
local fg,bg=0x6D6D55,0x000000 local fg,bg=0x6D6D55,0x000000
@@ -197,7 +198,6 @@ local ok, err = xpcall(function()
local smallestDiff = math.huge local smallestDiff = math.huge
for k, _ in pairs(tbl) do for k, _ in pairs(tbl) do
if k==target then return k end
local diff = math.abs(k - target) local diff = math.abs(k - target)
if diff < smallestDiff then if diff < smallestDiff then
smallestDiff = diff smallestDiff = diff
@@ -248,162 +248,6 @@ local ok, err = xpcall(function()
return value return value
end 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 = { local EFI = {
getEpochMs = function() return apis.os.epoch("utc") end, getEpochMs = function() return apis.os.epoch("utc") end,
getUptime = function() return apis.os.clock() * 1000 end, getUptime = function() return apis.os.clock() * 1000 end,
@@ -424,38 +268,22 @@ local ok, err = xpcall(function()
initfs=fs, initfs=fs,
disks=initFs, disks=initFs,
screenCtl={ screenCtl={
print = function(_, text) print = function(_, text) write(text .. "\n") end,
for i=1, #allscreens do printInline = function(_, text) write(text) end,
write(text.."\n", allscreens[i])
end
end,
printInline = function(_, text)
for i=1, #allscreens do
write(text, allscreens[i])
end
end,
clear = function() clear = function()
for i=1, #allscreens do apis.term.clear()
allscreens[i].clear() apis.term.setCursorPos(1, 1)
allscreens[i].setCursorPos(1, 1)
end
end, end,
resetCursor = function() setCursorPos = function(_, x, y)
for i=1, #allscreens do apis.term.setCursorPos(x, y)
allscreens[i].setCursorPos(1, 1)
end
end, end,
getCursorPos = function() return apis.term.getCursorPos() end,
getSize = function() return apis.term.getSize() end,
setBackgroundColor = function(_, color) setBackgroundColor = function(_, color)
bg=color apis.term.setBackgroundColor(aprox(color))
for i=1, #allscreens do
allscreens[i].setBackgroundColor(aprox(color))
end
end, end,
setTextColor = function(_, color) setTextColor = function(_, color)
fg=color apis.term.setTextColor(aprox(color))
for i=1, #allscreens do
allscreens[i].setTextColor(aprox(color))
end
end, end,
getBackgroundColor = function() getBackgroundColor = function()
return bg return bg
@@ -474,8 +302,7 @@ local ok, err = xpcall(function()
h.close() h.close()
end, end,
firmware=apis, firmware=apis,
reboot=false, reboot=false
beep=function() end
} }
apis.term.setBackgroundColor(0x8000) apis.term.setBackgroundColor(0x8000)
@@ -512,7 +339,7 @@ local ok, err = xpcall(function()
end end
end end
EFI.screenCtl:print("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n") write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
while true do while true do
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50) local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)

View File

@@ -258,7 +258,7 @@ local function refresh()
end end
end end
end end
local function iter() local function iter()
refresh() refresh()
local combined = {} local combined = {}

View File

@@ -1,306 +0,0 @@
--: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

@@ -1,95 +0,0 @@
--: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

@@ -1,6 +1,7 @@
--:Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local peripheral=kernel.cct.peripheral local peripheral=kernel.cct.peripheral
local keys=kernel.apis.keys
local colors = { local colors = {
[0xFFFFFF]=0x0001, [0xFFFFFF]=0x0001,
@@ -48,7 +49,6 @@ local function findClosest(tbl, target)
local smallestDiff = math.huge local smallestDiff = math.huge
for k, _ in pairs(tbl) do for k, _ in pairs(tbl) do
if k==target then return k end
local diff = math.abs(k - target) local diff = math.abs(k - target)
if diff < smallestDiff then if diff < smallestDiff then
smallestDiff = diff smallestDiff = diff
@@ -145,7 +145,7 @@ local function write(text, term)
end end
kernel.devfs.data.tty={} kernel.devfs.data.tty={}
kernel.cct.ctrl,kernel.cct.alt = false, false local ctrl,alt = false, false
local function serializeBool(bool) local function serializeBool(bool)
if bool then if bool then
@@ -221,10 +221,7 @@ local function newtty(obj, id, ev)
return bg return bg
end, end,
gctrl=function() gctrl=function()
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt) return serializeBool(ctrl)..";"..serializeBool(alt)
end,
isvirt=function()
return false
end, end,
gplt=function() gplt=function()
return plt return plt
@@ -244,41 +241,88 @@ local function newtty(obj, id, ev)
end end
local fifo = kernel.newFifo() local fifo = kernel.newFifo()
kernel.cct.fifo=fifo
newtty(kernel.apis.term, "1", function() end) kernel.processes.cctmond = function()
local timeout = false
while true do
local event = {kernel.EFI:getMachineEvent()}
for i,v in ipairs({peripheral.find("monitor")}) do if event[1] then
newtty(v,tostring(i+1),function () end) local eventType = event[1]
end local charOrKey = event[3]
kernel.devfs.data["input"]["keyboard1"] = function(op, mode) local ctrlKeyMap = {
if op=="type" then [keys.a]=1, [keys.b]=2, [keys.c]=3,
return "Keyboard" [keys.d]=4, [keys.e]=5, [keys.f]=6,
elseif op=="open" then [keys.g]=7, [keys.h]=8, [keys.i]=9,
local h = { [keys.j]=10, [keys.k]=11, [keys.l]=12,
read=function(amount) [keys.m]=13, [keys.n]=14, [keys.o]=15,
local rv="" [keys.p]=16, [keys.q]=17, [keys.r]=18,
for i=1, amount or 1 do [keys.s]=19, [keys.t]=20, [keys.u]=21,
local event = {fifo.pop()} [keys.v]=22, [keys.w]=23, [keys.x]=24,
if event[1] then [keys.y]=25, [keys.z]=26,
rv=rv..event[1] }
end
if eventType == "keyPressed" then
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
ctrl = true
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
alt = true
end end
if rv=="" then rv=nil end
return rv if ctrl then
end, local ctrlByte = ctrlKeyMap[charOrKey]
write=function(content) 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 = {
[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 fifo.push(special) end
end
elseif eventType == "keyReleased" then
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
ctrl = false
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
alt = false
end
elseif eventType == "keyTyped" then
if charOrKey then fifo.push(charOrKey) end
end end
}
if mode=="rw" then timeout = false
return h else
elseif mode=="r" then timeout = true
h["write"]=nil end
return h
elseif mode=="w" then if timeout then
h["read"]=nil sleep(0.05)
return h
end end
end end
end
newtty(kernel.apis.term, "1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5)
v.write("Initializing...")
newtty(v,tostring(i+1),function () end)
end end

View File

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

View File

@@ -1,14 +1,12 @@
--:Minify:-- --:Minify:--
local EFI=... local EFI=...
EFI.beep(440, 500)
local screen=EFI.screenCtl local screen=EFI.screenCtl
local ifs=EFI.initfs local ifs=EFI.initfs
local disks=EFI.disks local disks=EFI.disks
local arch=EFI.architecture local arch=EFI.architecture
local kernel = {} local kernel = {}
kernel.LOG_Text="" kernel.LOG_Text=""
kernel.version="HyperionOS V1.2.4-dev_5" kernel.version="HyperionOS V1.2.4"
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,34 +17,26 @@ 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, nopanic) function kernel.log(msg, level, c)
local f=function() c=c or 0x6D6D6D
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"
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(EFI:date()).." "..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 local file=kernel.vfs.open("/dev/console", "w")
local file=kernel.vfs.open("/dev/console", "w") kernel.vfs.devctl(file,"sfgc",c)
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.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n") kernel.vfs.close(file)
kernel.vfs.close(file) kernel.currentTask=kernel.standbyTask
kernel.currentTask=kernel.standbyTask
end
end
local ok,err = xpcall(f,debug.traceback)
if not ok and not nopanic then
kernel.panic(err)
end end
end end
@@ -60,7 +50,7 @@ function kernel.PANIC(msg)
screen:setTextColor(0xFF0000) screen:setTextColor(0xFF0000)
screen:setBackgroundColor(0x000000) screen:setBackgroundColor(0x000000)
screen:clear() screen:clear()
screen:resetCursor() screen:setCursorPos(1,1)
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...")
@@ -81,11 +71,17 @@ if windowsExp then
screen:setTextColor(0xFFFFFF) screen:setTextColor(0xFFFFFF)
screen:setBackgroundColor(0x0000FF) screen:setBackgroundColor(0x0000FF)
screen:clear() screen:clear()
screen:resetCursor() local w,h = screen:getSize()
screen:print("\n\n\n\n\n :(") screen:setCursorPos(3,5)
screen:print("\n Your PC ran into a problem and needs to restart. We're just collecting some error") screen:print(":(")
screen:print(" info, and then we'll restart for you.\n") screen:setCursorPos(3,7)
screen:print("\n\n\n Stop code: average windows experience") 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:setCursorPos(3,h-5)
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
@@ -254,19 +250,9 @@ function kernel.reboot()
kernel.status="reboot" kernel.status="reboot"
end 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["time"]=function() return kernel.EFI:getEpochMs() end
kernel.syscalls["date"]=function() return kernel.EFI:date() end kernel.syscalls["date"]=function() return kernel.EFI:date() end
kernel.syscalls["log"]=kernel.log kernel.syscalls["log"]=kernel.log
kernel.syscalls["tag"]=function() return kernel.tag end
kernel.syscalls["getUptime"]=function() return kernel.EFI:getUptime() 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
@@ -281,21 +267,8 @@ kernel.syscalls["sysdump"]=function()
end end
return rv return rv
end end
kernel.syscalls["reboot"]=function() kernel.syscalls["reboot"]=kernel.reboot
if kernel.uid==0 or kernel.groups.wheel then kernel.syscalls["shutdown"]=kernel.shutdown
kernel.reboot()
end
end
kernel.syscalls["shutdown"]=function()
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
kernel.log("Running modules") kernel.log("Running modules")
for _,p in ipairs(modules) do for _,p in ipairs(modules) do
@@ -317,18 +290,11 @@ kernel.log("Kernel initialized successfully.")
kernel.saveLog() kernel.saveLog()
kernel.status="running" kernel.status="running"
screen:disable() screen:disable()
local ok,err = xpcall(kernel.main, debug.traceback) kernel.main()
if not ok then
kernel.panic(err)
end
if kernel.status=="panic" then if kernel.status=="panic" then
kernel.panic(kernel.reason) kernel.panic(kernel.reason)
end end
if kernel.status=="reboot" then if kernel.status=="reboot" then
EFI.reboot=true EFI.reboot=true
return true return true
elseif kernel.status=="halt" then
kernel.log("System halted.")
kernel.saveLog()
while true do end
end end

View File

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

View File

@@ -1,63 +0,0 @@
--: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

@@ -1,141 +0,0 @@
--: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

@@ -1,194 +0,0 @@
--: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

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

View File

@@ -1,129 +0,0 @@
--: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

@@ -1,11 +0,0 @@
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

@@ -229,23 +229,6 @@ 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,8 +129,6 @@ 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
@@ -399,10 +397,6 @@ 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
@@ -505,7 +499,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
@@ -542,7 +536,6 @@ 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
@@ -551,93 +544,33 @@ 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, bind) function vfs.mount(target, diskOrId)
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)
local drive, path = resolvePath(target) local drive, path = resolvePath(target)
if not drive:directoryExists(path) then if not drive:directoryExists(path) then drive:makeDirectory(path) end
drive:makeDirectory(path) if drive:type(target) ~= "directory" then error("EINVAL") end
end
if drive:type(path) ~= "directory" then
error("EINVAL")
end
local disk, id local disk, id
-- bind mount if type(diskOrId) == "string" then
if bind then disk = kernel.disks[diskOrId]
local src = normalizeMountPoint(diskOrId) if not disk then error("ENODEV") end
local srcDrive = resolvePath(src) checkDisk(disk); id = diskOrId
if not srcDrive then error("ENOENT") end elseif type(diskOrId) == "table" then
id = "bind:" .. src checkDisk(diskOrId); disk = diskOrId
disk = { id = disk.address; vfs.disks[id] = disk
address = id, else error("EINVAL") end
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
disk = vfs.disks[diskOrId]
if not disk then error("ENODEV") end
checkDisk(disk)
id = diskOrId
elseif type(diskOrId) == "table" then
checkDisk(diskOrId)
disk = diskOrId
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 for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
if mp == target then
error("EBUSY")
end
end
vfs.mounts[id] = target vfs.mounts[id] = target
return true return true
end end
@@ -650,11 +583,7 @@ 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 vfs.mounts[id] = nil; return true
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")
@@ -666,21 +595,6 @@ 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)
@@ -782,17 +696,10 @@ 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)
if meta.etype == 0x02 then local ok, attrs = pcall(disk.attributes, disk, diskPath)
attrs = { size=0, modified=0, created=0 } if not ok then attrs = { size=0, modified=0, created=0 } end
else
local ok
ok, attrs = pcall(disk.attributes, disk, diskPath)
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,
@@ -807,15 +714,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 or meta.etype == 0x02 then if meta.etype == 0x01 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,
@@ -832,15 +739,8 @@ 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 attrs local disk, diskPath = resolvePath(file.path)
if file.meta.etype == 0x02 then local attrs = disk:attributes(diskPath)
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,
@@ -869,14 +769,6 @@ 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"
@@ -905,7 +797,6 @@ 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
@@ -927,13 +818,8 @@ function vfs.remove(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if kernel.unixSockets[namei(path)] then if kernel.unixSockets and kernel.unixSockets[path] then
if kernel.uid ~= 0 then kernel.unixSockets[path] = nil
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
@@ -942,7 +828,6 @@ 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
@@ -959,7 +844,6 @@ 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
@@ -968,8 +852,6 @@ 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
@@ -999,13 +881,7 @@ function vfs.readlink(path)
end end
function vfs.access(path, mode) function vfs.access(path, mode)
local meta local meta = getFileMeta(path)
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
@@ -1016,8 +892,6 @@ 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
@@ -1044,9 +918,6 @@ 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
@@ -1056,14 +927,10 @@ 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)
@@ -1072,14 +939,12 @@ 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 or meta.etype == 0x02 then return true end if meta.etype == 0x01 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
if disk:type(diskPath) then if disk:type(diskPath) then
@@ -1092,9 +957,6 @@ 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,40 @@
--: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

@@ -50,7 +50,7 @@ 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
error("ENOENT") error(type(step[steps[#steps]]))
end end
function proxy:list(path) function proxy:list(path)
@@ -140,83 +140,6 @@ function data.zero(op, mode)
end 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 if kernel.EFI:getEEPROM() then
function data.eeprom(op, mode) function data.eeprom(op, mode)
if op=="type" then if op=="type" then
@@ -293,8 +216,7 @@ if kernel.EFI:getNvram() then
end end
end end
data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}} data["disk"]={}
data["input"]={}
kernel.devfs={} kernel.devfs={}
kernel.devfs.data=data kernel.devfs.data=data
kernel.devfs.proxy=proxy kernel.devfs.proxy=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 true end proxy.isReadOnly = function() return false 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,8 +114,7 @@ local function newtaskproxy(task)
}, },
children={ children={
[".meta"]=strFile(buildMeta(children)) [".meta"]=strFile(buildMeta(children))
}, }
pid=strFile(task.pid)
} }
end end

View File

@@ -0,0 +1,134 @@
--: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

@@ -25,11 +25,10 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
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("Mounting '"..id.."' to '"..path.."'") kernel.log("Mounted "..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,24 +12,13 @@ function signal.sigsend(pid, sig)
end end
function signal.sigcatch(handler) function signal.sigcatch(handler)
local task=kernel.currentTask kernel.currentTask.sigh=handler
task.sigh=handler if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end
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()
local task=kernel.currentTask kernel.currentTask.sigh=nil
task.sigh=nil kernel.currentTask.sigq=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,23 @@
--:Minify:--
-- Supports:
-- AF_UNIX - local IPC via /var/run/*.sock paths
-- AF_INET - network sockets with three backends:
-- Implemented by drivers but expect http:// and https://
--
-- 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 socket={}
kernel.socket=socket
kernel.log("Loaded socket module")

View File

@@ -150,11 +150,16 @@ end
local pepper = getFile("/etc/pam.d/secret") local pepper = getFile("/etc/pam.d/secret")
local function genSalt() local function genSalt()
return toHex(math.random(0,2^32)) local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
local s = ""
for i = 1, 16 do
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
end
return s
end end
local function hashPassword(password, salt) local function hashPassword(password, salt)
local key = (pepper .. salt) local key = (pepper .. salt):sub(1, 32)
return blake2s(password, key) return blake2s(password, key)
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 kernel.log("Failed to load executable: " .. tostring(err).. " : ".. tostring(path)); error("ENOEXEC: " .. tostring(err)) end if not func then 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)
@@ -156,6 +156,7 @@ end
function sys.sleep(s) function sys.sleep(s)
kernel.currentTask.status = "S" kernel.currentTask.status = "S"
kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000 kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
coroutine.yield()
end end
function sys.getTask(pid) function sys.getTask(pid)
@@ -203,16 +204,12 @@ function sys.collect(pid)
end end
end end
function sys.kill(pid, force) function sys.kill(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 == "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
@@ -227,16 +224,10 @@ 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" and task.status ~= "S" then elseif task.status ~= "R" 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 else
if task.status == "S" then task.status = "T"
task.status = "ST"
else
task.status = "T"
end
return true return true
end end
end end
@@ -245,16 +236,10 @@ 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" and task.status ~= "ST" then elseif task.status ~= "T" 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 else
if task.status == "ST" then task.status = "R"
task.status = "S"
else
task.status = "R"
end
return true return true
end end
end end
@@ -305,21 +290,20 @@ 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 task.args = nil
task.args = nil task.envars = nil
task.envars = nil task.cwd = nil
task.cwd = nil task.numRuns = nil
task.numRuns = nil task.totalTime = nil
task.totalTime = nil task.lastTime = nil
task.lastTime = nil task.timeSlice = nil
task.timeSlice = nil
task.syscallReturn = nil task.syscallReturn = nil
task.sleep = nil task.sleep = nil
task.fd = nil task.fd = nil
task.reapTime = kernel.EFI:getEpochMs() + 30000 task.reapTime = kernel.EFI:getEpochMs() + 30000
elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
and task.status == "Z" then and task.status == "Z" then
@@ -351,23 +335,7 @@ 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
@@ -375,73 +343,40 @@ function kernel.main()
local taskTimes = {} local taskTimes = {}
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
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 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 == "ST" and kernel.EFI:getEpochMs() >= task.sleep then
task.status = "T"
task.sleep = 0
end
if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
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 if task.status == "R" then
kernel.currentTask = task
kernel.uid = task.euid or task.uid
kernel.process = task.name
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 status,err local sigret = { coroutine.resume(coro, table.remove(task.sigq, 1)) }
if kernel.config.preempt then while coroutine.status(coro) ~= "dead" do
status,err=coroutine.resumeWithTimeout(coro, 100, table.remove(task.sigq, 1)) if sigret[1] == false then break end
else if sigret[2] == "syscall" then
status,err=coroutine.resume(coro, table.remove(task.sigq, 1)) local scname = sigret[3]
end local sysret
if status=="error" or status==false then if kernel.syscalls[scname] then
task.sigd.error=err or "Unknown" sysret = { xpcall(kernel.syscalls[scname], debug.traceback, table.unpack(sigret, 4)) }
task.sigd.active=false else
task.sigh=nil sysret = { false, "Unknown syscall: " .. tostring(scname) }
task.sigq=nil end
task.sigd=nil if not sysret[1] then
elseif status=="success" or status==true then sigret = { coroutine.resume(coro, false, sysret[2]) }
if err=="syscall" then else
task.sigd.error="Cannot execute syscalls from signals" sigret = { coroutine.resume(coro, true, table.unpack(sysret, 2)) }
task.sigd.active=false end
task.sigh=nil else
task.sigq=nil sigret = { coroutine.resume(coro) }
task.sigd=nil
end end
end end
end end
@@ -449,15 +384,9 @@ function kernel.main()
if task.status == "R" then if task.status == "R" then
local startTime = kernel.EFI:getEpochMs() 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
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end
else else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) } ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end end
@@ -537,11 +466,6 @@ 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

@@ -107,7 +107,6 @@ 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"},

View File

@@ -1,10 +1,10 @@
--:Minify:-- --:Minify:--
local success, errorMsg = xpcall(function() syscall.open("/dev/tty/1","r") --stdin (Device 0)
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")
@@ -827,20 +827,6 @@ 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
@@ -995,7 +981,6 @@ local function doTabComplete(input, cursorPos)
end end
end end
local lastghost=""
local function getUserInput() local function getUserInput()
syscall.devctl(1,"sfgc",0x00FF00) syscall.devctl(1,"sfgc",0x00FF00)
syscall.write(1, userhost) syscall.write(1, userhost)
@@ -1162,7 +1147,7 @@ end
local function runCommand(command) local function runCommand(command)
do do
local func = load("return "..command, "@equation", "t", math) local func = load("return "..command, "@equation", "t", {})
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
@@ -1211,9 +1196,9 @@ local function runCommand(command)
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/input/keyboard1", "r") -- fd 0 stdin syscall.open("/dev/tty/1", "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
-- compiled from disk by the kernel (via loadExecutable -> freshUserEnv), -- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
-- so the child cannot share any upvalue or syscall table state with hysh. -- so the child cannot share any upvalue or syscall table state with hysh.

View File

@@ -1,5 +1,5 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/input/keyboard1", "r") --stdin (fd 0) syscall.open("/dev/tty/1", "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)

View File

@@ -95,8 +95,6 @@ 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
@@ -112,12 +110,10 @@ 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
@@ -140,14 +136,14 @@ if cloptions.l then
printInline(tostring(mtime) .. " ") printInline(tostring(mtime) .. " ")
if isSym then if isSym then
syscall.devctl(1, "sfgc", 0x00FFFF) syscall.devctl(1, "sfgc", 0xFF00FF)
printInline(v) printInline(v)
syscall.devctl(1, "sfgc", 0xFFFFFF) 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 0x00FFFF or 0xFF0000) syscall.devctl(1, "sfgc", targetExists and 0xFF00FF or 0xFF0000)
printInline(target) printInline(target)
syscall.devctl(1, "sfgc", 0xFFFFFF) syscall.devctl(1, "sfgc", 0xFFFFFF)
end end
@@ -155,10 +151,6 @@ if cloptions.l then
syscall.devctl(1, "sfgc", 0x6D00FF) syscall.devctl(1, "sfgc", 0x6D00FF)
printInline(v) printInline(v)
syscall.devctl(1, "sfgc", 0xFFFFFF) 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 0x00FF00 or 0xFFFFFF) syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
@@ -183,11 +175,9 @@ 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", 0x00FFFF) syscall.devctl(1, "sfgc", 0xFF00FF)
elseif isDir then elseif isDir then
syscall.devctl(1, "sfgc", 0x6D00FF) 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 0x00FF00 or 0xFFFFFF) syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)

View File

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

View File

@@ -1,113 +0,0 @@
--: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,12 +0,0 @@
[
"Hyperion-kernel",
"Hyperion-core",
"hysh",
"lua",
"micro",
"sysinit",
"bit32",
"json",
"muxzcat",
"spm"
]

View File

@@ -300,7 +300,6 @@ local function getUserInput(prompt, history)
end end
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n") c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
w("Type \"exit()\" to exit.")
c(C_NIL) c(C_NIL)
w("Interactive Lua REPL. exit() to quit.\n\n") w("Interactive Lua REPL. exit() to quit.\n\n")
c(1) c(1)

View File

@@ -1,29 +0,0 @@
local function usageError()
print(
"\nusage: minify <file> or unminify <file>\n" ..
" The modified code will be printed to the stdout, pipe it to a file,\n" ..
" or something else as desired EG:\n\n" ..
" minify minify input.lua > output.lua\n\n" ..
" * minify will minify the code in the file.\n" ..
" * unminify will beautify the code and replace the variable names with easily\n" ..
" find-replacable ones to aide in reverse engineering minified code.\n")
syscall.exit(0)
end
local args = {...}
if #args ~= 2 then
usageError()
end
local minify = require("minify")
local fs = require("fs")
if args[1] == 'minify' then
print(minify.minify(fs.readAllText(args[2])))
elseif args[1] == 'unminify' then
print(minify.beautify(fs.readAllText(args[2])))
else
usageError()
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,136 +0,0 @@
--:Minify:--
local pid = {}
pid.__index = pid
local function clamp(value, minimum, maximum)
if minimum and value < minimum then
return minimum
end
if maximum and value > maximum then
return maximum
end
return value
end
local function assertNumber(value, name)
if type(value) ~= "number" then
error("pid: " .. name .. " must be a number")
end
return value
end
function pid.new(kp, ki, kd, options)
assertNumber(kp, "kp")
assertNumber(ki, "ki")
assertNumber(kd, "kd")
options = options or {}
local controller = setmetatable({
kp = kp,
ki = ki,
kd = kd,
setpoint = options.setpoint or 0,
outputMin = options.outputMin,
outputMax = options.outputMax,
integralMin = options.integralMin,
integralMax = options.integralMax,
lastError = 0,
integral = 0,
lastMeasurement = nil,
lastOutput = 0,
lastTime = nil,
}, pid)
return controller
end
function pid:reset()
self.lastError = 0
self.integral = 0
self.lastMeasurement = nil
self.lastOutput = 0
self.lastTime = nil
end
function pid:setSetpoint(setpoint)
assertNumber(setpoint, "setpoint")
self.setpoint = setpoint
end
function pid:setCoefficients(kp, ki, kd)
assertNumber(kp, "kp")
assertNumber(ki, "ki")
assertNumber(kd, "kd")
self.kp = kp
self.ki = ki
self.kd = kd
end
function pid:setOutputLimits(minimum, maximum)
if minimum ~= nil then assertNumber(minimum, "outputMin") end
if maximum ~= nil then assertNumber(maximum, "outputMax") end
if minimum and maximum and minimum > maximum then
error("pid: outputMin must be <= outputMax")
end
self.outputMin = minimum
self.outputMax = maximum
end
function pid:setIntegralLimits(minimum, maximum)
if minimum ~= nil then assertNumber(minimum, "integralMin") end
if maximum ~= nil then assertNumber(maximum, "integralMax") end
if minimum and maximum and minimum > maximum then
error("pid: integralMin must be <= integralMax")
end
self.integralMin = minimum
self.integralMax = maximum
end
function pid:update(measurement, dt)
assertNumber(measurement, "measurement")
assertNumber(dt, "dt")
if dt <= 0 then
error("pid: dt must be greater than zero")
end
local errorValue = self.setpoint - measurement
self.integral = clamp(self.integral + errorValue * dt, self.integralMin, self.integralMax)
local derivative = 0
if self.lastTime ~= nil then
derivative = (errorValue - self.lastError) / dt
end
local output = self.kp * errorValue + self.ki * self.integral + self.kd * derivative
output = clamp(output, self.outputMin, self.outputMax)
self.lastError = errorValue
self.lastMeasurement = measurement
self.lastOutput = output
self.lastTime = (self.lastTime or 0) + dt
return output
end
function pid:compute(measurement, dt)
return self:update(measurement, dt)
end
function pid:getState()
return {
setpoint = self.setpoint,
kp = self.kp,
ki = self.ki,
kd = self.kd,
integral = self.integral,
lastError = self.lastError,
lastOutput = self.lastOutput,
}
end
function pid:resetIntegral()
self.integral = 0
end
return pid

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