Compare commits
15 Commits
5172da1b5b
...
1.2-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| f7b64c11b7 | |||
| 59e09a5995 | |||
| ab0a10105c | |||
| 22b5021e9d | |||
| 462c74686a | |||
| 0072547beb | |||
| 7deefc83ca | |||
| df62414229 | |||
| 055dd4e606 | |||
| 5755dd9cbe | |||
| 8e11faf9ec | |||
| 0ea42f9454 | |||
| d0f26a937f | |||
| 9b338328f0 | |||
| 4f9eebade2 |
212
LICENSE
212
LICENSE
@@ -1,18 +1,202 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Astronand
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
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:
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
1. Definitions.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
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
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
18
Src/Hyperion-core/data/lib/colors
Normal file
18
Src/Hyperion-core/data/lib/colors
Normal file
@@ -0,0 +1,18 @@
|
||||
--:Minify:--
|
||||
return {
|
||||
white=0xFFFFFF,
|
||||
red=0xFF0000,
|
||||
green=0x00FF00,
|
||||
blue=0x0000FF,
|
||||
cyan=0x00FFFF,
|
||||
yellow=0xFFFF00,
|
||||
purple=0xFF00FF,
|
||||
black=0x000000,
|
||||
gray=0x888888,
|
||||
lightgrey=0xBBBBBB,
|
||||
darkgrey=0x444444,
|
||||
orange=0xFF8800,
|
||||
mint=0x00FF88,
|
||||
brown=0xa52a2a,
|
||||
chocolate=0xd2691e
|
||||
}
|
||||
201
Src/Hyperion-core/data/lib/http
Normal file
201
Src/Hyperion-core/data/lib/http
Normal file
@@ -0,0 +1,201 @@
|
||||
--:Minify:--
|
||||
local http = {}
|
||||
|
||||
local syscall = syscall
|
||||
|
||||
local function parseUrl(url)
|
||||
local proto, host, path =
|
||||
url:match(
|
||||
"^(https?://)([^/]+)(/.*)$"
|
||||
)
|
||||
|
||||
if not proto then
|
||||
proto, host =
|
||||
url:match(
|
||||
"^(https?://)([^/]+)$"
|
||||
)
|
||||
|
||||
path = "/"
|
||||
end
|
||||
|
||||
if not proto then
|
||||
return nil, "EINVAL"
|
||||
end
|
||||
|
||||
return host, path
|
||||
end
|
||||
|
||||
local function buildRequest(req)
|
||||
local host, path =
|
||||
parseUrl(req.url)
|
||||
|
||||
if not host then
|
||||
return nil, path
|
||||
end
|
||||
|
||||
local method =
|
||||
req.method or "GET"
|
||||
|
||||
local body =
|
||||
req.body or ""
|
||||
|
||||
local headers =
|
||||
table.deepcopy(
|
||||
req.headers or {}
|
||||
)
|
||||
|
||||
headers.Host =
|
||||
headers.Host or host
|
||||
|
||||
headers.Connection =
|
||||
headers.Connection or "close"
|
||||
|
||||
if body ~= "" then
|
||||
headers["Content-Length"] =
|
||||
tostring(#body)
|
||||
end
|
||||
|
||||
local out = {
|
||||
method ..
|
||||
" " ..
|
||||
path ..
|
||||
" HTTP/1.1"
|
||||
}
|
||||
|
||||
for k, v in pairs(headers) do
|
||||
out[#out + 1] =
|
||||
tostring(k) ..
|
||||
": " ..
|
||||
tostring(v)
|
||||
end
|
||||
|
||||
out[#out + 1] = ""
|
||||
out[#out + 1] = body
|
||||
|
||||
return table.concat(out, "\r\n")
|
||||
end
|
||||
|
||||
local function parseResponse(raw)
|
||||
local headerEnd =
|
||||
raw:find(
|
||||
"\r\n\r\n",
|
||||
1,
|
||||
true
|
||||
)
|
||||
|
||||
if not headerEnd then
|
||||
return nil, "EBADMSG"
|
||||
end
|
||||
|
||||
local headerPart =
|
||||
raw:sub(1, headerEnd - 1)
|
||||
|
||||
local body =
|
||||
raw:sub(headerEnd + 4)
|
||||
|
||||
local lines = {}
|
||||
|
||||
for line in headerPart:gmatch("[^\r\n]+") do
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
|
||||
local _, code, msg =
|
||||
lines[1]:match(
|
||||
"^(HTTP/%S+)%s+(%d+)%s*(.*)$"
|
||||
)
|
||||
|
||||
local headers = {}
|
||||
|
||||
for i = 2, #lines do
|
||||
local k, v =
|
||||
lines[i]:match(
|
||||
"^([^:]+):%s*(.*)$"
|
||||
)
|
||||
|
||||
if k then
|
||||
headers[k:lower()] = v
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
code = tonumber(code),
|
||||
message = msg,
|
||||
headers = headers,
|
||||
body = body
|
||||
}
|
||||
end
|
||||
|
||||
local function readAll(fd)
|
||||
local out = ""
|
||||
|
||||
while true do
|
||||
local chunk =
|
||||
syscall.read(fd, 4096)
|
||||
|
||||
if not chunk or chunk == "" then
|
||||
break
|
||||
end
|
||||
|
||||
out = out .. chunk
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function http.request(req)
|
||||
local raw, err =
|
||||
buildRequest(req)
|
||||
|
||||
if not raw then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local fd =
|
||||
syscall.socket(0, 0)
|
||||
|
||||
if not fd then
|
||||
return nil, "ESOCKET"
|
||||
end
|
||||
|
||||
local ok, err =
|
||||
syscall.connect(fd, req.url)
|
||||
|
||||
if not ok then
|
||||
syscall.close(fd)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local ok2, err2 =
|
||||
syscall.write(fd, raw)
|
||||
|
||||
if not ok2 then
|
||||
syscall.close(fd)
|
||||
return nil, err2
|
||||
end
|
||||
|
||||
local resp =
|
||||
readAll(fd)
|
||||
|
||||
syscall.close(fd)
|
||||
|
||||
return parseResponse(resp)
|
||||
end
|
||||
|
||||
function http.get(url, headers)
|
||||
return http.request({
|
||||
url = url,
|
||||
method = "GET",
|
||||
headers = headers
|
||||
})
|
||||
end
|
||||
|
||||
function http.post(url, body, headers)
|
||||
return http.request({
|
||||
url = url,
|
||||
method = "POST",
|
||||
body = body,
|
||||
headers = headers
|
||||
})
|
||||
end
|
||||
|
||||
return http
|
||||
@@ -1,54 +1,39 @@
|
||||
-- :Minify:--
|
||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
local term = term
|
||||
local os = os
|
||||
--:Minify:--
|
||||
local args={...}
|
||||
local bootdrive=args[1]
|
||||
local gpu=components:getFirst("gpu")
|
||||
local screenTextBuffer=nil
|
||||
local cursorX,cursorY=0,0
|
||||
local screenSizeX,screenSizeY=128,25
|
||||
|
||||
|
||||
if gpu then
|
||||
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||
for t,v in components:list() do
|
||||
if t == "screen" then
|
||||
gpu:assignBuffer(screenTextBuffer, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function write(text)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
cursorX, cursorY = screenTextBuffer:pasteText(cursorX, cursorY, "SCROLL_SPILL_CLEAR", text)
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 0
|
||||
if cursorY >= screenSizeY then
|
||||
cursorY = screenSizeY-1
|
||||
screenTextBuffer:newline()
|
||||
end
|
||||
end
|
||||
|
||||
local function displaySuperBadError(err)
|
||||
gpu:freeAllBuffers()
|
||||
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||
for t,v in components:list() do
|
||||
if t == "screen" then
|
||||
gpu:assignBuffer(screenTextBuffer, v)
|
||||
end
|
||||
end
|
||||
term.setBackgroundColor(0x1)
|
||||
term.setTextColor(0x4)
|
||||
term.clear()
|
||||
@@ -59,9 +44,8 @@ local function displaySuperBadError(err)
|
||||
while true do end
|
||||
end
|
||||
|
||||
term.setCursorBlink(false)
|
||||
local ok, err = xpcall(function()
|
||||
local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH}
|
||||
local apis = {}
|
||||
|
||||
local lua = {
|
||||
coroutine = true,
|
||||
@@ -102,79 +86,125 @@ local ok, err = xpcall(function()
|
||||
end
|
||||
end
|
||||
|
||||
local acekeys={
|
||||
[apis.keys.enter]="\n",
|
||||
[apis.keys.tab]="\t",
|
||||
[apis.keys.backspace]="\b",
|
||||
[apis.keys.up]="\17",
|
||||
[apis.keys.down]="\18",
|
||||
[apis.keys.left]="\19",
|
||||
[apis.keys.right]="\20",
|
||||
}
|
||||
local function string_file(file)
|
||||
local str = file:read()
|
||||
local buf = {str or ""}
|
||||
local pos = 1
|
||||
local closed = false
|
||||
|
||||
function sleep(time)
|
||||
local stoptime = apis.os.clock() + (time)
|
||||
while stoptime > apis.os.clock() do end
|
||||
local function content()
|
||||
return table.concat(buf)
|
||||
end
|
||||
|
||||
local function set_content(s)
|
||||
buf = {s}
|
||||
end
|
||||
|
||||
local function flush()
|
||||
file.write(content())
|
||||
end
|
||||
|
||||
local file = {}
|
||||
|
||||
function file.read(n)
|
||||
assert(not closed, "file is closed")
|
||||
|
||||
local s = content()
|
||||
local len = #s
|
||||
|
||||
if not n then
|
||||
local out = s:sub(pos)
|
||||
pos = len + 1
|
||||
return out
|
||||
end
|
||||
|
||||
local out = s:sub(pos, pos + n - 1)
|
||||
pos = pos + #out
|
||||
return out
|
||||
end
|
||||
|
||||
function file.write(data)
|
||||
assert(not closed, "file is closed")
|
||||
|
||||
local s = content()
|
||||
local before = s:sub(1, pos - 1)
|
||||
local after = s:sub(pos + #data)
|
||||
|
||||
set_content(before .. data .. after)
|
||||
pos = pos + #data
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function file.seek(whence, offset)
|
||||
assert(not closed, "file is closed")
|
||||
|
||||
local s = content()
|
||||
local len = #s
|
||||
|
||||
whence = whence or "cur"
|
||||
offset = offset or 0
|
||||
|
||||
if whence == "set" then
|
||||
pos = offset + 1
|
||||
elseif whence == "cur" then
|
||||
pos = pos + offset
|
||||
elseif whence == "end" then
|
||||
pos = len + offset + 1
|
||||
else
|
||||
error("invalid whence")
|
||||
end
|
||||
|
||||
if pos < 1 then pos = 1 end
|
||||
if pos > len + 1 then pos = len + 1 end
|
||||
|
||||
return pos - 1
|
||||
end
|
||||
|
||||
function file:close()
|
||||
assert(not closed, "file is closed")
|
||||
flush()
|
||||
closed = true
|
||||
end
|
||||
|
||||
function file:flush()
|
||||
assert(not closed, "file is closed")
|
||||
flush()
|
||||
end
|
||||
|
||||
return file
|
||||
end
|
||||
|
||||
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||
apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||
|
||||
local function getFile(path)
|
||||
local file = apis.fs.open(path, "r")
|
||||
local file = bootdrive:open(path, "r")
|
||||
if not file then
|
||||
displaySuperBadError("Could not open file: " .. path)
|
||||
end
|
||||
local content = file.readAll()
|
||||
file.close()
|
||||
local content = file:read()
|
||||
return content
|
||||
end
|
||||
|
||||
local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel")
|
||||
local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis)
|
||||
local fs = load(getFile(BOOT_DRIVE_PATH .. "/boot/initfs"), "@InitFs")()
|
||||
local Kernel = load(getFile("/boot/kernel.lua"),"@Kernel")
|
||||
local initFs = load(getFile("/boot/ac/initdisks"),"@Init_disks")(apis)
|
||||
local fs = load(getFile("/boot/initfs"), "@InitFs")()
|
||||
|
||||
if not Kernel then displaySuperBadError("Could not load kernel.") end
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
table.insert(eventQueue, {event, ...})
|
||||
end
|
||||
|
||||
local computercomp=apis.components:getFirst("computer")
|
||||
local uefi=apis.components:getFirst("uefi")
|
||||
local computer = {
|
||||
time = function() return apis.os.epoch("utc") end,
|
||||
clock = function() return apis.os.clock() * 1000 end,
|
||||
shutdown = apis.os.shutdown,
|
||||
reboot = apis.os.reboot,
|
||||
getMachineEvent = function()
|
||||
if #eventQueue > 0 then
|
||||
return table.unpack(table.remove(eventQueue, 1))
|
||||
else
|
||||
return nil
|
||||
end
|
||||
return computercomp:getMachineEvent()
|
||||
end,
|
||||
getEEPROM = function() return getFile("/startup.lua") end,
|
||||
getEEPROM = function() return uefi.data end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open("/startup.lua", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
uefi.data=text
|
||||
end
|
||||
}
|
||||
|
||||
@@ -213,7 +243,7 @@ local ok, err = xpcall(function()
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
@@ -1,155 +0,0 @@
|
||||
-- :Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getType(name)
|
||||
if native.isPresent(name) then return native.getType(name) end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and
|
||||
native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getTypeRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getNames()
|
||||
local names = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then table.insert(names, side) end
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local hubSides = native.call(side, "getConnectedSides")
|
||||
for _, hubSide in ipairs(hubSides) do
|
||||
table.insert(names, hubSide)
|
||||
end
|
||||
end
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
local disks = {}
|
||||
local internal = {}
|
||||
|
||||
local function norm(path)
|
||||
if not path or path == "" then return "/" end
|
||||
return fs.combine("/", path)
|
||||
end
|
||||
|
||||
local function createDisk(id, basePath, readonly, periph)
|
||||
basePath = norm(basePath)
|
||||
|
||||
local disk = {address = id, isReadOnly = function() return readonly end}
|
||||
|
||||
function disk:spaceUsed()
|
||||
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||
end
|
||||
|
||||
function disk:spaceTotal() return fs.getCapacity(basePath) end
|
||||
|
||||
function disk:list(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) or not fs.isDir(p) then
|
||||
return nil, "not directory"
|
||||
end
|
||||
return fs.list(p)
|
||||
end
|
||||
|
||||
function disk:fileExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and not fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:directoryExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:type(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) then
|
||||
return nil
|
||||
elseif fs.isDir(p) then
|
||||
return "directory"
|
||||
else
|
||||
return "file"
|
||||
end
|
||||
end
|
||||
|
||||
function disk:makeDirectory(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
fs.makeDir(p)
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:remove(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if fs.exists(p) then fs.delete(p) end
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:setLabel(label) periph.setLabel(label) end
|
||||
|
||||
function disk:getLabel(label) return periph.getLabel() end
|
||||
|
||||
function disk:attributes(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.attributes(p)
|
||||
end
|
||||
|
||||
function disk:open(path, mode)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.open(p, mode)
|
||||
end
|
||||
|
||||
return disk
|
||||
end
|
||||
|
||||
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
setLabel = function(label)
|
||||
local h = fs.open("/.label", "w")
|
||||
h.write(label)
|
||||
h.close()
|
||||
end,
|
||||
getLabel = function()
|
||||
local h = fs.open("/.label", "r")
|
||||
if not h then return "$" end
|
||||
local label = h.readAll()
|
||||
h.close()
|
||||
return label
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
for id, _ in pairs(disks) do
|
||||
if not peripheral.getType(id) then disks[id] = nil end
|
||||
end
|
||||
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.getType(name) == "disk" then
|
||||
if not disks[name] then
|
||||
local mount = disk.getMountPath(name)
|
||||
if mount then
|
||||
disks[name] = createDisk(name, mount, false, disk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function iter()
|
||||
refresh()
|
||||
local combined = {}
|
||||
|
||||
for id, obj in pairs(internal) do combined[id] = obj end
|
||||
for id, obj in pairs(disks) do combined[id] = obj end
|
||||
|
||||
return pairs(combined)
|
||||
end
|
||||
|
||||
return {refresh = refresh, list = iter}
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
local term = term
|
||||
@@ -71,7 +71,6 @@ local ok, err = xpcall(function()
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
gcinfo = true,
|
||||
getfenv = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
__inext = true,
|
||||
@@ -85,7 +84,6 @@ local ok, err = xpcall(function()
|
||||
rawlen = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setfenv = true,
|
||||
setmetatable = true,
|
||||
string = true,
|
||||
table = true,
|
||||
@@ -154,17 +152,106 @@ local ok, err = xpcall(function()
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
if not apis.fs.exists("/nvram.dat") then
|
||||
local file = apis.fs.open("/nvram.dat", "w")
|
||||
file.write("Hello, World!")
|
||||
file.close()
|
||||
end
|
||||
|
||||
local eeprom
|
||||
if apis.fs.exists("/startup.lua") then
|
||||
eeprom="/startup.lua"
|
||||
elseif apis.fs.exists("/eeprom") then
|
||||
eeprom="/eeprom"
|
||||
end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
table.insert(eventQueue, {event, ...})
|
||||
end
|
||||
|
||||
local computer = {
|
||||
time = function() return apis.os.epoch("utc") end,
|
||||
clock = function() return apis.os.clock() * 1000 end,
|
||||
shutdown = apis.os.shutdown,
|
||||
reboot = apis.os.reboot,
|
||||
local colors = {
|
||||
[0x000000]=0x0001,
|
||||
[0xFFFFFF]=0x0002,
|
||||
[0xFF0000]=0x0004,
|
||||
[0x00FF00]=0x0008,
|
||||
[0x0000FF]=0x0010,
|
||||
[0x00FFFF]=0x0020,
|
||||
[0xFF00FF]=0x0040,
|
||||
[0xFFFF00]=0x0080,
|
||||
[0xFF6D00]=0x0100,
|
||||
[0x6DFF55]=0x0200,
|
||||
[0x24FFFF]=0x0400,
|
||||
[0x924900]=0x0800,
|
||||
[0x6D6D55]=0x1000,
|
||||
[0xDBDBAA]=0x2000,
|
||||
[0x6D00FF]=0x4000,
|
||||
[0xB6FF00]=0x8000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local EFI = {
|
||||
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||
getUptime = function() return apis.os.clock() * 1000 end,
|
||||
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||
getMachineEvent = function()
|
||||
if #eventQueue > 0 then
|
||||
return table.unpack(table.remove(eventQueue, 1))
|
||||
@@ -172,61 +259,15 @@ local ok, err = xpcall(function()
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
getEEPROM = function() return getFile("/startup.lua") end,
|
||||
getEEPROM = function() return getFile(eeprom) end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open("/startup.lua", "w")
|
||||
local h = apis.fs.open(eeprom, "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end
|
||||
}
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||
{
|
||||
end,
|
||||
initfs=fs,
|
||||
disks=initFs,
|
||||
screenCtl={
|
||||
print = function(_, text) write(text .. "\n") end,
|
||||
printInline = function(_, text) write(text) end,
|
||||
clear = function()
|
||||
@@ -239,25 +280,51 @@ local ok, err = xpcall(function()
|
||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||
getSize = function() return apis.term.getSize() end,
|
||||
setBackgroundColor = function(_, color)
|
||||
apis.term.setBackgroundColor(colors[color])
|
||||
apis.term.setBackgroundColor(aprox(color))
|
||||
end,
|
||||
setTextColor = function(_, color)
|
||||
apis.term.setTextColor(colors[color])
|
||||
apis.term.setTextColor(aprox(color))
|
||||
end,
|
||||
getBackgroundColor = function()
|
||||
return icolors[apis.term.getBackgroundColor()]
|
||||
return bg
|
||||
end,
|
||||
getTextColor = function()
|
||||
return icolors[apis.term.getTextColor()]
|
||||
end
|
||||
}, computer, fs, "$")
|
||||
if not ok then displaySuperBadError(err) end
|
||||
return fg
|
||||
end,
|
||||
enable=function() end,
|
||||
disable=function() end
|
||||
},
|
||||
architecture="cct",
|
||||
getNvram = function() return getFile("/nvram.dat") end,
|
||||
setNvram = function(_, text)
|
||||
local h = apis.fs.open("/nvram.dat", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
firmware=apis,
|
||||
reboot=false
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, EFI)
|
||||
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||
if err then
|
||||
apis.os.reboot()
|
||||
else
|
||||
apis.os.shutdown()
|
||||
end
|
||||
end)
|
||||
|
||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||
local startTime = computer.time()
|
||||
local startTime = EFI.getEpochMs()
|
||||
debug.sethook(co, function()
|
||||
if computer.time() > startTime + timeout then
|
||||
if EFI.getEpochMs() > startTime + timeout then
|
||||
return coroutine.yield("timeout")
|
||||
end
|
||||
end, "", 1000)
|
||||
@@ -306,11 +373,15 @@ local ok, err = xpcall(function()
|
||||
end
|
||||
end
|
||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||
if EFI.reboot then
|
||||
apis.os.reboot()
|
||||
end
|
||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||
coroutine.yield("key")
|
||||
end
|
||||
initFs:refresh()
|
||||
end
|
||||
end, debug.traceback)
|
||||
|
||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||
while true do coroutine.yield() end
|
||||
while true do coroutine.yield("key") end
|
||||
@@ -1,6 +1,6 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
@@ -241,11 +241,17 @@ internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
for id, _ in pairs(disks) do
|
||||
if not peripheral.getType(id) then disks[id] = nil end
|
||||
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||
setLabel = function(label)
|
||||
error("Device is read-only")
|
||||
end,
|
||||
getLabel = function()
|
||||
return "cctrom"
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
disks={}
|
||||
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||
if disk.isDiskPresent() then
|
||||
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||
@@ -311,7 +311,7 @@ local fifo = kernel.newFifo()
|
||||
kernel.processes.cctmond = function()
|
||||
local timeout = false
|
||||
while true do
|
||||
local event = {kernel.computer:getMachineEvent()}
|
||||
local event = {kernel.EFI:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
560
Src/Hyperion-firmware-cct/data/boot/cct/boot.lua
Normal file
560
Src/Hyperion-firmware-cct/data/boot/cct/boot.lua
Normal file
@@ -0,0 +1,560 @@
|
||||
--:Minify:--
|
||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
local lterm = term
|
||||
local function write(text, term)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
|
||||
local function displaySuperBadError(err)
|
||||
lterm.setBackgroundColor(0x1)
|
||||
lterm.setTextColor(0x4)
|
||||
lterm.clear()
|
||||
lterm.setCursorPos(1, 1)
|
||||
lterm.write("A critical error occurred while loading the system:")
|
||||
lterm.setCursorPos(1, 3)
|
||||
write(err, lterm)
|
||||
while true do end
|
||||
end
|
||||
|
||||
term.setCursorBlink(false)
|
||||
local ok, err = xpcall(function()
|
||||
local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH}
|
||||
|
||||
local lua = {
|
||||
coroutine = true,
|
||||
debug = true,
|
||||
_VERSION = true,
|
||||
assert = true,
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
gcinfo = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
__inext = true,
|
||||
load = true,
|
||||
math = true,
|
||||
next = true,
|
||||
pairs = true,
|
||||
pcall = true,
|
||||
rawequal = true,
|
||||
rawget = true,
|
||||
rawlen = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setmetatable = true,
|
||||
string = true,
|
||||
table = true,
|
||||
tonumber = true,
|
||||
tostring = true,
|
||||
type = true,
|
||||
xpcall = true,
|
||||
_G = true
|
||||
}
|
||||
|
||||
local debug = debug
|
||||
for i, v in pairs(_G) do
|
||||
if not lua[i] or lua[i] == nil then
|
||||
apis[i] = v
|
||||
_G[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local acekeys={
|
||||
[apis.keys.enter]="\n",
|
||||
[apis.keys.tab]="\t",
|
||||
[apis.keys.backspace]="\b",
|
||||
[apis.keys.up]="\17",
|
||||
[apis.keys.down]="\18",
|
||||
[apis.keys.left]="\19",
|
||||
[apis.keys.right]="\20",
|
||||
}
|
||||
|
||||
function sleep(time)
|
||||
local stoptime = apis.os.clock() + (time)
|
||||
while stoptime > apis.os.clock() do end
|
||||
end
|
||||
|
||||
apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||
apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||
apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||
apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||
apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||
apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||
apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||
apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||
apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||
apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||
apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||
apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||
apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||
apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||
apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||
apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||
|
||||
local function getFile(path)
|
||||
local file = apis.fs.open(path, "r")
|
||||
if not file then
|
||||
displaySuperBadError("Could not open file: " .. path)
|
||||
end
|
||||
local content = file.readAll()
|
||||
file.close()
|
||||
return content
|
||||
end
|
||||
|
||||
local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel")
|
||||
local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis)
|
||||
local fs = load(getFile(BOOT_DRIVE_PATH .. "/boot/initfs"), "@InitFs")()
|
||||
|
||||
if not Kernel then displaySuperBadError("Could not load kernel.") end
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
if not apis.fs.exists("/nvram.dat") then
|
||||
local file = apis.fs.open("/nvram.dat", "w")
|
||||
file.write("Hello, World!")
|
||||
file.close()
|
||||
end
|
||||
|
||||
local eeprom
|
||||
if apis.fs.exists("/startup.lua") then
|
||||
eeprom="/startup.lua"
|
||||
elseif apis.fs.exists("/eeprom") then
|
||||
eeprom="/eeprom"
|
||||
end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
table.insert(eventQueue, {event, ...})
|
||||
end
|
||||
|
||||
local colors = {
|
||||
[0xFFFFFF]=0x0001,
|
||||
[0xFF0000]=0x0002,
|
||||
[0x00FF00]=0x0004,
|
||||
[0x0000FF]=0x0008,
|
||||
[0x00FFFF]=0x0010,
|
||||
[0xFF00FF]=0x0020,
|
||||
[0xFFFF00]=0x0040,
|
||||
[0xFF6D00]=0x0080,
|
||||
[0x6DFF55]=0x0100,
|
||||
[0x24FFFF]=0x0200,
|
||||
[0x924900]=0x0400,
|
||||
[0x6D6D55]=0x0800,
|
||||
[0xDBDBAA]=0x1000,
|
||||
[0x6D00FF]=0x2000,
|
||||
[0xB6FF00]=0x4000,
|
||||
[0x000000]=0x8000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
if k==target then return k end
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local peripheral={}
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local allscreens = {peripheral.find("monitor")}
|
||||
for i=1, #allscreens do
|
||||
allscreens[i].setTextScale(.5)
|
||||
allscreens[i].clear()
|
||||
allscreens[i].setCursorPos(1,1)
|
||||
end
|
||||
|
||||
allscreens[#allscreens+1] = apis.term
|
||||
|
||||
local EFI = {
|
||||
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||
getUptime = function() return apis.os.clock() * 1000 end,
|
||||
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||
getMachineEvent = function()
|
||||
if #eventQueue > 0 then
|
||||
return table.unpack(table.remove(eventQueue, 1))
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
getEEPROM = function() return getFile(eeprom) end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open(eeprom, "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
initfs=fs,
|
||||
disks=initFs,
|
||||
screenCtl={
|
||||
print = function(_, text)
|
||||
for i=1, #allscreens do
|
||||
write(text.."\n", allscreens[i])
|
||||
end
|
||||
end,
|
||||
printInline = function(_, text)
|
||||
for i=1, #allscreens do
|
||||
write(text, allscreens[i])
|
||||
end
|
||||
end,
|
||||
clear = function()
|
||||
for i=1, #allscreens do
|
||||
allscreens[i].clear()
|
||||
allscreens[i].setCursorPos(1, 1)
|
||||
end
|
||||
end,
|
||||
resetCursor = function()
|
||||
for i=1, #allscreens do
|
||||
allscreens[i].setCursorPos(1, 1)
|
||||
end
|
||||
end,
|
||||
setBackgroundColor = function(_, color)
|
||||
bg=color
|
||||
for i=1, #allscreens do
|
||||
allscreens[i].setBackgroundColor(aprox(color))
|
||||
end
|
||||
end,
|
||||
setTextColor = function(_, color)
|
||||
fg=color
|
||||
for i=1, #allscreens do
|
||||
allscreens[i].setTextColor(aprox(color))
|
||||
end
|
||||
end,
|
||||
getBackgroundColor = function()
|
||||
return bg
|
||||
end,
|
||||
getTextColor = function()
|
||||
return fg
|
||||
end,
|
||||
enable=function() end,
|
||||
disable=function() end
|
||||
},
|
||||
architecture="cct",
|
||||
getNvram = function() return getFile("/nvram.dat") end,
|
||||
setNvram = function(_, text)
|
||||
local h = apis.fs.open("/nvram.dat", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
firmware=apis,
|
||||
reboot=false,
|
||||
beep=function() end
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, EFI)
|
||||
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||
if err then
|
||||
apis.os.reboot()
|
||||
else
|
||||
apis.os.shutdown()
|
||||
end
|
||||
end)
|
||||
|
||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||
local startTime = EFI.getEpochMs()
|
||||
debug.sethook(co, function()
|
||||
if EFI.getEpochMs() > startTime + timeout then
|
||||
return coroutine.yield("timeout")
|
||||
end
|
||||
end, "", 1000)
|
||||
local ret = {coroutine.resume(co, ...)}
|
||||
if ret[1] and ret[2] == "timeout" then
|
||||
return "timeout"
|
||||
elseif ret[1] == false then
|
||||
return "error", ret[2]
|
||||
else
|
||||
debug.sethook(co)
|
||||
return "success", table.unpack(ret, 2)
|
||||
end
|
||||
end
|
||||
|
||||
EFI.screenCtl:print("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||
|
||||
while true do
|
||||
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||
apis.os.queueEvent("NoSleep")
|
||||
local exit = false
|
||||
while not exit do
|
||||
local event = {coroutine.yield()}
|
||||
if event[1] == "key" then
|
||||
queueEvent("keyPressed", 1, event[2])
|
||||
if acekeys[event[2]] then
|
||||
queueEvent("keyTyped", 1, acekeys[event[2]])
|
||||
end
|
||||
elseif event[1] == "char" then
|
||||
queueEvent("keyTyped", 1, event[2])
|
||||
elseif event[1] == "key_up" then
|
||||
queueEvent("keyReleased", 1, event[2])
|
||||
elseif event[1] == "disk" then
|
||||
queueEvent("componentAdded", "disk")
|
||||
elseif event[1] == "disk_eject" then
|
||||
queueEvent("componentRemoved", "disk")
|
||||
elseif event[1] == "modem_message" then
|
||||
queueEvent("modem_message", table.unpack(event, 2))
|
||||
elseif event[1] == "rednet_message" then
|
||||
queueEvent("rednet_message", table.unpack(event, 2))
|
||||
elseif event[1] == "http_success" then
|
||||
queueEvent("http_success", table.unpack(event, 2))
|
||||
elseif event[1] == "http_failure" then
|
||||
queueEvent("http_failure", table.unpack(event, 2))
|
||||
elseif event[1] == "NoSleep" then
|
||||
exit = true
|
||||
end
|
||||
end
|
||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||
if EFI.reboot then
|
||||
apis.os.reboot()
|
||||
end
|
||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||
coroutine.yield("key")
|
||||
end
|
||||
initFs:refresh()
|
||||
end
|
||||
end, debug.traceback)
|
||||
|
||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||
while true do coroutine.yield("key") end
|
||||
272
Src/Hyperion-firmware-cct/data/boot/cct/initdisks
Normal file
272
Src/Hyperion-firmware-cct/data/boot/cct/initdisks
Normal file
@@ -0,0 +1,272 @@
|
||||
--:Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local disks = {}
|
||||
local internal = {}
|
||||
|
||||
local function norm(path)
|
||||
if not path or path == "" then return "/" end
|
||||
return fs.combine("/", path)
|
||||
end
|
||||
|
||||
local function createDisk(id, basePath, readonly, periph)
|
||||
basePath = norm(basePath)
|
||||
|
||||
local disk = {address = id, isReadOnly = function() return readonly end}
|
||||
|
||||
function disk:spaceUsed()
|
||||
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||
end
|
||||
|
||||
function disk:spaceTotal() return fs.getCapacity(basePath) end
|
||||
|
||||
function disk:list(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) or not fs.isDir(p) then
|
||||
return nil, "not directory"
|
||||
end
|
||||
return fs.list(p)
|
||||
end
|
||||
|
||||
function disk:fileExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and not fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:directoryExists(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.exists(p) and fs.isDir(p)
|
||||
end
|
||||
|
||||
function disk:type(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if not fs.exists(p) then
|
||||
return nil
|
||||
elseif fs.isDir(p) then
|
||||
return "directory"
|
||||
else
|
||||
return "file"
|
||||
end
|
||||
end
|
||||
|
||||
function disk:makeDirectory(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
fs.makeDir(p)
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:remove(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
if fs.exists(p) then fs.delete(p) end
|
||||
return true
|
||||
end
|
||||
|
||||
function disk:setLabel(label) periph.setLabel(label) end
|
||||
|
||||
function disk:getLabel(label) return periph.getLabel() end
|
||||
|
||||
function disk:attributes(path)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.attributes(p)
|
||||
end
|
||||
|
||||
function disk:open(path, mode)
|
||||
local p = fs.combine(basePath, path)
|
||||
return fs.open(p, mode)
|
||||
end
|
||||
|
||||
return disk
|
||||
end
|
||||
|
||||
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
setLabel = function(label)
|
||||
local h = fs.open("/.label", "w")
|
||||
h.write(label)
|
||||
h.close()
|
||||
end,
|
||||
getLabel = function()
|
||||
local h = fs.open("/.label", "r")
|
||||
if not h then return "$" end
|
||||
local label = h.readAll()
|
||||
h.close()
|
||||
return label
|
||||
end
|
||||
})
|
||||
|
||||
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||
setLabel = function(label)
|
||||
error("Device is read-only")
|
||||
end,
|
||||
getLabel = function()
|
||||
return "cctrom"
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
disks={}
|
||||
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||
if disk.isDiskPresent() then
|
||||
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function iter()
|
||||
refresh()
|
||||
local combined = {}
|
||||
|
||||
for id, obj in pairs(internal) do combined[id] = obj end
|
||||
for id, obj in pairs(disks) do combined[id] = obj end
|
||||
|
||||
return pairs(combined)
|
||||
end
|
||||
|
||||
return {refresh = refresh, list = iter}
|
||||
@@ -0,0 +1,152 @@
|
||||
--:Minify:--
|
||||
-- CCT driver peripheral helper
|
||||
local kernel=...
|
||||
kernel.cct={}
|
||||
kernel.cct.peripheral={}
|
||||
local peripheral=kernel.cct.peripheral
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
@@ -0,0 +1,306 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local handler = {}
|
||||
|
||||
local http = kernel.apis.http
|
||||
|
||||
kernel.cct.httpqueue = kernel.cct.httpqueue or {}
|
||||
kernel.cct.httpresponse = kernel.cct.httpresponse or {}
|
||||
kernel.cct.httperror = kernel.cct.httperror or {}
|
||||
|
||||
local function parseRawRequest(raw)
|
||||
local sepLen = 4
|
||||
|
||||
local headerEnd =
|
||||
raw:find("\r\n\r\n", 1, true)
|
||||
|
||||
if not headerEnd then
|
||||
headerEnd =
|
||||
raw:find("\n\n", 1, true)
|
||||
|
||||
sepLen = 2
|
||||
end
|
||||
|
||||
local headerPart
|
||||
local body = ""
|
||||
|
||||
if headerEnd then
|
||||
headerPart = raw:sub(1, headerEnd - 1)
|
||||
body = raw:sub(headerEnd + sepLen)
|
||||
else
|
||||
headerPart = raw
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
|
||||
for line in headerPart:gmatch("[^\r\n]+") do
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
|
||||
if #lines == 0 then
|
||||
return nil, "EINVAL"
|
||||
end
|
||||
|
||||
local method, path =
|
||||
lines[1]:match("^(%S+)%s+(%S+)")
|
||||
|
||||
if not method or not path then
|
||||
return nil, "EBADMSG"
|
||||
end
|
||||
|
||||
local headers = {}
|
||||
|
||||
for i = 2, #lines do
|
||||
local k, v =
|
||||
lines[i]:match("^([^:]+):%s*(.*)$")
|
||||
|
||||
if k then
|
||||
headers[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local host = headers.Host or headers.host
|
||||
|
||||
if not host and not path:match("^https?://") then
|
||||
return nil, "EHOSTUNREACH"
|
||||
end
|
||||
|
||||
local url
|
||||
|
||||
if path:match("^https?://") then
|
||||
url = path
|
||||
else
|
||||
url = "http://" .. host .. path
|
||||
end
|
||||
|
||||
local req = {
|
||||
url = url,
|
||||
method = method,
|
||||
headers = headers
|
||||
}
|
||||
|
||||
if body ~= "" then
|
||||
req.body = body
|
||||
end
|
||||
|
||||
return req
|
||||
end
|
||||
|
||||
local function buildResponse(resp)
|
||||
if not resp then
|
||||
return nil, "EINVAL"
|
||||
end
|
||||
|
||||
local code, msg = resp.getResponseCode()
|
||||
|
||||
local headers =
|
||||
resp:getResponseHeaders()
|
||||
|
||||
local body =
|
||||
resp:readAll() or ""
|
||||
|
||||
local out = {
|
||||
"HTTP/1.1 " ..
|
||||
tostring(code) ..
|
||||
" " ..
|
||||
tostring(msg)
|
||||
}
|
||||
|
||||
local hasLength = false
|
||||
|
||||
for k, v in pairs(headers or {}) do
|
||||
if k:lower() == "content-length" then
|
||||
hasLength = true
|
||||
end
|
||||
|
||||
out[#out + 1] =
|
||||
tostring(k) ..
|
||||
": " ..
|
||||
tostring(v)
|
||||
end
|
||||
|
||||
if not hasLength then
|
||||
out[#out + 1] =
|
||||
"Content-Length: " ..
|
||||
tostring(#body)
|
||||
end
|
||||
|
||||
out[#out + 1] = ""
|
||||
out[#out + 1] = body
|
||||
|
||||
return table.concat(out, "\r\n")
|
||||
end
|
||||
|
||||
function handler.connect(fd, address)
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
fdo.socket.rbuf = ""
|
||||
fdo.socket.closed = false
|
||||
fdo.socket.httpid = nil
|
||||
|
||||
fdo.handle.write = function(raw)
|
||||
local req, err =
|
||||
parseRawRequest(raw)
|
||||
|
||||
if not req then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local id =
|
||||
tostring(kernel.uuid())
|
||||
|
||||
fdo.socket.httpid = id
|
||||
|
||||
kernel.cct.httpqueue[id] = true
|
||||
|
||||
local ok, err =
|
||||
http.request(req, id)
|
||||
|
||||
if not ok then
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
fdo.handle.read = function(count)
|
||||
count = count or 4096
|
||||
|
||||
local sock = fdo.socket
|
||||
|
||||
if #sock.rbuf > 0 then
|
||||
local out =
|
||||
sock.rbuf:sub(1, count)
|
||||
|
||||
sock.rbuf =
|
||||
sock.rbuf:sub(count + 1)
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
local id = sock.httpid
|
||||
|
||||
if not id then
|
||||
return ""
|
||||
end
|
||||
|
||||
local function finish(resp)
|
||||
sock.rbuf =
|
||||
buildResponse(resp) or ""
|
||||
|
||||
local out =
|
||||
sock.rbuf:sub(1, count)
|
||||
|
||||
sock.rbuf =
|
||||
sock.rbuf:sub(count + 1)
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
if kernel.cct.httpresponse[id] then
|
||||
local resp =
|
||||
kernel.cct.httpresponse[id]
|
||||
|
||||
kernel.cct.httpresponse[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
return finish(resp)
|
||||
end
|
||||
|
||||
if kernel.cct.httperror[id] then
|
||||
local err =
|
||||
kernel.cct.httperror[id]
|
||||
|
||||
kernel.cct.httperror[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
return nil, err
|
||||
end
|
||||
|
||||
kernel.currentTask.status = "D"
|
||||
|
||||
local coro
|
||||
|
||||
coro = function()
|
||||
if kernel.cct.httpresponse[id] then
|
||||
local resp =
|
||||
kernel.cct.httpresponse[id]
|
||||
|
||||
kernel.cct.httpresponse[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
kernel.asyncReturn(
|
||||
finish(resp)
|
||||
)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if kernel.cct.httperror[id] then
|
||||
local err =
|
||||
kernel.cct.httperror[id]
|
||||
|
||||
kernel.cct.httperror[id] = nil
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
|
||||
kernel.asyncReturn(
|
||||
nil,
|
||||
err
|
||||
)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
kernel.currentTask.ksh =
|
||||
coroutine.create(function()
|
||||
local ok, err =
|
||||
xpcall(
|
||||
coro,
|
||||
debug.traceback
|
||||
)
|
||||
|
||||
if not ok then
|
||||
kernel.asyncReturn(
|
||||
nil,
|
||||
err
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
fdo.handle.close = function()
|
||||
fdo.socket.closed = true
|
||||
|
||||
local id =
|
||||
fdo.socket.httpid
|
||||
|
||||
if id then
|
||||
kernel.cct.httpqueue[id] = nil
|
||||
kernel.cct.httpresponse[id] = nil
|
||||
kernel.cct.httperror[id] = nil
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
kernel.socket.registerProtocal(
|
||||
"http://",
|
||||
handler
|
||||
)
|
||||
|
||||
kernel.socket.registerProtocal(
|
||||
"https://",
|
||||
handler
|
||||
)
|
||||
@@ -0,0 +1,284 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local peripheral=kernel.cct.peripheral
|
||||
|
||||
local colors = {
|
||||
[0xFFFFFF]=0x0001,
|
||||
[0xFF0000]=0x0002,
|
||||
[0x00FF00]=0x0004,
|
||||
[0x0000FF]=0x0008,
|
||||
[0x00FFFF]=0x0010,
|
||||
[0xFF00FF]=0x0020,
|
||||
[0xFFFF00]=0x0040,
|
||||
[0xFF6D00]=0x0080,
|
||||
[0x6DFF55]=0x0100,
|
||||
[0x24FFFF]=0x0200,
|
||||
[0x924900]=0x0400,
|
||||
[0x6D6D55]=0x0800,
|
||||
[0xDBDBAA]=0x1000,
|
||||
[0x6D00FF]=0x2000,
|
||||
[0xB6FF00]=0x4000,
|
||||
[0x000000]=0x8000
|
||||
}
|
||||
|
||||
local plt = {
|
||||
0xFFFFFF,
|
||||
0xFF0000,
|
||||
0x00FF00,
|
||||
0x0000FF,
|
||||
0x00FFFF,
|
||||
0xFF00FF,
|
||||
0xFFFF00,
|
||||
0xFF6D00,
|
||||
0x6DFF55,
|
||||
0x24FFFF,
|
||||
0x924900,
|
||||
0x6D6D55,
|
||||
0xDBDBAA,
|
||||
0x6D00FF,
|
||||
0xB6FF00,
|
||||
0x000000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
if k==target then return k end
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local function write(text, term)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
|
||||
kernel.devfs.data.tty={}
|
||||
kernel.cct.ctrl,kernel.cct.alt = false, false
|
||||
|
||||
local function serializeBool(bool)
|
||||
if bool then
|
||||
return "T"
|
||||
else
|
||||
return "F"
|
||||
end
|
||||
end
|
||||
|
||||
local function newtty(obj, id, ev)
|
||||
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "Terminal"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {ev()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
write(content, obj)
|
||||
end,
|
||||
size=function()
|
||||
local s={obj.getSize()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
clear=function()
|
||||
obj.clear()
|
||||
obj.setCursorPos(1,1)
|
||||
end,
|
||||
gpos=function()
|
||||
local s={obj.getCursorPos()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
spos=function(x,y)
|
||||
return obj.setCursorPos(x,y)
|
||||
end,
|
||||
sfgc=function(c)
|
||||
fg=c
|
||||
return obj.setTextColor(aprox(c))
|
||||
end,
|
||||
sbgc=function(c)
|
||||
bg=c
|
||||
return obj.setBackgroundColor(aprox(c))
|
||||
end,
|
||||
gfgc=function()
|
||||
return fg
|
||||
end,
|
||||
gbgc=function()
|
||||
return bg
|
||||
end,
|
||||
gctrl=function()
|
||||
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.alt)
|
||||
end,
|
||||
isvirt=function()
|
||||
return false
|
||||
end,
|
||||
gplt=function()
|
||||
return plt
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fifo = kernel.newFifo()
|
||||
kernel.cct.fifo=fifo
|
||||
|
||||
newtty(kernel.apis.term, "1", function() end)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
newtty(v,tostring(i+1),function () end)
|
||||
end
|
||||
|
||||
kernel.devfs.data["input"]["keyboard1"] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "Keyboard"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {fifo.pop()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,95 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local keys=kernel.apis.keys
|
||||
|
||||
kernel.processes.cctdeamon = function()
|
||||
local timeout = false
|
||||
kernel.log("CCT deamon started")
|
||||
while true do
|
||||
local event = {kernel.EFI:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
local ctrlKeyMap = {
|
||||
[keys.a]=1, [keys.b]=2, [keys.c]=3,
|
||||
[keys.d]=4, [keys.e]=5, [keys.f]=6,
|
||||
[keys.g]=7, [keys.h]=8, [keys.i]=9,
|
||||
[keys.j]=10, [keys.k]=11, [keys.l]=12,
|
||||
[keys.m]=13, [keys.n]=14, [keys.o]=15,
|
||||
[keys.p]=16, [keys.q]=17, [keys.r]=18,
|
||||
[keys.s]=19, [keys.t]=20, [keys.u]=21,
|
||||
[keys.v]=22, [keys.w]=23, [keys.x]=24,
|
||||
[keys.y]=25, [keys.z]=26,
|
||||
}
|
||||
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
|
||||
kernel.cct.ctrl = true
|
||||
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
|
||||
kernel.cct.alt = true
|
||||
end
|
||||
|
||||
if kernel.cct.ctrl then
|
||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||
if ctrlByte then
|
||||
if ctrlByte == 3 then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1)
|
||||
end
|
||||
else
|
||||
kernel.cct.fifo.push(string.char(ctrlByte))
|
||||
end
|
||||
end
|
||||
else
|
||||
local specialKeyMap = {
|
||||
[keys.up] = "[A",
|
||||
[keys.down] = "[B",
|
||||
[keys.right] = "[C",
|
||||
[keys.left] = "[D",
|
||||
[keys.home] = "[H",
|
||||
[keys["end"]] = "[F",
|
||||
[keys.pageUp] = "[5~",
|
||||
[keys.pageDown] = "[6~",
|
||||
[keys.delete] = "[3~",
|
||||
}
|
||||
local special = specialKeyMap[charOrKey]
|
||||
if special then kernel.cct.fifo.push(special) end
|
||||
end
|
||||
|
||||
elseif eventType == "keyReleased" then
|
||||
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
|
||||
kernel.cct.ctrl = false
|
||||
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
|
||||
kernel.cct.alt = false
|
||||
end
|
||||
|
||||
elseif eventType == "keyTyped" then
|
||||
if charOrKey then kernel.cct.fifo.push(charOrKey) end
|
||||
elseif eventType == "mouse_scroll" then
|
||||
if event[2] == 1 then
|
||||
kernel.cct.fifo.push("[nS")
|
||||
else
|
||||
kernel.cct.fifo.push("[nT")
|
||||
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")
|
||||
@@ -0,0 +1,25 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local rs=kernel.apis.rs
|
||||
local sides = {top=1, bottom=2, left=3, right=4, front=5, back=6}
|
||||
local function newGPIO(side)
|
||||
return function(mode, data)
|
||||
if mode=="w" then
|
||||
if type(data)~="boolean" then error("data: expected bool") end
|
||||
rs.setOutput(side, data)
|
||||
elseif mode=="wa" then
|
||||
if type(data)~="number" then error("data: expected bool") end
|
||||
rs.setAnalogOutput(side, data)
|
||||
elseif mode=="r" then
|
||||
return rs.getInput(side)
|
||||
elseif mode=="ra" then
|
||||
return rs.getAnalogInput(side)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for side, alt in pairs(sides) do
|
||||
local func=newGPIO(side)
|
||||
kernel.gpio[side]=func
|
||||
kernel.gpio[alt]=func
|
||||
end
|
||||
@@ -1,411 +0,0 @@
|
||||
--:Minify:--
|
||||
|
||||
local kernel = ...
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
local peripheral={}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
local function write(text, term)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
|
||||
kernel.devfs.data.tty={}
|
||||
local ctrl,alt = false, false
|
||||
|
||||
local function serializeBool(bool)
|
||||
if bool then
|
||||
return "T"
|
||||
else
|
||||
return "F"
|
||||
end
|
||||
end
|
||||
|
||||
local function newtty(obj, id, ev)
|
||||
obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000
|
||||
obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF
|
||||
obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000
|
||||
obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00
|
||||
obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF
|
||||
obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF
|
||||
obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF
|
||||
obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00
|
||||
obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00
|
||||
obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55
|
||||
obj.setPaletteColor(0x400, 0x924900) -- #24FFFF
|
||||
obj.setPaletteColor(0x800, 0x6D6D55) -- #924900
|
||||
obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55
|
||||
obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA
|
||||
obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF
|
||||
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {ev()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
write(content, obj)
|
||||
end,
|
||||
size=function()
|
||||
local s={obj.getSize()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
clear=function()
|
||||
obj.clear()
|
||||
obj.setCursorPos(1,1)
|
||||
end,
|
||||
gpos=function()
|
||||
local s={obj.getCursorPos()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
spos=function(x,y)
|
||||
return obj.setCursorPos(x,y)
|
||||
end,
|
||||
sfgc=function(c)
|
||||
return obj.setTextColor(colors[c])
|
||||
end,
|
||||
sbgc=function(c)
|
||||
return obj.setBackgroundColor(colors[c])
|
||||
end,
|
||||
gfgc=function()
|
||||
return icolors[obj.getTextColor()]
|
||||
end,
|
||||
gbgc=function()
|
||||
return icolors[obj.getBackgroundColor()]
|
||||
end,
|
||||
gctrl=function()
|
||||
return serializeBool(ctrl)..";"..serializeBool(alt)
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fifo = kernel.newFifo()
|
||||
|
||||
kernel.processes.cctmond = function()
|
||||
local timeout = false
|
||||
while true do
|
||||
local event = {kernel.computer:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
local ctrlKeyMap = {
|
||||
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
|
||||
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
|
||||
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
|
||||
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
|
||||
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
|
||||
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
|
||||
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
|
||||
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
|
||||
[apis.keys.y]=25, [apis.keys.z]=26,
|
||||
}
|
||||
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = true
|
||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||
alt = true
|
||||
end
|
||||
|
||||
if ctrl then
|
||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||
if ctrlByte then
|
||||
if ctrlByte == 3 then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1)
|
||||
end
|
||||
else
|
||||
fifo.push(string.char(ctrlByte))
|
||||
end
|
||||
end
|
||||
else
|
||||
local specialKeyMap = {
|
||||
[apis.keys.up] = "[A",
|
||||
[apis.keys.down] = "[B",
|
||||
[apis.keys.right] = "[C",
|
||||
[apis.keys.left] = "[D",
|
||||
[apis.keys.home] = "[H",
|
||||
[apis.keys["end"]] = "[F",
|
||||
[apis.keys.pageUp] = "[5~",
|
||||
[apis.keys.pageDown] = "[6~",
|
||||
[apis.keys.delete] = "[3~",
|
||||
}
|
||||
local special = specialKeyMap[charOrKey]
|
||||
if special then fifo.push(special) end
|
||||
end
|
||||
|
||||
elseif eventType == "keyReleased" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = false
|
||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||
alt = false
|
||||
end
|
||||
|
||||
elseif eventType == "keyTyped" then
|
||||
if charOrKey then fifo.push(charOrKey) end
|
||||
end
|
||||
|
||||
timeout = false
|
||||
else
|
||||
timeout = true
|
||||
end
|
||||
|
||||
if timeout then
|
||||
sleep(0.05)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newtty(apis.term, "1", fifo.pop)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
v.setTextScale(.5)
|
||||
v.write("Initializing...")
|
||||
newtty(v,tostring(i+1),function () end)
|
||||
end
|
||||
@@ -1,26 +0,0 @@
|
||||
local args={...}
|
||||
local kernel=args[1]
|
||||
local driver={}
|
||||
|
||||
driver.name="CCT Term Module"
|
||||
driver.version="0.1.0"
|
||||
driver.type="gpio"
|
||||
driver.description="CCT redstone Module Kernel Module"
|
||||
driver.arch="cct"
|
||||
driver.author="HyperionOS Dev Team"
|
||||
driver.license="MIT"
|
||||
driver.api={}
|
||||
|
||||
function driver.load()
|
||||
-- will
|
||||
end
|
||||
|
||||
function driver.unload()
|
||||
-- Nothing to unload
|
||||
end
|
||||
|
||||
function driver.main()
|
||||
-- Nothing to run
|
||||
end
|
||||
|
||||
-- kernel.drivers.register(driver)
|
||||
@@ -1,4 +1,4 @@
|
||||
U $;/
|
||||
U devfs0000;/dev/
|
||||
U tmpfs0000;/tmp/
|
||||
U procfs0000;/proc/
|
||||
U procfs0000;/proc/
|
||||
@@ -1,14 +1,14 @@
|
||||
--:Minify:--
|
||||
local args = {...}
|
||||
local apis = args[1]
|
||||
local disks = args[2]
|
||||
local arch = args[3]
|
||||
local screen = args[5]
|
||||
local computer = args[6]
|
||||
local ifs = args[7]
|
||||
local EFI=...
|
||||
EFI.beep(440, 500)
|
||||
local screen=EFI.screenCtl
|
||||
local ifs=EFI.initfs
|
||||
local disks=EFI.disks
|
||||
local arch=EFI.architecture
|
||||
local kernel = {}
|
||||
kernel.LOG_Text=""
|
||||
kernel.version="HyperionOS V1.2.3"
|
||||
kernel.version="HyperionOS V1.2.4-dev_5"
|
||||
kernel.tag="1.2.4-dev_5"
|
||||
kernel.process = "Kernel"
|
||||
kernel.users={[0]="root",[1]="User"}
|
||||
kernel.hostname = "hyperion"
|
||||
@@ -19,67 +19,73 @@ kernel.status = "start"
|
||||
kernel.key = {}
|
||||
kernel.cache = {}
|
||||
kernel.cache.preload = {}
|
||||
kernel.unixSockets={}
|
||||
kernel._G=_G
|
||||
kernel.sleep=sleep
|
||||
|
||||
_G.sleep=nil
|
||||
local windowsExp = false
|
||||
|
||||
function kernel.log(msg, level, c)
|
||||
c=c or 12
|
||||
kernel.LOG_Text = kernel.LOG_Text..string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||
if kernel.status == "start" then
|
||||
screen:setTextColor(c)
|
||||
screen:print(tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||
elseif kernel.status == "term" then
|
||||
kernel.standbyTask=kernel.currentTask
|
||||
kernel.currentTask=kernel.kernelTask
|
||||
kernel.vfs.devctl(1,"sfgc",c)
|
||||
kernel.vfs.write(1,tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||
kernel.currentTask=kernel.standbyTask
|
||||
function kernel.log(msg, level, c, nopanic)
|
||||
local f=function()
|
||||
c=c or 0x6D6D6D
|
||||
kernel.LOG_Text = kernel.LOG_Text..tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||
if kernel.status == "start" then
|
||||
screen:setTextColor(c)
|
||||
screen:print(tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||
elseif kernel.status == "term" then
|
||||
kernel.standbyTask=kernel.currentTask
|
||||
kernel.currentTask=kernel.kernelTask
|
||||
local file=kernel.vfs.open("/dev/console", "w")
|
||||
kernel.vfs.devctl(file,"sfgc",c)
|
||||
kernel.vfs.write(file,tostring(EFI:date()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
|
||||
kernel.vfs.close(file)
|
||||
kernel.currentTask=kernel.standbyTask
|
||||
end
|
||||
end
|
||||
|
||||
local ok,err = xpcall(f,debug.traceback)
|
||||
if not ok and not nopanic then
|
||||
kernel.panic(err)
|
||||
end
|
||||
end
|
||||
|
||||
function kernel.PANIC(msg)
|
||||
if kernel.status~="Panic" then
|
||||
kernel.log("PANIC: "..msg, "PANIC")
|
||||
kernel.log("PANIC: "..msg, "PANIC", 0xFF0000)
|
||||
pcall(kernel["saveLog"])
|
||||
kernel.status="Panic"
|
||||
kernel.reason=msg
|
||||
screen:setTextColor(2)
|
||||
screen:setBackgroundColor(16)
|
||||
screen:enable()
|
||||
screen:setTextColor(0xFF0000)
|
||||
screen:setBackgroundColor(0x000000)
|
||||
screen:clear()
|
||||
screen:setCursorPos(1,1)
|
||||
screen:resetCursor()
|
||||
screen:print(kernel.LOG_Text)
|
||||
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
|
||||
screen:print("Press any key to continue...")
|
||||
kernel.exitMain = true
|
||||
end
|
||||
while true do
|
||||
local event={computer:getMachineEvent()}
|
||||
local event={EFI:getMachineEvent()}
|
||||
if event[1]=="keyPressed" then
|
||||
break
|
||||
end
|
||||
end
|
||||
computer:reboot()
|
||||
EFI.reboot=true
|
||||
error("KERNEL PANIC")
|
||||
end
|
||||
kernel.panic=kernel.PANIC
|
||||
|
||||
if windowsExp then
|
||||
screen:setTextColor(1)
|
||||
screen:setBackgroundColor(4)
|
||||
screen:setTextColor(0xFFFFFF)
|
||||
screen:setBackgroundColor(0x0000FF)
|
||||
screen:clear()
|
||||
local w,h = screen:getSize()
|
||||
screen:setCursorPos(3,5)
|
||||
screen:print(":(")
|
||||
screen:setCursorPos(3,7)
|
||||
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
|
||||
screen:setCursorPos(3,8)
|
||||
screen:print("info, and then we'll restart for you.\n")
|
||||
screen: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")
|
||||
screen:resetCursor()
|
||||
screen:print("\n\n\n\n\n :(")
|
||||
screen:print("\n Your PC ran into a problem and needs to restart. We're just collecting some error")
|
||||
screen:print(" info, and then we'll restart for you.\n")
|
||||
screen:print("\n\n\n Stop code: average windows experience")
|
||||
while true do end
|
||||
end
|
||||
|
||||
@@ -113,7 +119,7 @@ local split = function(str, delim, maxResultCountOrNil)
|
||||
end
|
||||
|
||||
if not ifs.isFile("/boot/boot.cfg") then
|
||||
kernel.log("First boot detected writing boot.cfg", "INFO", 3)
|
||||
kernel.log("First boot detected writing boot.cfg", "INFO", 0x00FF00)
|
||||
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
||||
kernel.firstBoot=true
|
||||
end
|
||||
@@ -136,7 +142,7 @@ for i,v in ipairs(split(fstab,"\n")) do
|
||||
local id=""
|
||||
for i=3,#v do
|
||||
if v:sub(i,i)==";" then
|
||||
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") skip = true break end
|
||||
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN", 0xFF8800) skip = true break end
|
||||
id=v:sub(3,i-1)
|
||||
end
|
||||
end
|
||||
@@ -151,7 +157,13 @@ end
|
||||
kernel.log("Disks initialized")
|
||||
|
||||
function kernel.saveLog()
|
||||
ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text)
|
||||
if kernel.status=="running" then
|
||||
local file = kernel.vfs.open("/var/log/syslog.log", "w")
|
||||
kernel.vfs.write(file, kernel.LOG_Text)
|
||||
kernel.vfs.close(file)
|
||||
else
|
||||
ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text)
|
||||
end
|
||||
end
|
||||
|
||||
function kernel.newFifo()
|
||||
@@ -191,7 +203,7 @@ kernel.log("Gathering modules")
|
||||
for _, i in ipairs(ifs.list("/lib/modules")) do
|
||||
local modlist = ifs.list("/lib/modules/"..i)
|
||||
if not modlist then
|
||||
kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8)
|
||||
kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 0xFF8800)
|
||||
else
|
||||
for _,v in ipairs(modlist) do
|
||||
local prior=tonumber(v:sub(1,2))
|
||||
@@ -203,8 +215,8 @@ for _, i in ipairs(ifs.list("/lib/modules")) do
|
||||
end
|
||||
|
||||
kernel.ifs=ifs
|
||||
kernel.apis=apis
|
||||
kernel.computer=computer
|
||||
kernel.apis=EFI.firmware
|
||||
kernel.EFI=EFI
|
||||
kernel.arch=arch
|
||||
kernel.initdisks=disks
|
||||
kernel.screen=screen
|
||||
@@ -233,16 +245,29 @@ kernel.kernelTask = {
|
||||
kernel.currentTask = kernel.kernelTask
|
||||
|
||||
function kernel.shutdown()
|
||||
kernel.computer:shutdown()
|
||||
kernel.exitMain=true
|
||||
kernel.status="shutdown"
|
||||
end
|
||||
|
||||
function kernel.reboot()
|
||||
kernel.computer:reboot()
|
||||
kernel.exitMain=true
|
||||
kernel.status="reboot"
|
||||
end
|
||||
|
||||
kernel.syscalls["time"]=function() return kernel.computer:time() end
|
||||
function kernel.halt()
|
||||
kernel.exitMain=true
|
||||
kernel.status="halt"
|
||||
end
|
||||
|
||||
function kernel.asyncReturn(...)
|
||||
kernel.currentTask.syscallReturn = {...}
|
||||
end
|
||||
|
||||
kernel.syscalls["time"]=function() return kernel.EFI:getEpochMs() end
|
||||
kernel.syscalls["date"]=function() return kernel.EFI:date() end
|
||||
kernel.syscalls["log"]=kernel.log
|
||||
kernel.syscalls["getUptime"]=function() return kernel.computer:clock() end
|
||||
kernel.syscalls["tag"]=function() return kernel.tag end
|
||||
kernel.syscalls["getUptime"]=function() return kernel.EFI:getUptime() end
|
||||
kernel.syscalls["getUsername"]=function(uid) return kernel.users[uid or kernel.uid] end
|
||||
kernel.syscalls["getHostname"]=function() return kernel.hostname end
|
||||
kernel.syscalls["getHost"]=function() return kernel.apis._HOST end
|
||||
@@ -257,16 +282,25 @@ kernel.syscalls["sysdump"]=function()
|
||||
return rv
|
||||
end
|
||||
kernel.syscalls["reboot"]=function()
|
||||
kernel.computer:reboot()
|
||||
if kernel.uid==0 or kernel.groups.wheel then
|
||||
kernel.reboot()
|
||||
end
|
||||
end
|
||||
kernel.syscalls["shutdown"]=function()
|
||||
kernel.computer:reboot()
|
||||
if kernel.uid==0 or kernel.groups.wheel then
|
||||
kernel.shutdown()
|
||||
end
|
||||
end
|
||||
kernel.syscalls["halt"]=function()
|
||||
if kernel.uid==0 or kernel.groups.wheel then
|
||||
kernel.halt()
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("Running modules")
|
||||
for _,p in ipairs(modules) do
|
||||
for _,v in ipairs(p) do
|
||||
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 5) end
|
||||
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 0x00FFFF) end
|
||||
local code=ifs.readAllText(v)
|
||||
if not code then
|
||||
kernel.panic("Failed to read module "..v)
|
||||
@@ -275,15 +309,26 @@ for _,p in ipairs(modules) do
|
||||
if not func then kernel.panic("ModuLoadErr: "..tostring(err)) end
|
||||
local status, err = xpcall(func,debug.traceback, kernel)
|
||||
if not status then kernel.panic("ModuRunErr: "..tostring(err)) end
|
||||
if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 5) end
|
||||
if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 0x00FFFF) end
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("Kernel initialized successfully.")
|
||||
kernel.saveLog()
|
||||
kernel.status="running"
|
||||
kernel.main()
|
||||
if kernel.status=="panic" then
|
||||
kernel.panic()
|
||||
screen:disable()
|
||||
local ok,err = xpcall(kernel.main, debug.traceback)
|
||||
if not ok then
|
||||
kernel.panic(err)
|
||||
end
|
||||
kernel.PANIC("Execution complete")
|
||||
if kernel.status=="panic" then
|
||||
kernel.panic(kernel.reason)
|
||||
end
|
||||
if kernel.status=="reboot" then
|
||||
EFI.reboot=true
|
||||
return true
|
||||
elseif kernel.status=="halt" then
|
||||
kernel.log("System halted.")
|
||||
kernel.saveLog()
|
||||
while true do end
|
||||
end
|
||||
@@ -7,5 +7,5 @@ return {
|
||||
initPath = "/sbin/init",
|
||||
maxOpenFiles = 128,
|
||||
maxFilesPerTask = 16,
|
||||
preempt=true
|
||||
preempt = true,
|
||||
}
|
||||
@@ -229,6 +229,23 @@ function toHex(num)
|
||||
return string.format("%X", num)
|
||||
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 backing = {}
|
||||
return setmetatable(backing, {
|
||||
@@ -129,6 +129,8 @@ local function validateComponent(comp)
|
||||
end
|
||||
if lower == ".meta" then
|
||||
error("EINVAL: reserved path component: .meta", 3)
|
||||
elseif lower:sub(#lower) == "." then
|
||||
error("Cannot have empty name extention: " .. comp, 3)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -397,6 +399,10 @@ end
|
||||
local function getFileMeta(path, noFollow)
|
||||
local real = namei(path, noFollow)
|
||||
|
||||
if kernel.unixSockets[real] then
|
||||
return kernel.unixSockets[real].meta
|
||||
end
|
||||
|
||||
if real == "/" then
|
||||
return { etype = 0x00, owner = 0, group = 0, perms = 62, cmeta = "" }
|
||||
end
|
||||
@@ -499,7 +505,7 @@ local function checkperms(meta, mode)
|
||||
local bitmap = {
|
||||
r = {owner = 5, group = 3, everyone = 1},
|
||||
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]
|
||||
if not m then error("EINVAL") end
|
||||
@@ -536,6 +542,7 @@ local function allocFD(task)
|
||||
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
|
||||
return fd
|
||||
end
|
||||
|
||||
local function checkSystemLimit()
|
||||
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
|
||||
end
|
||||
@@ -544,32 +551,93 @@ local function newFileObj(handle, mode, path, meta, ftype)
|
||||
end
|
||||
|
||||
function vfs.newfd(fdobj)
|
||||
checkSystemLimit(); total = total + 1
|
||||
local fd = allocFD(kernel.currentTask)
|
||||
kernel.currentTask.fd[fd] = fdobj
|
||||
return fd
|
||||
end
|
||||
|
||||
function vfs.mount(target, diskOrId)
|
||||
function vfs.mount(target, diskOrId, bind)
|
||||
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
|
||||
if _euid ~= 0 then error("EPERM") end
|
||||
if not target then error("EINVAL") end
|
||||
target = normalizeMountPoint(target)
|
||||
if not vfs.exists(target) then vfs.mkdir(target) end
|
||||
if vfs.type(target) ~= "directory" then error("EINVAL") end
|
||||
|
||||
local drive, path = resolvePath(target)
|
||||
if not drive:directoryExists(path) then
|
||||
drive:makeDirectory(path)
|
||||
end
|
||||
if drive:type(path) ~= "directory" then
|
||||
error("EINVAL")
|
||||
end
|
||||
local disk, id
|
||||
if type(diskOrId) == "string" then
|
||||
disk = kernel.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
|
||||
|
||||
-- bind mount
|
||||
if bind then
|
||||
local src = normalizeMountPoint(diskOrId)
|
||||
local srcDrive = resolvePath(src)
|
||||
if not srcDrive then error("ENOENT") end
|
||||
id = "bind:" .. src
|
||||
disk = {
|
||||
address = id,
|
||||
isBind = true,
|
||||
source = srcDrive,
|
||||
exists = function(_, p)
|
||||
return srcDrive:exists(p)
|
||||
end,
|
||||
type = function(_, p)
|
||||
return srcDrive:type(p)
|
||||
end,
|
||||
list = function(_, p)
|
||||
return srcDrive:list(p)
|
||||
end,
|
||||
open = function(_, ...)
|
||||
return srcDrive:open(...)
|
||||
end,
|
||||
remove = function(_, p)
|
||||
return srcDrive:remove(p)
|
||||
end,
|
||||
makeDirectory = function(_, p)
|
||||
return srcDrive:makeDirectory(p)
|
||||
end,
|
||||
rename = function(_, a, b)
|
||||
return srcDrive:rename(a, b)
|
||||
end,
|
||||
size = function(_, p)
|
||||
return srcDrive:size(p)
|
||||
end,
|
||||
lastModified = function(_, p)
|
||||
return srcDrive:lastModified(p)
|
||||
end,
|
||||
isReadOnly = function()
|
||||
return srcDrive:isReadOnly()
|
||||
end,
|
||||
spaceTotal = function()
|
||||
return srcDrive:spaceTotal()
|
||||
end,
|
||||
spaceUsed = function()
|
||||
return srcDrive:spaceUsed()
|
||||
end
|
||||
}
|
||||
vfs.disks[id] = disk
|
||||
else
|
||||
if type(diskOrId) == "string" then
|
||||
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
|
||||
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end
|
||||
for _, mp in pairs(vfs.mounts) do
|
||||
if mp == target then
|
||||
error("EBUSY")
|
||||
end
|
||||
end
|
||||
vfs.mounts[id] = target
|
||||
return true
|
||||
end
|
||||
@@ -582,7 +650,11 @@ function vfs.umount(target)
|
||||
for id, mp in pairs(vfs.mounts) do
|
||||
if mp == target then
|
||||
if id == "$" then error("EBUSY") end
|
||||
vfs.mounts[id] = nil; return true
|
||||
vfs.mounts[id] = nil
|
||||
if vfs.disks[id] and vfs.disks[id].isBind then
|
||||
vfs.disks[id] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
error("EINVAL")
|
||||
@@ -594,6 +666,21 @@ function vfs.open(path, mode)
|
||||
local fd = allocFD(task)
|
||||
local disk, diskPath = resolvePath(path)
|
||||
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 isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
|
||||
@@ -695,10 +782,17 @@ function vfs.sendfile(outfd, infd, count)
|
||||
end
|
||||
|
||||
function vfs.stat(path)
|
||||
local attrs
|
||||
local disk, diskPath = resolvePath(path)
|
||||
local meta = getFileMeta(path)
|
||||
local ok, attrs = pcall(disk.attributes, disk, diskPath)
|
||||
if not ok then attrs = { size=0, modified=0, created=0 } end
|
||||
if meta.etype == 0x02 then
|
||||
attrs = { size=0, modified=0, created=0 }
|
||||
else
|
||||
local ok
|
||||
ok, attrs = pcall(disk.attributes, disk, diskPath)
|
||||
if not ok then attrs = { size=0, modified=0, created=0 } end
|
||||
end
|
||||
|
||||
return {
|
||||
size = attrs.size,
|
||||
modified = attrs.modified,
|
||||
@@ -713,15 +807,15 @@ end
|
||||
|
||||
function vfs.lstat(path)
|
||||
local meta = getFileMeta(path, true)
|
||||
|
||||
local attrs
|
||||
if meta.etype == 0x01 then
|
||||
if meta.etype == 0x01 or meta.etype == 0x02 then
|
||||
attrs = { size=0, modified=0, created=0 }
|
||||
else
|
||||
local disk, diskPath = resolvePath(path, true)
|
||||
local ok, a = pcall(disk.attributes, disk, diskPath)
|
||||
attrs = ok and a or { size=0, modified=0, created=0 }
|
||||
end
|
||||
|
||||
return {
|
||||
size = attrs.size,
|
||||
modified = attrs.modified,
|
||||
@@ -738,8 +832,15 @@ end
|
||||
function vfs.fstat(fd)
|
||||
local file = kernel.currentTask.fd[fd]
|
||||
if not file then error("EBADF") end
|
||||
local disk, diskPath = resolvePath(file.path)
|
||||
local attrs = disk:attributes(diskPath)
|
||||
local attrs
|
||||
if file.meta.etype == 0x02 then
|
||||
attrs = { size=0, modified=0, created=0 }
|
||||
else
|
||||
local disk, diskPath = resolvePath(file.path, true)
|
||||
local ok, a = pcall(disk.attributes, disk, diskPath)
|
||||
attrs = ok and a or { size=0, modified=0, created=0 }
|
||||
end
|
||||
|
||||
return {
|
||||
size = attrs.size,
|
||||
modified = attrs.modified,
|
||||
@@ -768,6 +869,14 @@ function vfs.listdir(path)
|
||||
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
|
||||
if diskPath == "/" then
|
||||
mp = ".meta"
|
||||
@@ -796,6 +905,7 @@ function vfs.mkdir(path)
|
||||
local parentMeta = getFileMeta(parent)
|
||||
checkperms(parentMeta, "w")
|
||||
local disk, diskPath = resolvePath(path)
|
||||
if disk:isReadOnly() then error("ERDONLY") end
|
||||
disk:makeDirectory(diskPath)
|
||||
local task = kernel.currentTask
|
||||
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||
@@ -817,8 +927,13 @@ function vfs.remove(path)
|
||||
|
||||
local meta = getFileMeta(path, true)
|
||||
|
||||
if kernel.unixSockets and kernel.unixSockets[path] then
|
||||
kernel.unixSockets[path] = nil
|
||||
if kernel.unixSockets[namei(path)] then
|
||||
if kernel.uid ~= 0 then
|
||||
if kernel.unixSockets[namei(path)].meta.owner~=kernel.uid then
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
kernel.unixSockets[namei(path)] = nil
|
||||
end
|
||||
|
||||
if meta.etype == 0x01 then
|
||||
@@ -827,6 +942,7 @@ function vfs.remove(path)
|
||||
if parent == "" then parent = "/" end
|
||||
local name = norm:match("[^/]+$")
|
||||
local disk, parentDiskPath = resolveMount(parent)
|
||||
if disk:isReadOnly() then error("ERDONLY") end
|
||||
local mp
|
||||
if parentDiskPath == "/" then mp = ".meta"
|
||||
else mp = parentDiskPath:gsub("^/+", "") .. "/.meta" end
|
||||
@@ -843,6 +959,7 @@ function vfs.remove(path)
|
||||
if f2.close then f2.close() end
|
||||
else
|
||||
local disk, diskPath = resolvePath(path)
|
||||
if disk:isReadOnly() then error("ERDONLY") end
|
||||
disk:remove(diskPath)
|
||||
end
|
||||
end
|
||||
@@ -851,6 +968,8 @@ function vfs.symlink(target, linkPath)
|
||||
if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end
|
||||
local norm = normalizePath(linkPath)
|
||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||
local disk = resolveMount(linkPath)
|
||||
if disk:isReadOnly() then error("ERDONLY") end
|
||||
if parent == "" then parent = "/" end
|
||||
local name = norm:match("[^/]+$")
|
||||
if not name then error("EINVAL") end
|
||||
@@ -880,7 +999,13 @@ function vfs.readlink(path)
|
||||
end
|
||||
|
||||
function vfs.access(path, mode)
|
||||
local meta = getFileMeta(path)
|
||||
local meta
|
||||
if kernel.unixSockets[normalizePath(path)] then
|
||||
meta = kernel.unixSockets[normalizePath(path)].meta
|
||||
else
|
||||
meta = getFileMeta(path)
|
||||
end
|
||||
|
||||
for i = 1, #mode do
|
||||
checkperms(meta, mode:sub(i,i))
|
||||
end
|
||||
@@ -891,6 +1016,8 @@ local function updateMeta(path, fn, noFollow)
|
||||
local real = namei(path, noFollow)
|
||||
local norm = real
|
||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||
local disk = resolveMount(path)
|
||||
if disk:isReadOnly() then error("ERDONLY") end
|
||||
if parent == "" then parent = "/" end
|
||||
local name = norm:match("[^/]+$")
|
||||
if not name then error("EINVAL") end
|
||||
@@ -917,6 +1044,9 @@ local function updateMeta(path, fn, noFollow)
|
||||
end
|
||||
|
||||
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 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
|
||||
@@ -926,10 +1056,14 @@ end
|
||||
function vfs.fchmod(fd, perms)
|
||||
local file = kernel.currentTask.fd[fd]
|
||||
if not file then error("EBADF") end
|
||||
if file.meta.etype==0x02 then error("EINVAL") end
|
||||
vfs.chmod(file.path, perms)
|
||||
end
|
||||
|
||||
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
|
||||
if _euid ~= 0 then error("EPERM") end
|
||||
updateMeta(path, function(e) e.owner = uid; e.group = gid end)
|
||||
@@ -938,20 +1072,29 @@ end
|
||||
function vfs.fchown(fd, uid, gid)
|
||||
local file = kernel.currentTask.fd[fd]
|
||||
if not file then error("EBADF") end
|
||||
if file.meta.etype==0x02 then error("EINVAL") end
|
||||
vfs.chown(file.path, uid, gid)
|
||||
end
|
||||
|
||||
function vfs.exists(path)
|
||||
if kernel.unixSockets[namei(path)] then return true end
|
||||
local meta = getFileMeta(path, true)
|
||||
if meta.etype == 0x01 then return true end
|
||||
if meta.etype == 0x01 or meta.etype == 0x02 then return true end
|
||||
local ok, disk, diskPath = pcall(resolvePath, path)
|
||||
if not ok then return false end
|
||||
return disk:fileExists(diskPath)
|
||||
if disk:type(diskPath) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function vfs.type(path)
|
||||
local meta = getFileMeta(path, true)
|
||||
if meta.etype == 0x01 then return "symlink" end
|
||||
if kernel.unixSockets[namei(path)] then
|
||||
return "socket"
|
||||
end
|
||||
local ok, disk, diskPath = pcall(resolvePath, path)
|
||||
if not ok then return nil end
|
||||
return disk:type(diskPath)
|
||||
63
Src/Hyperion-kernel/data/lib/modules/hyperion/11_ld.kmod
Normal file
63
Src/Hyperion-kernel/data/lib/modules/hyperion/11_ld.kmod
Normal file
@@ -0,0 +1,63 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
"?", "?.lua", "/lib/?", "/lib/?.lua", "/usr/lib/?", "/usr/lib/?.lua",
|
||||
"/usr/local/lib/?", "/usr/local/lib/?.lua"
|
||||
}
|
||||
kernel.reqcache = cache
|
||||
|
||||
local function require(module, ...)
|
||||
if cache[module] then return cache[module].ret end
|
||||
kernel.currentTask.status = "D"
|
||||
local args = {...}
|
||||
kernel.currentTask.ksh = coroutine.create(function()
|
||||
local coro = function()
|
||||
for _, path in ipairs(kernel.searchpaths) do
|
||||
local filepath = path:gsub("?", module)
|
||||
if kernel.vfs.exists(filepath) then
|
||||
if kernel.vfs.type(filepath) == "directory" then
|
||||
filepath = filepath .. "/init.lua"
|
||||
if kernel.vfs.type(filepath) == "directory" then
|
||||
kernel.asyncReturn(false, "Module not found: "..module)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local fd = kernel.vfs.open(filepath, "r")
|
||||
local chunks = {}
|
||||
while true do
|
||||
local chunk = kernel.vfs.read(fd, 4096)
|
||||
if not chunk or chunk == "" then break end
|
||||
chunks[#chunks + 1] = chunk
|
||||
coroutine.yield()
|
||||
end
|
||||
kernel.vfs.close(fd)
|
||||
local data = table.concat(chunks)
|
||||
local func, err = load(data, "@"..module, "t", kernel._U)
|
||||
if not func then
|
||||
kernel.asyncReturn(false, "Error loading module "..module..": "..tostring(err))
|
||||
return
|
||||
end
|
||||
local ok, ret = xpcall(func, debug.traceback, table.unpack(args))
|
||||
if not ok then
|
||||
kernel.asyncReturn(false, "Error running module "..module..": "..tostring(ret))
|
||||
return
|
||||
end
|
||||
cache[module] = {ret = ret, expires = kernel.EFI:getEpochMs() + 120000}
|
||||
kernel.asyncReturn(true, ret)
|
||||
return
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
kernel.asyncReturn(false, "Module not found: "..module)
|
||||
end
|
||||
local status, err = xpcall(coro, debug.traceback)
|
||||
if not status then
|
||||
kernel.asyncReturn(false, "Error in require coroutine for module "..module..": "..tostring(err))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
kernel.syscalls["require"] = require
|
||||
_G.require = function(...) return syscall.require(...) end
|
||||
301
Src/Hyperion-kernel/data/lib/modules/hyperion/12_devfs.kmod
Normal file
301
Src/Hyperion-kernel/data/lib/modules/hyperion/12_devfs.kmod
Normal file
@@ -0,0 +1,301 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
|
||||
proxy.address = "devfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
proxy.makeDirectory = function() error("EACCES") end
|
||||
proxy.remove = function() error("EACCES") end
|
||||
proxy.setLabel = function() error("EACCES") end
|
||||
proxy.getLabel = function() return "devfs" end
|
||||
proxy.attributes = function(path) return {
|
||||
size = 0,
|
||||
modified = 0,
|
||||
created = 0
|
||||
} end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1, #steps-1 do
|
||||
local dat = step[steps[i]]
|
||||
if type(dat) ~= "table" then error("ENFILE") end
|
||||
step=dat
|
||||
end
|
||||
if type(step[steps[#steps]]) == "function" then
|
||||
return step[steps[#steps]]("open", mode)
|
||||
end
|
||||
error("ENFILE")
|
||||
end
|
||||
|
||||
function proxy:type(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then
|
||||
return "directory"
|
||||
end
|
||||
for i=1, #steps-1 do
|
||||
local dat = step[steps[i]]
|
||||
if type(dat) ~= "table" then error("ENFILE") end
|
||||
step=dat
|
||||
end
|
||||
if type(step[steps[#steps]]) == "function" then
|
||||
return step[steps[#steps]]("type", mode)
|
||||
end
|
||||
if type(step[steps[#steps]]) == "table" then
|
||||
return "directory"
|
||||
end
|
||||
error("ENOENT")
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then
|
||||
return table.keys(data)
|
||||
end
|
||||
for i=1, #steps-1 do
|
||||
local dat = step[steps[i]]
|
||||
if type(dat) ~= "table" then error("ENOENT") end
|
||||
step=dat
|
||||
end
|
||||
if type(step[steps[#steps]]) == "table" then
|
||||
return table.keys(step[steps[#steps]])
|
||||
end
|
||||
error("ENOENT")
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local ok = pcall(function()
|
||||
return self:type(path)
|
||||
end)
|
||||
return ok
|
||||
end
|
||||
|
||||
function data.random(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=function(amount)
|
||||
local str = ""
|
||||
for i=1, amount or 1 do
|
||||
str=str..string.char(math.random(0, 255))
|
||||
end
|
||||
return str
|
||||
end
|
||||
}
|
||||
elseif mode=="w" or mode=="a" then
|
||||
return {
|
||||
write=function() end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data.null(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=function(amount) end
|
||||
}
|
||||
elseif mode=="w" or mode=="a" then
|
||||
return {
|
||||
write=function() end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data.zero(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=function(amount)
|
||||
local str = ""
|
||||
for i=1, amount or 1 do
|
||||
str=str..string.char(0)
|
||||
end
|
||||
return str
|
||||
end
|
||||
}
|
||||
elseif mode=="w" or mode=="a" then
|
||||
return {
|
||||
write=function() end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function buildMeta(entries, opts)
|
||||
opts = opts or {}
|
||||
local uid = opts.uid or 0
|
||||
local gid = opts.gid or 0
|
||||
local perms = opts.perms or 0x3F -- default read/write for owner/group/world
|
||||
|
||||
local chunks = {}
|
||||
table.insert(chunks, string.char(0x02)) -- version header
|
||||
|
||||
for path, target in pairs(entries) do
|
||||
local name = path
|
||||
local nameLen = #name
|
||||
if nameLen > 255 then
|
||||
error("Filename too long (>255 bytes): "..name)
|
||||
end
|
||||
|
||||
-- Determine entry type: 0x01 = symlink if target ~= nil and target ~= ""
|
||||
local entryType = 0x00
|
||||
local cmeta = ""
|
||||
if target and target ~= "" then
|
||||
entryType = 0x01
|
||||
cmeta = target
|
||||
end
|
||||
local cmetaLen = #cmeta
|
||||
if cmetaLen > 255 then
|
||||
error("cmeta too long (>255 bytes) for "..name)
|
||||
end
|
||||
|
||||
-- Build entry as bytes
|
||||
table.insert(chunks, string.char(nameLen)) -- name length
|
||||
table.insert(chunks, name) -- name
|
||||
table.insert(chunks, string.char(entryType)) -- entry type
|
||||
table.insert(chunks, string.char(uid % 256, math.floor(uid/256) % 256)) -- uid
|
||||
table.insert(chunks, string.char(gid % 256, math.floor(gid/256) % 256)) -- gid
|
||||
table.insert(chunks, string.char(perms % 256, math.floor(perms/256) % 256)) -- perms
|
||||
table.insert(chunks, string.char(cmetaLen)) -- cmeta length
|
||||
if cmetaLen > 0 then
|
||||
table.insert(chunks, cmeta)
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(chunks)
|
||||
end
|
||||
|
||||
local function simpleFile(r,w)
|
||||
return function(op, mode)
|
||||
if op=="type" then
|
||||
return "file"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=r
|
||||
}
|
||||
elseif mode=="w" then
|
||||
return {
|
||||
write=w
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function strFile(str)
|
||||
local dat=tostring(str)
|
||||
local pos=1
|
||||
return simpleFile(function(amount)
|
||||
pos=pos+amount
|
||||
return dat:sub(pos-amount, pos)
|
||||
end,function() error("EACCES") end)
|
||||
end
|
||||
|
||||
data[".meta"]=strFile(buildMeta({
|
||||
stdin="/proc/self/fd/0",
|
||||
stdout="/proc/self/fd/1",
|
||||
log="/var/log/syslog.log"
|
||||
}))
|
||||
|
||||
if kernel.EFI:getEEPROM() then
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,eeprom=1,kernel.EFI:getEEPROM()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return eeprom:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
local firstwrite=true
|
||||
return {
|
||||
write=function(data)
|
||||
if firstwrite then
|
||||
kernel.EFI:setEEPROM(data)
|
||||
else
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if kernel.EFI:getNvram() then
|
||||
function data.nvram(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,nvram=1,kernel.EFI:getNvram()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return nvram:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
local firstwrite=true
|
||||
return {
|
||||
write=function(data)
|
||||
if firstwrite then
|
||||
kernel.EFI:setNvram(data)
|
||||
else
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}}
|
||||
data["input"]={}
|
||||
kernel.devfs={}
|
||||
kernel.devfs.data=data
|
||||
kernel.devfs.proxy=proxy
|
||||
kernel.disks["devfs0000"]=proxy
|
||||
@@ -6,7 +6,7 @@ local data = {}
|
||||
|
||||
proxy.address = "procfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
proxy.isReadOnly = function() return true end
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
proxy.makeDirectory = function() error("EACCES") end
|
||||
@@ -114,7 +114,8 @@ local function newtaskproxy(task)
|
||||
},
|
||||
children={
|
||||
[".meta"]=strFile(buildMeta(children))
|
||||
}
|
||||
},
|
||||
pid=strFile(task.pid)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -258,7 +259,7 @@ function proxy:fileExists(path)
|
||||
return ok
|
||||
end
|
||||
|
||||
data.uptime=simpleFile(function()return tostring(kernel.computer:getUptime())end,function()error("EACCES")end)
|
||||
data.uptime=simpleFile(function()return tostring(kernel.EFI:getUptime())end,function()error("EACCES")end)
|
||||
kernel.procfs={}
|
||||
kernel.procfs.data=data
|
||||
kernel.procfs.proxy=proxy
|
||||
141
Src/Hyperion-kernel/data/lib/modules/hyperion/12_tmpfs.kmod
Normal file
141
Src/Hyperion-kernel/data/lib/modules/hyperion/12_tmpfs.kmod
Normal file
@@ -0,0 +1,141 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
kernel.vfs.remove("/tmp")
|
||||
kernel.vfs.mkdir("/tmp")
|
||||
if not kernel.config.tmpToDisk then
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
|
||||
proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
if not step[steps[i]] then
|
||||
step[steps[i]] = {}
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
end
|
||||
|
||||
proxy.remove = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
step[steps[#steps]] = nil
|
||||
end
|
||||
|
||||
proxy.setLabel = function(_, label) end
|
||||
proxy.getLabel = function() return "tmpfs" end
|
||||
|
||||
proxy.attributes = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
return {
|
||||
size = type(step) == "string" and #step or 0,
|
||||
modified = 0,
|
||||
created = 0,
|
||||
}
|
||||
end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
if not step[steps[i]] then
|
||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
local filename = steps[#steps]
|
||||
|
||||
if mode == "r" then
|
||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
return {
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
|
||||
function proxy:type(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then return "directory" end
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then return false end
|
||||
end
|
||||
if type(step) == "table" then return "directory" end
|
||||
if type(step) == "string" then return "file" end
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
if type(step) ~= "table" then error("ENOTDIR") end
|
||||
local keys = {}
|
||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
||||
return keys
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
else
|
||||
kernel.log("tmpToDisk enabled, skipping tmpfs module")
|
||||
kernel.log("tmpfs0000 will passthrough to /tmp on the disk")
|
||||
end
|
||||
@@ -21,14 +21,15 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
||||
end
|
||||
|
||||
if not semicolon_pos or semicolon_pos == 3 then
|
||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8)
|
||||
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 0xFF8800)
|
||||
else
|
||||
local id = line:sub(3, semicolon_pos - 1)
|
||||
local path = trim(line:sub(semicolon_pos + 1))
|
||||
kernel.log("Mounted "..id.." to "..path)
|
||||
kernel.log("Mounting '"..id.."' to '"..path.."'")
|
||||
if id ~= "$" then
|
||||
kernel.vfs.mount(path, id)
|
||||
end
|
||||
kernel.log("Mounted "..id.." to "..path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,13 +12,24 @@ function signal.sigsend(pid, sig)
|
||||
end
|
||||
|
||||
function signal.sigcatch(handler)
|
||||
kernel.currentTask.sigh=handler
|
||||
if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end
|
||||
local task=kernel.currentTask
|
||||
task.sigh=handler
|
||||
if not task.sigq then task.sigq={} end
|
||||
local handle={
|
||||
error="",
|
||||
active=true
|
||||
}
|
||||
if task.sigd then task.sigd.active=false; end
|
||||
task.sigd=handle
|
||||
return handle
|
||||
end
|
||||
|
||||
function signal.sigignore()
|
||||
kernel.currentTask.sigh=nil
|
||||
kernel.currentTask.sigq=nil
|
||||
local task=kernel.currentTask
|
||||
task.sigh=nil
|
||||
task.sigq=nil
|
||||
if task.sigd then task.sigd.active=false end
|
||||
task.sigd=nil
|
||||
end
|
||||
|
||||
local s=kernel.syscalls
|
||||
194
Src/Hyperion-kernel/data/lib/modules/hyperion/20_socket.kmod
Normal file
194
Src/Hyperion-kernel/data/lib/modules/hyperion/20_socket.kmod
Normal file
@@ -0,0 +1,194 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local socket = {}
|
||||
|
||||
socket.handlers = {}
|
||||
|
||||
kernel.socket = socket
|
||||
|
||||
local P = kernel.vfs.P
|
||||
local sys = kernel.syscalls
|
||||
|
||||
function socket.registerProtocal(proto, handler)
|
||||
socket.handlers[proto] = handler
|
||||
end
|
||||
|
||||
local function getHandler(address)
|
||||
for proto, handler in pairs(socket.handlers) do
|
||||
if string.hasPrefix(address, proto) then
|
||||
return handler
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function socket.socket(domain, socktype)
|
||||
local fd = kernel.vfs.newfd({
|
||||
handle = {},
|
||||
|
||||
type = "socket",
|
||||
|
||||
refcount = 1,
|
||||
|
||||
socket = {
|
||||
connected = false,
|
||||
protocol = nil,
|
||||
address = nil
|
||||
},
|
||||
|
||||
meta = {
|
||||
owner = kernel.currentTask.uid,
|
||||
group = kernel.currentTask.uid,
|
||||
etype = 2,
|
||||
|
||||
perms =
|
||||
P.OWNER_R +
|
||||
P.OWNER_W +
|
||||
P.GROUP_R +
|
||||
P.GROUP_W
|
||||
},
|
||||
|
||||
isvirt = true
|
||||
})
|
||||
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
fdo.handle.read = function()
|
||||
return ""
|
||||
end
|
||||
|
||||
fdo.handle.write = function()
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
fdo.handle.close = function()
|
||||
fdo.socket.connected = false
|
||||
return true
|
||||
end
|
||||
|
||||
return fd
|
||||
end
|
||||
|
||||
function socket.connect(fd, address)
|
||||
local fdo = kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
local handler =
|
||||
getHandler(address)
|
||||
|
||||
if not handler then
|
||||
return nil, "EPROTONOSUPPORT"
|
||||
end
|
||||
|
||||
fdo.socket.protocol = handler
|
||||
fdo.socket.address = address
|
||||
|
||||
local ok, err =
|
||||
handler.connect(fd, address)
|
||||
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
fdo.socket.connected = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function socket.listen(fd, backlog)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if not fdo.handle.listen then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.listen(backlog)
|
||||
end
|
||||
|
||||
function socket.send(fd, data)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if not fdo.socket.connected then
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
if not fdo.handle.write then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.write(data)
|
||||
end
|
||||
|
||||
function socket.recv(fd, amount)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if not fdo.socket.connected then
|
||||
return nil, "ENOTCONN"
|
||||
end
|
||||
|
||||
if not fdo.handle.read then
|
||||
return nil, "EOPNOTSUPP"
|
||||
end
|
||||
|
||||
return fdo.handle.read(amount)
|
||||
end
|
||||
|
||||
function socket.sockshutdown(fd)
|
||||
local fdo =
|
||||
kernel.currentTask.fd[fd]
|
||||
|
||||
if not fdo then
|
||||
return nil, "EBADF"
|
||||
end
|
||||
|
||||
if fdo.type ~= "socket" then
|
||||
return nil, "ENOTSOCK"
|
||||
end
|
||||
|
||||
if fdo.handle.close then
|
||||
return fdo.handle.close()
|
||||
end
|
||||
|
||||
fdo.socket.connected = false
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
sys.socket = socket.socket
|
||||
sys.connect = socket.connect
|
||||
sys.listen = socket.listen
|
||||
sys.send = socket.send
|
||||
sys.recv = socket.recv
|
||||
sys.sockshutdown = socket.sockshutdown
|
||||
|
||||
kernel.log("Loaded socket module")
|
||||
@@ -0,0 +1,4 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
kernel.unixSockets={}
|
||||
local socket=kernel.socket
|
||||
129
Src/Hyperion-kernel/data/lib/modules/hyperion/30_vterm.kmod
Normal file
129
Src/Hyperion-kernel/data/lib/modules/hyperion/30_vterm.kmod
Normal file
@@ -0,0 +1,129 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local vterms = {}
|
||||
|
||||
local function createVt(id, width, height)
|
||||
local vt = {
|
||||
id = id,
|
||||
width = width,
|
||||
height = height,
|
||||
buffer = {},
|
||||
cursorX = 1,
|
||||
cursorY = 1,
|
||||
fgColor = 0xFFFFFF,
|
||||
bgColor = 0x000000,
|
||||
obj = {}
|
||||
}
|
||||
|
||||
for y = 1, height do
|
||||
vt.buffer[y] = {}
|
||||
for x = 1, width do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
|
||||
local function scroll(lines)
|
||||
for _ = 1, lines do
|
||||
table.remove(vt.buffer, 1)
|
||||
vt.buffer[vt.height] = {}
|
||||
for x = 1, vt.width do
|
||||
vt.buffer[vt.height][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function vt.obj:write(content)
|
||||
local x, y = vt.cursorX, vt.cursorY
|
||||
if x>vt.width then return end
|
||||
if y>vt.height then return end
|
||||
for i = 1, #content do
|
||||
local c = content:sub(i, i)
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
for _ = 1, spaces do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
x = x + 1
|
||||
if x > vt.width then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
else
|
||||
if x <= vt.width and y <= vt.height then
|
||||
vt.buffer[y][x] = {char = c, fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > vt.width then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y > vt.height then
|
||||
scroll(1)
|
||||
y = vt.height
|
||||
end
|
||||
end
|
||||
|
||||
vt.cursorX, vt.cursorY = x, y
|
||||
end
|
||||
|
||||
function vt.obj:spos(x, y)
|
||||
vt.cursorX = tonumber(x)
|
||||
vt.cursorY = tonumber(y)
|
||||
end
|
||||
|
||||
function vt.obj:gpos()
|
||||
return tostring(vt.cursorX)..";"..tostring(vt.cursorY)
|
||||
end
|
||||
|
||||
function vt.obj:sfgc(color)
|
||||
vt.fgColor = tonumber(color)
|
||||
end
|
||||
|
||||
function vt.obj:sbgc(color)
|
||||
vt.bgColor = tonumber(color)
|
||||
end
|
||||
|
||||
function vt.obj:gfgc()
|
||||
return vt.fgColor
|
||||
end
|
||||
|
||||
function vt.obj:gbgc()
|
||||
return vt.bgColor
|
||||
end
|
||||
|
||||
function vt.obj:gplt()
|
||||
return 24
|
||||
end
|
||||
|
||||
function vt.obj:clear()
|
||||
for y = 1, vt.height do
|
||||
for x = 1, vt.width do
|
||||
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
|
||||
end
|
||||
end
|
||||
vt.cursorX, vt.cursorY = 1, 1
|
||||
end
|
||||
|
||||
function vt.obj:isvirt()
|
||||
return true
|
||||
end
|
||||
|
||||
function vt.obj:gctrl()
|
||||
return serializeBool(vt.ctrl)..";"..serializeBool(vt.alt)
|
||||
end
|
||||
|
||||
return vt
|
||||
end
|
||||
@@ -139,7 +139,7 @@ end
|
||||
if not blake2s then error("Failed to load blake2s") end
|
||||
|
||||
if not kernel.vfs.exists("/etc/pam.d/secret") then
|
||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT")
|
||||
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT", "WARN", 0xFF8800)
|
||||
local key = ""
|
||||
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
|
||||
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
|
||||
@@ -150,16 +150,11 @@ end
|
||||
local pepper = getFile("/etc/pam.d/secret")
|
||||
|
||||
local function genSalt()
|
||||
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
|
||||
local s = ""
|
||||
for i = 1, 16 do
|
||||
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
|
||||
end
|
||||
return s
|
||||
return toHex(math.random(0,2^32))
|
||||
end
|
||||
|
||||
local function hashPassword(password, salt)
|
||||
local key = (pepper .. salt):sub(1, 32)
|
||||
local key = (pepper .. salt)
|
||||
return blake2s(password, key)
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ local function loadExecutable(path)
|
||||
local env = kernel.freshUserEnv()
|
||||
|
||||
local func, err = load(data, "@" .. path, "t", env)
|
||||
if not func then error("ENOEXEC: " .. tostring(err)) end
|
||||
if not func then kernel.log("Failed to load executable: " .. tostring(err).. " : ".. tostring(path)); error("ENOEXEC: " .. tostring(err)) end
|
||||
|
||||
local meta = kernel.vfs.lstat(path)
|
||||
local suid_set = bit_is_set(meta.perms, 6)
|
||||
@@ -41,11 +41,11 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
|
||||
|
||||
if kernel.config.logTaskExit then
|
||||
if not ok then
|
||||
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 2)
|
||||
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 0xFF0000)
|
||||
elseif err then
|
||||
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "INFO")
|
||||
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "DBUG", 0x00FFFF)
|
||||
else
|
||||
kernel.log("Task " .. tostring(id) .. " exited without code", "INFO")
|
||||
kernel.log("Task " .. tostring(id) .. " exited without code", "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -110,7 +110,7 @@ function sys.execspawn(path, name, envars, args, tgid)
|
||||
kernel.log(
|
||||
"execspawn: suid exec '" .. path ..
|
||||
"' caller_uid=" .. tostring(real_uid) ..
|
||||
" -> euid=" .. tostring(euid), "INFO"
|
||||
" -> euid=" .. tostring(euid)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -136,9 +136,9 @@ function sys.exec(path, args, envars)
|
||||
local ok, err = xpcall(func, debug.traceback, table.unpack(task.args))
|
||||
if kernel.config.logTaskExit then
|
||||
if not ok then
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 2)
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 0xFF0000)
|
||||
else
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "INFO")
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
if type(err) == "number" then tasks[tostring(task.pid)].exit = err end
|
||||
@@ -155,8 +155,7 @@ end
|
||||
|
||||
function sys.sleep(s)
|
||||
kernel.currentTask.status = "S"
|
||||
kernel.currentTask.sleep = kernel.computer:time() + s * 1000
|
||||
coroutine.yield()
|
||||
kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
|
||||
end
|
||||
|
||||
function sys.getTask(pid)
|
||||
@@ -204,12 +203,16 @@ function sys.collect(pid)
|
||||
end
|
||||
end
|
||||
|
||||
function sys.kill(pid)
|
||||
function sys.kill(pid, force)
|
||||
local task = tasks[tostring(pid)]
|
||||
if not task then
|
||||
return false, "Task does not exist"
|
||||
elseif task.status == "Z" then
|
||||
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
|
||||
local caller = kernel.currentTask
|
||||
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
|
||||
@@ -224,10 +227,16 @@ function sys.stop(pid)
|
||||
local task = tasks[tostring(pid)]
|
||||
if not task then
|
||||
return false, "Task does not exist"
|
||||
elseif task.status ~= "R" then
|
||||
elseif task.status ~= "R" and task.status ~= "S" then
|
||||
return false, "Cannot stop non-running task"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
else
|
||||
task.status = "T"
|
||||
if task.status == "S" then
|
||||
task.status = "ST"
|
||||
else
|
||||
task.status = "T"
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -236,10 +245,16 @@ function sys.continue(pid)
|
||||
local task = tasks[tostring(pid)]
|
||||
if not task then
|
||||
return false, "Task does not exist"
|
||||
elseif task.status ~= "T" then
|
||||
elseif task.status ~= "T" and task.status ~= "ST" then
|
||||
return false, "Task is not stopped"
|
||||
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
|
||||
return false, "Different user"
|
||||
else
|
||||
task.status = "R"
|
||||
if task.status == "ST" then
|
||||
task.status = "S"
|
||||
else
|
||||
task.status = "R"
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -260,9 +275,9 @@ function sys.exit(code)
|
||||
local task = kernel.currentTask
|
||||
if kernel.config.logTaskExit then
|
||||
if code then
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "INFO")
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "DBUG", 0x00FFFF)
|
||||
else
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "INFO")
|
||||
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
tasks[tostring(task.pid)].status = "Z"
|
||||
@@ -290,22 +305,23 @@ function sys.getuid() return kernel.currentTask.uid end
|
||||
local function reapDeadTasks()
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.status == "Z" and not task.reapTime then
|
||||
task.coro = nil
|
||||
task.ivs = nil
|
||||
task.vs = nil
|
||||
task.args = nil
|
||||
task.envars = nil
|
||||
task.cwd = nil
|
||||
task.numRuns = nil
|
||||
task.totalTime = nil
|
||||
task.lastTime = nil
|
||||
task.timeSlice = nil
|
||||
if task.pid == 1 then kernel.panic("Attempted to gc init!") end
|
||||
task.coro = nil
|
||||
task.ivs = nil
|
||||
task.vs = nil
|
||||
task.args = nil
|
||||
task.envars = nil
|
||||
task.cwd = nil
|
||||
task.numRuns = nil
|
||||
task.totalTime = nil
|
||||
task.lastTime = nil
|
||||
task.timeSlice = nil
|
||||
task.syscallReturn = nil
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.computer:time() + 30000
|
||||
task.sleep = nil
|
||||
task.fd = nil
|
||||
task.reapTime = kernel.EFI:getEpochMs() + 30000
|
||||
|
||||
elseif task.reapTime and kernel.computer:time() > task.reapTime
|
||||
elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
|
||||
and task.status == "Z" then
|
||||
for _, child in ipairs(task.children) do
|
||||
child.parent = tasks["1"]
|
||||
@@ -335,7 +351,23 @@ local k_max = 0.5
|
||||
local B = 0.01
|
||||
|
||||
function kernel.main()
|
||||
kernel.log("Starting main loop...")
|
||||
kernel.saveLog()
|
||||
local stopLog=5
|
||||
local logTasks=100
|
||||
|
||||
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 Tmin_hit = 0
|
||||
local Tmax_hit = 0
|
||||
@@ -343,55 +375,94 @@ function kernel.main()
|
||||
local taskTimes = {}
|
||||
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.status == "S" and kernel.computer:time() >= task.sleep then
|
||||
if kernel.exitMain then break end
|
||||
kernel.currentTask = task
|
||||
kernel.uid = task.euid or task.uid
|
||||
kernel.process = task.name
|
||||
|
||||
if task.status == "S" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||
task.status = "R"
|
||||
task.sleep = 0
|
||||
end
|
||||
|
||||
if task.status == "R" then
|
||||
kernel.currentTask = task
|
||||
if task.status == "ST" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||
task.status = "T"
|
||||
task.sleep = 0
|
||||
end
|
||||
|
||||
kernel.uid = task.euid or task.uid
|
||||
kernel.process = task.name
|
||||
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
|
||||
N = N + 1
|
||||
|
||||
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
|
||||
|
||||
if task.sigq and #task.sigq ~= 0 and task.sigh then
|
||||
local coro = coroutine.create(task.sigh)
|
||||
local sigret = { coroutine.resume(coro, table.remove(task.sigq, 1)) }
|
||||
while coroutine.status(coro) ~= "dead" do
|
||||
if sigret[1] == false then break end
|
||||
if sigret[2] == "syscall" then
|
||||
local scname = sigret[3]
|
||||
local sysret
|
||||
if kernel.syscalls[scname] then
|
||||
sysret = { xpcall(kernel.syscalls[scname], debug.traceback, table.unpack(sigret, 4)) }
|
||||
else
|
||||
sysret = { false, "Unknown syscall: " .. tostring(scname) }
|
||||
end
|
||||
if not sysret[1] then
|
||||
sigret = { coroutine.resume(coro, false, sysret[2]) }
|
||||
else
|
||||
sigret = { coroutine.resume(coro, true, table.unpack(sysret, 2)) }
|
||||
end
|
||||
else
|
||||
sigret = { coroutine.resume(coro) }
|
||||
local status,err
|
||||
if kernel.config.preempt then
|
||||
status,err=coroutine.resumeWithTimeout(coro, 100, table.remove(task.sigq, 1))
|
||||
else
|
||||
status,err=coroutine.resume(coro, table.remove(task.sigq, 1))
|
||||
end
|
||||
if status=="error" or status==false then
|
||||
task.sigd.error=err or "Unknown"
|
||||
task.sigd.active=false
|
||||
task.sigh=nil
|
||||
task.sigq=nil
|
||||
task.sigd=nil
|
||||
elseif status=="success" or status==true then
|
||||
if err=="syscall" then
|
||||
task.sigd.error="Cannot execute syscalls from signals"
|
||||
task.sigd.active=false
|
||||
task.sigh=nil
|
||||
task.sigq=nil
|
||||
task.sigd=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if task.status == "R" then
|
||||
local startTime = kernel.computer:time()
|
||||
local startTime = kernel.EFI:getEpochMs()
|
||||
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
|
||||
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
|
||||
if not task.debugger then
|
||||
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
|
||||
else
|
||||
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
|
||||
end
|
||||
else
|
||||
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
|
||||
end
|
||||
|
||||
local elapsed = kernel.computer:time() - startTime
|
||||
local elapsed = kernel.EFI:getEpochMs() - startTime
|
||||
task.lastTime = elapsed
|
||||
task.totalTime = (task.totalTime or 0) + elapsed
|
||||
task.numRuns = (task.numRuns or 0) + 1
|
||||
@@ -403,7 +474,7 @@ function kernel.main()
|
||||
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
|
||||
|
||||
if ret[1] == "error" or ret[1] == false then
|
||||
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 2)
|
||||
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 0xFF0000)
|
||||
task.status = "Z"
|
||||
task.exit = "processHandlerException: " .. tostring(ret[2])
|
||||
|
||||
@@ -418,9 +489,9 @@ function kernel.main()
|
||||
local scname = ret[3]
|
||||
if kernel.syscalls[scname] then
|
||||
if kernel.config.debugSyscalls then
|
||||
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 5)
|
||||
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 0x00FFFF)
|
||||
for i = 4, #ret do
|
||||
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 5)
|
||||
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -428,12 +499,12 @@ function kernel.main()
|
||||
|
||||
if kernel.config.debugSyscalls then
|
||||
if not sysret[1] then
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 2)
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 0xFF0000)
|
||||
else
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 5)
|
||||
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 0x00FFFF)
|
||||
for i = 2, #sysret do
|
||||
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
|
||||
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 5)
|
||||
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -466,6 +537,11 @@ function kernel.main()
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@@ -9,8 +9,20 @@ sysc["gpio_write"]=function(pin, data)
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_writeAnalog"]=function(pin, data)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("wa", data)
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_read"]=function(pin)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("r")
|
||||
end
|
||||
end
|
||||
|
||||
sysc["gpio_readAnalog"]=function(pin)
|
||||
if kernel.gpio[pin] then
|
||||
return kernel.gpio[pin]("ra")
|
||||
end
|
||||
end
|
||||
@@ -9,11 +9,13 @@ if not initOk then
|
||||
end
|
||||
|
||||
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
||||
if not handle then kernel.panic("Failed to open "..kernel.config.initPath) end
|
||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
if not handle then kernel.panic("Failed to read "..kernel.config.initPath) end
|
||||
kernel.vfs.close(handle)
|
||||
|
||||
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
|
||||
if not initFunc then error("Failed to load init system: " .. err) end
|
||||
if not initFunc then kernel.PANIC("Failed to load init system: " .. err) end
|
||||
|
||||
kernel.tasks["1"] = {
|
||||
coro = coroutine.create(function()
|
||||
11
Src/Hyperion-kernel/data/lib/modules/hyperion/90_kgc.kmod
Normal file
11
Src/Hyperion-kernel/data/lib/modules/hyperion/90_kgc.kmod
Normal file
@@ -0,0 +1,11 @@
|
||||
local kernel = ...
|
||||
kernel.processes.kgc = function()
|
||||
while true do
|
||||
for i,v in pairs(kernel.reqcache) do
|
||||
if v.expires and kernel.EFI:getEpochMs() > v.expires then
|
||||
kernel.reqcache[i] = nil
|
||||
end
|
||||
end
|
||||
kernel.sleep(5000)
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,10 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
if not kernel.vfs.exists("/root") then kernel.vfs.mkdir("/root") end
|
||||
|
||||
kernel.processes.login = function()
|
||||
local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login")
|
||||
if not ok then
|
||||
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
|
||||
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 0xFF0000)
|
||||
end
|
||||
end
|
||||
121
Src/Hyperion-kernel/data/lib/modules/hyperion/92_setup.kmod
Normal file
121
Src/Hyperion-kernel/data/lib/modules/hyperion/92_setup.kmod
Normal file
@@ -0,0 +1,121 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
if kernel.firstBoot then
|
||||
local P = kernel.vfs.P
|
||||
local PERM = kernel.vfs.PERM
|
||||
|
||||
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
|
||||
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
|
||||
+ P.GROUP_R + P.GROUP_X
|
||||
+ P.WORLD_R + P.WORLD_X
|
||||
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
|
||||
local RW____ = P.OWNER_R + P.OWNER_W
|
||||
local RWXRWXRWX = PERM.RWXRWXRWX
|
||||
local SUID_755 = PERM.SUID_755
|
||||
|
||||
local META_VERSION = 0x02
|
||||
local rootDisk = kernel.disks["$"]
|
||||
|
||||
local function makeEntry(name, etype, owner, group, perms, cmeta)
|
||||
cmeta = cmeta or ""
|
||||
local plo = perms % 256
|
||||
local phi = math.floor(perms / 256) % 256
|
||||
local olo = (owner or 0) % 256
|
||||
local ohi = math.floor((owner or 0) / 256) % 256
|
||||
local glo = (group or 0) % 256
|
||||
local ghi = math.floor((group or 0) / 256) % 256
|
||||
return string.char(#name) .. name
|
||||
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
|
||||
.. string.char(#cmeta) .. cmeta
|
||||
end
|
||||
|
||||
local REG = 0x00
|
||||
|
||||
local function mergeMeta(dir, entries)
|
||||
local diskDir = dir
|
||||
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
|
||||
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
|
||||
|
||||
local existing = {}
|
||||
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
|
||||
if rok and rf then
|
||||
local raw = rf.read(65535)
|
||||
if rf.close then rf.close() end
|
||||
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
|
||||
end
|
||||
|
||||
for _, e in ipairs(entries) do
|
||||
local name = e[1]
|
||||
local etype = e[2] or REG
|
||||
local owner = e[3] or 0
|
||||
local group = e[4] or 0
|
||||
local perms = e[5] or RWX_RX_RX
|
||||
local cmeta = e[6] or ""
|
||||
existing[name] = {
|
||||
etype = etype,
|
||||
owner = owner,
|
||||
group = group,
|
||||
perms = perms,
|
||||
cmeta = cmeta,
|
||||
}
|
||||
end
|
||||
|
||||
local data = string.char(META_VERSION)
|
||||
for name, m in pairs(existing) do
|
||||
data = data .. makeEntry(
|
||||
name,
|
||||
m.etype or REG,
|
||||
m.owner or 0,
|
||||
m.group or 0,
|
||||
m.perms or RWX_RX_RX,
|
||||
m.cmeta or ""
|
||||
)
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local f = rootDisk:open(metaPath, "w")
|
||||
f.write(data)
|
||||
f.close()
|
||||
end)
|
||||
if not ok then
|
||||
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 0xFF8800)
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("Seeding filesystem permissions...")
|
||||
|
||||
mergeMeta("/", {
|
||||
{"bin", REG, 0, 0, RWX_RX_RX},
|
||||
{"boot", REG, 0, 0, RWX_RX_RX},
|
||||
{"dev", REG, 0, 0, RWXRWXRWX},
|
||||
{"etc", REG, 0, 0, RWX_RX_RX},
|
||||
{"home", REG, 0, 0, RWX_RX_RX},
|
||||
{"lib", REG, 0, 0, RWX_RX_RX},
|
||||
{"proc", REG, 0, 0, RWXRWXRWX},
|
||||
{"root", REG, 0, 0, RW____ },
|
||||
{"sbin", REG, 0, 0, RWX_RX_RX},
|
||||
{"tmp", REG, 0, 0, RWXRWXRWX},
|
||||
{"usr", REG, 0, 0, RWX_RX_RX},
|
||||
{"var", REG, 0, 0, RWXRWXRWX},
|
||||
{"opt", REG, 0, 0, RWX_RX_RX},
|
||||
})
|
||||
|
||||
mergeMeta("/bin", {
|
||||
{"login", REG, 0, 0, SUID_755 },
|
||||
{"su", REG, 0, 0, SUID_755 },
|
||||
{"sudo", REG, 0, 0, SUID_755 },
|
||||
})
|
||||
|
||||
mergeMeta("/etc", {
|
||||
{"passwd", REG, 0, 0, RW_R_R },
|
||||
{"shadow", REG, 0, 0, RW____ },
|
||||
{"pam.d", REG, 0, 0, RW____ },
|
||||
})
|
||||
|
||||
mergeMeta("/etc/pam.d", {
|
||||
{"secret", REG, 0, 0, RW____},
|
||||
})
|
||||
|
||||
kernel.log("Filesystem permissions seeded.")
|
||||
end
|
||||
@@ -1,40 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
|
||||
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
|
||||
}
|
||||
|
||||
function require(module, ...)
|
||||
if cache[module] then return cache[module] end
|
||||
local modpath = module:gsub("%.", "/")
|
||||
local failed = {}
|
||||
for _, path in ipairs(kernel.searchpaths) do
|
||||
local full_path = string.replace(path, "?", modpath)
|
||||
if full_path:sub(1, 1) ~= "/" then
|
||||
full_path = kernel.currentTask.cwd .. full_path
|
||||
end
|
||||
|
||||
if kernel.vfs.exists(full_path) then
|
||||
if kernel.vfs.type(full_path) == "directory" then
|
||||
full_path = full_path .. "/init"
|
||||
end
|
||||
|
||||
if kernel.vfs.exists(full_path) then
|
||||
local handle = kernel.vfs.open(full_path, "r")
|
||||
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||
kernel.vfs.close(handle)
|
||||
|
||||
return
|
||||
assert(load(file_content, full_path, "t", kernel._U))(...)
|
||||
else
|
||||
table.insert(failed, full_path)
|
||||
end
|
||||
else
|
||||
table.insert(failed, full_path)
|
||||
end
|
||||
end
|
||||
|
||||
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
|
||||
end
|
||||
@@ -1,147 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
|
||||
proxy.address = "devfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
proxy.makeDirectory = function() error("EACCES") end
|
||||
proxy.remove = function() error("EACCES") end
|
||||
proxy.setLabel = function() error("EACCES") end
|
||||
proxy.getLabel = function() return "devfs" end
|
||||
proxy.attributes = function(path) return {
|
||||
size = 0,
|
||||
modified = 0,
|
||||
created = 0
|
||||
} end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1, #steps-1 do
|
||||
local dat = step[steps[i]]
|
||||
if type(dat) ~= "table" then error("ENFILE") end
|
||||
step=dat
|
||||
end
|
||||
if type(step[steps[#steps]]) == "function" then
|
||||
return step[steps[#steps]]("open", mode)
|
||||
end
|
||||
error("ENFILE")
|
||||
end
|
||||
|
||||
function proxy:type(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then
|
||||
return "directory"
|
||||
end
|
||||
for i=1, #steps-1 do
|
||||
local dat = step[steps[i]]
|
||||
if type(dat) ~= "table" then error("ENFILE") end
|
||||
step=dat
|
||||
end
|
||||
if type(step[steps[#steps]]) == "function" then
|
||||
return step[steps[#steps]]("type", mode)
|
||||
end
|
||||
if type(step[steps[#steps]]) == "table" then
|
||||
return "directory"
|
||||
end
|
||||
error("ENOENT")
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then
|
||||
return table.keys(data)
|
||||
end
|
||||
for i=1, #steps-1 do
|
||||
local dat = step[steps[i]]
|
||||
if type(dat) ~= "table" then error("ENOENT") end
|
||||
step=dat
|
||||
end
|
||||
if type(step[steps[#steps]]) == "table" then
|
||||
return table.keys(step[steps[#steps]])
|
||||
end
|
||||
error("ENOENT")
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local ok = pcall(function()
|
||||
return self:type(path)
|
||||
end)
|
||||
return ok
|
||||
end
|
||||
|
||||
function data.random(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=function(amount)
|
||||
local str = ""
|
||||
for i=1, amount or 1 do
|
||||
str=str..string.char(math.random(0, 255))
|
||||
end
|
||||
return str
|
||||
end
|
||||
}
|
||||
elseif mode=="w" or mode=="a" then
|
||||
return {
|
||||
write=function() end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data.null(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=function(amount) end
|
||||
}
|
||||
elseif mode=="w" or mode=="a" then
|
||||
return {
|
||||
write=function() end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data.zero(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
return {
|
||||
read=function(amount)
|
||||
local str = ""
|
||||
for i=1, amount or 1 do
|
||||
str=str..string.char(0)
|
||||
end
|
||||
return str
|
||||
end
|
||||
}
|
||||
elseif mode=="w" or mode=="a" then
|
||||
return {
|
||||
write=function() end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
data["disk"]={}
|
||||
kernel.devfs={}
|
||||
kernel.devfs.data=data
|
||||
kernel.devfs.proxy=proxy
|
||||
kernel.disks["devfs0000"]=proxy
|
||||
@@ -1,134 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
local data = {}
|
||||
|
||||
proxy.address = "tmpfs0000"
|
||||
proxy.isvirt = true
|
||||
proxy.isReadOnly = function() return false end
|
||||
|
||||
proxy.spaceUsed = function() return 0 end
|
||||
proxy.spaceTotal = function() return 0 end
|
||||
|
||||
proxy.makeDirectory = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
if not step[steps[i]] then
|
||||
step[steps[i]] = {}
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
end
|
||||
|
||||
proxy.remove = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
step[steps[#steps]] = nil
|
||||
end
|
||||
|
||||
proxy.setLabel = function(_, label) end
|
||||
proxy.getLabel = function() return "tmpfs" end
|
||||
|
||||
proxy.attributes = function(_, path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
return {
|
||||
size = type(step) == "string" and #step or 0,
|
||||
modified = 0,
|
||||
created = 0,
|
||||
}
|
||||
end
|
||||
|
||||
function proxy:open(path, mode)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps-1 do
|
||||
if not step[steps[i]] then
|
||||
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
|
||||
elseif type(step[steps[i]]) ~= "table" then
|
||||
error("ENOTDIR")
|
||||
end
|
||||
step = step[steps[i]]
|
||||
end
|
||||
local filename = steps[#steps]
|
||||
|
||||
if mode == "r" then
|
||||
if type(step[filename]) ~= "string" then error("ENOENT") end
|
||||
local content = step[filename]
|
||||
local pos = 1
|
||||
return {
|
||||
read = function(amount)
|
||||
amount = amount or #content
|
||||
local chunk = content:sub(pos, pos+amount-1)
|
||||
pos = pos + #chunk
|
||||
return chunk
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
elseif mode == "w" then
|
||||
step[filename] = ""
|
||||
local buf = {}
|
||||
return {
|
||||
write = function(str)
|
||||
buf[#buf + 1] = str
|
||||
end,
|
||||
close = function()
|
||||
step[filename] = table.concat(buf)
|
||||
end,
|
||||
}
|
||||
elseif mode == "a" then
|
||||
if type(step[filename]) ~= "string" then step[filename] = "" end
|
||||
return {
|
||||
write = function(str)
|
||||
step[filename] = step[filename] .. str
|
||||
end,
|
||||
close = function() end,
|
||||
}
|
||||
else
|
||||
error("EACCES")
|
||||
end
|
||||
end
|
||||
|
||||
function proxy:type(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
if #steps == 0 then return "directory" end
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then return false end
|
||||
end
|
||||
if type(step) == "table" then return "directory" end
|
||||
if type(step) == "string" then return "file" end
|
||||
end
|
||||
|
||||
function proxy:list(path)
|
||||
local steps = kernel.vfs.splitPath(path)
|
||||
local step = data
|
||||
for i=1,#steps do
|
||||
step = step[steps[i]]
|
||||
if not step then error("ENOENT") end
|
||||
end
|
||||
if type(step) ~= "table" then error("ENOTDIR") end
|
||||
local keys = {}
|
||||
for k,_ in pairs(step) do table.insert(keys, k) end
|
||||
return keys
|
||||
end
|
||||
|
||||
function proxy:fileExists(path)
|
||||
local t = self:type(path)
|
||||
return t == "file" or t == "directory"
|
||||
end
|
||||
|
||||
kernel.disks["tmpfs0000"] = proxy
|
||||
@@ -1,556 +0,0 @@
|
||||
--:Minify:--
|
||||
-- Supports:
|
||||
-- AF_UNIX - local IPC via /var/run/*.sock paths
|
||||
-- AF_INET - network sockets with three backends:
|
||||
-- rednet://0.0.B.C or rednet+PROTO://0.0.B.C -> CC rednet (computer B*256+C)
|
||||
-- modem://0.0.B.C -> raw CC modem frames
|
||||
-- http://host/path or https://... -> HTTP via CC http API
|
||||
-- A.B.C.D (dotted quad, non-zero A) -> HTTP
|
||||
--
|
||||
-- Socket lifecycle:
|
||||
-- fd = syscall.socket(domain, socktype) -- "unix"/"inet", "stream"/"dgram"
|
||||
-- syscall.bind(fd, address) -- server: claim address
|
||||
-- syscall.listen(fd, backlog) -- server: mark as listening
|
||||
-- cfd = syscall.accept(fd) -- server: get connected client fd (blocking poll)
|
||||
-- syscall.connect(fd, address) -- client: connect to server
|
||||
-- syscall.send(fd, data) -- send bytes
|
||||
-- syscall.recv(fd, len) -- receive bytes (blocking poll, returns "" on nothing)
|
||||
-- syscall.sockshutdown(fd) -- half-close send side
|
||||
-- -- normal vfs.close(fd) closes the socket
|
||||
|
||||
local kernel = ...
|
||||
|
||||
local sockets = {}
|
||||
local unixSocks = {}
|
||||
local nextSockId = 1
|
||||
|
||||
local function allocSockId()
|
||||
local id = nextSockId
|
||||
nextSockId = nextSockId + 1
|
||||
return id
|
||||
end
|
||||
|
||||
local function parseAddress(addr)
|
||||
if not addr then error("EINVAL") end
|
||||
|
||||
if addr:sub(1,1) == "/" or addr:sub(1,5) == "unix:" then
|
||||
local path = addr:sub(1,5) == "unix:" and addr:sub(6) or addr
|
||||
return { backend="unix", path=path }
|
||||
end
|
||||
|
||||
local rproto, raddr = addr:match("^rednet%+?([^:/]*)://(.+)$")
|
||||
if raddr then
|
||||
local a,b,c,d = raddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if not a then error("EINVAL: bad rednet address " .. raddr) end
|
||||
local compId = tonumber(c)*256 + tonumber(d)
|
||||
return { backend="rednet", compId=compId,
|
||||
protocol=(rproto ~= "" and rproto or "hyperion") }
|
||||
end
|
||||
|
||||
local maddr = addr:match("^modem://(.+)$")
|
||||
if maddr then
|
||||
local a,b,c,d = maddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if not a then error("EINVAL: bad modem address " .. maddr) end
|
||||
local compId = tonumber(c)*256 + tonumber(d)
|
||||
local port = tonumber(maddr:match(":(%d+)$")) or 0
|
||||
return { backend="modem", compId=compId, port=port }
|
||||
end
|
||||
|
||||
local scheme, rest = addr:match("^(https?)://(.+)$")
|
||||
if scheme then
|
||||
return { backend=scheme, url=addr }
|
||||
end
|
||||
|
||||
local a,b,c,d = addr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)")
|
||||
if a and tonumber(a) ~= 0 then
|
||||
return { backend="http", url="http://" .. addr }
|
||||
end
|
||||
|
||||
error("EINVAL: unrecognised address format: " .. tostring(addr))
|
||||
end
|
||||
|
||||
local rednetOpen = false
|
||||
local function ensureRednet()
|
||||
if rednetOpen then return end
|
||||
local rn = kernel.apis and kernel.apis.rednet
|
||||
if not rn then error("ENODEV: no rednet API available") end
|
||||
local peripheral = kernel.apis.peripheral
|
||||
if peripheral then
|
||||
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
|
||||
if peripheral.getType(name) == "modem" then
|
||||
pcall(rn.open, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
rednetOpen = true
|
||||
end
|
||||
|
||||
local function getModem()
|
||||
local peripheral = kernel.apis and kernel.apis.peripheral
|
||||
if not peripheral then error("ENODEV") end
|
||||
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
|
||||
if peripheral.getType(name) == "modem" then
|
||||
local m = peripheral.wrap(name)
|
||||
if m then return m, name end
|
||||
end
|
||||
end
|
||||
error("ENODEV: no modem peripheral found")
|
||||
end
|
||||
|
||||
local function pumpEvents()
|
||||
local ev = kernel.computer:getMachineEvent()
|
||||
while ev do
|
||||
if ev == "rednet_message" then
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "rednet" and sock.bound then
|
||||
if sock.address.protocol == tostring(select(4, table.unpack({ev}))) or
|
||||
sock.address.protocol == "hyperion" then
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ev = kernel.computer:getMachineEvent()
|
||||
end
|
||||
end
|
||||
|
||||
local function pollEvent()
|
||||
local results = table.pack(kernel.computer:getMachineEvent())
|
||||
if results.n == 0 or results[1] == nil then return nil end
|
||||
return results
|
||||
end
|
||||
|
||||
local function dispatchEvent(ev)
|
||||
if not ev then return end
|
||||
local evtype = ev[1]
|
||||
|
||||
if evtype == "rednet_message" then
|
||||
local senderId = ev[2]
|
||||
local message = ev[3]
|
||||
local protocol = ev[4] or "hyperion"
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "rednet" and (sock.listening or sock.connected) then
|
||||
if sock.address and sock.address.protocol == protocol then
|
||||
table.insert(sock.rxbuf, { from=senderId, data=message })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif evtype == "modem_message" then
|
||||
local channel = ev[3]
|
||||
local msg = ev[5]
|
||||
local fromCh = ev[4]
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "modem" and sock.modemChannel == channel then
|
||||
table.insert(sock.rxbuf, { from=fromCh, data=msg })
|
||||
end
|
||||
end
|
||||
|
||||
elseif evtype == "http_success" then
|
||||
local url = ev[2]
|
||||
local handle = ev[3]
|
||||
for _, sock in pairs(sockets) do
|
||||
if sock.backend == "http" or sock.backend == "https" then
|
||||
if sock.pendingUrl == url then
|
||||
local body = handle.readAll and handle.readAll() or ""
|
||||
handle.close()
|
||||
table.insert(sock.rxbuf, { data=body, done=true })
|
||||
sock.pendingUrl = nil
|
||||
sock.connected = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif evtype == "http_failure" then
|
||||
local url = ev[2]
|
||||
local err = ev[3]
|
||||
for _, sock in pairs(sockets) do
|
||||
if (sock.backend == "http" or sock.backend == "https") and
|
||||
sock.pendingUrl == url then
|
||||
sock.error = err
|
||||
sock.pendingUrl = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pumpAll()
|
||||
local ev = pollEvent()
|
||||
while ev do
|
||||
dispatchEvent(ev)
|
||||
ev = pollEvent()
|
||||
end
|
||||
end
|
||||
|
||||
local function newSocket(domain, socktype)
|
||||
local sock = {
|
||||
id = allocSockId(),
|
||||
domain = domain, -- "unix" | "inet"
|
||||
socktype = socktype, -- "stream" | "dgram"
|
||||
backend = nil,
|
||||
state = "idle", -- idle | bound | listening | connected | closed
|
||||
rxbuf = {},
|
||||
txbuf = {},
|
||||
backlog = {},
|
||||
address = nil,
|
||||
peer = nil,
|
||||
modemChannel = nil,
|
||||
modem = nil,
|
||||
pendingUrl = nil,
|
||||
bound = false,
|
||||
listening = false,
|
||||
connected = false,
|
||||
error = nil,
|
||||
}
|
||||
sockets[sock.id] = sock
|
||||
return sock
|
||||
end
|
||||
|
||||
local sockSend, sockClose
|
||||
|
||||
local function socketToFd(sock)
|
||||
return {
|
||||
isSocket = true,
|
||||
sockId = sock.id,
|
||||
mode = "rw",
|
||||
meta = { etype=0, owner=0, group=0, perms=0x1FF, cmeta="" },
|
||||
type = "socket",
|
||||
refcount = 1,
|
||||
handle = {
|
||||
read = function(count)
|
||||
pumpAll()
|
||||
if #sock.rxbuf == 0 then return "" end
|
||||
local item = table.remove(sock.rxbuf, 1)
|
||||
local data = type(item) == "table" and (item.data or "") or tostring(item)
|
||||
if count and #data > count then
|
||||
table.insert(sock.rxbuf, 1, { data=data:sub(count+1), from=item.from })
|
||||
data = data:sub(1, count)
|
||||
end
|
||||
return data
|
||||
end,
|
||||
write = function(data)
|
||||
if sock.state == "closed" then error("EBADF") end
|
||||
return sockSend(sock, data)
|
||||
end,
|
||||
close = function()
|
||||
sockClose(sock)
|
||||
end,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
sockSend = function(sock, data)
|
||||
if sock.backend == "unix" then
|
||||
local peer = sock.peer
|
||||
if not peer then error("ENOTCONN") end
|
||||
table.insert(peer.rxbuf, { data=data })
|
||||
return #data
|
||||
|
||||
elseif sock.backend == "rednet" then
|
||||
ensureRednet()
|
||||
local rn = kernel.apis.rednet
|
||||
rn.send(sock.address.compId, data, sock.address.protocol)
|
||||
return #data
|
||||
|
||||
elseif sock.backend == "modem" then
|
||||
local modem = sock.modem
|
||||
if not modem then error("ENOTCONN") end
|
||||
modem.transmit(sock.address.port, sock.modemChannel or 0, data)
|
||||
return #data
|
||||
|
||||
elseif sock.backend == "http" or sock.backend == "https" then
|
||||
local http = kernel.apis and kernel.apis.http
|
||||
if not http then error("ENODEV: no http API") end
|
||||
local url = sock.address.url
|
||||
local ok, err = pcall(http.request, url, data, {
|
||||
["Content-Type"] = "application/octet-stream"
|
||||
})
|
||||
if not ok then error("ENETDOWN: " .. tostring(err)) end
|
||||
sock.pendingUrl = url
|
||||
return #data
|
||||
end
|
||||
error("EPROTONOSUPPORT")
|
||||
end
|
||||
|
||||
sockClose = function(sock)
|
||||
if sock.state == "closed" then return end
|
||||
sock.state = "closed"
|
||||
|
||||
if sock.backend == "unix" then
|
||||
if sock.peer then
|
||||
sock.peer.peer = nil
|
||||
sock.peer.state = "closed"
|
||||
end
|
||||
if sock.bound and sock.address and sock.address.path then
|
||||
unixSocks[sock.address.path] = nil
|
||||
end
|
||||
|
||||
elseif sock.backend == "modem" and sock.modem and sock.modemChannel then
|
||||
pcall(sock.modem.close, sock.modemChannel)
|
||||
|
||||
elseif sock.backend == "rednet" then
|
||||
end
|
||||
|
||||
sockets[sock.id] = nil
|
||||
end
|
||||
|
||||
kernel.syscalls["socket"] = function(domain, socktype)
|
||||
domain = domain or "inet"
|
||||
socktype = socktype or "stream"
|
||||
if domain ~= "unix" and domain ~= "inet" then error("EAFNOSUPPORT") end
|
||||
if socktype ~= "stream" and socktype ~= "dgram" then error("EPROTOTYPE") end
|
||||
|
||||
local sock = newSocket(domain, socktype)
|
||||
local fdobj = socketToFd(sock)
|
||||
local fd = kernel.vfs.newfd(fdobj)
|
||||
return fd
|
||||
end
|
||||
|
||||
kernel.syscalls["bind"] = function(fd, address)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if sock.bound then error("EINVAL") end
|
||||
|
||||
local parsed = parseAddress(address)
|
||||
|
||||
if parsed.backend == "unix" then
|
||||
local existing = unixSocks[parsed.path]
|
||||
if existing then
|
||||
if existing.state == "closed" then
|
||||
unixSocks[parsed.path] = nil
|
||||
else
|
||||
error("EADDRINUSE")
|
||||
end
|
||||
end
|
||||
sock.backend = "unix"
|
||||
sock.address = parsed
|
||||
sock.bound = true
|
||||
sock.state = "bound"
|
||||
unixSocks[parsed.path] = sock
|
||||
|
||||
elseif parsed.backend == "rednet" then
|
||||
ensureRednet()
|
||||
sock.backend = "rednet"
|
||||
sock.address = parsed
|
||||
sock.bound = true
|
||||
sock.state = "bound"
|
||||
|
||||
elseif parsed.backend == "modem" then
|
||||
local modem, side = getModem()
|
||||
sock.backend = "modem"
|
||||
sock.address = parsed
|
||||
sock.modem = modem
|
||||
sock.modemChannel = parsed.port
|
||||
sock.bound = true
|
||||
sock.state = "bound"
|
||||
modem.open(parsed.port)
|
||||
|
||||
else
|
||||
error("EOPNOTSUPP: cannot bind to " .. parsed.backend .. " address")
|
||||
end
|
||||
end
|
||||
|
||||
kernel.syscalls["listen"] = function(fd, backlog)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if not sock.bound then error("EDESTADDRREQ") end
|
||||
sock.listening = true
|
||||
sock.state = "listening"
|
||||
sock.maxBacklog = backlog or 5
|
||||
end
|
||||
|
||||
kernel.syscalls["accept"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if not sock.listening then error("EINVAL") end
|
||||
|
||||
local deadline = kernel.computer:time() + 30000
|
||||
while #sock.backlog == 0 do
|
||||
pumpAll()
|
||||
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
local clientSock = table.remove(sock.backlog, 1)
|
||||
local cfdobj = socketToFd(clientSock)
|
||||
local newfd = kernel.vfs.newfd(cfdobj)
|
||||
return newfd
|
||||
end
|
||||
|
||||
kernel.syscalls["connect"] = function(fd, address)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
if sock.connected then error("EISCONN") end
|
||||
|
||||
local parsed = parseAddress(address)
|
||||
sock.address = parsed
|
||||
sock.backend = parsed.backend
|
||||
|
||||
if parsed.backend == "unix" then
|
||||
local server = unixSocks[parsed.path]
|
||||
if not server then error("ECONNREFUSED") end
|
||||
if not server.listening then error("ECONNREFUSED") end
|
||||
if #server.backlog >= (server.maxBacklog or 5) then error("ECONNREFUSED") end
|
||||
|
||||
local serverPeer = newSocket("unix", sock.socktype)
|
||||
serverPeer.backend = "unix"
|
||||
serverPeer.connected = true
|
||||
serverPeer.state = "connected"
|
||||
serverPeer.peer = sock
|
||||
|
||||
sock.peer = serverPeer
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
|
||||
table.insert(server.backlog, serverPeer)
|
||||
|
||||
elseif parsed.backend == "rednet" then
|
||||
ensureRednet()
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
|
||||
elseif parsed.backend == "modem" then
|
||||
local modem, side = getModem()
|
||||
local replyChannel = math.random(1024, 65534)
|
||||
sock.modem = modem
|
||||
sock.modemChannel = replyChannel
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
modem.open(replyChannel)
|
||||
|
||||
elseif parsed.backend == "http" or parsed.backend == "https" then
|
||||
sock.connected = true
|
||||
sock.state = "connected"
|
||||
|
||||
else
|
||||
error("EAFNOSUPPORT")
|
||||
end
|
||||
end
|
||||
|
||||
kernel.syscalls["send"] = function(fd, data)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
return sockSend(sock, data)
|
||||
end
|
||||
|
||||
kernel.syscalls["recv"] = function(fd, maxlen, timeout_ms)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
|
||||
local deadline = kernel.computer:time() + (timeout_ms or 10000)
|
||||
while #sock.rxbuf == 0 do
|
||||
pumpAll()
|
||||
if #sock.rxbuf > 0 then break end
|
||||
if sock.state == "closed" or sock.error then
|
||||
if sock.error then error("ECONNRESET: " .. tostring(sock.error)) end
|
||||
return ""
|
||||
end
|
||||
if kernel.computer:time() > deadline then return "" end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
local item = table.remove(sock.rxbuf, 1)
|
||||
local data = type(item) == "table" and (item.data or "") or tostring(item)
|
||||
if maxlen and #data > maxlen then
|
||||
table.insert(sock.rxbuf, 1, { data=data:sub(maxlen+1), from=item and item.from })
|
||||
data = data:sub(1, maxlen)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
kernel.syscalls["sockshutdown"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if sock then sockClose(sock) end
|
||||
end
|
||||
|
||||
kernel.syscalls["getpeername"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock or not sock.connected then error("ENOTCONN") end
|
||||
if sock.address then return sock.address end
|
||||
return nil
|
||||
end
|
||||
|
||||
kernel.syscalls["getsockname"] = function(fd)
|
||||
local task = kernel.currentTask
|
||||
local fdobj = task.fd[fd]
|
||||
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
|
||||
local sock = sockets[fdobj.sockId]
|
||||
if not sock then error("EBADF") end
|
||||
return sock.address
|
||||
end
|
||||
|
||||
kernel.syscalls["httpget"] = function(url, headers)
|
||||
local http = kernel.apis and kernel.apis.http
|
||||
if not http then error("ENODEV: no http API") end
|
||||
|
||||
local ok, err = pcall(http.request, url, nil, headers)
|
||||
if not ok then error("ENETDOWN: " .. tostring(err)) end
|
||||
|
||||
local deadline = kernel.computer:time() + 15000
|
||||
while true do
|
||||
local ev = pollEvent()
|
||||
if ev then
|
||||
if ev[1] == "http_success" and ev[2] == url then
|
||||
local handle = ev[3]
|
||||
local body = handle.readAll and handle.readAll() or ""
|
||||
handle.close()
|
||||
return body
|
||||
elseif ev[1] == "http_failure" and ev[2] == url then
|
||||
error("ECONNREFUSED: " .. tostring(ev[3]))
|
||||
else
|
||||
dispatchEvent(ev)
|
||||
end
|
||||
end
|
||||
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
kernel.syscalls["resolve"] = function(hostname)
|
||||
if hostname:match("^%d+%.%d+%.%d+%.%d+$") then return hostname end
|
||||
|
||||
local a,b,c,d = hostname:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if a and tonumber(a) == 0 and tonumber(b) == 0 then
|
||||
return hostname
|
||||
end
|
||||
|
||||
local http = kernel.apis and kernel.apis.http
|
||||
if not http then error("ENODEV: no http API for DNS") end
|
||||
|
||||
local url = "https://cloudflare-dns.com/dns-query?name=" .. hostname .. "&type=A"
|
||||
local body = kernel.syscalls["httpget"](url, {
|
||||
["Accept"] = "application/dns-json"
|
||||
})
|
||||
|
||||
local ip = body:match('"type":1[^}]*"data":"([%d%.]+)"')
|
||||
if not ip then error("ENOENT: could not resolve " .. hostname) end
|
||||
return ip
|
||||
end
|
||||
|
||||
kernel.sockets = sockets
|
||||
kernel.unixSockets = unixSocks
|
||||
|
||||
kernel.log("Loaded socket module")
|
||||
@@ -1,120 +0,0 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local P = kernel.vfs.P
|
||||
local PERM = kernel.vfs.PERM
|
||||
|
||||
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
|
||||
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
|
||||
+ P.GROUP_R + P.GROUP_X
|
||||
+ P.WORLD_R + P.WORLD_X
|
||||
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
|
||||
local RW____ = P.OWNER_R + P.OWNER_W
|
||||
local RWXRWXRWX = PERM.RWXRWXRWX
|
||||
local SUID_755 = PERM.SUID_755
|
||||
|
||||
local META_VERSION = 0x02
|
||||
local rootDisk = kernel.disks["$"]
|
||||
|
||||
local function makeEntry(name, etype, owner, group, perms, cmeta)
|
||||
cmeta = cmeta or ""
|
||||
local plo = perms % 256
|
||||
local phi = math.floor(perms / 256) % 256
|
||||
local olo = (owner or 0) % 256
|
||||
local ohi = math.floor((owner or 0) / 256) % 256
|
||||
local glo = (group or 0) % 256
|
||||
local ghi = math.floor((group or 0) / 256) % 256
|
||||
return string.char(#name) .. name
|
||||
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
|
||||
.. string.char(#cmeta) .. cmeta
|
||||
end
|
||||
|
||||
local REG = 0x00
|
||||
|
||||
local function mergeMeta(dir, entries)
|
||||
local diskDir = dir
|
||||
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
|
||||
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
|
||||
|
||||
local existing = {}
|
||||
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
|
||||
if rok and rf then
|
||||
local raw = rf.read(65535)
|
||||
if rf.close then rf.close() end
|
||||
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
|
||||
end
|
||||
|
||||
for _, e in ipairs(entries) do
|
||||
local name = e[1]
|
||||
local etype = e[2] or REG
|
||||
local owner = e[3] or 0
|
||||
local group = e[4] or 0
|
||||
local perms = e[5] or RWX_RX_RX
|
||||
local cmeta = e[6] or ""
|
||||
existing[name] = {
|
||||
etype = etype,
|
||||
owner = owner,
|
||||
group = group,
|
||||
perms = perms,
|
||||
cmeta = cmeta,
|
||||
}
|
||||
end
|
||||
|
||||
local data = string.char(META_VERSION)
|
||||
for name, m in pairs(existing) do
|
||||
data = data .. makeEntry(
|
||||
name,
|
||||
m.etype or REG,
|
||||
m.owner or 0,
|
||||
m.group or 0,
|
||||
m.perms or RWX_RX_RX,
|
||||
m.cmeta or ""
|
||||
)
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local f = rootDisk:open(metaPath, "w")
|
||||
f.write(data)
|
||||
f.close()
|
||||
end)
|
||||
if not ok then
|
||||
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
|
||||
end
|
||||
end
|
||||
|
||||
if kernel.firstBoot then
|
||||
kernel.log("Seeding filesystem permissions...")
|
||||
|
||||
mergeMeta("/", {
|
||||
{"bin", REG, 0, 0, RWX_RX_RX},
|
||||
{"boot", REG, 0, 0, RWX_RX_RX},
|
||||
{"dev", REG, 0, 0, RWXRWXRWX},
|
||||
{"etc", REG, 0, 0, RWX_RX_RX},
|
||||
{"home", REG, 0, 0, RWX_RX_RX},
|
||||
{"lib", REG, 0, 0, RWX_RX_RX},
|
||||
{"root", REG, 0, 0, RW____ },
|
||||
{"sbin", REG, 0, 0, RWX_RX_RX},
|
||||
{"tmp", REG, 0, 0, RWXRWXRWX},
|
||||
{"usr", REG, 0, 0, RWX_RX_RX},
|
||||
{"var", REG, 0, 0, RWXRWXRWX},
|
||||
{"opt", REG, 0, 0, RWX_RX_RX},
|
||||
})
|
||||
|
||||
mergeMeta("/bin", {
|
||||
{"login", REG, 0, 0, SUID_755 },
|
||||
{"su", REG, 0, 0, SUID_755 },
|
||||
{"sudo", REG, 0, 0, SUID_755 },
|
||||
})
|
||||
|
||||
mergeMeta("/etc", {
|
||||
{"passwd", REG, 0, 0, RW_R_R },
|
||||
{"shadow", REG, 0, 0, RW____ },
|
||||
{"pam.d", REG, 0, 0, RW____ },
|
||||
})
|
||||
|
||||
mergeMeta("/etc/pam.d", {
|
||||
{"secret", REG, 0, 0, RW____},
|
||||
})
|
||||
|
||||
kernel.log("Filesystem permissions seeded.")
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/1", "r") --stdin (fd 0)
|
||||
syscall.open("/dev/input/keyboard1", "r") --stdin (fd 0)
|
||||
syscall.open("/dev/tty/1", "w") --stdout (fd 1)
|
||||
syscall.open("/dev/null", "w") --stderr (fd 2)
|
||||
|
||||
@@ -37,9 +37,9 @@ local function firstBoot()
|
||||
|
||||
syscall.devctl(1, "clear")
|
||||
syscall.devctl(1, "spos", 1, 1)
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||
syscall.write(1, "HyperionOS First Boot Setup\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
syscall.write(1, "No root password is set. Please create one now.\n\n")
|
||||
|
||||
while true do
|
||||
@@ -49,25 +49,25 @@ local function firstBoot()
|
||||
local pw2 = readLine("*")
|
||||
|
||||
if pw1 ~= pw2 then
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||
syscall.write(1, "Passwords do not match. Try again.\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
elseif #pw1 < 6 then
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||
syscall.write(1, "Password too short (minimum 6 characters).\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
else
|
||||
local ok, err = syscall.setpassword(0, pw1)
|
||||
if ok then
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||
syscall.write(1, "Root password set.\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
sleep(0.5)
|
||||
break
|
||||
else
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||
syscall.write(1, "Error: " .. tostring(err) .. "\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -112,8 +112,8 @@ end
|
||||
|
||||
local function doLogin()
|
||||
syscall.devctl(1, "clear")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sbgc", 16)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
syscall.devctl(1, "sbgc", 0x000000)
|
||||
syscall.devctl(1, "spos", 1, 1)
|
||||
|
||||
local hostname = syscall.getHostname() or "hyperion"
|
||||
@@ -122,7 +122,7 @@ local function doLogin()
|
||||
|
||||
local attempts = 0
|
||||
while attempts < MAX_ATTEMPTS do
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
syscall.write(1, "Username: ")
|
||||
local username = readLine(nil)
|
||||
|
||||
@@ -140,9 +140,9 @@ local function doLogin()
|
||||
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||
local homedir = (pwent and pwent.homedir) or "/"
|
||||
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||
syscall.write(1, "\nWelcome, " .. username .. "!\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
sleep(0.3)
|
||||
|
||||
spawnShell(username, uid, shell, homedir)
|
||||
@@ -150,16 +150,16 @@ local function doLogin()
|
||||
else
|
||||
attempts = attempts + 1
|
||||
sleep(1)
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||
syscall.write(1, "Login incorrect.\n\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
syscall.devctl(1, "sfgc", 2)
|
||||
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||
syscall.write(1, "Maximum login attempts exceeded.\n")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
sleep(5)
|
||||
end
|
||||
|
||||
@@ -5,15 +5,15 @@ if not users or #users == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
syscall.devctl(1,"sfgc",13)
|
||||
syscall.devctl(1,"sfgc",0xDBDBDB)
|
||||
print(string.format("%-6s %-6s %-16s %-20s %s", "UID", "GID", "Username", "Home", "Shell"))
|
||||
print(string.rep("-", 65))
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
|
||||
for _, u in ipairs(users) do
|
||||
local lock_marker = u.locked and " [locked]" or ""
|
||||
if u.locked then syscall.devctl(1,"sfgc",2) end
|
||||
if u.locked then syscall.devctl(1,"sfgc",0xFF0000) end
|
||||
print(string.format("%-6d %-6d %-16s %-20s %s%s",
|
||||
u.uid, u.gid, u.username, u.homedir, u.shell, lock_marker))
|
||||
if u.locked then syscall.devctl(1,"sfgc",1) end
|
||||
if u.locked then syscall.devctl(1,"sfgc",0xFFFFFF) end
|
||||
end
|
||||
@@ -69,17 +69,17 @@ if #args == 0 and not opts.o then
|
||||
end
|
||||
|
||||
if next(loDevs) == nil then
|
||||
syscall.devctl(1, "sfgc", 14)
|
||||
syscall.devctl(1, "sfgc", 0xFF00FF)
|
||||
print("(no loop devices attached)")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
for id, info in pairs(loDevs) do
|
||||
local colour = info.mode == "image" and 5 or 4
|
||||
local colour = info.mode == "image" and 0x00FFFF or 0x0000FF
|
||||
syscall.devctl(1, "sfgc", colour)
|
||||
printInline(info.mode.." "..id)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
print(" on "..info.path)
|
||||
end
|
||||
return
|
||||
@@ -119,9 +119,9 @@ if opts.o and opts.o:lower() == "loop" then
|
||||
print(name..": mount: "..msg); syscall.exit(1); return
|
||||
end
|
||||
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": "..loopId.." mounted at "..dest)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -141,9 +141,9 @@ if #args == 2 then
|
||||
print(name..": "..msg); syscall.exit(1); return
|
||||
end
|
||||
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": "..loopId.." mounted at "..dest)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
--:Minify:--
|
||||
local targetUser = ({ ... })[1]
|
||||
local currentUid = syscall.getuid()
|
||||
if syscall.geteuid()~=0 then
|
||||
syscall.exec("/bin/su", {...})
|
||||
end
|
||||
local targetUid
|
||||
if targetUser then
|
||||
targetUid = syscall.getuidbyname(targetUser)
|
||||
@@ -59,9 +59,9 @@ if opts.l then
|
||||
end
|
||||
print(name..": "..msg); syscall.exit(1); return
|
||||
end
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": detached "..id)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -95,17 +95,17 @@ if not ok then
|
||||
print(name..": "..msg); syscall.exit(1); return
|
||||
end
|
||||
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": unmounted "..mpt)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
|
||||
if loopIdToDetach then
|
||||
for _, id in ipairs(loopIdToDetach) do
|
||||
local dok = pcall(syscall.lodetach, id)
|
||||
if dok then
|
||||
syscall.devctl(1, "sfgc", 14)
|
||||
syscall.devctl(1, "sfgc", 0xFF00FF)
|
||||
print(name..": auto-detached "..id)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
3
Src/hysh/control/symlinks.json
Normal file
3
Src/hysh/control/symlinks.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"/bin/sh": "/bin/hysh"
|
||||
}
|
||||
@@ -107,6 +107,7 @@ local COMMANDS = {
|
||||
{ name="whoami", usage="whoami", desc="Print the current username.", 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="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="uname", usage="uname [-asnrm]", desc="Print system information (OS name, hostname, release, machine).", flags={
|
||||
{"-a","Print all fields"},
|
||||
@@ -261,16 +262,16 @@ local function render()
|
||||
syscall.write(1, "\n")
|
||||
end
|
||||
end
|
||||
syscall.devctl(1, "sfgc", 16)
|
||||
syscall.devctl(1, "sbgc", 13)
|
||||
syscall.devctl(1, "sfgc", 0x000000)
|
||||
syscall.devctl(1, "sbgc", 0xDBDBDB)
|
||||
local pct = math.floor(math.min(100, (scroll + pageSize) / totalLines * 100))
|
||||
local status = string.format(" help -- line %d/%d (%d%%) [up/down: scroll q: quit] ",
|
||||
scroll + 1, totalLines, pct)
|
||||
if #status > screenW then status = status:sub(1, screenW) end
|
||||
syscall.devctl(1, "spos", 1, screenH)
|
||||
syscall.write(1, status .. string.rep(" ", screenW - #status))
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sbgc", 16)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
syscall.devctl(1, "sbgc", 0x000000)
|
||||
dirty = false
|
||||
end
|
||||
|
||||
@@ -281,7 +282,7 @@ if totalLines <= pageSize then
|
||||
syscall.devctl(1, "sfgc", line[2])
|
||||
syscall.write(1, line[1] .. "\n")
|
||||
end
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -305,5 +306,5 @@ end
|
||||
|
||||
syscall.devctl(1, "clear")
|
||||
syscall.devctl(1, "spos", 1, 1)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sbgc", 16)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
syscall.devctl(1, "sbgc", 0x000000)
|
||||
@@ -3,11 +3,11 @@
|
||||
-- 1=white, 2=red, 3=green, 4=blue, 5=cyan, 6=magenta, 7=yellow
|
||||
-- 8=orange, 9=lime, 10=lightcyan, 11=brown, 12=darkgrey, 13=lightgrey, 14=purple, 15=chartreuse, 16=black
|
||||
|
||||
local C_LOGO = 5 -- cyan
|
||||
local C_WHITE = 1 -- white
|
||||
local C_LABEL = 13 -- light grey (key names)
|
||||
local C_SEP = 12 -- dark grey (---- separator)
|
||||
local C_USER = 3 -- green (user@host)
|
||||
local C_LOGO = 0x00FFFF -- cyan
|
||||
local C_WHITE = 0xFFFFFF -- white
|
||||
local C_LABEL = 0xDBDBDB -- light grey (key names)
|
||||
local C_SEP = 0x6D6D6D -- dark grey (---- separator)
|
||||
local C_USER = 0x00FF00 -- green (user@host)
|
||||
|
||||
local function c(col) syscall.devctl(1, "sfgc", col) end
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/1","r") --stdin (Device 0)
|
||||
local success, errorMsg = xpcall(function()
|
||||
|
||||
syscall.open("/dev/input/keyboard1","r") --stdin (Device 0)
|
||||
syscall.open("/dev/tty/1","w") --stdout (Device 1)
|
||||
syscall.open("/dev/null","w") --stderr (device 2)
|
||||
|
||||
local success, errorMsg = xpcall(function()
|
||||
|
||||
local fs = require("fs")
|
||||
|
||||
syscall.devctl(1,"clear")
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
syscall.devctl(1,"spos",1,1)
|
||||
print("HyperionOS hysh Shell")
|
||||
|
||||
@@ -26,8 +26,27 @@ else
|
||||
end
|
||||
local oldWD = ""
|
||||
|
||||
local colors = {
|
||||
0xFFFFFF,
|
||||
0xFF0000,
|
||||
0x00FF00,
|
||||
0x0000FF,
|
||||
0x00FFFF,
|
||||
0xFF00FF,
|
||||
0xFFFF00,
|
||||
0xFF6D00,
|
||||
0x6DFF55,
|
||||
0x24FFFF,
|
||||
0x924900,
|
||||
0x6D6D55,
|
||||
0xDBDBAA,
|
||||
0x6D00FF,
|
||||
0xB6FF00,
|
||||
0x000000
|
||||
}
|
||||
|
||||
for i = 1, 16 do
|
||||
syscall.devctl(1,"sbgc",i); printInline(" ")
|
||||
syscall.devctl(1,"sbgc",colors[i]); printInline(" ")
|
||||
end
|
||||
print("\n")
|
||||
|
||||
@@ -221,7 +240,7 @@ end
|
||||
|
||||
builtinCmds.clear = function()
|
||||
syscall.devctl(1,"clear")
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
syscall.devctl(1,"spos",1,1)
|
||||
end
|
||||
|
||||
@@ -429,7 +448,7 @@ builtinCmds.head = function(...)
|
||||
local multi = #files > 1
|
||||
local function dohead(text, label)
|
||||
if multi then
|
||||
syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0x0000FF); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
end
|
||||
local count = 0
|
||||
for line in (text.."\n"):gmatch("([^\n]*)\n") do
|
||||
@@ -468,7 +487,7 @@ builtinCmds.tail = function(...)
|
||||
local multi = #files > 1
|
||||
local function dotail(text, label)
|
||||
if multi then
|
||||
syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0x0000FF); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
end
|
||||
local lines = splitlines(text)
|
||||
local start = math.max(1, #lines - n + 1)
|
||||
@@ -808,6 +827,20 @@ builtinCmds.df = function(...)
|
||||
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 ok, entries = pcall(syscall.listdir, dir)
|
||||
if not ok or not entries then return {} end
|
||||
@@ -962,14 +995,15 @@ local function doTabComplete(input, cursorPos)
|
||||
end
|
||||
end
|
||||
|
||||
local lastghost=""
|
||||
local function getUserInput()
|
||||
syscall.devctl(1,"sfgc",3)
|
||||
syscall.devctl(1,"sfgc",0x00FF00)
|
||||
syscall.write(1, userhost)
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
syscall.write(1, ":")
|
||||
syscall.devctl(1,"sfgc",10)
|
||||
syscall.devctl(1,"sfgc",0x24FFFF)
|
||||
syscall.write(1, syscall.getcwd())
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
syscall.write(1, "$ ")
|
||||
local curOffsetStr = syscall.devctl(1, "gpos")
|
||||
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
|
||||
@@ -1020,21 +1054,21 @@ local function getUserInput()
|
||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
||||
if blinkState then
|
||||
syscall.devctl(1,"sfgc",16); syscall.devctl(1,"sbgc",1)
|
||||
syscall.devctl(1,"sfgc",0x000000); syscall.devctl(1,"sbgc",0xFFFFFF)
|
||||
end
|
||||
if cursorPos > #input then
|
||||
syscall.write(1, " ")
|
||||
else
|
||||
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
||||
end
|
||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",0x000000)
|
||||
local after = string.sub(input, cursorPos+1)
|
||||
syscall.write(1, after)
|
||||
local ghost = getGhostSuffix()
|
||||
if #ghost > 0 then
|
||||
syscall.devctl(1,"sfgc",14)
|
||||
syscall.devctl(1,"sfgc",0x6D00FF)
|
||||
syscall.write(1, ghost)
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
syscall.write(1, " ")
|
||||
else
|
||||
syscall.write(1, " ")
|
||||
@@ -1082,10 +1116,10 @@ local function getUserInput()
|
||||
syscall.write(1, string.rep(" ", tw))
|
||||
syscall.devctl(1,"spos",1,py)
|
||||
end
|
||||
syscall.devctl(1,"sfgc",3); syscall.write(1, userhost)
|
||||
syscall.devctl(1,"sfgc",1); syscall.write(1, ":")
|
||||
syscall.devctl(1,"sfgc",10); syscall.write(1, syscall.getcwd())
|
||||
syscall.devctl(1,"sfgc",1); syscall.write(1, "$ ")
|
||||
syscall.devctl(1,"sfgc",0x00FF00); syscall.write(1, userhost)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.write(1, ":")
|
||||
syscall.devctl(1,"sfgc",0x00FFFF); syscall.write(1, syscall.getcwd())
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.write(1, "$ ")
|
||||
posStr = syscall.devctl(1, "gpos")
|
||||
sep = posStr:find(";")
|
||||
curOffsetX = tonumber(posStr:sub(1, sep-1))
|
||||
@@ -1098,7 +1132,7 @@ local function getUserInput()
|
||||
cursorPos=cursorPos-1;dirty=true
|
||||
end
|
||||
elseif key=="\n" then
|
||||
syscall.devctl(1,"sfgc",1);syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF);syscall.devctl(1,"sbgc",0x000000)
|
||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||
syscall.write(1, input.." \n")
|
||||
return input
|
||||
@@ -1114,7 +1148,7 @@ local function getUserInput()
|
||||
end
|
||||
|
||||
local function printError(progName, msg)
|
||||
syscall.devctl(1,"sfgc",2)
|
||||
syscall.devctl(1,"sfgc",0xFF0000)
|
||||
local s = tostring(msg)
|
||||
local line, rest = s:match("%]:(%d+): (.+)$")
|
||||
if not line then line, rest = s:match(":(%d+): (.+)$") end
|
||||
@@ -1123,12 +1157,12 @@ local function printError(progName, msg)
|
||||
else
|
||||
print(progName..": "..s)
|
||||
end
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
end
|
||||
|
||||
local function runCommand(command)
|
||||
do
|
||||
local func = load("return "..command, "@equation", "t", {})
|
||||
local func = load("return "..command, "@equation", "t", math)
|
||||
if func then
|
||||
local ok, result = pcall(func)
|
||||
if ok and type(result)=="number" then print(result); return end
|
||||
@@ -1171,15 +1205,15 @@ local function runCommand(command)
|
||||
|
||||
local xok, xerr = pcall(syscall.access, cmdPath, "x")
|
||||
if not xok then
|
||||
syscall.devctl(1,"sfgc",2); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sfgc",0xFF0000); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
local proc = syscall.spawn(function()
|
||||
-- Open standard fds so programs that don't do it themselves work correctly.
|
||||
syscall.open("/dev/tty/1", "r") -- fd 0 stdin
|
||||
syscall.open("/dev/input/keyboard1", "r") -- fd 0 stdin
|
||||
syscall.open("/dev/tty/1", "w") -- fd 1 stdout
|
||||
syscall.open("/dev/null", "w") -- fd 2 stderr
|
||||
syscall.open("/dev/null", "w") -- fd 2 stderr
|
||||
-- exec replaces this coroutine's code with a fresh isolated environment
|
||||
-- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
|
||||
-- so the child cannot share any upvalue or syscall table state with hysh.
|
||||
@@ -1195,8 +1229,8 @@ local function runCommand(command)
|
||||
if terminate then
|
||||
local ok2 = syscall.kill(proc)
|
||||
if ok2 then
|
||||
syscall.devctl(1,"sbgc",16); syscall.devctl(1,"sfgc",2)
|
||||
print("\nProgram Terminated."); syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"sbgc",16); syscall.devctl(1,"sfgc",0xFF0000)
|
||||
print("\nProgram Terminated."); syscall.devctl(1,"sfgc",0xFFFFFF)
|
||||
end
|
||||
terminate = false; break
|
||||
end
|
||||
@@ -1218,8 +1252,8 @@ end, debug.traceback)
|
||||
|
||||
if not success then
|
||||
syscall.log("Error running shell: "..errorMsg, "ERROR")
|
||||
syscall.devctl(1,"sfgc",2)
|
||||
syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",0xFF0000)
|
||||
syscall.devctl(1,"sbgc",0x000000)
|
||||
print()
|
||||
print("Error running shell: ")
|
||||
print(errorMsg)
|
||||
@@ -110,9 +110,9 @@ if opts.x then
|
||||
pcall(syscall.umount, tmpMnt)
|
||||
pcall(syscall.lodetach, loopId)
|
||||
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": extracted "..count.." file(s) to "..destPath)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -150,8 +150,8 @@ local lineCount = 0
|
||||
for _ in imgStr:gmatch("\n") do lineCount = lineCount + 1 end
|
||||
local byteCount = #imgStr
|
||||
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": image written to "..imgPath)
|
||||
syscall.devctl(1, "sfgc", 14)
|
||||
syscall.devctl(1, "sfgc", 0x6D00FF)
|
||||
print(string.format(" %d records, %d bytes", lineCount - 1, byteCount))
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
@@ -60,18 +60,18 @@ if opts.l then
|
||||
local info = devs[id]
|
||||
local mode = (type(info) == "table" and info.mode) or "bind"
|
||||
local path = (type(info) == "table" and info.path) or tostring(info)
|
||||
local colour = mode == "image" and 5 or 4
|
||||
syscall.devctl(1, "sfgc", 3)
|
||||
local colour = mode == "image" and 0x00FFFF or 0x0000FF
|
||||
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||
printInline(string.format("%-10s", id))
|
||||
syscall.devctl(1, "sfgc", colour)
|
||||
printInline(string.format("%-7s", "["..mode.."]"))
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
print(" "..path)
|
||||
end
|
||||
if not any then
|
||||
syscall.devctl(1, "sfgc", 14)
|
||||
syscall.devctl(1, "sfgc", 0x6D00FF)
|
||||
print(name..": no loop devices attached")
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -92,9 +92,9 @@ if opts.d then
|
||||
end
|
||||
print(name..": "..msg); syscall.exit(1); return
|
||||
end
|
||||
syscall.devctl(1, "sfgc", 10)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
print(name..": detached "..id)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -95,6 +95,8 @@ local screenSizeStr = syscall.devctl(1, "size")
|
||||
local sizeX = tonumber(screenSizeStr:match("^(%d+)")) or 80
|
||||
|
||||
local list = fs.list(dir)
|
||||
list[#list+1] = "."
|
||||
list[#list+1] = ".."
|
||||
if not cloptions.a then
|
||||
for i = #list, 1, -1 do
|
||||
if list[i]:sub(1, 1) == "." then table.remove(list, i) end
|
||||
@@ -110,10 +112,12 @@ if cloptions.l then
|
||||
local stat = syscall.lstat and syscall.lstat(fullPath) or syscall.stat(fullPath)
|
||||
local isDir = fs.isDir(fullPath)
|
||||
local isSym = stat and stat.etype == 0x01
|
||||
local isSock = stat and stat.etype == 0x02
|
||||
|
||||
local typeChar
|
||||
if isSym then typeChar = "l"
|
||||
if isSym then typeChar = "l"
|
||||
elseif isDir then typeChar = "d"
|
||||
elseif isSock then typeChar = "s"
|
||||
else typeChar = "-" end
|
||||
|
||||
local pstr
|
||||
@@ -136,26 +140,30 @@ if cloptions.l then
|
||||
printInline(tostring(mtime) .. " ")
|
||||
|
||||
if isSym then
|
||||
syscall.devctl(1, "sfgc", 6)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
printInline(v)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
local ok, target = pcall(syscall.readlink, fullPath)
|
||||
if ok then
|
||||
printInline(" -> ")
|
||||
local targetExists = pcall(syscall.stat, fullPath)
|
||||
syscall.devctl(1, "sfgc", targetExists and 6 or 2)
|
||||
syscall.devctl(1, "sfgc", targetExists and 0x00FFFF or 0xFF0000)
|
||||
printInline(target)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
end
|
||||
elseif isDir then
|
||||
syscall.devctl(1, "sfgc", 14)
|
||||
syscall.devctl(1, "sfgc", 0x6D00FF)
|
||||
printInline(v)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
elseif isSock then
|
||||
syscall.devctl(1, "sfgc", 0xFF00FF)
|
||||
printInline(v)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
else
|
||||
local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
|
||||
syscall.devctl(1, "sfgc", isExec and 3 or 1)
|
||||
syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
|
||||
printInline(v)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
end
|
||||
print("")
|
||||
end
|
||||
@@ -175,16 +183,18 @@ for i, v in ipairs(list) do
|
||||
local isSym = stat and stat.etype == 0x01
|
||||
|
||||
if isSym then
|
||||
syscall.devctl(1, "sfgc", 6)
|
||||
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||
elseif isDir then
|
||||
syscall.devctl(1, "sfgc", 14)
|
||||
syscall.devctl(1, "sfgc", 0x6D00FF)
|
||||
elseif isSock then
|
||||
syscall.devctl(1, "sfgc", 0xFF00FF)
|
||||
else
|
||||
local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
|
||||
syscall.devctl(1, "sfgc", isExec and 3 or 1)
|
||||
syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
|
||||
end
|
||||
|
||||
printInline(v)
|
||||
syscall.devctl(1, "sfgc", 1)
|
||||
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||
printInline((" "):rep(colWidth - #v))
|
||||
|
||||
if i % numCols == 0 then print("") end
|
||||
113
Src/hysh/data/bin/tree
Normal file
113
Src/hysh/data/bin/tree
Normal file
@@ -0,0 +1,113 @@
|
||||
--:Minify:--
|
||||
local cloptions = {
|
||||
a = false,
|
||||
h = false,
|
||||
l = false,
|
||||
help = false,
|
||||
}
|
||||
local inpArgs = { ... }
|
||||
local args = {}
|
||||
local name = syscall.getTask(syscall.getpid()).name
|
||||
|
||||
for _, v in pairs(inpArgs) do
|
||||
if v:sub(1, 2) == "--" then
|
||||
local opt = v:sub(3)
|
||||
if cloptions[opt] == nil then
|
||||
print(name .. ": unrecognized option '" .. v .. "'.")
|
||||
print("try '" .. name .. " --help' for more information.")
|
||||
return
|
||||
end
|
||||
cloptions[opt] = true
|
||||
elseif v:sub(1, 1) == "-" then
|
||||
for i = 2, #v do
|
||||
local opt = v:sub(i, i)
|
||||
if cloptions[opt] == nil then
|
||||
print(name .. ": invalid option '-" .. opt .. "'.")
|
||||
print("try '" .. name .. " --help' for more information.")
|
||||
return
|
||||
end
|
||||
cloptions[opt] = true
|
||||
end
|
||||
else
|
||||
table.insert(args, v)
|
||||
end
|
||||
end
|
||||
|
||||
if cloptions.help then
|
||||
print("Usage: " .. name .. " [OPTION]... [DIR]")
|
||||
print("List all entries in the specified DIRectory, or cwd if not specified.")
|
||||
print("Display directory tree structure.")
|
||||
print("")
|
||||
print("Options:")
|
||||
print(" -a do not ignore entries starting with .")
|
||||
print(" -h with -l, print sizes in human readable format (e.g., 1K 234M 2G)")
|
||||
print(" -l use a long listing format (show file sizes)")
|
||||
print(" --help display this help and exit")
|
||||
return
|
||||
end
|
||||
|
||||
local fs = require("fs")
|
||||
local dir = args[1] or ""
|
||||
if dir:sub(1, 1) ~= "/" then
|
||||
dir = syscall.getcwd() .. "/" .. dir
|
||||
end
|
||||
if dir:sub(-1) ~= "/" then dir = dir .. "/" end
|
||||
|
||||
if not fs.isDir(dir) then
|
||||
print(name .. ": cannot access '" .. (args[1] or dir) .. "': no such directory")
|
||||
return
|
||||
end
|
||||
|
||||
local function format_size(size)
|
||||
if not cloptions.h then return tostring(size) end
|
||||
if size < 1024 then return tostring(size) .. "B"
|
||||
elseif size < 1024*1024 then return string.format("%.1fK", size/1024)
|
||||
elseif size < 1024*1024*1024 then return string.format("%.1fM", size/1024/1024)
|
||||
else return string.format("%.1fG", size/1024/1024/1024) end
|
||||
end
|
||||
|
||||
local function pdir(current_dir, level, prefix)
|
||||
prefix = prefix or ""
|
||||
local list = fs.list(current_dir)
|
||||
if not cloptions.a then
|
||||
for i = #list, 1, -1 do
|
||||
if list[i]:sub(1, 1) == "." then table.remove(list, i) end
|
||||
end
|
||||
end
|
||||
table.sort(list)
|
||||
if current_dir:sub(-1) ~= "/" then current_dir = current_dir .. "/" end
|
||||
|
||||
for i, entry in ipairs(list) do
|
||||
local full_path = current_dir .. entry
|
||||
local is_last = (i == #list)
|
||||
local branch = is_last and "`--" or "|--"
|
||||
local indent = prefix .. branch
|
||||
local suffix = ""
|
||||
local info = ""
|
||||
|
||||
if fs.isDir(full_path) then
|
||||
suffix = "/"
|
||||
end
|
||||
|
||||
if cloptions.l then
|
||||
local stat = fs.stat and fs.stat(full_path)
|
||||
if stat then
|
||||
local size = stat.size or 0
|
||||
info = " " .. format_size(size)
|
||||
end
|
||||
end
|
||||
|
||||
print(indent .. entry .. suffix .. info)
|
||||
|
||||
if fs.isDir(full_path) then
|
||||
local new_prefix = prefix .. (is_last and " " or "| ")
|
||||
pcall(pdir, full_path, level + 1, new_prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Print root directory
|
||||
local root_name = dir:sub(1, -2) -- remove trailing /
|
||||
if root_name == "" then root_name = "/" end
|
||||
print(root_name)
|
||||
pdir(dir, 0, "")
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
local ini = {}
|
||||
|
||||
function ini.parse(str)
|
||||
12
Src/install.json
Normal file
12
Src/install.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
"Hyperion-kernel",
|
||||
"Hyperion-core",
|
||||
"hysh",
|
||||
"lua",
|
||||
"micro",
|
||||
"sysinit",
|
||||
"bit32",
|
||||
"json",
|
||||
"muxzcat",
|
||||
"spm"
|
||||
]
|
||||
@@ -1,14 +1,14 @@
|
||||
--:Minify:--
|
||||
local C_PROMPT = 7
|
||||
local C_CONT = 13
|
||||
local C_OUT = 5
|
||||
local C_ERR = 2
|
||||
local C_KEY = 3
|
||||
local C_STR = 9
|
||||
local C_NUM = 10
|
||||
local C_BOOL = 8
|
||||
local C_NIL = 12
|
||||
local C_TABLE = 13
|
||||
local C_PROMPT = 0xFFFF00
|
||||
local C_CONT = 0xDBDBDB
|
||||
local C_OUT = 0x00FFFF
|
||||
local C_ERR = 0xFF0000
|
||||
local C_KEY = 0x00FF00
|
||||
local C_STR = 0x00FF88
|
||||
local C_NUM = 0x24FFFF
|
||||
local C_BOOL = 0xFF6D00
|
||||
local C_NIL = 0x6D6D6D
|
||||
local C_TABLE = 0xDBDBDB
|
||||
|
||||
local function c(col) syscall.devctl(1, "sfgc", col) end
|
||||
local function w(s) syscall.write(1, tostring(s)) end
|
||||
@@ -250,9 +250,9 @@ local function getUserInput(prompt, history)
|
||||
local function redraw()
|
||||
syscall.devctl(1, "spos", ox, oy)
|
||||
w(input:sub(1, cursor-1))
|
||||
if blink then syscall.devctl(1,"sfgc",16); syscall.devctl(1,"sbgc",1) end
|
||||
if blink then syscall.devctl(1,"sfgc",0x000000); syscall.devctl(1,"sbgc",1) end
|
||||
w(cursor > #input and " " or input:sub(cursor, cursor))
|
||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",16)
|
||||
w(input:sub(cursor+1) .. " ")
|
||||
dirty = false
|
||||
end
|
||||
@@ -284,7 +284,7 @@ local function getUserInput(prompt, history)
|
||||
cursor = cursor - 1; dirty = true
|
||||
end
|
||||
elseif key == "\n" then
|
||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",16)
|
||||
syscall.devctl(1,"spos",ox,oy)
|
||||
w(input .. " \n")
|
||||
return input
|
||||
@@ -300,6 +300,7 @@ local function getUserInput(prompt, history)
|
||||
end
|
||||
|
||||
c(C_PROMPT); w("HyperionOS " .. _VERSION .. "\n")
|
||||
w("Type \"exit()\" to exit.")
|
||||
c(C_NIL)
|
||||
w("Interactive Lua REPL. exit() to quit.\n\n")
|
||||
c(1)
|
||||
@@ -135,16 +135,16 @@ local function pad(s, w)
|
||||
end
|
||||
|
||||
local function drawTop()
|
||||
tpos(1,1); tbg(4); tfg(16)
|
||||
tpos(1,1); tbg(0x0000FF); tfg(0x000000)
|
||||
local left = " edit" .. (fname and (" - "..fname) or "")
|
||||
if dirty then left = left .. " [+]" end
|
||||
local right = tostring(cy)..","..tostring(cx).." "
|
||||
twrite(pad(left..string.rep(" ", math.max(1, W-#left-#right))..right, W))
|
||||
tbg(16); tfg(1)
|
||||
tbg(0x000000); tfg(0xFFFFFF)
|
||||
end
|
||||
|
||||
local function drawBottom()
|
||||
tpos(1, H); tbg(4); tfg(16)
|
||||
tpos(1, H); tbg(0x0000FF); tfg(0x000000)
|
||||
if msg ~= "" then
|
||||
if msgErr then tbg(2) end
|
||||
twrite(pad(" "..msg, W))
|
||||
@@ -152,7 +152,7 @@ local function drawBottom()
|
||||
else
|
||||
twrite(pad(" ^W Save ^X Quit+Save ^P Quit ^K Cut ^U Paste ^F Find ^G Go", W))
|
||||
end
|
||||
tbg(16); tfg(1)
|
||||
tbg(0x000000); tfg(0xFFFFFF)
|
||||
end
|
||||
|
||||
local function drawLines(map)
|
||||
@@ -177,29 +177,29 @@ local function drawLines(map)
|
||||
local curCh = ci > #seg and " " or seg:sub(ci, ci)
|
||||
local after = seg:sub(ci+1)
|
||||
|
||||
tfg(1); tbg(16); twrite(before)
|
||||
if blinkState then tfg(16); tbg(1) else tfg(1); tbg(16) end
|
||||
tfg(0xFFFFFF); tbg(0x000000); twrite(before)
|
||||
if blinkState then tfg(0x000000); tbg(0xFFFFFF) else tfg(0xFFFFFF); tbg(0x000000) end
|
||||
twrite(curCh)
|
||||
tfg(1); tbg(16); twrite(after)
|
||||
tfg(0xFFFFFF); tbg(0x000000); twrite(after)
|
||||
local drawn = #before + 1 + #after
|
||||
if drawn < W then twrite(string.rep(" ", W - drawn)) end
|
||||
else
|
||||
if li == sLine and sPat ~= "" and entry[2] == 1 then
|
||||
local s, e = seg:find(sPat)
|
||||
if s then
|
||||
tfg(1); tbg(16); twrite(seg:sub(1,s-1))
|
||||
tfg(16); tbg(3); twrite(seg:sub(s,e))
|
||||
tfg(1); tbg(16); twrite(seg:sub(e+1))
|
||||
tfg(0xFFFFFF); tbg(0x000000); twrite(seg:sub(1,s-1))
|
||||
tfg(0x000000); tbg(0x00FF00); twrite(seg:sub(s,e))
|
||||
tfg(0xFFFFFF); tbg(0x000000); twrite(seg:sub(e+1))
|
||||
twrite(string.rep(" ", W - #seg))
|
||||
else
|
||||
tfg(1); tbg(16); twrite(pad(seg, W))
|
||||
tfg(0xFFFFFF); tbg(0x000000); twrite(pad(seg, W))
|
||||
end
|
||||
else
|
||||
tfg(1); tbg(16); twrite(pad(seg, W))
|
||||
tfg(0xFFFFFF); tbg(0x000000); twrite(pad(seg, W))
|
||||
end
|
||||
end
|
||||
else
|
||||
tfg(13); tbg(16); twrite(pad("~", W)); tfg(1)
|
||||
tfg(0xDBDBDB); tbg(0x000000); twrite(pad("~", W)); tfg(0xFFFFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -212,15 +212,15 @@ local function redraw()
|
||||
drawLines(map)
|
||||
drawBottom()
|
||||
tpos(1, H)
|
||||
tbg(16); tfg(1)
|
||||
tbg(0x000000); tfg(0xFFFFFF)
|
||||
end
|
||||
|
||||
local function prompt(label, default)
|
||||
local inp = default or ""
|
||||
while true do
|
||||
tpos(1, H); tbg(3); tfg(16)
|
||||
tpos(1, H); tbg(0x00FF00); tfg(0x000000)
|
||||
twrite(pad(" "..label..inp.." ", W))
|
||||
tbg(16); tfg(1)
|
||||
tbg(0x000000); tfg(0xFFFFFF)
|
||||
local key = syscall.read(0)
|
||||
if not key or key == "" then sleep(0.02)
|
||||
elseif key == "" then return nil
|
||||
@@ -415,5 +415,5 @@ while running do
|
||||
end
|
||||
end
|
||||
|
||||
tclear(); tfg(1); tbg(16); tpos(1,1)
|
||||
tclear(); tfg(0xFFFFFF); tbg(0x000000); tpos(1,1)
|
||||
print("edit: exited"..(fname and (" - "..fname) or ""))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user