Compare commits
126 Commits
e77fbcaf5d
...
1.2-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| f7b64c11b7 | |||
| 59e09a5995 | |||
| ab0a10105c | |||
| 22b5021e9d | |||
| 462c74686a | |||
| 0072547beb | |||
| 7deefc83ca | |||
| df62414229 | |||
| 055dd4e606 | |||
| 5755dd9cbe | |||
| 8e11faf9ec | |||
| 0ea42f9454 | |||
| d0f26a937f | |||
| 9b338328f0 | |||
| 4f9eebade2 | |||
| d08935b68a | |||
| 45b46cf3c4 | |||
| aeea68bc9b | |||
| f983b13d56 | |||
| 03c5b106c4 | |||
| 08323e00ff | |||
| 5e3cdbe40c | |||
| 8762b8f022 | |||
| 677b2cccec | |||
| a5e8624368 | |||
| bbda3b3937 | |||
| 585d39bec2 | |||
| b08b14763a | |||
| de6696003b | |||
| 9220281365 | |||
| 813ddabd9d | |||
| 5177639d71 | |||
| 528b4f31bd | |||
| 60162c7c57 | |||
| 18f5c454bb | |||
| 849ecb7dd6 | |||
| e41bd6bee7 | |||
| 359198c1ea | |||
| beebf01223 | |||
| 2d4ea1bbf4 | |||
| ea3a7e99a7 | |||
| be0fe5dc5a | |||
| 12669d9f82 | |||
| f12159bfb9 | |||
| 1590e1f3f7 | |||
| a69f945b91 | |||
| 7da67899db | |||
| 62e032e4c5 | |||
| 6fefa2d9ff | |||
| bb354cc706 | |||
| fabc061731 | |||
| 82c3e2b346 | |||
| e2e1d5b8a5 | |||
| 9342b9b2b3 | |||
| 9a7db6c243 | |||
| c7545e6947 | |||
| b7f52dd17b | |||
| eb5bed0f09 | |||
| 1827a463eb | |||
| b532a63fc6 | |||
| 4e5a4172bf | |||
| 31ce894fda | |||
| 16c900de84 | |||
| 413afd96de | |||
| a0a0ac69d4 | |||
| 17453983ad | |||
| a6550aa069 | |||
| 02e7b3897c | |||
| 34b89a8e34 | |||
| 0eabfebd0f | |||
| 5b2e5eac65 | |||
| 415064480a | |||
| f00453f703 | |||
| ab1e847d1c | |||
| 62a03bfe6b | |||
| e77a8b3636 | |||
| 6bb7f03a3e | |||
| 8798a2f4fe | |||
| a6d2f6dca7 | |||
| b015d5880a | |||
| 6694711423 | |||
| 40c97ca000 | |||
| dd2437d4af | |||
| d026cfbb03 | |||
| aad7efd055 | |||
| 93c3bab263 | |||
| 5bd16b8fd4 | |||
| 3cc1459769 | |||
| 0655f2a39e | |||
| 57b1d46837 | |||
| 875759022a | |||
|
|
bb14ea34c3 | ||
|
|
149fb18564 | ||
| 10bc775e64 | |||
| 287b146621 | |||
|
|
371954373e | ||
|
|
ecef2c6cb0 | ||
|
|
6770533581 | ||
| 19a9c72c6d | |||
| fdb18d4ac5 | |||
|
|
303449e13c | ||
| b6d1b9398f | |||
| bb829cdd8e | |||
| 6b9bed5047 | |||
|
|
753c34bffa | ||
| 403178c832 | |||
| 33cd291c21 | |||
| 1c4f48bd65 | |||
| ec5e63898d | |||
| ea2a0e0e94 | |||
| 4f50d90b79 | |||
| bf1dc9da7a | |||
| 1a455a6025 | |||
| c9ac447484 | |||
| cb73f49962 | |||
| d9caf655fb | |||
| 1c3d2c8b48 | |||
| 9bd9cdaba4 | |||
| 72bfce7b08 | |||
| 6e48fcd5b9 | |||
| 2d98ff64ce | |||
| 6a14b87f44 | |||
| 63bcc2df5c | |||
| fd7ee1aa3b | |||
| 1073362007 | |||
| da5ad3b5cb |
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -4,6 +4,8 @@
|
|||||||
"isEqualToAll",
|
"isEqualToAll",
|
||||||
"syscall",
|
"syscall",
|
||||||
"printf",
|
"printf",
|
||||||
"printInline"
|
"printInline",
|
||||||
|
"toHex",
|
||||||
|
"loadcstr"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
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
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
||||||
following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
1. Definitions.
|
||||||
portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|||||||
20
Makefile
Normal file
20
Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
PYTHON := python3
|
||||||
|
ARCH_FLAG := $(if $(ARCH),--arch $(ARCH),)
|
||||||
|
MODE_FLAG := $(if $(DEV),--dev,--release)
|
||||||
|
|
||||||
|
.PHONY: build build-mini build-test build-mini-test clean
|
||||||
|
|
||||||
|
build:
|
||||||
|
$(PYTHON) build.py build $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
build-mini:
|
||||||
|
$(PYTHON) build.py build-mini $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
build-test:
|
||||||
|
$(PYTHON) build.py build-test $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
build-mini-test:
|
||||||
|
$(PYTHON) build.py build-mini-test $(ARCH_FLAG) $(MODE_FLAG)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(PYTHON) build.py clean
|
||||||
57
README.md
57
README.md
@@ -1,18 +1,53 @@
|
|||||||
|
[](https://pinestore.cc/projects/225/hyperionos)
|
||||||
# HyperionOS
|
# HyperionOS
|
||||||
|
|
||||||
**HyperionOS** is a modular, Unix-like (almost) operating system environment written entirely in Lua. It features a microkernel-inspired architecture with a robust system call (syscall) interface, a virtual file system (VFS), and a custom hypervisor for managing isolated tasks.
|
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
* **Modular Kernel:** Functionality is distributed across discrete kernel modules (`.kmod`), including IO, VFS, IPC, and TTY, allowing for a highly extensible system.
|
|
||||||
|
|
||||||
* **Hypervisor & Task Management:** A custom hypervisor manages threads in a task-based architecture.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Build requirements
|
## Building
|
||||||
* **Source:** None it builds directly in vs-code, altough it is the biggest build method it gives the most readability
|
|
||||||
|
See `building.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Functionality is split into kernel modules (`.kmod`)
|
||||||
|
- Task-based lightweight thread/task preemptive scheduler with process isolation and IPC support
|
||||||
|
- Virtual filesystem unified interface for disk, RAM, and virtual filesystems
|
||||||
|
- TTY & Shell
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kernel Modules
|
||||||
|
|
||||||
|
Modules are loaded in priority order from `/lib/modules/`.
|
||||||
|
|
||||||
|
You can add your own `.kmod` files to extend kernel functionality without modifying the core.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Debugging & Logging
|
||||||
|
|
||||||
|
The kernel logs to `/var/log/syslog.log` during runtime.
|
||||||
|
you can add to it by doing `syscall.log(text, tag, color)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome, though please follow these guidelines:
|
||||||
|
|
||||||
|
1. No AI-generated kernel code, keep the core human written.
|
||||||
|
2. Modularize, new features should go into kernel modules where possible.
|
||||||
|
3. Document, update comments and docs when adding/changing functionality.
|
||||||
|
4. Test, ensure your changes don’t break existing functionality.
|
||||||
|
|
||||||
|
Add your name to `contributors.md` when your PR is merged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See the LICENSE file for details.
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Hyperion bash",
|
|
||||||
"Version": "1.0.0",
|
|
||||||
"Publishers": ["HyperionOS Dev Team"],
|
|
||||||
"Description": "basic bash shell",
|
|
||||||
"Dependencies": [
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v1.0.0.pkg"
|
|
||||||
],
|
|
||||||
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-bash-v1.0.0.tar"
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
syscall.TTY_clear()
|
|
||||||
syscall.TTY_setTextColor(1)
|
|
||||||
syscall.TTY_setCursorPos(1, 1)
|
|
||||||
print("HyperionOS Bash Shell")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
local preCommandText = "example@testPrompt:~$ "
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local command = ""
|
|
||||||
local cursorPos = 1
|
|
||||||
local typingCmd = true
|
|
||||||
while typingCmd do
|
|
||||||
local event = syscall.IO_getEventAny()
|
|
||||||
if event then
|
|
||||||
if event[1] == "keyTyped" then
|
|
||||||
if event[3] == "\n" then
|
|
||||||
--Enter
|
|
||||||
print("")
|
|
||||||
typingCmd = false
|
|
||||||
elseif event[3] == "\b" then
|
|
||||||
--Backspace
|
|
||||||
if cursorPos > 1 then
|
|
||||||
cursorPos = cursorPos - 1
|
|
||||||
command = command:sub(1, cursorPos - 1)..command:sub(cursorPos + 1)
|
|
||||||
end
|
|
||||||
elseif event[3]:sub(1, 1) == "\x1b" then
|
|
||||||
--Escape Sequence
|
|
||||||
elseif event[3] ~= "\t" and #event[3] == 1 then
|
|
||||||
--Standard Character
|
|
||||||
command = command:sub(1, cursorPos - 1)..event[3]..command:sub(cursorPos + 1)
|
|
||||||
cursorPos = cursorPos + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local _, cursorY = syscall.TTY_getCursorPos()
|
|
||||||
syscall.TTY_setCursorPos(1, cursorY)
|
|
||||||
printInline(preCommandText..command.." ")
|
|
||||||
syscall.TTY_setCursorPos(#preCommandText + cursorPos, cursorY)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
local fs = require("sys.fs")
|
|
||||||
local stuff = fs.list("")
|
|
||||||
for i,v in ipairs(stuff) do
|
|
||||||
if fs.isDir(v) then
|
|
||||||
print(v.."/")
|
|
||||||
else
|
|
||||||
print(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
print(".. *. ..")
|
|
||||||
print(" *= +@* +* ")
|
|
||||||
print(" .@#. -@@@= :#@. ")
|
|
||||||
print(" =@@+ *@@@# +@@= ")
|
|
||||||
print(" %@@%: *@@@# -%@@% ")
|
|
||||||
print(" :@@@@+ *@@@# .*@@@@: ")
|
|
||||||
print(" :*@@@%- *@@@# -@@@@*: ")
|
|
||||||
print(" =%@@#. *@@@# .#@@%= ")
|
|
||||||
print(" :=. :*@@= *@@@# =@@+: .=: ")
|
|
||||||
print(" %@#=..*# +@@@# #*..=#@# ")
|
|
||||||
print(" .@@@@+=# .%@%: #=+@@@@. ")
|
|
||||||
print(" .....=# -@= *+...:. ")
|
|
||||||
print(" -*%*-@= - =@-*%*- ")
|
|
||||||
print(" -@*. -@%. :%@- :*@- ")
|
|
||||||
print(" .#@#@* ")
|
|
||||||
print(" -#- ")
|
|
||||||
print(" ")
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
local fs = require("sys.fs")
|
|
||||||
local bashStr = fs.readAllText("/bin/bash")
|
|
||||||
local bashFun = load(bashStr)
|
|
||||||
syscall.HPV_spawn(bashFun, "bash")
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Hyperion core",
|
|
||||||
"Version": "1.0.0",
|
|
||||||
"Publishers": ["HyperionOS Dev Team"],
|
|
||||||
"Description": "Core system files and librarys for HyperionOS",
|
|
||||||
"Dependencies": [
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v1.0.0.pkg"
|
|
||||||
],
|
|
||||||
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v1.0.0.tar"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
local sys = {}
|
|
||||||
local fs = require("sys.fs")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return sys
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
local sys = {}
|
|
||||||
sys.fs = require("sys.fs")
|
|
||||||
sys.hpv = require("sys.hpv")
|
|
||||||
sys.ipc = require("sys.ipc")
|
|
||||||
return sys
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
local ipc = {}
|
|
||||||
|
|
||||||
return ipc
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
local term = {}
|
|
||||||
|
|
||||||
function term.clear()
|
|
||||||
coroutine.yield("VFS_write", 1, "\27C\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setCursorPos(x, y)
|
|
||||||
coroutine.yield("VFS_write", 1, "\27cs"..tostring(y)..";"..tostring(x).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.size()
|
|
||||||
coroutine.yield("VFS_write", 1, "\27ts\25")
|
|
||||||
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
|
|
||||||
if not ok then error("Failed to get terminal size") end
|
|
||||||
local x, y = string.match(data, "%R(%d+);(%d+)\25")
|
|
||||||
return tonumber(x), tonumber(y)
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.getCursorPos()
|
|
||||||
coroutine.yield("VFS_write", 1, "\27gc\25")
|
|
||||||
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
|
|
||||||
if not ok then error("Failed to get cursor position") end
|
|
||||||
local y, x = string.match(data, "%R(%d+);(%d+)\25")
|
|
||||||
return tonumber(x), tonumber(y)
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.write(data)
|
|
||||||
coroutine.yield("VFS_write", 1, data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setTextColor(color)
|
|
||||||
local ok, err = coroutine.yield("VFS_type", 1)
|
|
||||||
if not ok then error(err) end
|
|
||||||
if ok ~= "tty" then return end
|
|
||||||
coroutine.yield("VFS_write", 1, "\27f"..tostring(color).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setBackgroundColor(color)
|
|
||||||
local ok, err = coroutine.yield("VFS_type", 1)
|
|
||||||
if not ok then error(err) end
|
|
||||||
if ok ~= "tty" then return end
|
|
||||||
coroutine.yield("VFS_write", 1, "\27b"..tostring(color).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.isColor()
|
|
||||||
local ok, err = coroutine.yield("VFS_type", 1)
|
|
||||||
if not ok then error(err) end
|
|
||||||
return ok == "tty"
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.scroll(n)
|
|
||||||
coroutine.yield("VFS_write", 1, "\27S"..tostring(n).."\25")
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.setDefault(color, layer)
|
|
||||||
if layer then
|
|
||||||
coroutine.yield("VFS_write", 1, "\27F"..tostring(color).."\25")
|
|
||||||
else
|
|
||||||
coroutine.yield("VFS_write", 1, "\27B"..tostring(color).."\25")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.showCursor(show)
|
|
||||||
if show then
|
|
||||||
coroutine.yield("VFS_write", 1, "\27sc\25")
|
|
||||||
else
|
|
||||||
coroutine.yield("VFS_write", 1, "\27hc\25")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return term
|
|
||||||
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
|
||||||
|
}
|
||||||
@@ -1,33 +1,32 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
local fs={}
|
local fs={}
|
||||||
|
|
||||||
-- "VFS_open" : open
|
-- "open" : open
|
||||||
-- "VFS_read" : read
|
-- "read" : read
|
||||||
-- "VFS_write" : write
|
-- "write" : write
|
||||||
-- "VFS_close" : close
|
-- "close" : close
|
||||||
|
|
||||||
function fs.open(path, mode)
|
function fs.open(path, mode)
|
||||||
local fd=syscall.VFS_open(path,mode)
|
local fd=syscall.open(path,mode)
|
||||||
local ret={
|
local ret={
|
||||||
close=function()
|
close=function()
|
||||||
-- close file
|
-- close file
|
||||||
return syscall.VFS_close(fd)
|
return syscall.close(fd)
|
||||||
end,
|
end,
|
||||||
flush=function()
|
flush=function()
|
||||||
-- close and reopen file to flush buffers
|
-- close and reopen file to flush buffers
|
||||||
syscall.VFS_close(fd)
|
syscall.fsync(fd)
|
||||||
fd=syscall.VFS_open(path,mode)
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
if mode=="r" then
|
if mode=="r" then
|
||||||
ret.read=function(count)
|
ret.read=function(count)
|
||||||
local data = syscall.VFS_read(fd,count)
|
local data = syscall.read(fd,count)
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
ret.readAll=function(chunkSize)
|
ret.readAll=function(chunkSize)
|
||||||
local chunks={} -- to store read chunks
|
local chunks={} -- to store read chunks
|
||||||
while true do
|
while true do
|
||||||
local chunk=syscall.VFS_read(fd,chunkSize or 65536)
|
local chunk=syscall.read(fd,chunkSize or 65536)
|
||||||
if chunk==nil or #chunk==0 then break end
|
if chunk==nil or #chunk==0 then break end
|
||||||
table.insert(chunks,chunk)
|
table.insert(chunks,chunk)
|
||||||
end
|
end
|
||||||
@@ -60,7 +59,7 @@ function fs.open(path, mode)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Read the next chunk
|
-- Read the next chunk
|
||||||
local chunk = syscall.VFS_read(fd, chunk_size)
|
local chunk = syscall.read(fd, chunk_size)
|
||||||
if not chunk or chunk == "" then
|
if not chunk or chunk == "" then
|
||||||
eof = true
|
eof = true
|
||||||
else
|
else
|
||||||
@@ -71,12 +70,12 @@ function fs.open(path, mode)
|
|||||||
elseif mode=="w" then
|
elseif mode=="w" then
|
||||||
ret.write=function(data)
|
ret.write=function(data)
|
||||||
-- write data to file
|
-- write data to file
|
||||||
return syscall.VFS_write(fd,data)
|
return syscall.write(fd,data)
|
||||||
end
|
end
|
||||||
elseif mode=="a" then
|
elseif mode=="a" then
|
||||||
ret.write=function(data)
|
ret.write=function(data)
|
||||||
-- append data to file
|
-- append data to file
|
||||||
return syscall.VFS_write(fd,data)
|
return syscall.write(fd,data)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
error("Invalid mode '"..mode.."'",2)
|
error("Invalid mode '"..mode.."'",2)
|
||||||
@@ -106,39 +105,39 @@ function fs.appendAllText(path, data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function fs.mkdir(path)
|
function fs.mkdir(path)
|
||||||
return syscall.VFS_mkdir(path)
|
return syscall.mkdir(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.remove(path)
|
function fs.remove(path)
|
||||||
return syscall.VFS_remove(path)
|
return syscall.remove(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.list(path)
|
function fs.list(path)
|
||||||
return syscall.VFS_list(path)
|
return syscall.listdir(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.type(path)
|
function fs.type(path)
|
||||||
return syscall.VFS_type(path)
|
return syscall.type(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.attributes(path)
|
function fs.stat(path)
|
||||||
return syscall.VFS_attributes(path)
|
return syscall.stat(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.exists(path)
|
function fs.exists(path)
|
||||||
return syscall.VFS_exists(path)
|
return syscall.exists(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.getcwd()
|
function fs.getcwd()
|
||||||
return syscall.VFS_getcwd()
|
return syscall.getcwd()
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.setcwd(path)
|
function fs.chdir(path)
|
||||||
return syscall.VFS_setcwd(path)
|
return syscall.chdir(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.isDir(path)
|
function fs.isDir(path)
|
||||||
return syscall.VFS_isDirectory(path)
|
return syscall.type(path) == "directory"
|
||||||
end
|
end
|
||||||
|
|
||||||
return fs
|
return fs
|
||||||
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
|
||||||
6
Src/Hyperion-core/data/lib/io
Normal file
6
Src/Hyperion-core/data/lib/io
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
local io = {}
|
||||||
|
local fs = require("fs")
|
||||||
|
|
||||||
|
function io.open(path, mode)
|
||||||
|
return fs.open(path, mode)
|
||||||
|
end
|
||||||
344
Src/Hyperion-firmware-ac/data/boot/ac/boot.lua
Normal file
344
Src/Hyperion-firmware-ac/data/boot/ac/boot.lua
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local args={...}
|
||||||
|
local bootdrive=args[1]
|
||||||
|
local gpu=components:getFirst("gpu")
|
||||||
|
local screenTextBuffer=nil
|
||||||
|
local cursorX,cursorY=0,0
|
||||||
|
local screenSizeX,screenSizeY=128,25
|
||||||
|
|
||||||
|
|
||||||
|
if gpu then
|
||||||
|
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||||
|
for t,v in components:list() do
|
||||||
|
if t == "screen" then
|
||||||
|
gpu:assignBuffer(screenTextBuffer, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write(text)
|
||||||
|
cursorX, cursorY = screenTextBuffer:pasteText(cursorX, cursorY, "SCROLL_SPILL_CLEAR", text)
|
||||||
|
cursorY = cursorY + 1
|
||||||
|
cursorX = 0
|
||||||
|
if cursorY >= screenSizeY then
|
||||||
|
cursorY = screenSizeY-1
|
||||||
|
screenTextBuffer:newline()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function displaySuperBadError(err)
|
||||||
|
gpu:freeAllBuffers()
|
||||||
|
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
|
||||||
|
for t,v in components:list() do
|
||||||
|
if t == "screen" then
|
||||||
|
gpu:assignBuffer(screenTextBuffer, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
term.setBackgroundColor(0x1)
|
||||||
|
term.setTextColor(0x4)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.write("A critical error occurred while loading the system:")
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
write(err)
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = xpcall(function()
|
||||||
|
local apis = {}
|
||||||
|
|
||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = true,
|
||||||
|
getmetatable = true,
|
||||||
|
ipairs = true,
|
||||||
|
__inext = true,
|
||||||
|
load = true,
|
||||||
|
math = true,
|
||||||
|
next = true,
|
||||||
|
pairs = true,
|
||||||
|
pcall = true,
|
||||||
|
rawequal = true,
|
||||||
|
rawget = true,
|
||||||
|
rawlen = true,
|
||||||
|
rawset = true,
|
||||||
|
select = true,
|
||||||
|
setmetatable = true,
|
||||||
|
string = true,
|
||||||
|
table = true,
|
||||||
|
tonumber = true,
|
||||||
|
tostring = true,
|
||||||
|
type = true,
|
||||||
|
xpcall = true,
|
||||||
|
_G = true
|
||||||
|
}
|
||||||
|
|
||||||
|
local debug = debug
|
||||||
|
for i, v in pairs(_G) do
|
||||||
|
if not lua[i] or lua[i] == nil then
|
||||||
|
apis[i] = v
|
||||||
|
_G[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function string_file(file)
|
||||||
|
local str = file:read()
|
||||||
|
local buf = {str or ""}
|
||||||
|
local pos = 1
|
||||||
|
local closed = false
|
||||||
|
|
||||||
|
local function content()
|
||||||
|
return table.concat(buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_content(s)
|
||||||
|
buf = {s}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function flush()
|
||||||
|
file.write(content())
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = {}
|
||||||
|
|
||||||
|
function file.read(n)
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
|
||||||
|
local s = content()
|
||||||
|
local len = #s
|
||||||
|
|
||||||
|
if not n then
|
||||||
|
local out = s:sub(pos)
|
||||||
|
pos = len + 1
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
local out = s:sub(pos, pos + n - 1)
|
||||||
|
pos = pos + #out
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function file.write(data)
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
|
||||||
|
local s = content()
|
||||||
|
local before = s:sub(1, pos - 1)
|
||||||
|
local after = s:sub(pos + #data)
|
||||||
|
|
||||||
|
set_content(before .. data .. after)
|
||||||
|
pos = pos + #data
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function file.seek(whence, offset)
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
|
||||||
|
local s = content()
|
||||||
|
local len = #s
|
||||||
|
|
||||||
|
whence = whence or "cur"
|
||||||
|
offset = offset or 0
|
||||||
|
|
||||||
|
if whence == "set" then
|
||||||
|
pos = offset + 1
|
||||||
|
elseif whence == "cur" then
|
||||||
|
pos = pos + offset
|
||||||
|
elseif whence == "end" then
|
||||||
|
pos = len + offset + 1
|
||||||
|
else
|
||||||
|
error("invalid whence")
|
||||||
|
end
|
||||||
|
|
||||||
|
if pos < 1 then pos = 1 end
|
||||||
|
if pos > len + 1 then pos = len + 1 end
|
||||||
|
|
||||||
|
return pos - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function file:close()
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
flush()
|
||||||
|
closed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function file:flush()
|
||||||
|
assert(not closed, "file is closed")
|
||||||
|
flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
return file
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getFile(path)
|
||||||
|
local file = bootdrive:open(path, "r")
|
||||||
|
if not file then
|
||||||
|
displaySuperBadError("Could not open file: " .. path)
|
||||||
|
end
|
||||||
|
local content = file:read()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
local Kernel = load(getFile("/boot/kernel.lua"),"@Kernel")
|
||||||
|
local initFs = load(getFile("/boot/ac/initdisks"),"@Init_disks")(apis)
|
||||||
|
local fs = load(getFile("/boot/initfs"), "@InitFs")()
|
||||||
|
|
||||||
|
if not Kernel then displaySuperBadError("Could not load kernel.") end
|
||||||
|
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||||
|
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||||
|
|
||||||
|
local computercomp=apis.components:getFirst("computer")
|
||||||
|
local uefi=apis.components:getFirst("uefi")
|
||||||
|
local computer = {
|
||||||
|
time = function() return apis.os.epoch("utc") end,
|
||||||
|
clock = function() return apis.os.clock() * 1000 end,
|
||||||
|
shutdown = apis.os.shutdown,
|
||||||
|
reboot = apis.os.reboot,
|
||||||
|
getMachineEvent = function()
|
||||||
|
return computercomp:getMachineEvent()
|
||||||
|
end,
|
||||||
|
getEEPROM = function() return uefi.data end,
|
||||||
|
setEEPROM = function(_, text)
|
||||||
|
uefi.data=text
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
local icolors = {
|
||||||
|
[0x1] = 1, -- #000000
|
||||||
|
[0x2] = 2, -- #FFFFFF
|
||||||
|
[0x4] = 3, -- #FF0000
|
||||||
|
[0x8] = 4, -- #00FF00
|
||||||
|
[0x10] = 5, -- #0000FF
|
||||||
|
[0x20] = 6, -- #00FFFF
|
||||||
|
[0x40] = 7, -- #FF00FF
|
||||||
|
[0x80] = 8, -- #FFFF00
|
||||||
|
[0x100] = 9, -- #FF6D00
|
||||||
|
[0x200] = 10, -- #6DFF55
|
||||||
|
[0x400] = 11, -- #24FFFF
|
||||||
|
[0x800] = 12, -- #924900
|
||||||
|
[0x1000] = 13, -- #6D6D55
|
||||||
|
[0x2000] = 14, -- #DBDBAA
|
||||||
|
[0x4000] = 15, -- #6D00FF
|
||||||
|
[0x8000] = 16 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
local colors = {
|
||||||
|
0x0001, -- #000000
|
||||||
|
0x0002, -- #FFFFFF
|
||||||
|
0x0004, -- #FF0000
|
||||||
|
0x0008, -- #00FF00
|
||||||
|
0x0010, -- #0000FF
|
||||||
|
0x0020, -- #00FFFF
|
||||||
|
0x0040, -- #FF00FF
|
||||||
|
0x0080, -- #FFFF00
|
||||||
|
0x0100, -- #FF6D00
|
||||||
|
0x0200, -- #6DFF55
|
||||||
|
0x0400, -- #24FFFF
|
||||||
|
0x0800, -- #924900
|
||||||
|
0x1000, -- #6D6D55
|
||||||
|
0x2000, -- #DBDBAA
|
||||||
|
0x4000, -- #6D00FF
|
||||||
|
0x8000 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
apis.term.setBackgroundColor(0x8000)
|
||||||
|
apis.term.setTextColor(0x1000)
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
|
||||||
|
local kernelCoro = coroutine.create(function()
|
||||||
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||||
|
{
|
||||||
|
print = function(_, text) write(text .. "\n") end,
|
||||||
|
printInline = function(_, text) write(text) end,
|
||||||
|
clear = function()
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
end,
|
||||||
|
setCursorPos = function(_, x, y)
|
||||||
|
apis.term.setCursorPos(x, y)
|
||||||
|
end,
|
||||||
|
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||||
|
getSize = function() return apis.term.getSize() end,
|
||||||
|
setBackgroundColor = function(_, color)
|
||||||
|
apis.term.setBackgroundColor(colors[color])
|
||||||
|
end,
|
||||||
|
setTextColor = function(_, color)
|
||||||
|
apis.term.setTextColor(colors[color])
|
||||||
|
end,
|
||||||
|
getBackgroundColor = function()
|
||||||
|
return icolors[apis.term.getBackgroundColor()]
|
||||||
|
end,
|
||||||
|
getTextColor = function()
|
||||||
|
return icolors[apis.term.getTextColor()]
|
||||||
|
end
|
||||||
|
}, computer, fs, "$")
|
||||||
|
if not ok then displaySuperBadError(err) end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
|
local startTime = computer.time()
|
||||||
|
debug.sethook(co, function()
|
||||||
|
if computer.time() > startTime + timeout then
|
||||||
|
return coroutine.yield("timeout")
|
||||||
|
end
|
||||||
|
end, "", 1000)
|
||||||
|
local ret = {coroutine.resume(co, ...)}
|
||||||
|
if ret[1] and ret[2] == "timeout" then
|
||||||
|
return "timeout"
|
||||||
|
elseif ret[1] == false then
|
||||||
|
return "error", ret[2]
|
||||||
|
else
|
||||||
|
debug.sethook(co)
|
||||||
|
return "success", table.unpack(ret, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||||
|
apis.os.queueEvent("NoSleep")
|
||||||
|
local exit = false
|
||||||
|
while not exit do
|
||||||
|
local event = {coroutine.yield()}
|
||||||
|
if event[1] == "key" then
|
||||||
|
queueEvent("keyPressed", 1, event[2])
|
||||||
|
if acekeys[event[2]] then
|
||||||
|
queueEvent("keyTyped", 1, acekeys[event[2]])
|
||||||
|
end
|
||||||
|
elseif event[1] == "char" then
|
||||||
|
queueEvent("keyTyped", 1, event[2])
|
||||||
|
elseif event[1] == "key_up" then
|
||||||
|
queueEvent("keyReleased", 1, event[2])
|
||||||
|
elseif event[1] == "disk" then
|
||||||
|
queueEvent("componentAdded", "disk")
|
||||||
|
elseif event[1] == "disk_eject" then
|
||||||
|
queueEvent("componentRemoved", "disk")
|
||||||
|
elseif event[1] == "modem_message" then
|
||||||
|
queueEvent("modem_message", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "rednet_message" then
|
||||||
|
queueEvent("rednet_message", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "http_success" then
|
||||||
|
queueEvent("http_success", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "http_failure" then
|
||||||
|
queueEvent("http_failure", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "NoSleep" then
|
||||||
|
exit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||||
|
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||||
|
coroutine.yield("key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, debug.traceback)
|
||||||
|
|
||||||
|
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||||
|
while true do coroutine.yield() end
|
||||||
387
Src/Hyperion-firmware-ccpc/data/boot/ccpc/boot.lua
Normal file
387
Src/Hyperion-firmware-ccpc/data/boot/ccpc/boot.lua
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||||
|
---@diagnostic disable-next-line: undefined-global
|
||||||
|
local term = term
|
||||||
|
local os = os
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function displaySuperBadError(err)
|
||||||
|
term.setBackgroundColor(0x1)
|
||||||
|
term.setTextColor(0x4)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.write("A critical error occurred while loading the system:")
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
write(err)
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
[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))
|
||||||
|
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) write(text .. "\n") end,
|
||||||
|
printInline = function(_, text) write(text) end,
|
||||||
|
clear = function()
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
end,
|
||||||
|
setCursorPos = function(_, x, y)
|
||||||
|
apis.term.setCursorPos(x, y)
|
||||||
|
end,
|
||||||
|
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||||
|
getSize = function() return apis.term.getSize() end,
|
||||||
|
setBackgroundColor = function(_, color)
|
||||||
|
apis.term.setBackgroundColor(aprox(color))
|
||||||
|
end,
|
||||||
|
setTextColor = function(_, color)
|
||||||
|
apis.term.setTextColor(aprox(color))
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||||
|
apis.os.queueEvent("NoSleep")
|
||||||
|
local exit = false
|
||||||
|
while not exit do
|
||||||
|
local event = {coroutine.yield()}
|
||||||
|
if event[1] == "key" then
|
||||||
|
queueEvent("keyPressed", 1, event[2])
|
||||||
|
if acekeys[event[2]] then
|
||||||
|
queueEvent("keyTyped", 1, acekeys[event[2]])
|
||||||
|
end
|
||||||
|
elseif event[1] == "char" then
|
||||||
|
queueEvent("keyTyped", 1, event[2])
|
||||||
|
elseif event[1] == "key_up" then
|
||||||
|
queueEvent("keyReleased", 1, event[2])
|
||||||
|
elseif event[1] == "disk" then
|
||||||
|
queueEvent("componentAdded", "disk")
|
||||||
|
elseif event[1] == "disk_eject" then
|
||||||
|
queueEvent("componentRemoved", "disk")
|
||||||
|
elseif event[1] == "modem_message" then
|
||||||
|
queueEvent("modem_message", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "rednet_message" then
|
||||||
|
queueEvent("rednet_message", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "http_success" then
|
||||||
|
queueEvent("http_success", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "http_failure" then
|
||||||
|
queueEvent("http_failure", table.unpack(event, 2))
|
||||||
|
elseif event[1] == "NoSleep" then
|
||||||
|
exit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||||
|
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
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
--:Minify:--
|
--:Minify:--
|
||||||
sleep(1)
|
|
||||||
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
||||||
-- UnBIOS by JackMacWindows
|
-- UnBIOS by JackMacWindows
|
||||||
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
@@ -16,7 +15,6 @@ local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
|||||||
-- * `turtle.equip[Left|Right]`
|
-- * `turtle.equip[Left|Right]`
|
||||||
-- Licensed under the MIT license
|
-- Licensed under the MIT license
|
||||||
local args = {...}
|
local args = {...}
|
||||||
if _HOST:find("UnBIOS") then return end
|
|
||||||
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
||||||
local t = {}
|
local t = {}
|
||||||
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
||||||
@@ -32,7 +30,6 @@ if _G.commands then _G.commands = _G.commands.native end
|
|||||||
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
||||||
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
||||||
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
||||||
_G._HOST = _G._HOST .. " (UnBIOS)"
|
|
||||||
-- Set up TLCO
|
-- Set up TLCO
|
||||||
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
||||||
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
||||||
272
Src/Hyperion-firmware-ccpc/data/boot/ccpc/initdisks
Normal file
272
Src/Hyperion-firmware-ccpc/data/boot/ccpc/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}
|
||||||
394
Src/Hyperion-firmware-ccpc/data/lib/modules/ccpc/25_gfx.kmod
Normal file
394
Src/Hyperion-firmware-ccpc/data/lib/modules/ccpc/25_gfx.kmod
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
--: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)
|
||||||
|
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.EFI: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,11 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Hyperion CCT Firmware",
|
|
||||||
"Version": "1.0.0",
|
|
||||||
"Publishers": ["HyperionOS Dev Team"],
|
|
||||||
"Description": "The ComputerCraftTweaked firmware package for HyperionOS.",
|
|
||||||
"Dependencies": [
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v1.0.0.pkg"
|
|
||||||
],
|
|
||||||
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-firmware-cct-v1.0.0.tar",
|
|
||||||
"BuildCommand": "sudo cp boot/cct/eeprom /dev/eeprom"
|
|
||||||
}
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
|
||||||
---@diagnostic disable-next-line: undefined-global
|
|
||||||
local term = term
|
|
||||||
local os = os
|
|
||||||
-- Function to write text to the terminal with special character handling
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Function to display critical errors and halt the system
|
|
||||||
local function displaySuperBadError(err)
|
|
||||||
term.setBackgroundColor(0x1)
|
|
||||||
term.setTextColor(0x4)
|
|
||||||
term.clear()
|
|
||||||
term.setCursorPos(1, 1)
|
|
||||||
term.write("A critical error occurred while loading the system:")
|
|
||||||
term.setCursorPos(1, 3)
|
|
||||||
write(err)
|
|
||||||
while true do end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setCursorBlink(false)
|
|
||||||
-- Wrap all in xpcall to catch errors
|
|
||||||
local ok, err = xpcall(function()
|
|
||||||
local apis={BOOT_DRIVE_PATH=BOOT_DRIVE_PATH}
|
|
||||||
|
|
||||||
-- List of standard Lua globals
|
|
||||||
local lua = {
|
|
||||||
coroutine = true,
|
|
||||||
debug = true,
|
|
||||||
_HOST = true,
|
|
||||||
_VERSION = true,
|
|
||||||
assert = true,
|
|
||||||
collectgarbage = true,
|
|
||||||
error = true,
|
|
||||||
gcinfo = true,
|
|
||||||
getfenv = true,
|
|
||||||
getmetatable = true,
|
|
||||||
ipairs = true,
|
|
||||||
__inext = true,
|
|
||||||
load = true,
|
|
||||||
math = true,
|
|
||||||
next = true,
|
|
||||||
pairs = true,
|
|
||||||
pcall = true,
|
|
||||||
rawequal = true,
|
|
||||||
rawget = true,
|
|
||||||
rawlen = true,
|
|
||||||
rawset = true,
|
|
||||||
select = true,
|
|
||||||
setfenv = true,
|
|
||||||
setmetatable = true,
|
|
||||||
string = true,
|
|
||||||
table = true,
|
|
||||||
tonumber = true,
|
|
||||||
tostring = true,
|
|
||||||
type = true,
|
|
||||||
xpcall = true,
|
|
||||||
_G=true
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Move all non-Lua standard library globals into the apis table
|
|
||||||
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
|
|
||||||
|
|
||||||
function sleep(time)
|
|
||||||
local stoptime = apis.os.clock() + (time)
|
|
||||||
while stoptime > apis.os.clock() do end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set up terminal default colors
|
|
||||||
apis.term.setPaletteColor(0x1, 0x000000) -- #000000
|
|
||||||
apis.term.setPaletteColor(0x2, 0xFFFFFF) -- #FFFFFF
|
|
||||||
apis.term.setPaletteColor(0x4, 0xFF0000) -- #FF0000
|
|
||||||
apis.term.setPaletteColor(0x8, 0x00FF00) -- #00FF00
|
|
||||||
apis.term.setPaletteColor(0x10, 0x0000FF) -- #0000FF
|
|
||||||
apis.term.setPaletteColor(0x20, 0x00FFFF) -- #00FFFF
|
|
||||||
apis.term.setPaletteColor(0x40, 0xFF00FF) -- #FF00FF
|
|
||||||
apis.term.setPaletteColor(0x80, 0xFFFF00) -- #FFFF00
|
|
||||||
apis.term.setPaletteColor(0x100, 0xFF6D00) -- #FF6D00
|
|
||||||
apis.term.setPaletteColor(0x200, 0x6DFF55) -- #6DFF55
|
|
||||||
apis.term.setPaletteColor(0x400, 0x24FFFF) -- #24FFFF
|
|
||||||
apis.term.setPaletteColor(0x800, 0x924900) -- #924900
|
|
||||||
apis.term.setPaletteColor(0x1000, 0x6D6D55) -- #6D6D55
|
|
||||||
apis.term.setPaletteColor(0x2000, 0xDBDBAA) -- #DBDBAA
|
|
||||||
apis.term.setPaletteColor(0x4000, 0x6D00FF) -- #6D00FF
|
|
||||||
apis.term.setPaletteColor(0x8000, 0xB6FF00) -- #B6FF00
|
|
||||||
|
|
||||||
-- Wrapper to read files
|
|
||||||
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
|
|
||||||
|
|
||||||
-- Load kernel first if it fails, we can't continue so we display an error
|
|
||||||
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 key = load(getFile(BOOT_DRIVE_PATH.."/boot/cct/keys.lua"),"@keyhelper")(apis)
|
|
||||||
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 key then
|
|
||||||
displaySuperBadError("Could not load key helper.")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set up event queue
|
|
||||||
local eventQueue = {}
|
|
||||||
|
|
||||||
-- Function to queue events
|
|
||||||
local function queueEvent(event, ...)
|
|
||||||
table.insert(eventQueue, {event, ...})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set up computer api
|
|
||||||
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
|
|
||||||
end,
|
|
||||||
getEEPROM = function()
|
|
||||||
return getFile("/startup.lua")
|
|
||||||
end,
|
|
||||||
setEEPROM = function(_,text)
|
|
||||||
local h = apis.fs.open("/startup.lua", "w")
|
|
||||||
h.write(text)
|
|
||||||
h.close()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Set up terminal colors
|
|
||||||
local icolors={
|
|
||||||
[0x1] =0, -- #000000
|
|
||||||
[0x2] =1, -- #FFFFFF
|
|
||||||
[0x4] =2, -- #FF0000
|
|
||||||
[0x8] =3, -- #00FF00
|
|
||||||
[0x10] =4, -- #0000FF
|
|
||||||
[0x20] =5, -- #00FFFF
|
|
||||||
[0x40] =6, -- #FF00FF
|
|
||||||
[0x80] =7, -- #FFFF00
|
|
||||||
[0x100] =8, -- #FF6D00
|
|
||||||
[0x200] =9, -- #6DFF55
|
|
||||||
[0x400] =10, -- #24FFFF
|
|
||||||
[0x800] =11, -- #924900
|
|
||||||
[0x1000] =12, -- #6D6D55
|
|
||||||
[0x2000] =13, -- #DBDBAA
|
|
||||||
[0x4000] =14, -- #6D00FF
|
|
||||||
[0x8000] =15 -- #B6FF00
|
|
||||||
}
|
|
||||||
|
|
||||||
local colors={
|
|
||||||
[0]=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(0x1)
|
|
||||||
apis.term.setTextColor(0x1000)
|
|
||||||
apis.term.clear()
|
|
||||||
apis.term.setCursorPos(1, 1)
|
|
||||||
|
|
||||||
-- Make the kernel coroutine so we can hook its execution
|
|
||||||
local kernelCoro = coroutine.create(function()
|
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
|
||||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init", {
|
|
||||||
print=function(_,text) write(text.."\n") end,
|
|
||||||
printInline=function(_,text) write(text) end,
|
|
||||||
clear=function() apis.term.clear() apis.term.setCursorPos(1,1) end,
|
|
||||||
setCursorPos=function(_,x,y) apis.term.setCursorPos(x,y) end,
|
|
||||||
getCursorPos=function() return apis.term.getCursorPos() end,
|
|
||||||
getSize=function() return apis.term.getSize() end,
|
|
||||||
setBackgroundColor=function(_,color) apis.term.setBackgroundColor(colors[color]) end,
|
|
||||||
setTextColor=function(_,color) apis.term.setTextColor(colors[color]) end,
|
|
||||||
getBackgroundColor=function() return icolors[apis.term.getBackgroundColor()] end,
|
|
||||||
getTextColor=function() return icolors[apis.term.getTextColor()] end
|
|
||||||
}, computer, fs, "$")
|
|
||||||
if not ok then
|
|
||||||
displaySuperBadError(err)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Diffine a coroutine.resumeWithTimeout function to avoid hanging the system, time is in milliseconds
|
|
||||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
|
||||||
local startTime = computer.time()
|
|
||||||
debug.sethook(co, function()
|
|
||||||
if computer.time() > startTime + timeout then
|
|
||||||
return coroutine.yield("timeout")
|
|
||||||
end
|
|
||||||
end, "", 1000)
|
|
||||||
local ret = {coroutine.resume(co, ...)}
|
|
||||||
if ret[1] and ret[2]=="timeout" then
|
|
||||||
return "timeout"
|
|
||||||
elseif ret[1]==false then
|
|
||||||
return "error", ret[2]
|
|
||||||
else
|
|
||||||
debug.sethook(co)
|
|
||||||
return "success", table.unpack(ret, 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
write("Loaded in "..tostring(apis.os.clock()).." seconds.\n")
|
|
||||||
|
|
||||||
-- Main loop to run the kernel coroutine
|
|
||||||
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])
|
|
||||||
key(event, queueEvent)
|
|
||||||
elseif event[1] == "key_up" then
|
|
||||||
queueEvent("keyReleased", 1, event[2])
|
|
||||||
key(event, queueEvent)
|
|
||||||
elseif event[1] == "disk" then
|
|
||||||
queueEvent("componentAdded", "disk")
|
|
||||||
elseif event[1] == "disk_eject" then
|
|
||||||
queueEvent("componentRemoved", "disk")
|
|
||||||
elseif event[1] == "NoSleep" then
|
|
||||||
exit=true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if status == "error" or coroutine.status(kernelCoro)=="dead" then
|
|
||||||
displaySuperBadError("Kernel error: "..tostring(err))
|
|
||||||
coroutine.yield("key")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end, debug.traceback)
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
displaySuperBadError("Fatal error during boot: "..err)
|
|
||||||
end
|
|
||||||
while true do coroutine.yield() end
|
|
||||||
@@ -1,192 +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
|
|
||||||
|
|
||||||
---------------------------------------------------------
|
|
||||||
-- STORAGE
|
|
||||||
---------------------------------------------------------
|
|
||||||
local disks = {} -- real disks
|
|
||||||
local internal = {} -- "$" disk
|
|
||||||
|
|
||||||
---------------------------------------------------------
|
|
||||||
-- HELPERS
|
|
||||||
---------------------------------------------------------
|
|
||||||
local function norm(path)
|
|
||||||
if not path or path == "" then return "/" end
|
|
||||||
return fs.combine("/", path)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Creates a disk object given a base path
|
|
||||||
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 DISK "$" (mapped to "/")
|
|
||||||
---------------------------------------------------------
|
|
||||||
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")
|
|
||||||
local label = h.readAll()
|
|
||||||
h.close()
|
|
||||||
return label
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
---------------------------------------------------------
|
|
||||||
-- SCAN REAL DISK PERIPHERALS
|
|
||||||
---------------------------------------------------------
|
|
||||||
local function refresh()
|
|
||||||
-- remove disks that no longer exist
|
|
||||||
for id, _ in pairs(disks) do
|
|
||||||
if not peripheral.getType(id) then
|
|
||||||
disks[id] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- detect new disks
|
|
||||||
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
|
|
||||||
|
|
||||||
---------------------------------------------------------
|
|
||||||
-- ITERATOR
|
|
||||||
---------------------------------------------------------
|
|
||||||
local function iter()
|
|
||||||
refresh()
|
|
||||||
-- first internal
|
|
||||||
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
|
|
||||||
|
|
||||||
---------------------------------------------------------
|
|
||||||
-- MODULE RETURN
|
|
||||||
---------------------------------------------------------
|
|
||||||
return {
|
|
||||||
refresh = refresh,
|
|
||||||
list = iter
|
|
||||||
}
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local apis=...
|
|
||||||
local keys=apis.keys
|
|
||||||
local tKeys = {}
|
|
||||||
tKeys[keys.space] = ' '
|
|
||||||
tKeys[keys.grave] = '`'
|
|
||||||
tKeys[keys.comma] = ','
|
|
||||||
tKeys[keys.minus] = '-'
|
|
||||||
tKeys[keys.period] = '.'
|
|
||||||
tKeys[keys.slash] = '/'
|
|
||||||
tKeys[keys.zero] = '0'
|
|
||||||
tKeys[keys.one] = '1'
|
|
||||||
tKeys[keys.two] = '2'
|
|
||||||
tKeys[keys.three] = '3'
|
|
||||||
tKeys[keys.four] = '4'
|
|
||||||
tKeys[keys.five] = '5'
|
|
||||||
tKeys[keys.six] = '6'
|
|
||||||
tKeys[keys.seven] = '7'
|
|
||||||
tKeys[keys.eight] = '8'
|
|
||||||
tKeys[keys.nine] = '9'
|
|
||||||
tKeys[keys.semicolon or keys.semiColon] = ';'
|
|
||||||
tKeys[keys.equals] = '='
|
|
||||||
tKeys[keys.a] = 'a'
|
|
||||||
tKeys[keys.b] = 'b'
|
|
||||||
tKeys[keys.c] = 'c'
|
|
||||||
tKeys[keys.d] = 'd'
|
|
||||||
tKeys[keys.e] = 'e'
|
|
||||||
tKeys[keys.f] = 'f'
|
|
||||||
tKeys[keys.g] = 'g'
|
|
||||||
tKeys[keys.h] = 'h'
|
|
||||||
tKeys[keys.i] = 'i'
|
|
||||||
tKeys[keys.j] = 'j'
|
|
||||||
tKeys[keys.k] = 'k'
|
|
||||||
tKeys[keys.l] = 'l'
|
|
||||||
tKeys[keys.m] = 'm'
|
|
||||||
tKeys[keys.n] = 'n'
|
|
||||||
tKeys[keys.o] = 'o'
|
|
||||||
tKeys[keys.p] = 'p'
|
|
||||||
tKeys[keys.q] = 'q'
|
|
||||||
tKeys[keys.r] = 'r'
|
|
||||||
tKeys[keys.s] = 's'
|
|
||||||
tKeys[keys.t] = 't'
|
|
||||||
tKeys[keys.u] = 'u'
|
|
||||||
tKeys[keys.v] = 'v'
|
|
||||||
tKeys[keys.w] = 'w'
|
|
||||||
tKeys[keys.x] = 'x'
|
|
||||||
tKeys[keys.y] = 'y'
|
|
||||||
tKeys[keys.z] = 'z'
|
|
||||||
tKeys[keys.leftBracket] = '['
|
|
||||||
tKeys[keys.backslash] = '\\'
|
|
||||||
tKeys[keys.rightBracket] = ']'
|
|
||||||
tKeys[keys.apostrophe] = "'"
|
|
||||||
tKeys[keys.enter] = '\n'
|
|
||||||
tKeys[keys.tab] = '\t'
|
|
||||||
tKeys[keys.backspace] = '\b'
|
|
||||||
tKeys[keys.insert] = '\x1b[2~'
|
|
||||||
tKeys[keys.delete] = '\x1b[3~'
|
|
||||||
tKeys[keys.right] = '\x1b[C'
|
|
||||||
tKeys[keys.left] = '\x1b[D'
|
|
||||||
tKeys[keys.down] = '\x1b[B'
|
|
||||||
tKeys[keys.up] = '\x1b[A'
|
|
||||||
tKeys[keys.pageUp] = '\x1b[5~'
|
|
||||||
tKeys[keys.pageDown] = '\x1b[6~'
|
|
||||||
tKeys[keys.home] = '\x1b[1~'
|
|
||||||
tKeys[keys["end"]] = '\x1b[4~'
|
|
||||||
tKeys[keys.capsLock] = '\x1b[capsLock'
|
|
||||||
tKeys[keys.scrollLock] = '\x1b[scrollLock'
|
|
||||||
tKeys[keys.numLock] = '\x1b[numLock'
|
|
||||||
if keys.printScreen then
|
|
||||||
tKeys[keys.printScreen] = '\x1b[printScreen'
|
|
||||||
end
|
|
||||||
tKeys[keys.pause] = '\x1b[pause'
|
|
||||||
tKeys[keys.f1] = '\x1b[11~'
|
|
||||||
tKeys[keys.f2] = '\x1b[12~'
|
|
||||||
tKeys[keys.f3] = '\x1b[13~'
|
|
||||||
tKeys[keys.f4] = '\x1b[14~'
|
|
||||||
tKeys[keys.f5] = '\x1b[15~'
|
|
||||||
tKeys[keys.f6] = '\x1b[17~'
|
|
||||||
tKeys[keys.f7] = '\x1b[18~'
|
|
||||||
tKeys[keys.f8] = '\x1b[19~'
|
|
||||||
tKeys[keys.f9] = '\x1b[20~'
|
|
||||||
tKeys[keys.f10] = '\x1b[21~'
|
|
||||||
tKeys[keys.f11] = '\x1b[23~'
|
|
||||||
tKeys[keys.f12] = '\x1b[24~'
|
|
||||||
--tKeys[keys.f13] = '\x1b[25~'
|
|
||||||
--tKeys[keys.f14] = '\x1b[26~'
|
|
||||||
--tKeys[keys.f15] = '\x1b[28~'
|
|
||||||
--tKeys[keys.f16] = '\x1b[29~'
|
|
||||||
--tKeys[keys.f17] = '\x1b[31~'
|
|
||||||
--tKeys[keys.f18] = '\x1b[32~'
|
|
||||||
--tKeys[keys.f19] = '\x1b[33~'
|
|
||||||
--tKeys[keys.f20] = '\x1b[34~'
|
|
||||||
--tKeys[keys.f21] = '\x1b[42~'
|
|
||||||
--tKeys[keys.f22] = '\x1b[43~'
|
|
||||||
--tKeys[keys.f23] = '\x1b[44~'
|
|
||||||
--tKeys[keys.f24] = '\x1b[45~'
|
|
||||||
--tKeys[keys.f25] = '\x1b[46~'
|
|
||||||
|
|
||||||
-- Numpad
|
|
||||||
tKeys[keys.numPad0] = '0'
|
|
||||||
tKeys[keys.numPad1] = '1'
|
|
||||||
tKeys[keys.numPad2] = '2'
|
|
||||||
tKeys[keys.numPad3] = '3'
|
|
||||||
tKeys[keys.numPad4] = '4'
|
|
||||||
tKeys[keys.numPad5] = '5'
|
|
||||||
tKeys[keys.numPad6] = '6'
|
|
||||||
tKeys[keys.numPad7] = '7'
|
|
||||||
tKeys[keys.numPad8] = '8'
|
|
||||||
tKeys[keys.numPad9] = '9'
|
|
||||||
|
|
||||||
--tKeys[340] = 'leftShift'
|
|
||||||
--tKeys[341] = 'leftCtrl'
|
|
||||||
--tKeys[342] = 'leftAlt'
|
|
||||||
--tKeys[343] = 'leftSuper'
|
|
||||||
--tKeys[344] = 'rightShift'
|
|
||||||
--tKeys[345] = 'rightCtrl'
|
|
||||||
--tKeys[346] = 'rightAlt'
|
|
||||||
--tKeys[347] = 'rightSuper'
|
|
||||||
--tKeys[348] = 'menu'
|
|
||||||
|
|
||||||
local shift = false
|
|
||||||
local ctrl = false
|
|
||||||
local alt = false
|
|
||||||
|
|
||||||
local function s(char)
|
|
||||||
if not shift then return char end
|
|
||||||
-- Letters
|
|
||||||
if char == "a" then return "A" end
|
|
||||||
if char == "b" then return "B" end
|
|
||||||
if char == "c" then return "C" end
|
|
||||||
if char == "d" then return "D" end
|
|
||||||
if char == "e" then return "E" end
|
|
||||||
if char == "f" then return "F" end
|
|
||||||
if char == "g" then return "G" end
|
|
||||||
if char == "h" then return "H" end
|
|
||||||
if char == "i" then return "I" end
|
|
||||||
if char == "j" then return "J" end
|
|
||||||
if char == "k" then return "K" end
|
|
||||||
if char == "l" then return "L" end
|
|
||||||
if char == "m" then return "M" end
|
|
||||||
if char == "n" then return "N" end
|
|
||||||
if char == "o" then return "O" end
|
|
||||||
if char == "p" then return "P" end
|
|
||||||
if char == "q" then return "Q" end
|
|
||||||
if char == "r" then return "R" end
|
|
||||||
if char == "s" then return "S" end
|
|
||||||
if char == "t" then return "T" end
|
|
||||||
if char == "u" then return "U" end
|
|
||||||
if char == "v" then return "V" end
|
|
||||||
if char == "w" then return "W" end
|
|
||||||
if char == "x" then return "X" end
|
|
||||||
if char == "y" then return "Y" end
|
|
||||||
if char == "z" then return "Z" end
|
|
||||||
-- Symbols
|
|
||||||
if char == "`" then return "~" end
|
|
||||||
if char == "1" then return "!" end
|
|
||||||
if char == "2" then return "@" end
|
|
||||||
if char == "3" then return "#" end
|
|
||||||
if char == "4" then return "$" end
|
|
||||||
if char == "5" then return "%" end
|
|
||||||
if char == "6" then return "^" end
|
|
||||||
if char == "7" then return "&" end
|
|
||||||
if char == "8" then return "*" end
|
|
||||||
if char == "9" then return "(" end
|
|
||||||
if char == "0" then return ")" end
|
|
||||||
if char == "-" then return "_" end
|
|
||||||
if char == "=" then return "+" end
|
|
||||||
if char == "[" then return "{" end
|
|
||||||
if char == "]" then return "}" end
|
|
||||||
if char == "\\" then return "|" end
|
|
||||||
if char == ";" then return ":" end
|
|
||||||
if char == "'" then return '"' end
|
|
||||||
if char == "," then return "<" end
|
|
||||||
if char == "." then return ">" end
|
|
||||||
if char == "/" then return "?" end
|
|
||||||
return char or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local function p()
|
|
||||||
local str = ""
|
|
||||||
if alt then
|
|
||||||
str=str.."\x1b"
|
|
||||||
end
|
|
||||||
if ctrl then
|
|
||||||
str=str.."^"
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
|
|
||||||
return function(event, q)
|
|
||||||
if event[1] == "key" then
|
|
||||||
if event[2]==keys.leftCtrl or event[2]==keys.rightCtrl then ctrl=true return
|
|
||||||
elseif event[2]==keys.leftAlt or event[2]==keys.rightAlt then alt=true return
|
|
||||||
elseif event[2]==keys.leftShift or event[2]==keys.rightCtrl then shift=true return
|
|
||||||
end
|
|
||||||
q("keyTyped", 1, p()..s(tKeys[event[2]]))
|
|
||||||
elseif event[1] == "key_up" then
|
|
||||||
if event[2]==keys.leftCtrl or event[2]==keys.rightCtrl then ctrl=false return
|
|
||||||
elseif event[2]==keys.leftAlt or event[2]==keys.rightAlt then alt=false return
|
|
||||||
elseif event[2]==keys.leftShift or event[2]==keys.rightCtrl then shift=false return
|
|
||||||
end
|
|
||||||
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,182 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
local apis=kernel.apis
|
|
||||||
local main=apis.term
|
|
||||||
local native=apis.peripheral
|
|
||||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
|
||||||
|
|
||||||
local function 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
|
|
||||||
|
|
||||||
local function 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 function wrapPeripheral(name)
|
|
||||||
if native.isPresent(name) then
|
|
||||||
return wrapPeripheral(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, "wrapRemote", name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local colors={
|
|
||||||
[0]=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 icolors={
|
|
||||||
[0x1] =0, -- #000000
|
|
||||||
[0x2] =1, -- #FFFFFF
|
|
||||||
[0x4] =2, -- #FF0000
|
|
||||||
[0x8] =3, -- #00FF00
|
|
||||||
[0x10] =4, -- #0000FF
|
|
||||||
[0x20] =5, -- #00FFFF
|
|
||||||
[0x40] =6, -- #FF00FF
|
|
||||||
[0x80] =7, -- #FFFF00
|
|
||||||
[0x100] =8, -- #FF6D00
|
|
||||||
[0x200] =9, -- #6DFF55
|
|
||||||
[0x400] =10, -- #24FFFF
|
|
||||||
[0x800] =11, -- #924900
|
|
||||||
[0x1000] =12, -- #6D6D55
|
|
||||||
[0x2000] =13, -- #DBDBAA
|
|
||||||
[0x4000] =14, -- #6D00FF
|
|
||||||
[0x8000] =15 -- #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
|
|
||||||
|
|
||||||
-- Handle wrapping if we go past right edge
|
|
||||||
if x > w then
|
|
||||||
x = 1
|
|
||||||
y = y + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Handle scrolling if we go past bottom
|
|
||||||
if y-1 > h then
|
|
||||||
term.scroll(1)
|
|
||||||
y = h
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setCursorPos(x, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newTTY(term)
|
|
||||||
local ret={}
|
|
||||||
function ret.print(text)
|
|
||||||
write(text.."\n", term)
|
|
||||||
end
|
|
||||||
function ret.printInline(text)
|
|
||||||
write(text, term)
|
|
||||||
end
|
|
||||||
function ret.clear()
|
|
||||||
term.clear()
|
|
||||||
term.setCursorPos(1,1)
|
|
||||||
end
|
|
||||||
function ret.setCursorPos(x,y)
|
|
||||||
term.setCursorPos(x,y)
|
|
||||||
end
|
|
||||||
function ret.getCursorPos()
|
|
||||||
return term.getCursorPos()
|
|
||||||
end
|
|
||||||
function ret.getSize()
|
|
||||||
return term.getSize()
|
|
||||||
end
|
|
||||||
function ret.setBackgroundColor(color)
|
|
||||||
term.setBackgroundColor(colors[color])
|
|
||||||
end
|
|
||||||
function ret.setTextColor(color)
|
|
||||||
term.setTextColor(colors[color])
|
|
||||||
end
|
|
||||||
function ret.getBackgroundColor()
|
|
||||||
return icolors[term.getBackgroundColor()]
|
|
||||||
end
|
|
||||||
function ret.getTextColor()
|
|
||||||
return icolors[term.getTextColor()]
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.tty.register("tty0", newTTY(main))
|
|
||||||
|
|
||||||
for _, name in ipairs(getNames()) do
|
|
||||||
local t = getType(name)
|
|
||||||
if t == "monitor" then
|
|
||||||
local monitorTerm = wrapPeripheral(name)
|
|
||||||
monitorTerm.setTextScale(0.5)
|
|
||||||
kernel.tty.register(name, newTTY(monitorTerm))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
||||||
119
Src/Hyperion-firmware-cct/data/boot/cct/eeprom
Normal file
119
Src/Hyperion-firmware-cct/data/boot/cct/eeprom
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local BOOT_DRIVE_PATH=({...})[1] or "/$"
|
||||||
|
-- UnBIOS by JackMacWindows
|
||||||
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
|
-- To use, just place a `bios.lua` in the root of the drive, and run this program
|
||||||
|
-- Here's a list of things that are irreversibly changed:
|
||||||
|
-- * both `bit` and `bit32` are kept for compatibility
|
||||||
|
-- * string metatable blocking (on old versions of CC)
|
||||||
|
-- In addition, if `debug` is not available these things are also irreversibly changed:
|
||||||
|
-- * old Lua 5.1 `load` function (for loading from a function)
|
||||||
|
-- * `loadstring` prefixing (before CC:T 1.96.0)
|
||||||
|
-- * `http.request`
|
||||||
|
-- * `os.shutdown` and `os.reboot`
|
||||||
|
-- * `peripheral`
|
||||||
|
-- * `turtle.equip[Left|Right]`
|
||||||
|
-- Licensed under the MIT license
|
||||||
|
local args = {...}
|
||||||
|
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
||||||
|
local t = {}
|
||||||
|
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
||||||
|
for _,k in ipairs(t) do _G[k] = nil end
|
||||||
|
local native = _G.term.native()
|
||||||
|
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
|
||||||
|
_G.term = native
|
||||||
|
if _G.http then
|
||||||
|
_G.http.checkURL = _G.http.checkURLAsync
|
||||||
|
_G.http.websocket = _G.http.websocketAsync
|
||||||
|
end
|
||||||
|
if _G.commands then _G.commands = _G.commands.native end
|
||||||
|
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
||||||
|
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
||||||
|
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
||||||
|
-- Set up TLCO
|
||||||
|
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
||||||
|
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
||||||
|
-- empty placeholder to let it continue and return without throwing. This results
|
||||||
|
-- in the `pcall` returning successfully, preventing the error-displaying code
|
||||||
|
-- from running - essentially making it so that `os.shutdown` is called immediately
|
||||||
|
-- after the new BIOS exits.
|
||||||
|
--
|
||||||
|
-- From there, the setup code is placed in `term.native` since it's the first
|
||||||
|
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
|
||||||
|
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
|
||||||
|
-- allow it to be the last function called in the original BIOS, and returns.
|
||||||
|
-- From there execution continues, calling the `term.redirect` dummy, skipping
|
||||||
|
-- over the error-handling code (since `pcall` returned ok), and calling
|
||||||
|
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
|
||||||
|
-- called, which effectively makes it run as the main chunk.
|
||||||
|
local olderror = error
|
||||||
|
_G.error = function() end
|
||||||
|
_G.term.redirect = function() end
|
||||||
|
function _G.term.native()
|
||||||
|
_G.term.native = nil
|
||||||
|
_G.term.redirect = nil
|
||||||
|
_G.error = olderror
|
||||||
|
term.setBackgroundColor(32768)
|
||||||
|
term.setTextColor(1)
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.setCursorBlink(true)
|
||||||
|
term.clear()
|
||||||
|
local file = fs.open(BOOT_DRIVE_PATH.."/boot/cct/boot.lua", "r")
|
||||||
|
if file == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
local fn, err = loadstring(file.readAll(), "@bootloader")
|
||||||
|
file.close()
|
||||||
|
if fn == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write(err)
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
setfenv(fn, _G)
|
||||||
|
local oldshutdown = os.shutdown
|
||||||
|
os.shutdown = function()
|
||||||
|
os.shutdown = oldshutdown
|
||||||
|
return fn(BOOT_DRIVE_PATH)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if debug then
|
||||||
|
-- Restore functions that were overwritten in the BIOS
|
||||||
|
-- Apparently this has to be done *after* redefining term.native
|
||||||
|
local function restoreValue(tab, idx, name, hint)
|
||||||
|
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
|
||||||
|
while key ~= name and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(tab[idx], i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
tab[idx] = value or tab[idx]
|
||||||
|
end
|
||||||
|
restoreValue(_G, "loadstring", "nativeloadstring", 1)
|
||||||
|
restoreValue(_G, "load", "nativeload", 5)
|
||||||
|
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
|
||||||
|
restoreValue(os, "shutdown", "nativeShutdown", 1)
|
||||||
|
restoreValue(os, "reboot", "nativeReboot", 1)
|
||||||
|
if turtle then
|
||||||
|
restoreValue(turtle, "equipLeft", "v", 1)
|
||||||
|
restoreValue(turtle, "equipRight", "v", 1)
|
||||||
|
end
|
||||||
|
do
|
||||||
|
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
|
||||||
|
while key ~= "native" and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(peripheral.isPresent, i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
_G.peripheral = value or peripheral
|
||||||
|
end
|
||||||
|
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,11 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Hyperion OC Firmware",
|
|
||||||
"Version": "1.0.0",
|
|
||||||
"Publishers": ["HyperionOS Dev Team"],
|
|
||||||
"Description": "The OpenComputers firmware package for HyperionOS.",
|
|
||||||
"Dependencies": [
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v1.0.0.pkg"
|
|
||||||
],
|
|
||||||
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-firmware-oc-v1.0.0.tar",
|
|
||||||
"BuildCommand": "cp boot/oc/eeprom /dev/eeprom"
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Hyperion Kernel",
|
|
||||||
"Version": "1.0.0",
|
|
||||||
"Publishers": ["HyperionOS Dev Team"],
|
|
||||||
"Description": "The kernel package for HyperionOS.",
|
|
||||||
"Dependencies": [],
|
|
||||||
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v1.0.0.tar"
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
U $;/
|
|
||||||
U devfs0000;/dev/
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
--: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 LOG_Text = ""
|
|
||||||
local kernel = {}
|
|
||||||
kernel.process = "Kernel"
|
|
||||||
kernel.user = "root"
|
|
||||||
kernel.group = "root"
|
|
||||||
kernel.groups = {0}
|
|
||||||
kernel.uid = 0
|
|
||||||
kernel.gid = 0
|
|
||||||
kernel.status = "start"
|
|
||||||
kernel.key = {}
|
|
||||||
kernel.cache = {}
|
|
||||||
kernel.cache.preload = {}
|
|
||||||
kernel._G=_G
|
|
||||||
kernel.sleep=sleep
|
|
||||||
|
|
||||||
_G.sleep=nil
|
|
||||||
local windowsExp = false
|
|
||||||
|
|
||||||
function kernel.log(msg, level)
|
|
||||||
LOG_Text = LOG_Text..tostring(computer:time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
|
||||||
if kernel.status == "start" then
|
|
||||||
screen:print(tostring(computer:time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
|
||||||
elseif kernel.status == "init" then
|
|
||||||
kernel.standbyTask=kernel.currentTask
|
|
||||||
kernel.currentTask=kernel.kernelTask
|
|
||||||
kernel.tty.print(tostring(computer:time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
|
||||||
kernel.currentTask=kernel.standbyTask
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.PANIC(msg)
|
|
||||||
if kernel.status~="Panic" then
|
|
||||||
kernel.log("PANIC: "..msg, "PANIC")
|
|
||||||
pcall(kernel["saveLog"])
|
|
||||||
kernel.status="Panic"
|
|
||||||
kernel.reason=msg
|
|
||||||
screen:setTextColor(2)
|
|
||||||
screen:setBackgroundColor(0)
|
|
||||||
screen:clear()
|
|
||||||
screen:setCursorPos(1,1)
|
|
||||||
screen:print(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()}
|
|
||||||
if event[1]=="keyPressed" then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
computer:reboot()
|
|
||||||
end
|
|
||||||
kernel.panic=kernel.PANIC
|
|
||||||
|
|
||||||
if windowsExp then
|
|
||||||
screen:setTextColor(1)
|
|
||||||
screen:setBackgroundColor(4)
|
|
||||||
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")
|
|
||||||
while true do end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("Kernel loaded.")
|
|
||||||
kernel.log("Mounting init disks...")
|
|
||||||
disks.refresh()
|
|
||||||
ifs.update(disks)
|
|
||||||
kernel.disks={}
|
|
||||||
for _,v in disks.list() do
|
|
||||||
kernel.disks[v.address] = v
|
|
||||||
end
|
|
||||||
ifs.mount("$", "/")
|
|
||||||
|
|
||||||
local fstab=ifs.readAllText("/boot/fstab")
|
|
||||||
local split = function(str, delim, maxResultCountOrNil)
|
|
||||||
assert(#delim == 1, "only delim len 1 supported for now")
|
|
||||||
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
|
||||||
local rv = {}
|
|
||||||
local buf = ""
|
|
||||||
for i = 1, #str do
|
|
||||||
local c = string.sub(str,i,i)
|
|
||||||
if #rv ~= maxResultCountOrNil and c == delim then
|
|
||||||
table.insert(rv, buf)
|
|
||||||
buf = ""
|
|
||||||
else
|
|
||||||
buf = buf..c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(rv, buf)
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
if not ifs.isFile("/boot/boot.cfg") then
|
|
||||||
kernel.log("boot.cfg missing or corrupted!, Attempting to write recovery boot.cfg", "ERROR")
|
|
||||||
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
|
||||||
if not initCfgFunc then
|
|
||||||
kernel.PANIC("Failed to load /boot/boot.cfg: "..tostring(err))
|
|
||||||
end
|
|
||||||
|
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
|
||||||
local initCfgStatus, config = pcall(initCfgFunc)
|
|
||||||
if not initCfgStatus then
|
|
||||||
kernel.PANIC("Error in /boot/boot.cfg: "..tostring(config))
|
|
||||||
end
|
|
||||||
kernel.config = config
|
|
||||||
|
|
||||||
for i,v in ipairs(split(fstab,"\n")) do
|
|
||||||
if v:sub(1,1)=="U" then
|
|
||||||
local id=""
|
|
||||||
for i=3,#v do
|
|
||||||
if v:sub(i,i)==";" then
|
|
||||||
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
|
||||||
id=v:sub(3,i-1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local path=v:sub(#id+4)
|
|
||||||
ifs.mount(id,path)
|
|
||||||
::endline::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
kernel.log("Disks initialized")
|
|
||||||
|
|
||||||
function kernel.saveLog()
|
|
||||||
ifs.writeAllText("/var/log/syslog.log", LOG_Text)
|
|
||||||
end
|
|
||||||
|
|
||||||
ifs.remove("/tmp")
|
|
||||||
ifs.makeDir("/tmp")
|
|
||||||
|
|
||||||
function kernel.newFifo()
|
|
||||||
local fifo = {}
|
|
||||||
fifo.push=function(data)
|
|
||||||
table.insert(fifo, data)
|
|
||||||
end
|
|
||||||
fifo.pop=function()
|
|
||||||
return table.remove(fifo,1)
|
|
||||||
end
|
|
||||||
return fifo
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.newUUID()
|
|
||||||
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
|
||||||
local uuid = ""
|
|
||||||
for i = 1, #template do
|
|
||||||
local c = template:sub(i,i)
|
|
||||||
if c == "x" then
|
|
||||||
uuid = uuid .. string.format("%x", math.random(0, 15))
|
|
||||||
elseif c == "y" then
|
|
||||||
uuid = uuid .. string.format("%x", math.random(8, 11))
|
|
||||||
else
|
|
||||||
uuid = uuid .. c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return uuid
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls={}
|
|
||||||
local modules={[0]={}}
|
|
||||||
for i=0, 100 do
|
|
||||||
modules[i]={}
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("Gathering modules")
|
|
||||||
for _, i in ipairs(ifs.list("/lib/modules")) do
|
|
||||||
for _,v in ipairs(ifs.list("/lib/modules/"..i)) do
|
|
||||||
local prior=tonumber(v:sub(1,2))
|
|
||||||
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.ifs=ifs
|
|
||||||
kernel.apis=apis
|
|
||||||
kernel.computer=computer
|
|
||||||
kernel.arch=arch
|
|
||||||
kernel.initdisks=disks
|
|
||||||
kernel.screen=screen
|
|
||||||
kernel.processes={}
|
|
||||||
kernel.fstab=fstab
|
|
||||||
|
|
||||||
kernel.kernelTask = {
|
|
||||||
name="kernel",
|
|
||||||
status="R",
|
|
||||||
pid=0,
|
|
||||||
tgid=0,
|
|
||||||
user="root",
|
|
||||||
uid=0,
|
|
||||||
fd={},
|
|
||||||
exit="",
|
|
||||||
sleep=0,
|
|
||||||
ivs=0,
|
|
||||||
vs=0,
|
|
||||||
children={},
|
|
||||||
syscallReturn={},
|
|
||||||
cwd="/",
|
|
||||||
timeSlice=0,
|
|
||||||
lastTime=0,
|
|
||||||
totalTime=0,
|
|
||||||
numRuns=0
|
|
||||||
}
|
|
||||||
kernel.currentTask = kernel.kernelTask
|
|
||||||
|
|
||||||
function kernel.shutdown()
|
|
||||||
kernel.computer:shutdown()
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.reboot()
|
|
||||||
kernel.computer:reboot()
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls["OS_time"]=function() return kernel.computer:time() end
|
|
||||||
kernel.syscalls["OS_log"]=kernel.log
|
|
||||||
|
|
||||||
kernel.log("Running modules")
|
|
||||||
for _,p in ipairs(modules) do
|
|
||||||
for _,v in ipairs(p) do
|
|
||||||
local code=ifs.readAllText(v)
|
|
||||||
if not code then
|
|
||||||
kernel.log("ModuReadErr: "..v, "WARN")
|
|
||||||
goto skip
|
|
||||||
end
|
|
||||||
local func,err=load(code,"@"..v)
|
|
||||||
if not func then kernel.panic("ModuLoadErr: "..tostring(err)) goto skip end
|
|
||||||
local status, err = xpcall(func,debug.traceback, kernel)
|
|
||||||
if not status then kernel.panic("ModuRunErr: "..tostring(err)) end
|
|
||||||
::skip::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.log("Kernel initialized successfully.")
|
|
||||||
kernel.status="running"
|
|
||||||
kernel.main()
|
|
||||||
if kernel.status=="panic" then
|
|
||||||
kernel.panic()
|
|
||||||
end
|
|
||||||
kernel.PANIC("Execution complete")
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
function string.hasSuffix(str, suffix)
|
|
||||||
return string.sub(str, #suffix+1) == suffix
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.hasPrefix(str, prefix)
|
|
||||||
return string.sub(str, 1, #prefix) == prefix
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.getSuffix(str, prefix)
|
|
||||||
return string.sub(str, #prefix+1)
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.getPrefix(str, suffix)
|
|
||||||
return string.sub(str, 1, #suffix)
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.join(str, ...)
|
|
||||||
return table.concat(table.pack(str, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.delim(str, ...)
|
|
||||||
return table.concat(table.pack(...), str)
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.split(str, delim, maxResultCountOrNil)
|
|
||||||
assert(#delim == 1, "only delim len 1 supported for now")
|
|
||||||
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
|
||||||
local rv = {}
|
|
||||||
local buf = ""
|
|
||||||
for i = 1, #str do
|
|
||||||
local c = string.sub(str,i,i)
|
|
||||||
if #rv ~= maxResultCountOrNil and c == delim then
|
|
||||||
table.insert(rv, buf)
|
|
||||||
buf = ""
|
|
||||||
else
|
|
||||||
buf = buf..c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(rv, buf)
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
function string.replace(str, search, replacement)
|
|
||||||
local rv = ""
|
|
||||||
local consumedLen = 1
|
|
||||||
local i = 1
|
|
||||||
while i<#str do
|
|
||||||
if string.sub(str, i, i+#search-1) == search then
|
|
||||||
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
|
|
||||||
i=i+#search
|
|
||||||
consumedLen = i
|
|
||||||
end
|
|
||||||
i=i+1
|
|
||||||
end
|
|
||||||
return rv .. string.sub(str, consumedLen)
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.deepcopy(orig, copies)
|
|
||||||
copies = copies or {}
|
|
||||||
|
|
||||||
if type(orig) ~= 'table' then
|
|
||||||
return orig
|
|
||||||
elseif copies[orig] then
|
|
||||||
return copies[orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
local copy = {}
|
|
||||||
copies[orig] = copy
|
|
||||||
|
|
||||||
for k, v in next, orig, nil do
|
|
||||||
local copied_key = table.deepcopy(k, copies)
|
|
||||||
local copied_val = table.deepcopy(v, copies)
|
|
||||||
copy[copied_key] = copied_val
|
|
||||||
end
|
|
||||||
|
|
||||||
return copy
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.hasKey(tabl, query)
|
|
||||||
for i,v in pairs(tabl) do
|
|
||||||
if i==query then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.hasVal(tabl, query)
|
|
||||||
for i,v in pairs(tabl) do
|
|
||||||
if v==query then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.proxy(tbl)
|
|
||||||
local proxies = setmetatable({}, {__mode = "k"}) -- Weak table to avoid cycles
|
|
||||||
|
|
||||||
local function createProxy(t)
|
|
||||||
if type(t) ~= "table" then return t end
|
|
||||||
if proxies[t] then return proxies[t] end -- reuse proxy for the same table (handle cycles)
|
|
||||||
|
|
||||||
local proxy = {}
|
|
||||||
proxies[t] = proxy
|
|
||||||
|
|
||||||
local mt
|
|
||||||
mt = {
|
|
||||||
__index = function(_, k)
|
|
||||||
local value = t[k]
|
|
||||||
if type(value) == "table" then
|
|
||||||
return createProxy(value) -- recursively proxy subtables
|
|
||||||
else
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__newindex = function()
|
|
||||||
error("Attempt to modify table proxy", 2)
|
|
||||||
end,
|
|
||||||
__pairs = function()
|
|
||||||
return function(_, k)
|
|
||||||
local nextKey, nextValue = next(t, k)
|
|
||||||
if type(nextValue) == "table" then
|
|
||||||
nextValue = createProxy(nextValue)
|
|
||||||
end
|
|
||||||
return nextKey, nextValue
|
|
||||||
end, nil, nil
|
|
||||||
end,
|
|
||||||
__ipairs = function()
|
|
||||||
local i = 0
|
|
||||||
local n = #t
|
|
||||||
return function()
|
|
||||||
i = i + 1
|
|
||||||
if i <= n then
|
|
||||||
local v = t[i]
|
|
||||||
if type(v) == "table" then
|
|
||||||
v = createProxy(v)
|
|
||||||
end
|
|
||||||
return i, v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__metatable = false
|
|
||||||
}
|
|
||||||
|
|
||||||
setmetatable(proxy, mt)
|
|
||||||
return proxy
|
|
||||||
end
|
|
||||||
|
|
||||||
return createProxy(tbl)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function serialize(tbl, seen)
|
|
||||||
seen = seen or {}
|
|
||||||
|
|
||||||
-- If we've seen this table before, return a placeholder to prevent infinite loops
|
|
||||||
if seen[tbl] then
|
|
||||||
return '"[Circular Reference]"'
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Mark this table as seen
|
|
||||||
seen[tbl] = true
|
|
||||||
|
|
||||||
local output = "{"
|
|
||||||
local first = true
|
|
||||||
|
|
||||||
for i, v in pairs(tbl) do
|
|
||||||
-- Handle comma placement more cleanly
|
|
||||||
if not first then
|
|
||||||
output = output .. ","
|
|
||||||
end
|
|
||||||
first = false
|
|
||||||
|
|
||||||
-- Serialize Key
|
|
||||||
if type(i) == "string" then
|
|
||||||
output = output .. "[\"" .. i .. "\"]="
|
|
||||||
elseif type(i) == "number" then
|
|
||||||
output = output .. "[" .. tostring(i) .. "]="
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Serialize Value
|
|
||||||
if type(v) == "table" then
|
|
||||||
-- Pass the 'seen' table down to the recursive call
|
|
||||||
output = output .. serialize(v, seen)
|
|
||||||
elseif type(v) == "string" then
|
|
||||||
output = output .. "[=[" .. v .. "]=]"
|
|
||||||
elseif type(v) == "number" or type(v) == "boolean" then
|
|
||||||
output = output .. tostring(v)
|
|
||||||
elseif type(v) == "function" then
|
|
||||||
output = output .. "\"" .. tostring(v) .. "\""
|
|
||||||
elseif type(v) == "thread" then
|
|
||||||
output = output .. "\"" .. tostring(v) .. "\""
|
|
||||||
else
|
|
||||||
error("serialization of type \"" .. type(v) .. "\" is not supported")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
seen[tbl] = nil
|
|
||||||
|
|
||||||
output = output .. "}"
|
|
||||||
return output
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldtype=type
|
|
||||||
local oldgetmetatable=getmetatable
|
|
||||||
function type(object, trueType)
|
|
||||||
if trueType then
|
|
||||||
return oldtype(object)
|
|
||||||
end
|
|
||||||
if oldtype(object)~="table" then
|
|
||||||
return oldtype(object)
|
|
||||||
else
|
|
||||||
if oldtype(oldgetmetatable(object))=="table" then
|
|
||||||
local metatable = oldgetmetatable(object)
|
|
||||||
---@diagnostic disable-next-line: need-check-nil
|
|
||||||
if metatable.__type then return metatable.__type end
|
|
||||||
else
|
|
||||||
return "table"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function getmetatable(object)
|
|
||||||
if oldtype(object)~="table" then return end
|
|
||||||
if oldtype(oldgetmetatable(object))=="table" then
|
|
||||||
if oldgetmetatable(object).__isuserdata then
|
|
||||||
if oldtype(oldgetmetatable(object).__usermeta)=="function" then
|
|
||||||
return oldgetmetatable(object).__usermeta()
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object).__usermeta
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return oldgetmetatable(object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function isEqualToAny(a, ...)
|
|
||||||
local args={...}
|
|
||||||
for i=0, #args do
|
|
||||||
if a==args[i] then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function isEqualToAll(a, ...)
|
|
||||||
local args={...}
|
|
||||||
for i=0, #args do
|
|
||||||
if a~=args[i] then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.keys(t)
|
|
||||||
local a = {}
|
|
||||||
for n in pairs(t) do table.insert(a, n) end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.values(t)
|
|
||||||
local a = {}
|
|
||||||
for _, n in pairs(t) do table.insert(a, n) end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.indexOf(t, value)
|
|
||||||
for i,v in ipairs(t) do
|
|
||||||
if v==value then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
syscall = setmetatable({}, {
|
|
||||||
__index = function(self, name)
|
|
||||||
return function(...)
|
|
||||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
|
||||||
if res[1] then
|
|
||||||
return table.unpack(res, 2, res.n)
|
|
||||||
else
|
|
||||||
error(res[2], 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
table.serialize=serialize
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
local io = {}
|
|
||||||
kernel.io=io
|
|
||||||
io.eventq={}
|
|
||||||
|
|
||||||
function io.pushEvent(queue, ...)
|
|
||||||
queue=tostring(queue)
|
|
||||||
if not io.eventq[queue] then
|
|
||||||
io.eventq[queue]={}
|
|
||||||
end
|
|
||||||
io.eventq[queue][#io.eventq[queue]+1]={...}
|
|
||||||
end
|
|
||||||
|
|
||||||
function io.bind(queue)
|
|
||||||
queue=tostring(queue)
|
|
||||||
kernel.currentTask.eventq=queue
|
|
||||||
end
|
|
||||||
|
|
||||||
function io.pullEvent()
|
|
||||||
if io.eventq[kernel.currentTask.eventq] then
|
|
||||||
if #io.eventq[kernel.currentTask.eventq]==1 then
|
|
||||||
local event = table.remove(io.eventq[kernel.currentTask.eventq] or {}, 1)
|
|
||||||
io.eventq[kernel.currentTask.eventq]=nil
|
|
||||||
return table.unpack(event)
|
|
||||||
end
|
|
||||||
local event = table.remove(io.eventq[kernel.currentTask.eventq] or {}, 1)
|
|
||||||
if not event then return end
|
|
||||||
return table.unpack(event)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function io.getBoundQueue()
|
|
||||||
return kernel.currentTask.eventq
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls["IO_pushEvent"]=io.pushEvent
|
|
||||||
kernel.syscalls["IO_pullEvent"]=io.pullEvent
|
|
||||||
kernel.syscalls["IO_bind"]=io.bind
|
|
||||||
kernel.syscalls["IO_getBoundQueue"]=io.getBoundQueue
|
|
||||||
|
|
||||||
kernel.log("IO pipeline initialized")
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local vfs = {}
|
|
||||||
vfs.mounts = { ["$"] = "/" }
|
|
||||||
local disks = kernel.disks
|
|
||||||
|
|
||||||
-- Path handling
|
|
||||||
local function normalizePath(path)
|
|
||||||
local parts = {}
|
|
||||||
for part in path:gmatch("[^/]+") do
|
|
||||||
if part == ".." then
|
|
||||||
if #parts > 0 then table.remove(parts) end
|
|
||||||
elseif part ~= "." and part ~= "" then
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "/" .. table.concat(parts, "/") .. (path:sub(-1) == "/" and "/" or "")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function resolvePath(path)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local cwd = task.cwd or "/"
|
|
||||||
|
|
||||||
if path:sub(1,1) ~= "/" then
|
|
||||||
path = cwd .. path
|
|
||||||
end
|
|
||||||
|
|
||||||
path = normalizePath(path)
|
|
||||||
|
|
||||||
local mountPoint = "/"
|
|
||||||
local mountId = "$"
|
|
||||||
for k,v in pairs(vfs.mounts) do
|
|
||||||
if path:sub(1,#v) == v and #v > #mountPoint then
|
|
||||||
mountPoint = v
|
|
||||||
mountId = k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local diskPath = path:sub(#mountPoint + 1)
|
|
||||||
return disks[mountId], diskPath
|
|
||||||
end
|
|
||||||
|
|
||||||
-- File object creation
|
|
||||||
local function newFileObject(disk, handle, mode, path)
|
|
||||||
return {
|
|
||||||
disk = disk,
|
|
||||||
handle = handle,
|
|
||||||
mode = mode,
|
|
||||||
path = path,
|
|
||||||
refcount = 1
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function allocFD(task)
|
|
||||||
local fd = 0
|
|
||||||
while task.fd[fd] do fd = fd + 1 end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
local function allocFD(task)
|
|
||||||
-- enforce per-task limit
|
|
||||||
local count = 0
|
|
||||||
for _ in pairs(task.fd) do count = count + 1 end
|
|
||||||
if count >= kernel.config.maxFilesPerTask then
|
|
||||||
error("EMFILE") -- Too many open files in this task
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find first free FD
|
|
||||||
local fd = 0
|
|
||||||
while task.fd[fd] do fd = fd + 1 end
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
local total=0
|
|
||||||
local function checkSystemLimit()
|
|
||||||
if total >= kernel.config.maxOpenFiles-16 then
|
|
||||||
error("ENFILE") -- Too many open files in the system
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- VFS syscalls
|
|
||||||
function vfs.open(path, mode)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
|
|
||||||
-- check limits
|
|
||||||
checkSystemLimit()
|
|
||||||
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then
|
|
||||||
error("No disk mounted for path '"..path.."'")
|
|
||||||
end
|
|
||||||
|
|
||||||
local handle = disk:open(diskPath, mode)
|
|
||||||
if not handle then return nil end
|
|
||||||
|
|
||||||
local file = newFileObject(disk.address, handle, mode, path)
|
|
||||||
local fd = allocFD(task)
|
|
||||||
task.fd[fd] = file
|
|
||||||
total=total+1
|
|
||||||
|
|
||||||
return fd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.close(fd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
task.fd[fd] = nil
|
|
||||||
file.refcount = file.refcount - 1
|
|
||||||
if file.refcount == 0 then
|
|
||||||
file.handle.close()
|
|
||||||
end
|
|
||||||
total=total-1
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.read(fd, count)
|
|
||||||
local file = kernel.currentTask.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.mode:find("r") then error("File not open for reading") end
|
|
||||||
return file.handle.read(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.write(fd, data)
|
|
||||||
local file = kernel.currentTask.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
if not file.mode:find("w") then error("File not open for writing") end
|
|
||||||
return file.handle.write(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.whereis(fd)
|
|
||||||
local file = kernel.currentTask.fd[fd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
return file.path
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Filesystem operations
|
|
||||||
function vfs.mkdir(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("No disk mounted") end
|
|
||||||
return disk:makeDirectory(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.remove(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("No disk mounted") end
|
|
||||||
return disk:remove(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.attributes(path)
|
|
||||||
if type(path) == "number" then
|
|
||||||
local file = kernel.currentTask.fd[path]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
return disks[file.disk]:attributes(file.path)
|
|
||||||
end
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("No disk mounted") end
|
|
||||||
return disk:attributes(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.list(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("No disk mounted") end
|
|
||||||
return disk:list(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.exists(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then return false end
|
|
||||||
return disk:directoryExists(diskPath) or disk:fileExists(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.type(path)
|
|
||||||
if type(path) == "number" then
|
|
||||||
local file = kernel.currentTask.fd[path]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
return disks[file.disk]:type(file.path)
|
|
||||||
end
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then error("No disk mounted") end
|
|
||||||
return disk:type(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.isDirectory(path)
|
|
||||||
local disk, diskPath = resolvePath(path)
|
|
||||||
if not disk then return false end
|
|
||||||
return disk:directoryExists(diskPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- CWD
|
|
||||||
function vfs.getcwd()
|
|
||||||
return kernel.currentTask.cwd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.setcwd(path)
|
|
||||||
if path:sub(-1) ~= "/" then path = path .. "/" end
|
|
||||||
if path:sub(1,1) ~= "/" then path = "/" .. path end
|
|
||||||
kernel.currentTask.cwd = normalizePath(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Mounting
|
|
||||||
function vfs.mount(diskId, path)
|
|
||||||
if kernel.uid ~= 0 then error("Permission denied") end
|
|
||||||
if not disks[diskId] then error("Unknown disk '"..diskId.."'") end
|
|
||||||
if path:sub(-1) ~= "/" then path = path .. "/" end
|
|
||||||
|
|
||||||
for _,v in pairs(vfs.mounts) do
|
|
||||||
if v == path then error("Mount point already used") end
|
|
||||||
end
|
|
||||||
vfs.mounts[diskId] = path
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.unmount(path)
|
|
||||||
if kernel.uid ~= 0 then error("Permission denied") end
|
|
||||||
for k,v in pairs(vfs.mounts) do
|
|
||||||
if v == path then
|
|
||||||
vfs.mounts[k] = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.getMounts()
|
|
||||||
return vfs.mounts
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.virtdisk(obj)
|
|
||||||
kernel.disks[obj.address] = obj
|
|
||||||
kernel.log("Registered virtual disk at "..obj.address)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect file operations to VFS
|
|
||||||
function vfs.dup(oldfd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[oldfd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
local newfd = allocFD(task)
|
|
||||||
task.fd[newfd] = file
|
|
||||||
file.refcount = file.refcount + 1
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfs.dup2(oldfd, newfd)
|
|
||||||
local task = kernel.currentTask
|
|
||||||
local file = task.fd[oldfd]
|
|
||||||
if not file then error("EBADF") end
|
|
||||||
|
|
||||||
if oldfd == newfd then return newfd end
|
|
||||||
|
|
||||||
if task.fd[newfd] then
|
|
||||||
vfs.close(newfd)
|
|
||||||
end
|
|
||||||
|
|
||||||
task.fd[newfd] = file
|
|
||||||
file.refcount = file.refcount + 1
|
|
||||||
return newfd
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Syscall registration
|
|
||||||
kernel.vfs = vfs
|
|
||||||
kernel.syscalls["VFS_open"] = vfs.open
|
|
||||||
kernel.syscalls["VFS_read"] = vfs.read
|
|
||||||
kernel.syscalls["VFS_write"] = vfs.write
|
|
||||||
kernel.syscalls["VFS_close"] = vfs.close
|
|
||||||
kernel.syscalls["VFS_list"] = vfs.list
|
|
||||||
kernel.syscalls["VFS_type"] = vfs.type
|
|
||||||
kernel.syscalls["VFS_attributes"] = vfs.attributes
|
|
||||||
kernel.syscalls["VFS_mkdir"] = vfs.mkdir
|
|
||||||
kernel.syscalls["VFS_remove"] = vfs.remove
|
|
||||||
kernel.syscalls["VFS_exists"] = vfs.exists
|
|
||||||
kernel.syscalls["VFS_mount"] = vfs.mount
|
|
||||||
kernel.syscalls["VFS_unmount"] = vfs.unmount
|
|
||||||
kernel.syscalls["VFS_getcwd"] = vfs.getcwd
|
|
||||||
kernel.syscalls["VFS_setcwd"] = vfs.setcwd
|
|
||||||
kernel.syscalls["VFS_whereis"] = vfs.whereis
|
|
||||||
kernel.syscalls["VFS_dup"] = vfs.dup
|
|
||||||
kernel.syscalls["VFS_dup2"] = vfs.dup2
|
|
||||||
kernel.syscalls["VFS_isDirectory"]= vfs.isDirectory
|
|
||||||
|
|
||||||
kernel.log("VFS module loaded")
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local cache = {}
|
|
||||||
local 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(searchpaths) do
|
|
||||||
local full_path = string.gsub(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,173 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
local proxy = {}
|
|
||||||
local data = {}
|
|
||||||
|
|
||||||
proxy.address = "devfs0000"
|
|
||||||
proxy.isReadOnly = false
|
|
||||||
proxy.spaceUsed = function() return 0 end
|
|
||||||
proxy.spaceTotal = function() return 0 end
|
|
||||||
proxy.makeDirectory = function() error("Permission denied") end
|
|
||||||
proxy.remove = function() error("Permission denied") end
|
|
||||||
proxy.setLabel = function() error("Permission denied") end
|
|
||||||
proxy.getLabel = function() return "devfs" end
|
|
||||||
proxy.attributes = function(path) return {
|
|
||||||
type = proxy.type(path),
|
|
||||||
isReadOnly = false,
|
|
||||||
size = 0,
|
|
||||||
lastModified = 0,
|
|
||||||
created = 0,
|
|
||||||
Permissions = "666",
|
|
||||||
owner = "root",
|
|
||||||
group = "root"
|
|
||||||
} end
|
|
||||||
|
|
||||||
local function getNode(path)
|
|
||||||
local parts = string.split(path, "/")
|
|
||||||
if parts[1] == "" then
|
|
||||||
table.remove(parts, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local node = data
|
|
||||||
for _, part in ipairs(parts) do
|
|
||||||
if node[part] then
|
|
||||||
node = node[part]
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
proxy.type = function(path)
|
|
||||||
local node = getNode(path)
|
|
||||||
if node then
|
|
||||||
return node.type
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
proxy.list = function(path)
|
|
||||||
local node = getNode(path)
|
|
||||||
if node and node.type == "directory" then
|
|
||||||
local content = table.keys(node)
|
|
||||||
table.remove(content, table.indexOf(content, "type"))
|
|
||||||
return content
|
|
||||||
else
|
|
||||||
error("Not a directory")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
proxy.open = function(path, mode)
|
|
||||||
local node = getNode(path)
|
|
||||||
if node and (node.type == "file" or node.type == "character device") then
|
|
||||||
if mode == "r" then
|
|
||||||
return {
|
|
||||||
read = node.read,
|
|
||||||
close = function() end
|
|
||||||
}
|
|
||||||
elseif mode == "w" then
|
|
||||||
return {
|
|
||||||
write = node.write,
|
|
||||||
close = function() end
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error("Invalid mode")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
error("Not a file"..type(node))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newStringFile(content)
|
|
||||||
return {
|
|
||||||
type = "file",
|
|
||||||
read = function() return content end,
|
|
||||||
write = function(newContent) content = newContent end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newDirectory()
|
|
||||||
return {
|
|
||||||
type = "directory"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
data["random"] = {
|
|
||||||
type = "character device",
|
|
||||||
read = function(amount)
|
|
||||||
local result = ""
|
|
||||||
for _ = 1, amount do
|
|
||||||
result = result .. string.char(math.random(0, 255))
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end,
|
|
||||||
write = function() error("Permission denied") end
|
|
||||||
}
|
|
||||||
|
|
||||||
data["null"] = {
|
|
||||||
type = "character device",
|
|
||||||
read = function() return "" end,
|
|
||||||
write = function() end
|
|
||||||
}
|
|
||||||
|
|
||||||
data["zero"] = {
|
|
||||||
type = "character device",
|
|
||||||
read = function(amount)
|
|
||||||
return string.rep("\0", amount)
|
|
||||||
end,
|
|
||||||
write = function() error("Permission denied") end
|
|
||||||
}
|
|
||||||
|
|
||||||
data["rtc"] = {
|
|
||||||
type = "character device",
|
|
||||||
read = function() return kernel.computer:time() end,
|
|
||||||
write = function() error("Permission denied") end
|
|
||||||
}
|
|
||||||
|
|
||||||
data["rtc0"] = {
|
|
||||||
type = "character device",
|
|
||||||
read = function() return kernel.computer:time() end,
|
|
||||||
write = function() error("Permission denied") end
|
|
||||||
}
|
|
||||||
|
|
||||||
data["eeprom"] = {
|
|
||||||
type = "character device",
|
|
||||||
read = function() return kernel.computer:getEEPROM() end,
|
|
||||||
write = function(data)
|
|
||||||
if kernel.uid ~= 0 then
|
|
||||||
error("Permission denied")
|
|
||||||
end
|
|
||||||
kernel.computer:setEEPROM(data)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local keyboard = kernel.newFifo()
|
|
||||||
local mouse = kernel.newFifo()
|
|
||||||
data["input"] = newDirectory()
|
|
||||||
data["input"]["keyboard"] = {
|
|
||||||
type = "pipe",
|
|
||||||
read = function(amount)
|
|
||||||
return keyboard.pop()
|
|
||||||
end,
|
|
||||||
write = function() error("Permission denied") end
|
|
||||||
}
|
|
||||||
data["input"]["mouse"] = {
|
|
||||||
type = "pipe",
|
|
||||||
read = function(amount)
|
|
||||||
return mouse.pop()
|
|
||||||
end,
|
|
||||||
write = function() error("Permission denied") end
|
|
||||||
}
|
|
||||||
|
|
||||||
data["pts"] = newDirectory()
|
|
||||||
|
|
||||||
kernel.devfs = {}
|
|
||||||
kernel.devfs.keyboard = keyboard
|
|
||||||
kernel.devfs.mouse = mouse
|
|
||||||
kernel.devfs.proxy = proxy
|
|
||||||
kernel.devfs.data = data
|
|
||||||
kernel.vfs.virtdisk(proxy)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
|
|
||||||
kernel.processes.keventd = function()
|
|
||||||
while true do
|
|
||||||
local event = {kernel.computer:getMachineEvent()}
|
|
||||||
if event[1] then
|
|
||||||
if event[1]=="keyTyped" then
|
|
||||||
if event[3]=="\x1b^s" then
|
|
||||||
kernel.shutdown()
|
|
||||||
elseif event[3]=="\x1b^r" then
|
|
||||||
kernel.reboot()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
kernel.io.pushEvent("raw", table.unpack(event))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
for i,v in ipairs(string.split(kernel.fstab,"\n")) do
|
|
||||||
if v:sub(1,1)=="U" then
|
|
||||||
local id=""
|
|
||||||
for i=3,#v do
|
|
||||||
if v:sub(i,i)==";" then
|
|
||||||
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
|
||||||
id=v:sub(3,i-1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local path=v:sub(#id+4)
|
|
||||||
if id~="$" then
|
|
||||||
kernel.vfs.mount(id,path)
|
|
||||||
end
|
|
||||||
::endline::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local ports = {}
|
|
||||||
local signals = {}
|
|
||||||
local ipc = {}
|
|
||||||
|
|
||||||
function ipc.open(port)
|
|
||||||
if not ports[port] then
|
|
||||||
local handle = kernel.newUUID()
|
|
||||||
ports[port] = {owner = kernel.currentProcess.pid, handle = handle, messages = {}}
|
|
||||||
return ports[port].handle
|
|
||||||
end
|
|
||||||
error("Port already opened")
|
|
||||||
end
|
|
||||||
|
|
||||||
function ipc.close(port)
|
|
||||||
if ports[port] then
|
|
||||||
if ports[port].owner == kernel.currentProcess.pid then
|
|
||||||
ports[port] = nil
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
error("Cannot close port you do not own")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error("Port not opened")
|
|
||||||
end
|
|
||||||
|
|
||||||
function ipc.send(port, message)
|
|
||||||
if ports[port] then
|
|
||||||
table.insert(ports[port].messages, {from = kernel.currentProcess.pid, message = message})
|
|
||||||
if signals[ports[port].owner] then
|
|
||||||
signals[ports[port].owner](port, message)
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
error("Port not opened")
|
|
||||||
end
|
|
||||||
|
|
||||||
function ipc.receive(port)
|
|
||||||
if ports[port] then
|
|
||||||
if #ports[port].messages > 0 then
|
|
||||||
return table.remove(ports[port].messages, 1)
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error("Port not opened")
|
|
||||||
end
|
|
||||||
|
|
||||||
function ipc.setSignalHandler(pid, handler)
|
|
||||||
signals[pid] = handler
|
|
||||||
end
|
|
||||||
|
|
||||||
function ipc.clearSignalHandler(pid)
|
|
||||||
signals[pid] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function ipc.sendSignal(pid, ...)
|
|
||||||
coroutine.resumeWithTimeout(coroutine.create(function(...)
|
|
||||||
if signals[pid] then
|
|
||||||
signals[pid](...)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end), 100)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.ipc = ipc
|
|
||||||
kernel.syscalls["ipc_open"] = ipc.open
|
|
||||||
kernel.syscalls["ipc_close"] = ipc.close
|
|
||||||
kernel.syscalls["ipc_send"] = ipc.send
|
|
||||||
kernel.syscalls["ipc_receive"] = ipc.receive
|
|
||||||
kernel.syscalls["ipc_setSignalHandler"] = ipc.setSignalHandler
|
|
||||||
kernel.syscalls["ipc_clearSignalHandler"] = ipc.clearSignalHandler
|
|
||||||
kernel.syscalls["ipc_sendSignal"] = ipc.sendSignal
|
|
||||||
kernel.log("Loaded IPC module")
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local args={...}
|
|
||||||
local kernel=args[1]
|
|
||||||
kernel._G=_G
|
|
||||||
kernel._U=setmetatable({},{
|
|
||||||
__index = kernel._G,
|
|
||||||
__newindex = function(t,k,v)
|
|
||||||
if kernel.config.allowGlobalOverwrites or kernel.allowGlobalOverwrites then
|
|
||||||
rawset(t,k,v)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
error("Attempt to modify global variable '"..k.."'",2)
|
|
||||||
end,
|
|
||||||
__metatable = false
|
|
||||||
})
|
|
||||||
kernel.allowGlobalOverwrites=true
|
|
||||||
kernel._U._G=kernel._U
|
|
||||||
kernel.allowGlobalOverwrites=false
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local pam = {}
|
|
||||||
kernel.pam = pam
|
|
||||||
local loggedIn = {}
|
|
||||||
|
|
||||||
local function getFile(path)
|
|
||||||
local file = kernel.vfs.open(path, "r")
|
|
||||||
if not file then error("Failed to open file: "..path) end
|
|
||||||
local content = kernel.vfs.read(file, 1024000)
|
|
||||||
kernel.vfs.close(file)
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
local blake2s = require("crypto.blake2s")
|
|
||||||
if not blake2s then error("Failed to load blake2s") end
|
|
||||||
|
|
||||||
if not kernel.vfs.exists("/etc/pam.d/secret") then
|
|
||||||
local key = ""
|
|
||||||
for i=1, 256 do
|
|
||||||
key=key..string.char(math.random(1,255))
|
|
||||||
end
|
|
||||||
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
|
|
||||||
kernel.vfs.write(handle, key)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
end
|
|
||||||
|
|
||||||
local pepper = getFile("/etc/pam.d/secret")
|
|
||||||
|
|
||||||
function pam.authenticate(username, password)
|
|
||||||
local fpasswd = getFile("/etc/passwd")
|
|
||||||
local fshadow = getFile("/etc/shadow")
|
|
||||||
|
|
||||||
local passwdLines = string.split(fpasswd, "\n")
|
|
||||||
local shadowLines = string.split(fshadow, "\n")
|
|
||||||
|
|
||||||
local passwd = {}
|
|
||||||
local shadow = {}
|
|
||||||
for _, line in ipairs(passwdLines) do
|
|
||||||
local fields = string.split(line, ":")
|
|
||||||
passwd[fields[1]] = fields
|
|
||||||
end
|
|
||||||
for _, line in ipairs(shadowLines) do
|
|
||||||
local fields = string.split(line, ":")
|
|
||||||
shadow[fields[1]] = fields
|
|
||||||
end
|
|
||||||
|
|
||||||
for user, fields in pairs(passwd) do
|
|
||||||
if user == username then
|
|
||||||
local shadowPasswd = string.split(shadow[user][2], "$")
|
|
||||||
local salt = shadowPasswd[2]
|
|
||||||
local hashedPassword = blake2s(password .. salt, pepper)
|
|
||||||
if hashedPassword == shadowPasswd[3] then
|
|
||||||
loggedIn[username] = kernel.newUUID()
|
|
||||||
return loggedIn[username]
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function pam.authToken(username, token)
|
|
||||||
return loggedIn[username] == token
|
|
||||||
end
|
|
||||||
|
|
||||||
@@ -1,347 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
local tasks = {}
|
|
||||||
local sys = {}
|
|
||||||
local nextpid = 2
|
|
||||||
kernel.exitMain=false
|
|
||||||
|
|
||||||
function sys.spawn(func, name, envars, args, tgid)
|
|
||||||
local id = nextpid
|
|
||||||
nextpid = nextpid + 1
|
|
||||||
tasks[tostring(id)] = {
|
|
||||||
coro=coroutine.create(function()
|
|
||||||
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
|
|
||||||
if not ok then
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
kernel.log("Task "..tostring(id).." exited with err: "..tostring(err), "ERROR")
|
|
||||||
end
|
|
||||||
tasks[tostring(id)].status="Z"
|
|
||||||
tasks[tostring(id)].exit=err
|
|
||||||
else
|
|
||||||
if kernel.config.logTaskExit then
|
|
||||||
if err then
|
|
||||||
kernel.log("Task "..tostring(id).." exited with code: "..tostring(err), "INFO")
|
|
||||||
else
|
|
||||||
kernel.log("Task "..tostring(id).." exited without code", "INFO")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tasks[tostring(id)].status="Z"
|
|
||||||
tasks[tostring(id)].exit=err
|
|
||||||
end
|
|
||||||
end),
|
|
||||||
name=name or ("task"..tostring(id)),
|
|
||||||
envars=envars or {},
|
|
||||||
args=args or {},
|
|
||||||
status="R",
|
|
||||||
pid=id,
|
|
||||||
tgid=tgid or kernel.currentTask.tgid,
|
|
||||||
user=kernel.user,
|
|
||||||
uid=kernel.uid,
|
|
||||||
fd={},
|
|
||||||
exit="",
|
|
||||||
sleep=0,
|
|
||||||
ivs=0,
|
|
||||||
vs=0,
|
|
||||||
children={},
|
|
||||||
parent=kernel.currentTask,
|
|
||||||
siblings=kernel.currentTask.children,
|
|
||||||
syscallReturn={},
|
|
||||||
cwd=kernel.currentTask.cwd,
|
|
||||||
term=kernel.currentTask.term,
|
|
||||||
timeSlice=0,
|
|
||||||
lastTime=0,
|
|
||||||
totalTime=0,
|
|
||||||
numRuns=0,
|
|
||||||
privacy=0,
|
|
||||||
debugger=false,
|
|
||||||
eventq=kernel.currentTask.eventq
|
|
||||||
}
|
|
||||||
table.insert(kernel.currentTask.children, tasks[tostring(id)])
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.sleep(ms)
|
|
||||||
kernel.currentTask.status="S"
|
|
||||||
kernel.currentTask.sleep=kernel.computer:time()+ms
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getTaskInfo(pid)
|
|
||||||
if tasks[tostring(pid)] then
|
|
||||||
local task = tasks[tostring(pid)]
|
|
||||||
local children = {}
|
|
||||||
local siblings = {}
|
|
||||||
for i,v in ipairs(task.children) do
|
|
||||||
children[i]=v.pid
|
|
||||||
end
|
|
||||||
for i,v in ipairs(task.siblings) do
|
|
||||||
siblings[i]=v.pid
|
|
||||||
end
|
|
||||||
return {
|
|
||||||
name=task.name,
|
|
||||||
envars=table.deepcopy(task.envars),
|
|
||||||
args=table.deepcopy(task.args),
|
|
||||||
status=task.status,
|
|
||||||
pid=task.pid,
|
|
||||||
tgid=task.tgid,
|
|
||||||
user=task.user,
|
|
||||||
uid=task.uid,
|
|
||||||
exit=task.exit,
|
|
||||||
sleep=task.sleep,
|
|
||||||
ivs=task.ivs,
|
|
||||||
vs=task.vs,
|
|
||||||
children=children,
|
|
||||||
siblings=siblings,
|
|
||||||
parent=task.parent.pid,
|
|
||||||
cwd=task.cwd,
|
|
||||||
term=task.term
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.collect(pid)
|
|
||||||
local children={}
|
|
||||||
for i,v in ipairs(kernel.currentTask.children) do
|
|
||||||
children[i]=v.pid
|
|
||||||
end
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
elseif tasks[tostring(pid)].status~="Z" then
|
|
||||||
return false, "Task must exit to collect status"
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].reapTime=0
|
|
||||||
return true, tasks[tostring(pid)].exit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.kill(pid)
|
|
||||||
local children={}
|
|
||||||
for i,v in ipairs(kernel.currentTask.children) do
|
|
||||||
children[i]=v.pid
|
|
||||||
end
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
elseif tasks[tostring(pid)].status=="Z" then
|
|
||||||
return false, "Task is already dead"
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status="Z"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.stop(pid)
|
|
||||||
local children={}
|
|
||||||
for i,v in ipairs(kernel.currentTask.children) do
|
|
||||||
children[i]=v.pid
|
|
||||||
end
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
elseif tasks[tostring(pid)].status~="R" then
|
|
||||||
return false, "Cannot stop non running task"
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status="T"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.continue(pid)
|
|
||||||
local children={}
|
|
||||||
for i,v in ipairs(kernel.currentTask.children) do
|
|
||||||
children[i]=v.pid
|
|
||||||
end
|
|
||||||
if not tasks[tostring(pid)] then
|
|
||||||
return false, "Task does not exist"
|
|
||||||
elseif not isEqualToAny(tasks[tostring(pid)].pid,table.unpack(children)) then
|
|
||||||
return false, "You do not own this task"
|
|
||||||
elseif tasks[tostring(pid)].status~="T" then
|
|
||||||
return false, "Task is not stopped"
|
|
||||||
else
|
|
||||||
tasks[tostring(pid)].status="R"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sys.getPid()
|
|
||||||
return kernel.currentTask.pid
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls["HPV_spawn"]=sys.spawn
|
|
||||||
kernel.syscalls["HPV_sleep"]=sys.sleep
|
|
||||||
kernel.syscalls["HPV_getTaskInfo"]=sys.getTaskInfo
|
|
||||||
kernel.syscalls["HPV_collect"]=sys.collect
|
|
||||||
kernel.syscalls["HPV_kill"]=sys.kill
|
|
||||||
kernel.syscalls["HPV_stop"]=sys.stop
|
|
||||||
kernel.syscalls["HPV_continue"]=sys.continue
|
|
||||||
kernel.syscalls["HPV_getPid"]=sys.getPid
|
|
||||||
kernel._G.sleep=function(...)coroutine.yield("syscall","HPV_sleep",...)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.term = nil
|
|
||||||
task.numRuns = nil
|
|
||||||
task.totalTime = nil
|
|
||||||
task.lastTime = nil
|
|
||||||
task.timeSlice = nil
|
|
||||||
task.syscallReturn = nil
|
|
||||||
task.sleep = nil
|
|
||||||
for k,v in pairs(task.fd) do
|
|
||||||
kernel.vfs.close(v)
|
|
||||||
end
|
|
||||||
task.fd = nil
|
|
||||||
task.debugger=nil
|
|
||||||
task.privacy=nil
|
|
||||||
task.reapTime = kernel.computer:time() + 30000
|
|
||||||
elseif task.reapTime and kernel.computer:time() > task.reapTime and task.status=="Z" then
|
|
||||||
for _,child in ipairs(task.children) do
|
|
||||||
child.parent = tasks["1"]
|
|
||||||
child.siblings = tasks["1"].children
|
|
||||||
table.insert(tasks["1"].children, child)
|
|
||||||
end
|
|
||||||
for i, sibling in ipairs(task.siblings) do
|
|
||||||
if sibling.pid == task.pid then
|
|
||||||
table.remove(task.siblings, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tasks[pid] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local alpha = 0.85
|
|
||||||
local C_target = 0.01
|
|
||||||
local Tmin = 0.0005
|
|
||||||
local Tmax = 0.5
|
|
||||||
local lambda_budget = 0.08
|
|
||||||
local lambda_clamp = 0.03
|
|
||||||
local lambda_var = 0.02
|
|
||||||
local k_min = 0.5
|
|
||||||
local k_max = 0.5
|
|
||||||
local B = 0.01
|
|
||||||
|
|
||||||
function kernel.main()
|
|
||||||
while not kernel.exitMain do
|
|
||||||
local N = 0
|
|
||||||
local Tmin_hit = 0
|
|
||||||
local Tmax_hit = 0
|
|
||||||
local totalTaskTime = 0
|
|
||||||
local taskTimes = {}
|
|
||||||
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.status == "S" then
|
|
||||||
if kernel.computer:time() >= task.sleep then
|
|
||||||
task.status="R"
|
|
||||||
task.sleep=0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if task.status == "R" then
|
|
||||||
kernel.currentTask = task
|
|
||||||
kernel.vfs.cwd = task.cwd
|
|
||||||
kernel.user = task.user
|
|
||||||
kernel.uid = task.uid
|
|
||||||
N = N + 1
|
|
||||||
|
|
||||||
-- assign adaptive time slice
|
|
||||||
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
|
|
||||||
|
|
||||||
-- measure execution time
|
|
||||||
local startTime = kernel.computer:time()
|
|
||||||
local ret
|
|
||||||
if kernel.config.preempt then
|
|
||||||
ret = {coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn))}
|
|
||||||
else
|
|
||||||
ret = {coroutine.resume(task.coro, table.unpack(task.syscallReturn))}
|
|
||||||
end
|
|
||||||
|
|
||||||
local elapsed = kernel.computer:time() - startTime
|
|
||||||
task.lastTime = elapsed
|
|
||||||
task.totalTime = (task.totalTime or 0) + elapsed
|
|
||||||
task.numRuns = (task.numRuns or 0) + 1
|
|
||||||
|
|
||||||
taskTimes[#taskTimes + 1] = elapsed
|
|
||||||
totalTaskTime = totalTaskTime + elapsed
|
|
||||||
|
|
||||||
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
|
|
||||||
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
|
|
||||||
|
|
||||||
-- handle task results
|
|
||||||
if ret[1] == "error" then
|
|
||||||
kernel.log("processHandlerException: "..ret[2])
|
|
||||||
task.status = "Z"
|
|
||||||
task.exit = "processHandlerException: "..ret[2]
|
|
||||||
elseif ret[1] == "timeout" then
|
|
||||||
task.ivs=task.ivs+1
|
|
||||||
task.syscallReturn = {}
|
|
||||||
elseif ret[1] == "success" then
|
|
||||||
task.vs=task.vs+1
|
|
||||||
if ret[2]=="syscall" then
|
|
||||||
if kernel.syscalls[ret[3]] then
|
|
||||||
if kernel.config.debugSyscalls then
|
|
||||||
kernel.log("Task "..task.pid.." invoking syscall: "..ret[3], "DBUG")
|
|
||||||
for i=4,#ret do
|
|
||||||
kernel.log(" inval["..tostring(i-3).."] = "..tostring(ret[i]), "DBUG")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local sysret = {xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))}
|
|
||||||
if kernel.config.debugSyscalls then
|
|
||||||
if not sysret[1] then
|
|
||||||
kernel.log("Task "..task.pid.." syscall "..ret[3].." failed: "..tostring(sysret[2]))
|
|
||||||
else
|
|
||||||
kernel.log("Task "..task.pid.." syscall "..ret[3].." completed returning "..tostring(#sysret-1).." values", "DBUG")
|
|
||||||
for i=2,#sysret do
|
|
||||||
if type(sysret[i])=="table" then
|
|
||||||
kernel.log(" retval["..tostring(i-1).."] = "..table.serialize(sysret[i]), "DBUG")
|
|
||||||
else
|
|
||||||
kernel.log(" retval["..tostring(i-1).."] = "..tostring(sysret[i]), "DBUG")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not sysret[1] then
|
|
||||||
task.syscallReturn={false, sysret[2]}
|
|
||||||
else
|
|
||||||
task.syscallReturn={true, table.unpack(sysret,2)}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
task.syscallReturn={false, "Unknown syscall: "..tostring(ret[3])}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
|
|
||||||
local T_prev_var = 0
|
|
||||||
for _, t in ipairs(taskTimes) do
|
|
||||||
T_prev_var = T_prev_var + (t - T_prev_avg)^2
|
|
||||||
end
|
|
||||||
if N > 0 then T_prev_var = T_prev_var / N end
|
|
||||||
|
|
||||||
if N > 0 then
|
|
||||||
local f_clamp = k_min*(Tmin_hit/N) - k_max*(Tmax_hit/N)
|
|
||||||
local B_budget = (C_target * (N^(alpha-1))) / math.max(T_prev_avg, 1e-8)
|
|
||||||
B = B + lambda_budget * (B_budget - B)
|
|
||||||
+ lambda_clamp * f_clamp
|
|
||||||
- lambda_var * T_prev_var
|
|
||||||
end
|
|
||||||
|
|
||||||
-- clean up dead tasks
|
|
||||||
reapDeadTasks()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.tasks=tasks
|
|
||||||
kernel.hpv=sys
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
kernel.tty={}
|
|
||||||
kernel.tty.inst={}
|
|
||||||
|
|
||||||
function kernel.tty.register(tty, ttyo)
|
|
||||||
kernel.tty.inst[tty]=ttyo
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.print(text)
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
kernel.tty.inst[term].print(text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.printInline(text)
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
kernel.tty.inst[term].printInline(text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.size()
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].size()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.setCursorPos(x,y)
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].setCursorPos(x,y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.getCursorPos()
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].getCursorPos()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.clear()
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].clear()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.setTextColor(color)
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].setTextColor(color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.setBackgroundColor(color)
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].setBackgroundColor(color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.scroll(n)
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] then
|
|
||||||
return kernel.tty.inst[term].scroll(n)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.getTextColor()
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] and kernel.tty.inst[term].getTextColor then
|
|
||||||
return kernel.tty.inst[term].getTextColor()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.getBackgroundColor()
|
|
||||||
local term=kernel.currentTask.term
|
|
||||||
if term and kernel.tty.inst[term] and kernel.tty.inst[term].getBackgroundColor then
|
|
||||||
return kernel.tty.inst[term].getBackgroundColor()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.bind(ttyid)
|
|
||||||
if not ttyid then
|
|
||||||
return false, "No TTY ID specified"
|
|
||||||
end
|
|
||||||
if not kernel.tty.inst[ttyid] then
|
|
||||||
return false, "TTY "..tostring(ttyid).." not registered"
|
|
||||||
end
|
|
||||||
kernel.currentTask.term=ttyid
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.unbind()
|
|
||||||
kernel.currentTask.term=false
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.isBound()
|
|
||||||
return kernel.currentTask.term ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function kernel.tty.getBoundTTY()
|
|
||||||
return kernel.currentTask.term
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.syscalls["TTY_print"]=kernel.tty.print
|
|
||||||
kernel.syscalls["TTY_printInline"]=kernel.tty.printInline
|
|
||||||
kernel.syscalls["TTY_size"]=kernel.tty.size
|
|
||||||
kernel.syscalls["TTY_setCursorPos"]=kernel.tty.setCursorPos
|
|
||||||
kernel.syscalls["TTY_getCursorPos"]=kernel.tty.getCursorPos
|
|
||||||
kernel.syscalls["TTY_clear"]=kernel.tty.clear
|
|
||||||
kernel.syscalls["TTY_setTextColor"]=kernel.tty.setTextColor
|
|
||||||
kernel.syscalls["TTY_setBackgroundColor"]=kernel.tty.setBackgroundColor
|
|
||||||
kernel.syscalls["TTY_scroll"]=kernel.tty.scroll
|
|
||||||
kernel.syscalls["TTY_getTextColor"]=kernel.tty.getTextColor
|
|
||||||
kernel.syscalls["TTY_getBackgroundColor"]=kernel.tty.getBackgroundColor
|
|
||||||
kernel.syscalls["TTY_bind"]=kernel.tty.bind
|
|
||||||
kernel.syscalls["TTY_unbind"]=kernel.tty.unbind
|
|
||||||
kernel.syscalls["TTY_isBound"]=kernel.tty.isBound
|
|
||||||
kernel.syscalls["TTY_getBoundTTY"]=kernel.tty.getBoundTTY
|
|
||||||
|
|
||||||
kernel.log("TTY module loaded attempting to register console tty")
|
|
||||||
kernel.status="init"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
kernel.tty.bind("tty0")
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel=...
|
|
||||||
function print(...)
|
|
||||||
local args={...}
|
|
||||||
local output=""
|
|
||||||
for i=1,#args do
|
|
||||||
output=output..tostring(args[i]).."\t"
|
|
||||||
end
|
|
||||||
output=output:sub(1,-2)
|
|
||||||
syscall.TTY_print(output)
|
|
||||||
end
|
|
||||||
|
|
||||||
function printf(fmt, ...)
|
|
||||||
coroutine.yield()
|
|
||||||
local output=string.format(fmt,...)
|
|
||||||
syscall.TTY_print(output)
|
|
||||||
end
|
|
||||||
|
|
||||||
function printInline(...)
|
|
||||||
coroutine.yield()
|
|
||||||
local args={...}
|
|
||||||
local output=""
|
|
||||||
for i=1,#args do
|
|
||||||
output=output..tostring(args[i]).."\t"
|
|
||||||
end
|
|
||||||
output=output:sub(1,-2)
|
|
||||||
syscall.TTY_printInline(output)
|
|
||||||
end
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
--:Minify:--
|
|
||||||
local kernel = ...
|
|
||||||
kernel.log("Loading init system...")
|
|
||||||
kernel.log("InitPath: "..kernel.config.initPath)
|
|
||||||
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
|
||||||
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
|
||||||
kernel.vfs.close(handle)
|
|
||||||
|
|
||||||
local initFunc, err = load(data, "@sysinit")
|
|
||||||
if not initFunc then
|
|
||||||
error("Failed to load init system: "..err)
|
|
||||||
end
|
|
||||||
|
|
||||||
kernel.tasks["1"] = {
|
|
||||||
coro=coroutine.create(function()
|
|
||||||
local ok, err = xpcall(initFunc, debug.traceback, kernel)
|
|
||||||
if not ok then
|
|
||||||
kernel.panic("Init system crashed: "..tostring(err))
|
|
||||||
else
|
|
||||||
kernel.panic("Init system exited: "..tostring(err))
|
|
||||||
end
|
|
||||||
end),
|
|
||||||
name="sysinit",
|
|
||||||
status="R",
|
|
||||||
pid=1,
|
|
||||||
tgid=1,
|
|
||||||
user="root",
|
|
||||||
uid=0,
|
|
||||||
fd={},
|
|
||||||
exit="",
|
|
||||||
sleep=0,
|
|
||||||
ivs=0,
|
|
||||||
vs=0,
|
|
||||||
parent=kernel.kernelTask,
|
|
||||||
siblings=kernel.kernelTask.children,
|
|
||||||
children={},
|
|
||||||
syscallReturn={},
|
|
||||||
cwd="/",
|
|
||||||
timeSlice=0,
|
|
||||||
lastTime=0,
|
|
||||||
totalTime=0,
|
|
||||||
numRuns=0
|
|
||||||
}
|
|
||||||
kernel.log("created init task with PID 1")
|
|
||||||
kernel.log("Initializing init system...")
|
|
||||||
4
Src/Hyperion-kernel/data/boot/fstab
Normal file
4
Src/Hyperion-kernel/data/boot/fstab
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
U $;/
|
||||||
|
U devfs0000;/dev/
|
||||||
|
U tmpfs0000;/tmp/
|
||||||
|
U procfs0000;/proc/
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
--:Minify:--
|
-- :Minify:--
|
||||||
local fs = {}
|
local fs = {}
|
||||||
local disks = {}
|
local disks = {}
|
||||||
local mounts = {}
|
local mounts = {}
|
||||||
@@ -15,39 +15,42 @@ local function resolve(path)
|
|||||||
local newPath = path:sub(#mountPoint + 1)
|
local newPath = path:sub(#mountPoint + 1)
|
||||||
return disks[mounts[mountPoint]], newPath
|
return disks[mounts[mountPoint]], newPath
|
||||||
end
|
end
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
function fs.update(initdisks)
|
function fs.update(initdisks)
|
||||||
disks = {}
|
disks = {}
|
||||||
for k, v in initdisks.list() do
|
for k, v in initdisks.list() do disks[k] = v end
|
||||||
disks[k] = v
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.exists(path)
|
function fs.exists(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
return disk:directoryExists(newPath) or disk:fileExists(newPath)
|
return disk:directoryExists(newPath) or disk:fileExists(newPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.isFile(path)
|
function fs.isFile(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
return disk:fileExists(newPath)
|
return disk:fileExists(newPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.isDir(path)
|
function fs.isDir(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
return disk:directoryExists(newPath)
|
return disk:directoryExists(newPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.list(path)
|
function fs.list(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
return disk:list(newPath)
|
return disk:list(newPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.makeDir(path)
|
function fs.makeDir(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
return disk:makeDirectory(newPath)
|
return disk:makeDirectory(newPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.remove(path)
|
function fs.remove(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
return disk:remove(newPath)
|
return disk:remove(newPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.readAllText(path)
|
function fs.readAllText(path)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
local handle = disk:open(newPath, "r")
|
local handle = disk:open(newPath, "r")
|
||||||
@@ -56,23 +59,26 @@ function fs.readAllText(path)
|
|||||||
handle.close()
|
handle.close()
|
||||||
return content
|
return content
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.writeAllText(path, text)
|
function fs.writeAllText(path, text)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
local handle = disk:open(newPath, "w")
|
local handle = disk:open(newPath, "w")
|
||||||
handle.write(text)
|
handle.write(text)
|
||||||
handle.close()
|
handle.close()
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.appendAllText(path, text)
|
function fs.appendAllText(path, text)
|
||||||
local disk, newPath = resolve(path)
|
local disk, newPath = resolve(path)
|
||||||
local handle = disk:open(newPath, "a")
|
local handle = disk:open(newPath, "a")
|
||||||
handle.write(text)
|
handle.write(text)
|
||||||
handle.close()
|
handle.close()
|
||||||
end
|
end
|
||||||
function fs.load(path)
|
|
||||||
return load(fs.readAllText(path), path)
|
function fs.load(path) return load(fs.readAllText(path), path) end
|
||||||
end
|
|
||||||
function fs.mount(disk, mountPoint)
|
function fs.mount(disk, mountPoint)
|
||||||
if not disks[disk] then return end
|
if not disks[disk] then return end
|
||||||
mounts[mountPoint] = disk
|
mounts[mountPoint] = disk
|
||||||
end
|
end
|
||||||
|
|
||||||
return fs
|
return fs
|
||||||
334
Src/Hyperion-kernel/data/boot/kernel.lua
Normal file
334
Src/Hyperion-kernel/data/boot/kernel.lua
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
--:Minify:--
|
||||||
|
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.4-dev_5"
|
||||||
|
kernel.tag="1.2.4-dev_5"
|
||||||
|
kernel.process = "Kernel"
|
||||||
|
kernel.users={[0]="root",[1]="User"}
|
||||||
|
kernel.hostname = "hyperion"
|
||||||
|
kernel.groups = {}
|
||||||
|
kernel.uid = 0
|
||||||
|
kernel.gid = 0
|
||||||
|
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, 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", 0xFF0000)
|
||||||
|
pcall(kernel["saveLog"])
|
||||||
|
kernel.status="Panic"
|
||||||
|
kernel.reason=msg
|
||||||
|
screen:enable()
|
||||||
|
screen:setTextColor(0xFF0000)
|
||||||
|
screen:setBackgroundColor(0x000000)
|
||||||
|
screen:clear()
|
||||||
|
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={EFI:getMachineEvent()}
|
||||||
|
if event[1]=="keyPressed" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
EFI.reboot=true
|
||||||
|
error("KERNEL PANIC")
|
||||||
|
end
|
||||||
|
kernel.panic=kernel.PANIC
|
||||||
|
|
||||||
|
if windowsExp then
|
||||||
|
screen:setTextColor(0xFFFFFF)
|
||||||
|
screen:setBackgroundColor(0x0000FF)
|
||||||
|
screen:clear()
|
||||||
|
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
|
||||||
|
|
||||||
|
kernel.log("Kernel loaded.")
|
||||||
|
kernel.log("Mounting init disks...")
|
||||||
|
disks.refresh()
|
||||||
|
ifs.update(disks)
|
||||||
|
kernel.disks={}
|
||||||
|
for _,v in disks.list() do
|
||||||
|
kernel.disks[v.address] = v
|
||||||
|
end
|
||||||
|
ifs.mount("$", "/")
|
||||||
|
|
||||||
|
local fstab=ifs.readAllText("/boot/fstab")
|
||||||
|
local split = function(str, delim, maxResultCountOrNil)
|
||||||
|
assert(#delim == 1, "only delim len 1 supported for now")
|
||||||
|
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #str do
|
||||||
|
local c = string.sub(str,i,i)
|
||||||
|
if #rv ~= maxResultCountOrNil and c == delim then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf..c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ifs.isFile("/boot/boot.cfg") then
|
||||||
|
kernel.log("First boot detected writing boot.cfg", "INFO", 0x00FF00)
|
||||||
|
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
||||||
|
kernel.firstBoot=true
|
||||||
|
end
|
||||||
|
|
||||||
|
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
||||||
|
if not initCfgFunc then
|
||||||
|
kernel.PANIC("Failed to load /boot/boot.cfg: "..tostring(err))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
local initCfgStatus, config = pcall(initCfgFunc)
|
||||||
|
if not initCfgStatus then
|
||||||
|
kernel.PANIC("Error in /boot/boot.cfg: "..tostring(config))
|
||||||
|
end
|
||||||
|
kernel.config = config
|
||||||
|
|
||||||
|
local skip=false
|
||||||
|
for i,v in ipairs(split(fstab,"\n")) do
|
||||||
|
if v:sub(1,1)=="U" then
|
||||||
|
local id=""
|
||||||
|
for i=3,#v do
|
||||||
|
if v:sub(i,i)==";" then
|
||||||
|
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN", 0xFF8800) skip = true break end
|
||||||
|
id=v:sub(3,i-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not skip then
|
||||||
|
local path=v:sub(#id+4)
|
||||||
|
ifs.mount(id,path)
|
||||||
|
else
|
||||||
|
skip=false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
kernel.log("Disks initialized")
|
||||||
|
|
||||||
|
function kernel.saveLog()
|
||||||
|
if kernel.status=="running" then
|
||||||
|
local file = kernel.vfs.open("/var/log/syslog.log", "w")
|
||||||
|
kernel.vfs.write(file, kernel.LOG_Text)
|
||||||
|
kernel.vfs.close(file)
|
||||||
|
else
|
||||||
|
ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function kernel.newFifo()
|
||||||
|
local fifo = {}
|
||||||
|
fifo.push=function(data)
|
||||||
|
table.insert(fifo, data)
|
||||||
|
end
|
||||||
|
fifo.pop=function()
|
||||||
|
return table.remove(fifo,1)
|
||||||
|
end
|
||||||
|
return fifo
|
||||||
|
end
|
||||||
|
|
||||||
|
function kernel.newUUID()
|
||||||
|
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
||||||
|
local uuid = ""
|
||||||
|
for i = 1, #template do
|
||||||
|
local c = template:sub(i,i)
|
||||||
|
if c == "x" then
|
||||||
|
uuid = uuid .. string.format("%x", math.random(0, 15))
|
||||||
|
elseif c == "y" then
|
||||||
|
uuid = uuid .. string.format("%x", math.random(8, 11))
|
||||||
|
else
|
||||||
|
uuid = uuid .. c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return uuid
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.syscalls={}
|
||||||
|
local modules={[0]={}}
|
||||||
|
for i=0, 100 do
|
||||||
|
modules[i]={}
|
||||||
|
end
|
||||||
|
|
||||||
|
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", 0xFF8800)
|
||||||
|
else
|
||||||
|
for _,v in ipairs(modlist) do
|
||||||
|
local prior=tonumber(v:sub(1,2))
|
||||||
|
if prior then
|
||||||
|
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.ifs=ifs
|
||||||
|
kernel.apis=EFI.firmware
|
||||||
|
kernel.EFI=EFI
|
||||||
|
kernel.arch=arch
|
||||||
|
kernel.initdisks=disks
|
||||||
|
kernel.screen=screen
|
||||||
|
kernel.processes={}
|
||||||
|
kernel.fstab=fstab
|
||||||
|
|
||||||
|
kernel.kernelTask = {
|
||||||
|
name="kernel",
|
||||||
|
status="R",
|
||||||
|
pid=0,
|
||||||
|
tgid=0,
|
||||||
|
uid=0,
|
||||||
|
fd={},
|
||||||
|
exit="",
|
||||||
|
sleep=0,
|
||||||
|
ivs=0,
|
||||||
|
vs=0,
|
||||||
|
children={},
|
||||||
|
syscallReturn={},
|
||||||
|
cwd="/",
|
||||||
|
timeSlice=0,
|
||||||
|
lastTime=0,
|
||||||
|
totalTime=0,
|
||||||
|
numRuns=0
|
||||||
|
}
|
||||||
|
kernel.currentTask = kernel.kernelTask
|
||||||
|
|
||||||
|
function kernel.shutdown()
|
||||||
|
kernel.exitMain=true
|
||||||
|
kernel.status="shutdown"
|
||||||
|
end
|
||||||
|
|
||||||
|
function kernel.reboot()
|
||||||
|
kernel.exitMain=true
|
||||||
|
kernel.status="reboot"
|
||||||
|
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["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
|
||||||
|
kernel.syscalls["version"]=function() return kernel.version end
|
||||||
|
kernel.syscalls["setHostname"]=function(name) if kernel.uid~=0 then error("Permission denied") end kernel.hostname=name end
|
||||||
|
kernel.syscalls["arch"]=function() return arch end
|
||||||
|
kernel.syscalls["sysdump"]=function()
|
||||||
|
local rv={}
|
||||||
|
for i,v in pairs(kernel.syscalls) do
|
||||||
|
rv[#rv+1] = i
|
||||||
|
end
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
kernel.syscalls["reboot"]=function()
|
||||||
|
if kernel.uid==0 or kernel.groups.wheel then
|
||||||
|
kernel.reboot()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
kernel.syscalls["shutdown"]=function()
|
||||||
|
if kernel.uid==0 or kernel.groups.wheel then
|
||||||
|
kernel.shutdown()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
kernel.syscalls["halt"]=function()
|
||||||
|
if kernel.uid==0 or kernel.groups.wheel then
|
||||||
|
kernel.halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("Running modules")
|
||||||
|
for _,p in ipairs(modules) do
|
||||||
|
for _,v in ipairs(p) do
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
local func,err=load(code,"@"..v)
|
||||||
|
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", 0x00FFFF) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("Kernel initialized successfully.")
|
||||||
|
kernel.saveLog()
|
||||||
|
kernel.status="running"
|
||||||
|
screen:disable()
|
||||||
|
local ok,err = xpcall(kernel.main, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
kernel.panic(err)
|
||||||
|
end
|
||||||
|
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
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
-- This file is auto-generated during the build process.
|
-- This file is auto-generated during the build process.
|
||||||
-- DEFAULT BOOT CONFIGURATION FILE
|
-- DEFAULT BOOT CONFIGURATION FILE
|
||||||
return {
|
return {
|
||||||
initPath = "/sbin/init.lua",
|
initPath = "/sbin/init",
|
||||||
maxOpenFiles = 128,
|
maxOpenFiles = 128,
|
||||||
maxFilesPerTask = 16,
|
maxFilesPerTask = 16,
|
||||||
preempt=true
|
preempt = true,
|
||||||
}
|
}
|
||||||
1
Src/Hyperion-kernel/data/etc/passwd
Normal file
1
Src/Hyperion-kernel/data/etc/passwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0:0:root:/root:/bin/hysh
|
||||||
275
Src/Hyperion-kernel/data/lib/modules/hyperion/01_stdlib.kmod
Normal file
275
Src/Hyperion-kernel/data/lib/modules/hyperion/01_stdlib.kmod
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
--:Minify:--
|
||||||
|
--- @diagnostic disable: duplicate-set-field
|
||||||
|
local kernel = ...
|
||||||
|
kernel.allowGlobalOverwrites = true
|
||||||
|
|
||||||
|
function string.hasSuffix(str, suffix)
|
||||||
|
return string.sub(str, #suffix + 1) == suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.hasPrefix(str, prefix)
|
||||||
|
return string.sub(str, 1, #prefix) == prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.getSuffix(str, prefix) return string.sub(str, #prefix + 1) end
|
||||||
|
|
||||||
|
function string.getPrefix(str, suffix) return string.sub(str, 1, #suffix) end
|
||||||
|
|
||||||
|
function string.join(str, ...) return table.concat(table.pack(str, ...)) end
|
||||||
|
|
||||||
|
function string.delim(str, ...) return table.concat(table.pack(...), str) end
|
||||||
|
|
||||||
|
function string.split(str, delim, maxResultCountOrNil)
|
||||||
|
assert(#delim == 1, "only delim len 1 supported for now")
|
||||||
|
if not str then return false end
|
||||||
|
maxResultCountOrNil = (maxResultCountOrNil or 0) - 1
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #str do
|
||||||
|
local c = string.sub(str, i, i)
|
||||||
|
if #rv ~= maxResultCountOrNil and c == delim then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf .. c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.deepcopy(orig, copies)
|
||||||
|
copies = copies or {}
|
||||||
|
|
||||||
|
if type(orig) ~= 'table' then
|
||||||
|
return orig
|
||||||
|
elseif copies[orig] then
|
||||||
|
return copies[orig]
|
||||||
|
end
|
||||||
|
|
||||||
|
local copy = {}
|
||||||
|
copies[orig] = copy
|
||||||
|
|
||||||
|
for k, v in next, orig, nil do
|
||||||
|
local copied_key = table.deepcopy(k, copies)
|
||||||
|
local copied_val = table.deepcopy(v, copies)
|
||||||
|
copy[copied_key] = copied_val
|
||||||
|
end
|
||||||
|
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.hasKey(tabl, query)
|
||||||
|
for i, v in pairs(tabl) do if i == query then return v end end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.hasVal(tabl, query)
|
||||||
|
for i, v in pairs(tabl) do if v == query then return i end end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serialize(tbl, seen)
|
||||||
|
seen = seen or {}
|
||||||
|
|
||||||
|
if seen[tbl] then return '"[Circular Reference]"' end
|
||||||
|
|
||||||
|
seen[tbl] = true
|
||||||
|
|
||||||
|
local output = "{"
|
||||||
|
local first = true
|
||||||
|
|
||||||
|
for i, v in pairs(tbl) do
|
||||||
|
if not first then output = output .. "," end
|
||||||
|
first = false
|
||||||
|
|
||||||
|
if type(i) == "string" then
|
||||||
|
output = output .. "[\"" .. i .. "\"]="
|
||||||
|
elseif type(i) == "number" then
|
||||||
|
output = output .. "[" .. tostring(i) .. "]="
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(v) == "table" then
|
||||||
|
output = output .. serialize(v, seen)
|
||||||
|
elseif type(v) == "string" then
|
||||||
|
output = output .. "[=[" .. v .. "]=]"
|
||||||
|
elseif type(v) == "number" or type(v) == "boolean" then
|
||||||
|
output = output .. tostring(v)
|
||||||
|
elseif type(v) == "function" then
|
||||||
|
output = output .. "\"" .. tostring(v) .. "\""
|
||||||
|
elseif type(v) == "thread" then
|
||||||
|
output = output .. "\"" .. tostring(v) .. "\""
|
||||||
|
else
|
||||||
|
error("serialization of type \"" .. type(v) .. "\" is not supported")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
seen[tbl] = nil
|
||||||
|
|
||||||
|
output = output .. "}"
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldtype = type
|
||||||
|
local oldgetmetatable = getmetatable
|
||||||
|
function type(object, trueType)
|
||||||
|
if trueType then return oldtype(object) end
|
||||||
|
if oldtype(object) ~= "table" then
|
||||||
|
return oldtype(object)
|
||||||
|
else
|
||||||
|
if oldtype(oldgetmetatable(object)) == "table" then
|
||||||
|
local metatable = oldgetmetatable(object)
|
||||||
|
---@diagnostic disable-next-line: need-check-nil
|
||||||
|
if metatable.__type then return metatable.__type end
|
||||||
|
else
|
||||||
|
return "table"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function getmetatable(object)
|
||||||
|
if oldtype(object) ~= "table" then return end
|
||||||
|
if oldtype(oldgetmetatable(object)) == "table" then
|
||||||
|
if oldgetmetatable(object).__isuserdata then
|
||||||
|
if oldtype(oldgetmetatable(object).__usermeta) == "function" then
|
||||||
|
return oldgetmetatable(object).__usermeta()
|
||||||
|
else
|
||||||
|
return oldgetmetatable(object).__usermeta
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return oldgetmetatable(object)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return oldgetmetatable(object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function isEqualToAny(a, ...)
|
||||||
|
local args = {...}
|
||||||
|
for i = 0, #args do if a == args[i] then return true end end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function isEqualToAll(a, ...)
|
||||||
|
local args = {...}
|
||||||
|
for i = 0, #args do if a ~= args[i] then return false end end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.keys(t)
|
||||||
|
local a = {}
|
||||||
|
for n in pairs(t) do table.insert(a, n) end
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.values(t)
|
||||||
|
local a = {}
|
||||||
|
for _, n in pairs(t) do table.insert(a, n) end
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.indexOf(t, value)
|
||||||
|
for i, v in ipairs(t) do if v == value then return i end end
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.merge(...)
|
||||||
|
local args={...}
|
||||||
|
local out = {}
|
||||||
|
local outi = {}
|
||||||
|
|
||||||
|
for _,t in ipairs(args) do
|
||||||
|
for i,v in pairs(t) do
|
||||||
|
out[i]=v
|
||||||
|
end
|
||||||
|
for i,v in ipairs(t) do
|
||||||
|
outi[#outi+1]=v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(outi) do
|
||||||
|
out[i]=v
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.replace(s, target, repl)
|
||||||
|
local result = {}
|
||||||
|
local i = 1
|
||||||
|
local n = #s
|
||||||
|
local t_len = #target
|
||||||
|
|
||||||
|
while i <= n do
|
||||||
|
local match = true
|
||||||
|
if i + t_len - 1 <= n then
|
||||||
|
for j = 1, t_len do
|
||||||
|
if s:sub(i + j - 1, i + j - 1) ~= target:sub(j, j) then
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if match then
|
||||||
|
table.insert(result, repl)
|
||||||
|
i = i + t_len
|
||||||
|
else
|
||||||
|
table.insert(result, s:sub(i, i))
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function toHex(num)
|
||||||
|
return string.format("%X", num)
|
||||||
|
end
|
||||||
|
|
||||||
|
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, {
|
||||||
|
__index = function(self, name)
|
||||||
|
local raw = rawget(self, name)
|
||||||
|
if raw ~= nil then return raw end
|
||||||
|
return function(...)
|
||||||
|
local res = table.pack(coroutine.yield("syscall", name, ...))
|
||||||
|
if res[1] then
|
||||||
|
return table.unpack(res, 2, res.n)
|
||||||
|
else
|
||||||
|
error(res[2], 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
__newindex = function(self, k, v)
|
||||||
|
rawset(self, k, v)
|
||||||
|
end,
|
||||||
|
__metatable=false
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
syscall = makeSyscallProxy()
|
||||||
|
|
||||||
|
_makeSyscallProxy = makeSyscallProxy
|
||||||
|
|
||||||
|
table.serialize = serialize
|
||||||
1190
Src/Hyperion-kernel/data/lib/modules/hyperion/10_vfs.kmod
Normal file
1190
Src/Hyperion-kernel/data/lib/modules/hyperion/10_vfs.kmod
Normal file
File diff suppressed because it is too large
Load Diff
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
|
||||||
266
Src/Hyperion-kernel/data/lib/modules/hyperion/12_procfs.kmod
Normal file
266
Src/Hyperion-kernel/data/lib/modules/hyperion/12_procfs.kmod
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
|
||||||
|
local proxy = {}
|
||||||
|
local data = {}
|
||||||
|
|
||||||
|
proxy.address = "procfs0000"
|
||||||
|
proxy.isvirt = true
|
||||||
|
proxy.isReadOnly = function() return true end
|
||||||
|
proxy.spaceUsed = function() return 0 end
|
||||||
|
proxy.spaceTotal = function() return 0 end
|
||||||
|
proxy.makeDirectory = function() error("EACCES") end
|
||||||
|
proxy.remove = function() error("EACCES") end
|
||||||
|
proxy.setLabel = function() error("EACCES") end
|
||||||
|
proxy.getLabel = function() return "procfs" end
|
||||||
|
proxy.attributes = function(path) return {
|
||||||
|
size = 0,
|
||||||
|
modified = 0,
|
||||||
|
created = 0
|
||||||
|
} end
|
||||||
|
|
||||||
|
local function buildMeta(entries, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local uid = opts.uid or 0
|
||||||
|
local gid = opts.gid or 0
|
||||||
|
local perms = opts.perms or 0x3F -- default read/write for owner/group/world
|
||||||
|
|
||||||
|
local chunks = {}
|
||||||
|
table.insert(chunks, string.char(0x02)) -- version header
|
||||||
|
|
||||||
|
for path, target in pairs(entries) do
|
||||||
|
local name = path
|
||||||
|
local nameLen = #name
|
||||||
|
if nameLen > 255 then
|
||||||
|
error("Filename too long (>255 bytes): "..name)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Determine entry type: 0x01 = symlink if target ~= nil and target ~= ""
|
||||||
|
local entryType = 0x00
|
||||||
|
local cmeta = ""
|
||||||
|
if target and target ~= "" then
|
||||||
|
entryType = 0x01
|
||||||
|
cmeta = target
|
||||||
|
end
|
||||||
|
local cmetaLen = #cmeta
|
||||||
|
if cmetaLen > 255 then
|
||||||
|
error("cmeta too long (>255 bytes) for "..name)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Build entry as bytes
|
||||||
|
table.insert(chunks, string.char(nameLen)) -- name length
|
||||||
|
table.insert(chunks, name) -- name
|
||||||
|
table.insert(chunks, string.char(entryType)) -- entry type
|
||||||
|
table.insert(chunks, string.char(uid % 256, math.floor(uid/256) % 256)) -- uid
|
||||||
|
table.insert(chunks, string.char(gid % 256, math.floor(gid/256) % 256)) -- gid
|
||||||
|
table.insert(chunks, string.char(perms % 256, math.floor(perms/256) % 256)) -- perms
|
||||||
|
table.insert(chunks, string.char(cmetaLen)) -- cmeta length
|
||||||
|
if cmetaLen > 0 then
|
||||||
|
table.insert(chunks, cmeta)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(chunks)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function simpleFile(r,w)
|
||||||
|
return function(op, mode)
|
||||||
|
if op=="type" then
|
||||||
|
return "character device"
|
||||||
|
elseif op=="open" then
|
||||||
|
if mode=="r" then
|
||||||
|
return {
|
||||||
|
read=r
|
||||||
|
}
|
||||||
|
elseif mode=="w" then
|
||||||
|
return {
|
||||||
|
write=w
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function strFile(str)
|
||||||
|
local dat=tostring(str)
|
||||||
|
local pos=1
|
||||||
|
return simpleFile(function(amount)
|
||||||
|
pos=pos+amount
|
||||||
|
return dat:sub(pos-amount, pos)
|
||||||
|
end,function() error("EACCES") end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function newtaskproxy(task)
|
||||||
|
local files,siblings,children={},{},{}
|
||||||
|
if task.fd[0] then files["0"]=task.fd[0].path end
|
||||||
|
for i,v in ipairs(task.fd) do
|
||||||
|
files[tostring(i)]=tostring(v.path)
|
||||||
|
end
|
||||||
|
for i,v in ipairs(task.siblings) do
|
||||||
|
siblings[tostring(v.pid)]="/proc/"..tostring(v.pid)
|
||||||
|
end
|
||||||
|
for i,v in ipairs(task.children) do
|
||||||
|
children[tostring(v.pid)]="/proc/"..tostring(v.pid)
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
[".meta"]=strFile(buildMeta({cwd=task.cwd,parent="/proc/"..tostring(task.parent.pid)})),
|
||||||
|
uid=strFile(task.uid),
|
||||||
|
comm=strFile(task.name),
|
||||||
|
fd={
|
||||||
|
[".meta"]=strFile(buildMeta(files))
|
||||||
|
},
|
||||||
|
siblings={
|
||||||
|
[".meta"]=strFile(buildMeta(siblings))
|
||||||
|
},
|
||||||
|
children={
|
||||||
|
[".meta"]=strFile(buildMeta(children))
|
||||||
|
},
|
||||||
|
pid=strFile(task.pid)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:open(path, mode)
|
||||||
|
local steps = kernel.vfs.splitPath(path)
|
||||||
|
local step = data
|
||||||
|
if tonumber(steps[1]) then
|
||||||
|
local task=kernel.tasks[tostring(steps[1])]
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("open", mode)
|
||||||
|
end
|
||||||
|
elseif tostring(steps[1])=="self" then
|
||||||
|
local task=kernel.currentTask
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("open", mode)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i=1, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("open", mode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("ENFILE")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:type(path, mode)
|
||||||
|
local steps = kernel.vfs.splitPath(path)
|
||||||
|
local step = data
|
||||||
|
if #steps == 0 then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
if tonumber(steps[1]) then
|
||||||
|
local task=kernel.tasks[steps[1]]
|
||||||
|
if #steps==1 then return "directory" end
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("type", mode)
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
elseif tostring(steps[1])=="self" then
|
||||||
|
local task=kernel.currentTask
|
||||||
|
if #steps==1 then return "directory" end
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("type", mode)
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i=1, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENFILE") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "function" then
|
||||||
|
return step[steps[#steps]]("type", mode)
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return "directory"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("ENOENT")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:list(path)
|
||||||
|
local steps = kernel.vfs.splitPath(path)
|
||||||
|
local step = data
|
||||||
|
if #steps == 0 then
|
||||||
|
return table.merge(table.keys(data),table.keys(kernel.tasks),{"self"})
|
||||||
|
end
|
||||||
|
if tonumber(steps[1]) then
|
||||||
|
local task=kernel.tasks[steps[1]]
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
if #steps==1 then return table.keys(step) end
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENOENT") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return table.keys(step[steps[#steps]])
|
||||||
|
end
|
||||||
|
elseif tostring(steps[1])=="self" then
|
||||||
|
local task=kernel.currentTask
|
||||||
|
local step = newtaskproxy(task)
|
||||||
|
if #steps==1 then return table.keys(step) end
|
||||||
|
for i=2, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENOENT") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return table.keys(step[steps[#steps]])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i=1, #steps-1 do
|
||||||
|
local dat = step[steps[i]]
|
||||||
|
if type(dat) ~= "table" then error("ENOENT") end
|
||||||
|
step=dat
|
||||||
|
end
|
||||||
|
if type(step[steps[#steps]]) == "table" then
|
||||||
|
return table.keys(step[steps[#steps]])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("ENOENT")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy:fileExists(path)
|
||||||
|
local ok = pcall(function()
|
||||||
|
return self:type(path)
|
||||||
|
end)
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
data.uptime=simpleFile(function()return tostring(kernel.EFI:getUptime())end,function()error("EACCES")end)
|
||||||
|
kernel.procfs={}
|
||||||
|
kernel.procfs.data=data
|
||||||
|
kernel.procfs.proxy=proxy
|
||||||
|
kernel.disks["procfs0000"]=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
|
||||||
540
Src/Hyperion-kernel/data/lib/modules/hyperion/13_loopdev.kmod
Normal file
540
Src/Hyperion-kernel/data/lib/modules/hyperion/13_loopdev.kmod
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
--:Minify:--
|
||||||
|
-- Loop device driver:
|
||||||
|
--
|
||||||
|
-- BIND (directory) - re-routes VFS calls into a host directory subtree.
|
||||||
|
-- Identical to the original behaviour.
|
||||||
|
--
|
||||||
|
-- IMAGE (*.hfs file) - mounts a Hyperion Filesystem Image. The image is
|
||||||
|
-- loaded entirely into memory; reads and writes operate
|
||||||
|
-- on the in-memory tree, so the image file is only
|
||||||
|
-- touched on attach/detach.
|
||||||
|
--
|
||||||
|
-- BHFS v1 - Binary Hyperion Filesystem Image format:
|
||||||
|
--
|
||||||
|
-- File header (8 bytes):
|
||||||
|
-- [0-3] magic: 0x42 0x48 0x46 0x53 ("BHFS")
|
||||||
|
-- [4] version: 0x01
|
||||||
|
-- [5] flags: bit0 = per-file deflate compression enabled
|
||||||
|
-- [6-7] reserved: 0x00 0x00
|
||||||
|
--
|
||||||
|
-- Records (repeated until END record):
|
||||||
|
-- [0] type: 0x01=file 0x02=dir 0x03=symlink 0xFF=end
|
||||||
|
-- [1-4] path_len (uint32 LE) - byte length of the path string
|
||||||
|
-- [5-8] raw_size (uint32 LE) - original uncompressed data size (0 for dirs)
|
||||||
|
-- [9-12] stored_size (uint32 LE) - bytes that follow in stream
|
||||||
|
-- (< raw_size means deflate-compressed;
|
||||||
|
-- = raw_size means stored as-is)
|
||||||
|
-- [13 .. 13+path_len-1] path bytes (no null terminator)
|
||||||
|
-- [.. +stored_size] data bytes
|
||||||
|
--
|
||||||
|
-- Dirs have raw_size=0, stored_size=0, zero data bytes.
|
||||||
|
-- Symlinks store the target path as data; stored_size == raw_size (no compression).
|
||||||
|
--
|
||||||
|
-- Syscalls:
|
||||||
|
-- id = syscall.losetup(path) attach dir OR .hfs image
|
||||||
|
-- id = syscall.losetup(path, true) force image mode
|
||||||
|
-- syscall.lodetach(id) detach (must be unmounted first)
|
||||||
|
-- tbl = syscall.lolist() {id -> {path,mode}, ...}
|
||||||
|
-- str = syscall.loimgcreate(srcdir) serialise VFS dir -> BHFS binary string
|
||||||
|
-- syscall.loimgwrite(str, dest) write BHFS string to a file (binary)
|
||||||
|
|
||||||
|
local kernel = ...
|
||||||
|
|
||||||
|
local _deflate = nil
|
||||||
|
local function getDeflate()
|
||||||
|
if _deflate == nil then
|
||||||
|
local ok, lib = pcall(require, "store.deflate")
|
||||||
|
_deflate = ok and lib or false
|
||||||
|
end
|
||||||
|
return _deflate or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pack32(n)
|
||||||
|
n = math.floor(n) % 4294967296
|
||||||
|
return string.char(
|
||||||
|
n % 256,
|
||||||
|
math.floor(n / 256) % 256,
|
||||||
|
math.floor(n / 65536) % 256,
|
||||||
|
math.floor(n / 16777216) % 256
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack32(s, i)
|
||||||
|
local a, b, c, d = s:byte(i, i + 3)
|
||||||
|
return (a or 0)
|
||||||
|
+ (b or 0) * 256
|
||||||
|
+ (c or 0) * 65536
|
||||||
|
+ (d or 0) * 16777216
|
||||||
|
end
|
||||||
|
|
||||||
|
local BHFS_MAGIC = "BHFS"
|
||||||
|
local BHFS_VERSION = "\001"
|
||||||
|
local BHFS_FLAG_COMPRESS = 1
|
||||||
|
|
||||||
|
local TYPE_FILE = "\001"
|
||||||
|
local TYPE_DIR = "\002"
|
||||||
|
local TYPE_LINK = "\003"
|
||||||
|
local TYPE_END = "\255"
|
||||||
|
|
||||||
|
local B64D = {}
|
||||||
|
do
|
||||||
|
local a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
for i = 1, #a do B64D[a:sub(i, i)] = i - 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function b64dec(s)
|
||||||
|
s = s:gsub("[^A-Za-z0-9+/=]", "")
|
||||||
|
local t, i = {}, 1
|
||||||
|
while i <= #s do
|
||||||
|
local c1 = B64D[s:sub(i, i )] or 0
|
||||||
|
local c2 = B64D[s:sub(i+1, i+1)] or 0
|
||||||
|
local c3 = B64D[s:sub(i+2, i+2)] or 0
|
||||||
|
local c4 = B64D[s:sub(i+3, i+3)] or 0
|
||||||
|
local n = c1*262144 + c2*4096 + c3*64 + c4
|
||||||
|
t[#t+1] = string.char(math.floor(n/65536) % 256)
|
||||||
|
if s:sub(i+2, i+2) ~= "=" then t[#t+1] = string.char(math.floor(n/256) % 256) end
|
||||||
|
if s:sub(i+3, i+3) ~= "=" then t[#t+1] = string.char(n % 256) end
|
||||||
|
i = i + 4
|
||||||
|
end
|
||||||
|
return table.concat(t)
|
||||||
|
end
|
||||||
|
|
||||||
|
local loopDevs = {}
|
||||||
|
local nextLoop = 0
|
||||||
|
|
||||||
|
local function makeBindDisk(id, dirPath)
|
||||||
|
local disk = { address = id, isvirt = false }
|
||||||
|
disk.isReadOnly = function() return false end
|
||||||
|
disk.spaceUsed = function() return 0 end
|
||||||
|
disk.spaceTotal = function() return 0 end
|
||||||
|
disk.setLabel = function() end
|
||||||
|
disk.getLabel = function() return id end
|
||||||
|
|
||||||
|
local function resolveBase()
|
||||||
|
local mp, mid = "/", "$"
|
||||||
|
for id2, m in pairs(kernel.vfs.mounts) do
|
||||||
|
if dirPath == m or (m == "/" and dirPath:sub(1,1) == "/")
|
||||||
|
or dirPath:sub(1, #m+1) == m.."/" then
|
||||||
|
if #m > #mp then mp = m; mid = id2 end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return kernel.vfs.disks[mid], dirPath:sub(#mp+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dp(path)
|
||||||
|
local hd, base = resolveBase()
|
||||||
|
local b = (base == "" or base == "/") and "" or base:gsub("^/+","")
|
||||||
|
local p = path:gsub("^/+","")
|
||||||
|
local c = ((b=="") and "/"..p or "/"..b.."/"..p):gsub("//+","/")
|
||||||
|
local r = c:sub(2); if r == "" then r = "/" end
|
||||||
|
return hd, r
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:open(path,mode) local h,r=dp(path); return h:open(r,mode) end
|
||||||
|
function disk:type(path) local h,r=dp(path); return h:type(r) end
|
||||||
|
function disk:list(path) local h,r=dp(path); return h:list(r) end
|
||||||
|
function disk:fileExists(path) local h,r=dp(path); return h:fileExists(r) end
|
||||||
|
function disk:attributes(path) local h,r=dp(path); return h:attributes(r) end
|
||||||
|
function disk:makeDirectory(path) local h,r=dp(path); return h:makeDirectory(r) end
|
||||||
|
function disk:remove(path) local h,r=dp(path); return h:remove(r) end
|
||||||
|
return disk
|
||||||
|
end
|
||||||
|
|
||||||
|
local function makeImageDisk(id, imageStr)
|
||||||
|
local root = { kind="dir", children={} }
|
||||||
|
|
||||||
|
local function getNode(path, create)
|
||||||
|
local parts = {}
|
||||||
|
for p in path:gmatch("[^/]+") do parts[#parts+1] = p end
|
||||||
|
local node = root
|
||||||
|
for i = 1, #parts do
|
||||||
|
local name = parts[i]
|
||||||
|
if not node.children then
|
||||||
|
if not create then return nil end
|
||||||
|
node.children = {}
|
||||||
|
end
|
||||||
|
if not node.children[name] then
|
||||||
|
if not create then return nil end
|
||||||
|
node.children[name] = { kind="dir", children={} }
|
||||||
|
end
|
||||||
|
node = node.children[name]
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensureParent(path)
|
||||||
|
local par = path:match("^(.*)/[^/]+$") or ""
|
||||||
|
if par ~= "" then
|
||||||
|
local n = getNode(par, true)
|
||||||
|
if not n.children then n.children = {} end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if imageStr:sub(1, 4) == BHFS_MAGIC then
|
||||||
|
local pos = 9
|
||||||
|
|
||||||
|
while pos <= #imageStr do
|
||||||
|
local rtype = imageStr:sub(pos, pos)
|
||||||
|
pos = pos + 1
|
||||||
|
|
||||||
|
if rtype == TYPE_END then break end
|
||||||
|
|
||||||
|
local path_len = unpack32(imageStr, pos); pos = pos + 4
|
||||||
|
local raw_size = unpack32(imageStr, pos); pos = pos + 4
|
||||||
|
local stored_size = unpack32(imageStr, pos); pos = pos + 4
|
||||||
|
|
||||||
|
local path = imageStr:sub(pos, pos + path_len - 1)
|
||||||
|
pos = pos + path_len
|
||||||
|
|
||||||
|
local stored_data = imageStr:sub(pos, pos + stored_size - 1)
|
||||||
|
pos = pos + stored_size
|
||||||
|
local data = stored_data
|
||||||
|
if stored_size < raw_size then
|
||||||
|
local deflate = getDeflate()
|
||||||
|
if deflate then
|
||||||
|
data = deflate.decompress(stored_data) or stored_data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if rtype == TYPE_DIR then
|
||||||
|
if path ~= "" and path ~= "/" then
|
||||||
|
ensureParent(path)
|
||||||
|
local n = getNode(path, true)
|
||||||
|
n.kind = "dir"; n.children = n.children or {}
|
||||||
|
end
|
||||||
|
elseif rtype == TYPE_FILE then
|
||||||
|
ensureParent(path)
|
||||||
|
local n = getNode(path, true)
|
||||||
|
n.kind="file"; n.data=data; n.size=#data; n.children=nil
|
||||||
|
elseif rtype == TYPE_LINK then
|
||||||
|
ensureParent(path)
|
||||||
|
local n = getNode(path, true)
|
||||||
|
n.kind="link"; n.target=data; n.children=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for line in (imageStr.."\n"):gmatch("([^\n]*)\n") do
|
||||||
|
if line == "END" then
|
||||||
|
break
|
||||||
|
elseif line:sub(1,4) == "DIR " then
|
||||||
|
local p = line:sub(5):match("^%s*(.-)%s*$")
|
||||||
|
if p and p ~= "" and p ~= "/" then
|
||||||
|
ensureParent(p)
|
||||||
|
local n = getNode(p, true)
|
||||||
|
n.kind = "dir"; n.children = n.children or {}
|
||||||
|
end
|
||||||
|
elseif line:sub(1,5) == "FILE " then
|
||||||
|
local p, sz, body = line:sub(6):match("^(%S+)%s+(%d+)%s*(.-)%s*$")
|
||||||
|
if p then
|
||||||
|
ensureParent(p)
|
||||||
|
local data = (tonumber(sz) or 0) > 0 and b64dec(body) or ""
|
||||||
|
local n = getNode(p, true)
|
||||||
|
n.kind="file"; n.data=data; n.size=#data; n.children=nil
|
||||||
|
end
|
||||||
|
elseif line:sub(1,5) == "LINK " then
|
||||||
|
local p, tgt = line:sub(6):match("^(%S+)%s+(.+)$")
|
||||||
|
if p then
|
||||||
|
ensureParent(p)
|
||||||
|
local n = getNode(p, true)
|
||||||
|
n.kind="link"; n.target=tgt; n.children=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local disk = { address=id, isvirt=false }
|
||||||
|
disk.isReadOnly = function() return false end
|
||||||
|
disk.spaceTotal = function() return 1024*1024*64 end
|
||||||
|
disk.spaceUsed = function()
|
||||||
|
local tot = 0
|
||||||
|
local function w(n)
|
||||||
|
if n.kind=="file" then tot = tot + (n.size or 0)
|
||||||
|
elseif n.kind=="dir" then for _,c in pairs(n.children or {}) do w(c) end end
|
||||||
|
end
|
||||||
|
w(root); return tot
|
||||||
|
end
|
||||||
|
disk.setLabel = function() end
|
||||||
|
disk.getLabel = function() return id end
|
||||||
|
|
||||||
|
local function norm(path)
|
||||||
|
return path:gsub("^/+",""):gsub("/+$","")
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:type(path)
|
||||||
|
local p = norm(path)
|
||||||
|
if p == "" then return "directory" end
|
||||||
|
local n = getNode(p)
|
||||||
|
if not n then return nil end
|
||||||
|
if n.kind == "dir" then return "directory" end
|
||||||
|
return "file"
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:fileExists(path)
|
||||||
|
local p = norm(path)
|
||||||
|
if p == "" then return true end
|
||||||
|
return getNode(p) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:list(path)
|
||||||
|
local p = norm(path)
|
||||||
|
local node = (p=="") and root or getNode(p)
|
||||||
|
if not node or node.kind ~= "dir" then return {} end
|
||||||
|
local out = {}
|
||||||
|
for name in pairs(node.children or {}) do out[#out+1] = name end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:attributes(path)
|
||||||
|
local p = norm(path)
|
||||||
|
local node = (p=="") and root or getNode(p)
|
||||||
|
if not node then return nil end
|
||||||
|
return {
|
||||||
|
size = node.kind=="file" and (node.size or 0) or 0,
|
||||||
|
isDir = node.kind=="dir",
|
||||||
|
isReadOnly = false,
|
||||||
|
created = 0,
|
||||||
|
modified = 0,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:open(path, mode)
|
||||||
|
local p = norm(path)
|
||||||
|
local node = getNode(p)
|
||||||
|
|
||||||
|
if mode == "r" then
|
||||||
|
if not node or node.kind ~= "file" then error("ENOENT: "..path) end
|
||||||
|
local data, pos = node.data or "", 1
|
||||||
|
return {
|
||||||
|
read = function(n)
|
||||||
|
if pos > #data then return nil end
|
||||||
|
local chunk = data:sub(pos, pos + (n or 1) - 1)
|
||||||
|
pos = pos + #chunk; return chunk
|
||||||
|
end,
|
||||||
|
readAll = function()
|
||||||
|
local all = data:sub(pos); pos = #data + 1; return all
|
||||||
|
end,
|
||||||
|
readLine = function()
|
||||||
|
if pos > #data then return nil end
|
||||||
|
local nl = data:find("\n", pos, true)
|
||||||
|
local line
|
||||||
|
if nl then line=data:sub(pos, nl-1); pos=nl+1
|
||||||
|
else line=data:sub(pos); pos=#data+1 end
|
||||||
|
return line
|
||||||
|
end,
|
||||||
|
seek = function(w, o)
|
||||||
|
o = o or 0
|
||||||
|
if w == "set" then pos = o + 1
|
||||||
|
elseif w == "cur" then pos = pos + o
|
||||||
|
elseif w == "end" then pos = #data + 1 + o end
|
||||||
|
return pos - 1
|
||||||
|
end,
|
||||||
|
close = function() end,
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif mode == "w" or mode == "a" then
|
||||||
|
local buf = (mode=="a" and node and node.kind=="file")
|
||||||
|
and {node.data or ""} or {}
|
||||||
|
local done = false
|
||||||
|
local function commit()
|
||||||
|
if done then return end; done = true
|
||||||
|
local data = table.concat(buf)
|
||||||
|
if not node then ensureParent(p); node = getNode(p, true) end
|
||||||
|
node.kind="file"; node.data=data; node.size=#data; node.children=nil
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
write = function(s) buf[#buf+1] = tostring(s) end,
|
||||||
|
writeLine = function(s) buf[#buf+1] = tostring(s).."\n" end,
|
||||||
|
flush = function() end,
|
||||||
|
close = commit,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error("EINVAL: unknown mode: "..tostring(mode))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:makeDirectory(path)
|
||||||
|
local p = norm(path)
|
||||||
|
if p == "" then return end
|
||||||
|
ensureParent(p)
|
||||||
|
local n = getNode(p, true)
|
||||||
|
n.kind="dir"; n.children=n.children or {}; n.data=nil; n.size=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:remove(path)
|
||||||
|
local p = norm(path)
|
||||||
|
if p == "" then error("EBUSY: cannot remove root") end
|
||||||
|
local par = p:match("^(.*)/[^/]+$") or ""
|
||||||
|
local name = p:match("([^/]+)$")
|
||||||
|
local pn = (par=="") and root or getNode(par)
|
||||||
|
if pn and pn.children then pn.children[name] = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
disk._root = root
|
||||||
|
return disk
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serializeDir(srcPath)
|
||||||
|
local deflate = getDeflate()
|
||||||
|
local useCompress = deflate ~= nil
|
||||||
|
|
||||||
|
local flags = useCompress and BHFS_FLAG_COMPRESS or 0
|
||||||
|
local parts = {
|
||||||
|
BHFS_MAGIC,
|
||||||
|
BHFS_VERSION,
|
||||||
|
string.char(flags),
|
||||||
|
"\0\0",
|
||||||
|
}
|
||||||
|
|
||||||
|
srcPath = srcPath:gsub("/$", "")
|
||||||
|
|
||||||
|
local MIN_COMPRESS = 64
|
||||||
|
|
||||||
|
local function walk(vpath)
|
||||||
|
local ftype = kernel.vfs.type(vpath)
|
||||||
|
|
||||||
|
if ftype == "directory" then
|
||||||
|
if vpath ~= srcPath then
|
||||||
|
local relPath = vpath:sub(#srcPath + 1)
|
||||||
|
parts[#parts+1] = TYPE_DIR
|
||||||
|
parts[#parts+1] = pack32(#relPath)
|
||||||
|
parts[#parts+1] = pack32(0)
|
||||||
|
parts[#parts+1] = pack32(0)
|
||||||
|
parts[#parts+1] = relPath
|
||||||
|
end
|
||||||
|
local ok, entries = pcall(kernel.vfs.listdir, vpath)
|
||||||
|
if ok and entries then
|
||||||
|
table.sort(entries)
|
||||||
|
for _, name in ipairs(entries) do
|
||||||
|
walk(vpath:gsub("/$","").."/"..name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif ftype == "file" then
|
||||||
|
local relPath = vpath:sub(#srcPath + 1)
|
||||||
|
local ok, fd = pcall(kernel.vfs.open, vpath, "r")
|
||||||
|
if ok then
|
||||||
|
local rawData = ""
|
||||||
|
local ok2, content = pcall(kernel.vfs.read, fd, 1024*1024)
|
||||||
|
if ok2 then rawData = content or "" end
|
||||||
|
pcall(kernel.vfs.close, fd)
|
||||||
|
|
||||||
|
local storedData = rawData
|
||||||
|
if useCompress and #rawData >= MIN_COMPRESS then
|
||||||
|
local compressed = deflate.compress(rawData)
|
||||||
|
if compressed and #compressed < #rawData then
|
||||||
|
storedData = compressed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
parts[#parts+1] = TYPE_FILE
|
||||||
|
parts[#parts+1] = pack32(#relPath)
|
||||||
|
parts[#parts+1] = pack32(#rawData)
|
||||||
|
parts[#parts+1] = pack32(#storedData)
|
||||||
|
parts[#parts+1] = relPath
|
||||||
|
parts[#parts+1] = storedData
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
walk(srcPath)
|
||||||
|
|
||||||
|
parts[#parts+1] = TYPE_END
|
||||||
|
|
||||||
|
return table.concat(parts)
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.syscalls["losetup"] = function(filePath, forceImage)
|
||||||
|
if not filePath then error("EINVAL") end
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||||
|
if euid ~= 0 then error("EPERM") end
|
||||||
|
|
||||||
|
filePath = filePath:gsub("/$", "")
|
||||||
|
local id = "loop" .. tostring(nextLoop)
|
||||||
|
nextLoop = nextLoop + 1
|
||||||
|
|
||||||
|
local ftype = kernel.vfs.type(filePath)
|
||||||
|
local disk, mode
|
||||||
|
|
||||||
|
if not forceImage and ftype == "directory" then
|
||||||
|
disk = makeBindDisk(id, filePath)
|
||||||
|
mode = "bind"
|
||||||
|
elseif ftype == "file" or forceImage then
|
||||||
|
if ftype ~= "file" then error("ENOENT: not a file: "..filePath) end
|
||||||
|
|
||||||
|
local img
|
||||||
|
local ok, fd = pcall(kernel.vfs.open, filePath, "rb")
|
||||||
|
if ok then
|
||||||
|
local ok2, data = pcall(kernel.vfs.read, fd, 1024*1024*16)
|
||||||
|
pcall(kernel.vfs.close, fd)
|
||||||
|
if ok2 and data then img = data end
|
||||||
|
end
|
||||||
|
if not img then
|
||||||
|
local ok2, fd2 = pcall(kernel.vfs.open, filePath, "r")
|
||||||
|
if not ok2 then error("EIO: cannot open image: "..filePath) end
|
||||||
|
local ok3, data = pcall(kernel.vfs.read, fd2, 1024*1024*16)
|
||||||
|
pcall(kernel.vfs.close, fd2)
|
||||||
|
if not ok3 or not data then error("EIO: cannot read image: "..filePath) end
|
||||||
|
img = data
|
||||||
|
end
|
||||||
|
|
||||||
|
disk = makeImageDisk(id, img)
|
||||||
|
mode = "image"
|
||||||
|
else
|
||||||
|
error("EINVAL: path must be a directory or .hfs image file")
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.vfs.disks[id] = disk
|
||||||
|
loopDevs[id] = { path=filePath, disk=disk, mode=mode }
|
||||||
|
kernel.log("losetup: attached "..id.." ("..mode..") -> "..filePath, "INFO")
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.syscalls["lodetach"] = function(id)
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||||
|
if euid ~= 0 then error("EPERM") end
|
||||||
|
|
||||||
|
if not loopDevs[id] then error("ENXIO") end
|
||||||
|
for mid in pairs(kernel.vfs.mounts) do
|
||||||
|
if mid == id then error("EBUSY: loop device is still mounted") end
|
||||||
|
end
|
||||||
|
kernel.vfs.disks[id] = nil
|
||||||
|
loopDevs[id] = nil
|
||||||
|
kernel.log("lodetach: detached "..id, "INFO")
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.syscalls["lolist"] = function()
|
||||||
|
local rv = {}
|
||||||
|
for id, info in pairs(loopDevs) do
|
||||||
|
rv[id] = { path=info.path, mode=info.mode }
|
||||||
|
end
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.syscalls["loimgcreate"] = function(srcPath)
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||||
|
if euid ~= 0 then error("EPERM") end
|
||||||
|
if not srcPath then error("EINVAL") end
|
||||||
|
if kernel.vfs.type(srcPath) ~= "directory" then error("ENOTDIR: "..srcPath) end
|
||||||
|
return serializeDir(srcPath)
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.syscalls["loimgwrite"] = function(imgStr, destPath)
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||||
|
if euid ~= 0 then error("EPERM") end
|
||||||
|
if not imgStr or not destPath then error("EINVAL") end
|
||||||
|
|
||||||
|
local ok, fd = pcall(kernel.vfs.open, destPath, "wb")
|
||||||
|
if not ok then
|
||||||
|
ok, fd = pcall(kernel.vfs.open, destPath, "w")
|
||||||
|
if not ok then error("EIO: cannot write: "..tostring(destPath)) end
|
||||||
|
end
|
||||||
|
local ok2, werr = pcall(kernel.vfs.write, fd, imgStr)
|
||||||
|
pcall(kernel.vfs.close, fd)
|
||||||
|
if not ok2 then error("EIO: write failed: "..tostring(werr)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("Loop device driver loaded (bind + BHFS binary image + legacy HFS compat)")
|
||||||
35
Src/Hyperion-kernel/data/lib/modules/hyperion/19_fstab.kmod
Normal file
35
Src/Hyperion-kernel/data/lib/modules/hyperion/19_fstab.kmod
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
|
||||||
|
local function trim(str)
|
||||||
|
local s, e = 1, #str
|
||||||
|
while s <= e and (str:sub(s,s) == " " or str:sub(s,s) == "\t") do s = s + 1 end
|
||||||
|
while e >= s and (str:sub(e,e) == " " or str:sub(e,e) == "\t" or str:sub(e,e) == "\n" or str:sub(e,e) == "\r") do e = e - 1 end
|
||||||
|
if s > e then return "" end
|
||||||
|
return str:sub(s,e)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, line in ipairs(string.split(kernel.fstab, "\n")) do
|
||||||
|
line = trim(line)
|
||||||
|
if line ~= "" and line:sub(1,1) == "U" then
|
||||||
|
local semicolon_pos
|
||||||
|
for i = 3, #line do
|
||||||
|
if line:sub(i,i) == ";" then
|
||||||
|
semicolon_pos = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not semicolon_pos or semicolon_pos == 3 then
|
||||||
|
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 0xFF8800)
|
||||||
|
else
|
||||||
|
local id = line:sub(3, semicolon_pos - 1)
|
||||||
|
local path = trim(line:sub(semicolon_pos + 1))
|
||||||
|
kernel.log("Mounting '"..id.."' to '"..path.."'")
|
||||||
|
if id ~= "$" then
|
||||||
|
kernel.vfs.mount(path, id)
|
||||||
|
end
|
||||||
|
kernel.log("Mounted "..id.." to "..path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
local signal = {}
|
||||||
|
kernel.signal=signal
|
||||||
|
|
||||||
|
function signal.sigsend(pid, sig)
|
||||||
|
if sig<0 or sig>256 then error("EINVAL") end
|
||||||
|
local task = kernel.tasks[tostring(pid)]
|
||||||
|
if not task then error("ENOENT") end
|
||||||
|
if not task.sigq then return end
|
||||||
|
task.sigq[#task.sigq+1] = sig
|
||||||
|
end
|
||||||
|
|
||||||
|
function signal.sigcatch(handler)
|
||||||
|
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()
|
||||||
|
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
|
||||||
|
s["sigsend"] = signal.sigsend
|
||||||
|
s["sigcatch"] = signal.sigcatch
|
||||||
|
s["sigignore"] = signal.sigignore
|
||||||
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
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local args = {...}
|
||||||
|
local kernel = args[1]
|
||||||
|
kernel._G = _G
|
||||||
|
|
||||||
|
|
||||||
|
local function readonly(tbl)
|
||||||
|
return setmetatable({}, {
|
||||||
|
__index = function(_, key)
|
||||||
|
local value = tbl[key]
|
||||||
|
if type(value) == "table" then return readonly(value) end
|
||||||
|
return value
|
||||||
|
end,
|
||||||
|
|
||||||
|
__newindex = function(t, k, v)
|
||||||
|
if kernel.config.allowGlobalOverwrites or
|
||||||
|
kernel.allowGlobalOverwrites then
|
||||||
|
rawset(tbl, k, v)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
error("Attempt to modify global variable '" .. k .. "'", 2)
|
||||||
|
end,
|
||||||
|
|
||||||
|
__pairs = function(self)
|
||||||
|
local function iter(_, key)
|
||||||
|
local nextKey, value = next(tbl, key)
|
||||||
|
if type(value) == "table" then
|
||||||
|
value = readonly(value)
|
||||||
|
end
|
||||||
|
return nextKey, value
|
||||||
|
end
|
||||||
|
return iter, self, nil
|
||||||
|
end,
|
||||||
|
|
||||||
|
__ipairs = function()
|
||||||
|
local i = 0
|
||||||
|
return function()
|
||||||
|
i = i + 1
|
||||||
|
local value = tbl[i]
|
||||||
|
if value == nil then return end
|
||||||
|
if type(value) == "table" then
|
||||||
|
value = readonly(value)
|
||||||
|
end
|
||||||
|
return i, value
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
__len = function() return #tbl end,
|
||||||
|
|
||||||
|
__metatable = false
|
||||||
|
})
|
||||||
|
end
|
||||||
|
local origLoad = load
|
||||||
|
|
||||||
|
kernel._U = readonly(kernel._G)
|
||||||
|
kernel._U._G = kernel._U
|
||||||
|
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
|
||||||
|
|
||||||
|
function kernel.freshUserEnv()
|
||||||
|
local locals = {}
|
||||||
|
locals.syscall = _makeSyscallProxy()
|
||||||
|
|
||||||
|
local env = setmetatable(locals, {
|
||||||
|
__index = kernel._U,
|
||||||
|
__newindex = function(_, k, v) rawset(locals, k, v) end,
|
||||||
|
__metatable=false
|
||||||
|
})
|
||||||
|
|
||||||
|
locals._G = env
|
||||||
|
locals.load = function(a, b, c, d) return origLoad(a, b, c, d or env) end
|
||||||
|
|
||||||
|
return env
|
||||||
|
end
|
||||||
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
|
||||||
616
Src/Hyperion-kernel/data/lib/modules/hyperion/40_auth.kmod
Normal file
616
Src/Hyperion-kernel/data/lib/modules/hyperion/40_auth.kmod
Normal file
@@ -0,0 +1,616 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
local auth = {}
|
||||||
|
kernel.auth = auth
|
||||||
|
|
||||||
|
-- PASSWD FILE FORMAT: uid:gid:username:homedir:shell
|
||||||
|
-- SHADOW FILE FORMAT: uid:salt:hash
|
||||||
|
|
||||||
|
local function getFile(path)
|
||||||
|
local file = kernel.vfs.open(path, "r")
|
||||||
|
if not file then error("Failed to open file: " .. path) end
|
||||||
|
local content = kernel.vfs.read(file, 1024000)
|
||||||
|
kernel.vfs.close(file)
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
local function writeFile(path, content)
|
||||||
|
local file = kernel.vfs.open(path, "w")
|
||||||
|
if not file then error("Failed to open file for writing: " .. path) end
|
||||||
|
kernel.vfs.write(file, content)
|
||||||
|
kernel.vfs.close(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
local blake2s
|
||||||
|
do
|
||||||
|
local MOD32 = 2^32
|
||||||
|
local function norm(x) return x % MOD32 end
|
||||||
|
local function tobits(x)
|
||||||
|
x = norm(x)
|
||||||
|
local t = {}
|
||||||
|
for i = 0, 31 do local b = x % 2; t[i] = b; x = (x - b) / 2 end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
local function frombits(t)
|
||||||
|
local x, p = 0, 1
|
||||||
|
for i = 0, 31 do if t[i] == 1 then x = x + p end; p = p * 2 end
|
||||||
|
return norm(x)
|
||||||
|
end
|
||||||
|
local function bor(...)
|
||||||
|
local args = {...}
|
||||||
|
if #args == 0 then return 0 end
|
||||||
|
local bits = tobits(args[1])
|
||||||
|
for i = 2, #args do
|
||||||
|
local b = tobits(args[i])
|
||||||
|
for j = 0, 31 do bits[j] = (bits[j] == 1 or b[j] == 1) and 1 or 0 end
|
||||||
|
end
|
||||||
|
return frombits(bits)
|
||||||
|
end
|
||||||
|
local function bxor(...)
|
||||||
|
local args = {...}
|
||||||
|
if #args == 0 then return 0 end
|
||||||
|
local bits = tobits(args[1])
|
||||||
|
for i = 2, #args do
|
||||||
|
local b = tobits(args[i])
|
||||||
|
for j = 0, 31 do bits[j] = (bits[j] ~= b[j]) and 1 or 0 end
|
||||||
|
end
|
||||||
|
return frombits(bits)
|
||||||
|
end
|
||||||
|
local function lshift(x, n) return norm(norm(x) * 2^n) end
|
||||||
|
local function rshift(x, n) return math.floor(norm(x) / 2^n) end
|
||||||
|
local function rotr(x, n) return bor(rshift(x, n), lshift(x, 32 - n)) end
|
||||||
|
local IV = {
|
||||||
|
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||||
|
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||||
|
}
|
||||||
|
local SIGMA = {
|
||||||
|
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
|
||||||
|
{14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3},
|
||||||
|
{11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4},
|
||||||
|
{7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8},
|
||||||
|
{9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13},
|
||||||
|
{2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9},
|
||||||
|
{12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11},
|
||||||
|
{13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10},
|
||||||
|
{6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5},
|
||||||
|
{10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0}
|
||||||
|
}
|
||||||
|
local function G(v, a, b, c, d, x, y)
|
||||||
|
v[a] = (v[a] + v[b] + x) % MOD32
|
||||||
|
v[d] = rotr(bxor(v[d], v[a]), 16)
|
||||||
|
v[c] = (v[c] + v[d]) % MOD32
|
||||||
|
v[b] = rotr(bxor(v[b], v[c]), 12)
|
||||||
|
v[a] = (v[a] + v[b] + y) % MOD32
|
||||||
|
v[d] = rotr(bxor(v[d], v[a]), 8)
|
||||||
|
v[c] = (v[c] + v[d]) % MOD32
|
||||||
|
v[b] = rotr(bxor(v[b], v[c]), 7)
|
||||||
|
end
|
||||||
|
local function compress(h, block, t, last)
|
||||||
|
local v = {}
|
||||||
|
for i = 1, 8 do v[i] = h[i] end
|
||||||
|
for i = 1, 8 do v[i + 8] = IV[i] end
|
||||||
|
v[13] = bxor(v[13], t)
|
||||||
|
if last then v[15] = bxor(v[15], 0xFFFFFFFF) end
|
||||||
|
local m = {}
|
||||||
|
for i = 0, 15 do
|
||||||
|
local p = i * 4 + 1
|
||||||
|
m[i] = (block:byte(p) or 0)
|
||||||
|
+ ((block:byte(p+1) or 0) * 0x100)
|
||||||
|
+ ((block:byte(p+2) or 0) * 0x10000)
|
||||||
|
+ ((block:byte(p+3) or 0) * 0x1000000)
|
||||||
|
end
|
||||||
|
for r = 1, 10 do
|
||||||
|
local s = SIGMA[r]
|
||||||
|
G(v,1,5,9,13, m[s[1]], m[s[2]])
|
||||||
|
G(v,2,6,10,14, m[s[3]], m[s[4]])
|
||||||
|
G(v,3,7,11,15, m[s[5]], m[s[6]])
|
||||||
|
G(v,4,8,12,16, m[s[7]], m[s[8]])
|
||||||
|
G(v,1,6,11,16, m[s[9]], m[s[10]])
|
||||||
|
G(v,2,7,12,13, m[s[11]], m[s[12]])
|
||||||
|
G(v,3,8,9,14, m[s[13]], m[s[14]])
|
||||||
|
G(v,4,5,10,15, m[s[15]], m[s[16]])
|
||||||
|
end
|
||||||
|
for i = 1, 8 do h[i] = bxor(h[i], v[i], v[i+8]) end
|
||||||
|
end
|
||||||
|
function blake2s(msg, key)
|
||||||
|
key = key or ""
|
||||||
|
local h = {}
|
||||||
|
for i = 1, 8 do h[i] = IV[i] end
|
||||||
|
local outlen = 32
|
||||||
|
h[1] = bxor(h[1], 0x01010000 + lshift(#key, 8) + outlen)
|
||||||
|
local t = 0
|
||||||
|
if #key > 0 then
|
||||||
|
local block = key .. string.rep("\0", 64 - #key)
|
||||||
|
t = #key
|
||||||
|
compress(h, block, t, false)
|
||||||
|
end
|
||||||
|
for i = 1, #msg, 64 do
|
||||||
|
local block = msg:sub(i, i + 63)
|
||||||
|
if #block < 64 then block = block .. string.rep("\0", 64 - #block) end
|
||||||
|
t = t + math.min(64, #msg - i + 1)
|
||||||
|
compress(h, block, t, i + 64 > #msg)
|
||||||
|
end
|
||||||
|
local out = ""
|
||||||
|
for i = 1, 8 do out = out .. string.format("%08x", h[i]) end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not blake2s then error("Failed to load blake2s") end
|
||||||
|
|
||||||
|
if not kernel.vfs.exists("/etc/pam.d/secret") then
|
||||||
|
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT", "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")
|
||||||
|
kernel.vfs.write(handle, key)
|
||||||
|
kernel.vfs.close(handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
local pepper = getFile("/etc/pam.d/secret")
|
||||||
|
|
||||||
|
local function genSalt()
|
||||||
|
return toHex(math.random(0,2^32))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hashPassword(password, salt)
|
||||||
|
local key = (pepper .. salt)
|
||||||
|
return blake2s(password, key)
|
||||||
|
end
|
||||||
|
|
||||||
|
local passwdFile = getFile("/etc/passwd")
|
||||||
|
local shadowFile = getFile("/etc/shadow")
|
||||||
|
|
||||||
|
local passwdLines = string.split(passwdFile, "\n")
|
||||||
|
local shadowLines = string.split(shadowFile, "\n")
|
||||||
|
|
||||||
|
local passwd, shadow = {}, {}
|
||||||
|
for _, v in ipairs(passwdLines) do
|
||||||
|
local fields = string.split(v, ":")
|
||||||
|
if fields[1] and fields[1] ~= "" then
|
||||||
|
passwd[#passwd + 1] = fields
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, v in ipairs(shadowLines) do
|
||||||
|
local fields = string.split(v, ":")
|
||||||
|
if fields[1] and fields[1] ~= "" then
|
||||||
|
shadow[#shadow + 1] = fields
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(passwd) do
|
||||||
|
local uid = tonumber(v[1])
|
||||||
|
if uid then kernel.users[uid] = v[3] end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.passwd = passwd
|
||||||
|
|
||||||
|
local function flushPasswd()
|
||||||
|
local lines = {}
|
||||||
|
for _, v in ipairs(passwd) do
|
||||||
|
lines[#lines + 1] = table.concat(v, ":")
|
||||||
|
end
|
||||||
|
writeFile("/etc/passwd", table.concat(lines, "\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function flushShadow()
|
||||||
|
local lines = {}
|
||||||
|
for _, v in ipairs(shadow) do
|
||||||
|
lines[#lines + 1] = table.concat(v, ":")
|
||||||
|
end
|
||||||
|
writeFile("/etc/shadow", table.concat(lines, "\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getPasswdByUID(uid)
|
||||||
|
for _, v in ipairs(passwd) do
|
||||||
|
if tonumber(v[1]) == uid then return v end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getShadowByUID(uid)
|
||||||
|
for _, v in ipairs(shadow) do
|
||||||
|
if tonumber(v[1]) == uid then return v end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getPasswdByUsername(username)
|
||||||
|
for _, v in ipairs(passwd) do
|
||||||
|
if v[3] == username then return v end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function nextUID()
|
||||||
|
local max = 999
|
||||||
|
for _, v in ipairs(passwd) do
|
||||||
|
local uid = tonumber(v[1])
|
||||||
|
if uid and uid >= 1000 and uid > max then max = uid end
|
||||||
|
end
|
||||||
|
return max + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.login(uid, password)
|
||||||
|
if type(uid) ~= "number" or type(password) ~= "string" then
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then
|
||||||
|
-- timing attack resistance
|
||||||
|
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local sEntry = getShadowByUID(uid)
|
||||||
|
if not sEntry then
|
||||||
|
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local salt = sEntry[2]
|
||||||
|
local storedHash = sEntry[3]
|
||||||
|
|
||||||
|
local computed = hashPassword(password, salt)
|
||||||
|
if computed ~= storedHash then
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.currentUID = uid
|
||||||
|
|
||||||
|
local _task = kernel.currentTask
|
||||||
|
if _task then
|
||||||
|
_task.uid = uid
|
||||||
|
_task.euid = uid
|
||||||
|
_task.gid = tonumber(entry[2]) or uid
|
||||||
|
_task.egid = tonumber(entry[2]) or uid
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. getPasswdByUID(uid)[3] .. ")")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.setPassword(uid, newPassword)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
|
||||||
|
or kernel.currentUID or 0
|
||||||
|
|
||||||
|
if callerUID ~= 0 and callerUID ~= uid then
|
||||||
|
return nil, "Permission denied"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(newPassword) ~= "string" or #newPassword == 0 then
|
||||||
|
return nil, "Password may not be empty"
|
||||||
|
end
|
||||||
|
|
||||||
|
if #newPassword < 6 then
|
||||||
|
return nil, "Password is too short (minimum 6 characters)"
|
||||||
|
end
|
||||||
|
|
||||||
|
local salt = genSalt()
|
||||||
|
local hash = hashPassword(newPassword, salt)
|
||||||
|
|
||||||
|
local sEntry = getShadowByUID(uid)
|
||||||
|
if sEntry then
|
||||||
|
sEntry[2] = salt
|
||||||
|
sEntry[3] = hash
|
||||||
|
else
|
||||||
|
shadow[#shadow + 1] = { tostring(uid), salt, hash }
|
||||||
|
end
|
||||||
|
|
||||||
|
flushShadow()
|
||||||
|
kernel.log("AUTH: password changed for uid=" .. tostring(uid))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.setUsername(uid, newUsername)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
|
||||||
|
or kernel.currentUID or 0
|
||||||
|
|
||||||
|
if callerUID ~= 0 then
|
||||||
|
return nil, "Permission denied (root only)"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(newUsername) ~= "string" or #newUsername == 0 then
|
||||||
|
return nil, "Invalid username"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not newUsername:match("^[a-z_][a-z0-9_%-]*$") or #newUsername > 32 then
|
||||||
|
return nil, "Invalid username format"
|
||||||
|
end
|
||||||
|
|
||||||
|
if getPasswdByUsername(newUsername) then
|
||||||
|
return nil, "Username already taken"
|
||||||
|
end
|
||||||
|
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then return nil, "No such user" end
|
||||||
|
|
||||||
|
local oldName = entry[3]
|
||||||
|
entry[3] = newUsername
|
||||||
|
kernel.users[uid] = newUsername
|
||||||
|
|
||||||
|
flushPasswd()
|
||||||
|
kernel.log("AUTH: uid=" .. tostring(uid) .. " renamed '" .. oldName .. "' → '" .. newUsername .. "'")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.newUser(username, password, gid, homedir, shell)
|
||||||
|
local callerUID = (kernel.currentProcess and kernel.currentProcess.euid)
|
||||||
|
or kernel.currentUID or 0
|
||||||
|
|
||||||
|
if callerUID ~= 0 then
|
||||||
|
return nil, "Permission denied (root only)"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(username) ~= "string" or #username == 0 then
|
||||||
|
return nil, "Invalid username"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not username:match("^[a-z_][a-z0-9_%-]*$") or #username > 32 then
|
||||||
|
return nil, "Invalid username format"
|
||||||
|
end
|
||||||
|
|
||||||
|
if getPasswdByUsername(username) then
|
||||||
|
return nil, "Username already exists"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(password) ~= "string" or #password < 6 then
|
||||||
|
return nil, "Password is too short (minimum 6 characters)"
|
||||||
|
end
|
||||||
|
|
||||||
|
local uid = nextUID()
|
||||||
|
gid = tonumber(gid) or uid
|
||||||
|
homedir = homedir or ("/home/" .. username)
|
||||||
|
shell = shell or "/bin/hysh"
|
||||||
|
|
||||||
|
passwd[#passwd + 1] = {
|
||||||
|
tostring(uid),
|
||||||
|
tostring(gid),
|
||||||
|
username,
|
||||||
|
homedir,
|
||||||
|
shell
|
||||||
|
}
|
||||||
|
kernel.users[uid] = username
|
||||||
|
|
||||||
|
local salt = genSalt()
|
||||||
|
local hash = hashPassword(password, salt)
|
||||||
|
shadow[#shadow + 1] = { tostring(uid), salt, hash }
|
||||||
|
|
||||||
|
flushPasswd()
|
||||||
|
flushShadow()
|
||||||
|
|
||||||
|
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
|
||||||
|
kernel.vfs.mkdir(homedir)
|
||||||
|
-- Homedir must be owned by the new user, not root
|
||||||
|
pcall(kernel.vfs.chown, homedir, uid, uid)
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
|
||||||
|
return uid
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.whoami()
|
||||||
|
local uid = (kernel.currentProcess and kernel.currentProcess.euid)
|
||||||
|
or kernel.currentUID
|
||||||
|
if not uid then return nil, "Not logged in" end
|
||||||
|
return kernel.users[uid] or ("uid=" .. tostring(uid))
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.getUID(username)
|
||||||
|
local entry = getPasswdByUsername(username)
|
||||||
|
if entry then return tonumber(entry[1]) end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.getPasswd(uid)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then return nil end
|
||||||
|
return {
|
||||||
|
uid = tonumber(entry[1]),
|
||||||
|
gid = tonumber(entry[2]),
|
||||||
|
username = entry[3],
|
||||||
|
homedir = entry[4],
|
||||||
|
shell = entry[5],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.deleteUser(uid)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = kernel.uid or 0
|
||||||
|
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
||||||
|
if uid == 0 then return nil, "Cannot delete root" end
|
||||||
|
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then return nil, "No such user" end
|
||||||
|
local username = entry[3]
|
||||||
|
|
||||||
|
for i, v in ipairs(passwd) do
|
||||||
|
if tonumber(v[1]) == uid then table.remove(passwd, i); break end
|
||||||
|
end
|
||||||
|
for i, v in ipairs(shadow) do
|
||||||
|
if tonumber(v[1]) == uid then table.remove(shadow, i); break end
|
||||||
|
end
|
||||||
|
kernel.users[uid] = nil
|
||||||
|
|
||||||
|
flushPasswd()
|
||||||
|
flushShadow()
|
||||||
|
kernel.log("AUTH: deleted user '" .. username .. "' uid=" .. tostring(uid))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.lockUser(uid)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = kernel.uid or 0
|
||||||
|
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
||||||
|
if uid == 0 then return nil, "Cannot lock root" end
|
||||||
|
|
||||||
|
local sEntry = getShadowByUID(uid)
|
||||||
|
if not sEntry then return nil, "No shadow entry for uid" end
|
||||||
|
|
||||||
|
if sEntry[3]:sub(1,1) ~= "!" then
|
||||||
|
sEntry[3] = "!" .. sEntry[3]
|
||||||
|
end
|
||||||
|
flushShadow()
|
||||||
|
kernel.log("AUTH: locked uid=" .. tostring(uid))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.unlockUser(uid)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = kernel.uid or 0
|
||||||
|
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
||||||
|
|
||||||
|
local sEntry = getShadowByUID(uid)
|
||||||
|
if not sEntry then return nil, "No shadow entry for uid" end
|
||||||
|
|
||||||
|
if sEntry[3]:sub(1,1) == "!" then
|
||||||
|
sEntry[3] = sEntry[3]:sub(2)
|
||||||
|
end
|
||||||
|
flushShadow()
|
||||||
|
kernel.log("AUTH: unlocked uid=" .. tostring(uid))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.listUsers()
|
||||||
|
local result = {}
|
||||||
|
for _, v in ipairs(passwd) do
|
||||||
|
local uid = tonumber(v[1])
|
||||||
|
local sEntry = getShadowByUID(uid)
|
||||||
|
local locked = sEntry and sEntry[3]:sub(1,1) == "!"
|
||||||
|
result[#result+1] = {
|
||||||
|
uid = uid,
|
||||||
|
gid = tonumber(v[2]),
|
||||||
|
username = v[3],
|
||||||
|
homedir = v[4],
|
||||||
|
shell = v[5],
|
||||||
|
locked = locked or false,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.setShell(uid, shell)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = kernel.uid or 0
|
||||||
|
if callerUID ~= 0 and callerUID ~= uid then
|
||||||
|
return nil, "Permission denied"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(shell) ~= "string" or #shell == 0 then
|
||||||
|
return nil, "Invalid shell"
|
||||||
|
end
|
||||||
|
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then return nil, "No such user" end
|
||||||
|
entry[5] = shell
|
||||||
|
|
||||||
|
flushPasswd()
|
||||||
|
kernel.log("AUTH: uid=" .. tostring(uid) .. " shell -> " .. shell)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.setHomedir(uid, homedir)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
if not uid then return nil, "Invalid uid" end
|
||||||
|
|
||||||
|
local callerUID = kernel.uid or 0
|
||||||
|
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
||||||
|
|
||||||
|
if type(homedir) ~= "string" or #homedir == 0 then
|
||||||
|
return nil, "Invalid homedir"
|
||||||
|
end
|
||||||
|
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then return nil, "No such user" end
|
||||||
|
entry[4] = homedir
|
||||||
|
|
||||||
|
flushPasswd()
|
||||||
|
kernel.log("AUTH: uid=" .. tostring(uid) .. " homedir -> " .. homedir)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.setGID(uid, gid)
|
||||||
|
uid = tonumber(uid)
|
||||||
|
gid = tonumber(gid)
|
||||||
|
if not uid or not gid then return nil, "Invalid uid or gid" end
|
||||||
|
|
||||||
|
local callerUID = kernel.uid or 0
|
||||||
|
if callerUID ~= 0 then return nil, "Permission denied (root only)" end
|
||||||
|
|
||||||
|
local entry = getPasswdByUID(uid)
|
||||||
|
if not entry then return nil, "No such user" end
|
||||||
|
entry[2] = tostring(gid)
|
||||||
|
|
||||||
|
flushPasswd()
|
||||||
|
kernel.log("AUTH: uid=" .. tostring(uid) .. " gid -> " .. tostring(gid))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function auth.elevate(targetUsername, password)
|
||||||
|
if type(targetUsername) ~= "string" or type(password) ~= "string" then
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local entry = getPasswdByUsername(targetUsername)
|
||||||
|
if not entry then
|
||||||
|
hashPassword(password, "aaaaaaaaaaaaaaaa") -- timing resistance
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local uid = tonumber(entry[1])
|
||||||
|
local sEntry = getShadowByUID(uid)
|
||||||
|
if not sEntry then
|
||||||
|
hashPassword(password, "aaaaaaaaaaaaaaaa")
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local computed = hashPassword(password, sEntry[2])
|
||||||
|
if computed ~= sEntry[3] then
|
||||||
|
return nil, "Authentication failure"
|
||||||
|
end
|
||||||
|
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local prevUid = task.uid
|
||||||
|
task.uid = 0
|
||||||
|
task.euid = 0
|
||||||
|
task.gid = 0
|
||||||
|
task.egid = 0
|
||||||
|
kernel.uid = 0
|
||||||
|
|
||||||
|
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")")
|
||||||
|
return true, uid
|
||||||
|
end
|
||||||
|
|
||||||
|
if kernel.syscalls then
|
||||||
|
kernel.syscalls["login"] = auth.login
|
||||||
|
kernel.syscalls["setpassword"] = auth.setPassword
|
||||||
|
kernel.syscalls["setusername"] = auth.setUsername
|
||||||
|
kernel.syscalls["newuser"] = auth.newUser
|
||||||
|
kernel.syscalls["whoami"] = auth.whoami
|
||||||
|
kernel.syscalls["getuidbyname"]= auth.getUID
|
||||||
|
kernel.syscalls["getpasswd"] = auth.getPasswd
|
||||||
|
kernel.syscalls["elevate"] = auth.elevate
|
||||||
|
kernel.syscalls["deleteuser"] = auth.deleteUser
|
||||||
|
kernel.syscalls["lockuser"] = auth.lockUser
|
||||||
|
kernel.syscalls["unlockuser"] = auth.unlockUser
|
||||||
|
kernel.syscalls["listusers"] = auth.listUsers
|
||||||
|
kernel.syscalls["setshell"] = auth.setShell
|
||||||
|
kernel.syscalls["sethomedir"] = auth.setHomedir
|
||||||
|
kernel.syscalls["setgid"] = auth.setGID
|
||||||
|
end
|
||||||
571
Src/Hyperion-kernel/data/lib/modules/hyperion/45_hypervisor.kmod
Normal file
571
Src/Hyperion-kernel/data/lib/modules/hyperion/45_hypervisor.kmod
Normal file
@@ -0,0 +1,571 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
local tasks = {}
|
||||||
|
local sys = {}
|
||||||
|
local nextpid = 2
|
||||||
|
kernel.exitMain = false
|
||||||
|
|
||||||
|
local resumeWithTimeout = coroutine.resumeWithTimeout
|
||||||
|
|
||||||
|
local function bit_is_set(num, bit)
|
||||||
|
return math.floor(num / (2 ^ bit)) % 2 == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function loadExecutable(path)
|
||||||
|
kernel.vfs.access(path, "rx")
|
||||||
|
|
||||||
|
local fd = kernel.vfs.open(path, "r")
|
||||||
|
local data = kernel.vfs.read(fd, 1024 * 1024 * 4)
|
||||||
|
kernel.vfs.close(fd)
|
||||||
|
|
||||||
|
local env = kernel.freshUserEnv()
|
||||||
|
|
||||||
|
local func, err = load(data, "@" .. path, "t", env)
|
||||||
|
if not func then kernel.log("Failed to load executable: " .. tostring(err).. " : ".. tostring(path)); error("ENOEXEC: " .. tostring(err)) end
|
||||||
|
|
||||||
|
local meta = kernel.vfs.lstat(path)
|
||||||
|
local suid_set = bit_is_set(meta.perms, 6)
|
||||||
|
local caller_uid = kernel.currentTask and kernel.currentTask.uid or kernel.uid
|
||||||
|
local euid = suid_set and meta.owner or caller_uid
|
||||||
|
|
||||||
|
return func, euid, suid_set
|
||||||
|
end
|
||||||
|
|
||||||
|
local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
|
||||||
|
local id = nextpid
|
||||||
|
nextpid = nextpid + 1
|
||||||
|
|
||||||
|
tasks[tostring(id)] = {
|
||||||
|
coro = coroutine.create(function()
|
||||||
|
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
|
||||||
|
|
||||||
|
if kernel.config.logTaskExit then
|
||||||
|
if not ok then
|
||||||
|
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), "DBUG", 0x00FFFF)
|
||||||
|
else
|
||||||
|
kernel.log("Task " .. tostring(id) .. " exited without code", "DBUG", 0x00FFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(err) == "number" then
|
||||||
|
tasks[tostring(id)].exit = err
|
||||||
|
end
|
||||||
|
|
||||||
|
if tasks[tostring(id)].fd then
|
||||||
|
for fd, _ in pairs(tasks[tostring(id)].fd) do
|
||||||
|
pcall(kernel.vfs.close, fd)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tasks[tostring(id)].status = "Z"
|
||||||
|
end),
|
||||||
|
|
||||||
|
name = name or ("task" .. tostring(id)),
|
||||||
|
envars = envars or (kernel.currentTask and kernel.currentTask.envars or {}),
|
||||||
|
args = args or {},
|
||||||
|
status = "R",
|
||||||
|
pid = id,
|
||||||
|
tgid = tgid or (kernel.currentTask and kernel.currentTask.tgid or id),
|
||||||
|
uid = real_uid,
|
||||||
|
euid = eff_uid,
|
||||||
|
gid = (kernel.currentTask and kernel.currentTask.gid) or 0,
|
||||||
|
groups = (kernel.currentTask and kernel.currentTask.groups) or {},
|
||||||
|
fd = {},
|
||||||
|
sleep = 0,
|
||||||
|
ivs = 0,
|
||||||
|
vs = 0,
|
||||||
|
children = {},
|
||||||
|
parent = kernel.currentTask or kernel.kernelTask,
|
||||||
|
siblings = (kernel.currentTask and kernel.currentTask.children) or kernel.kernelTask.children,
|
||||||
|
syscallReturn = {},
|
||||||
|
cwd = (kernel.currentTask and kernel.currentTask.cwd) or "/",
|
||||||
|
timeSlice = 0,
|
||||||
|
lastTime = 0,
|
||||||
|
totalTime = 0,
|
||||||
|
numRuns = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(
|
||||||
|
(kernel.currentTask and kernel.currentTask.children) or kernel.kernelTask.children,
|
||||||
|
tasks[tostring(id)]
|
||||||
|
)
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.spawn(func, name, envars, args, tgid)
|
||||||
|
local caller = kernel.currentTask
|
||||||
|
local real_uid = caller and caller.uid or kernel.uid
|
||||||
|
local eff_uid = caller and caller.euid or real_uid
|
||||||
|
return createTask(func, name, envars, args, tgid, real_uid, eff_uid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.execspawn(path, name, envars, args, tgid)
|
||||||
|
local func, euid, suid_active = loadExecutable(path, kernel._U)
|
||||||
|
|
||||||
|
local caller = kernel.currentTask
|
||||||
|
local real_uid = caller and caller.uid or kernel.uid
|
||||||
|
|
||||||
|
if suid_active then
|
||||||
|
kernel.log(
|
||||||
|
"execspawn: suid exec '" .. path ..
|
||||||
|
"' caller_uid=" .. tostring(real_uid) ..
|
||||||
|
" -> euid=" .. tostring(euid)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return createTask(func, name or path, envars, args, tgid, real_uid, euid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.exec(path, args, envars)
|
||||||
|
local task = kernel.currentTask
|
||||||
|
local func, euid, _ = loadExecutable(path, kernel._U)
|
||||||
|
|
||||||
|
if task.fd then
|
||||||
|
for fd, _ in pairs(task.fd) do
|
||||||
|
if fd > 2 then pcall(kernel.vfs.close, fd) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
task.euid = euid
|
||||||
|
task.args = args or {}
|
||||||
|
task.envars = envars or task.envars
|
||||||
|
task.name = path
|
||||||
|
|
||||||
|
task.coro = coroutine.create(function()
|
||||||
|
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", 0xFF0000)
|
||||||
|
else
|
||||||
|
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
|
||||||
|
if tasks[tostring(task.pid)].fd then
|
||||||
|
for fd, _ in pairs(tasks[tostring(task.pid)].fd) do
|
||||||
|
pcall(kernel.vfs.close, fd)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tasks[tostring(task.pid)].status = "Z"
|
||||||
|
end)
|
||||||
|
task.syscallReturn = {}
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.sleep(s)
|
||||||
|
kernel.currentTask.status = "S"
|
||||||
|
kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.getTask(pid)
|
||||||
|
local task = tasks[tostring(pid)]
|
||||||
|
if not task then return nil end
|
||||||
|
|
||||||
|
local children, siblings = {}, {}
|
||||||
|
for i, v in ipairs(task.children) do children[i] = v.pid end
|
||||||
|
for i, v in ipairs(task.siblings) do siblings[i] = v.pid end
|
||||||
|
|
||||||
|
return {
|
||||||
|
name = task.name,
|
||||||
|
status = task.status,
|
||||||
|
pid = task.pid,
|
||||||
|
tgid = task.tgid,
|
||||||
|
username = kernel.users[task.uid],
|
||||||
|
uid = task.uid,
|
||||||
|
euid = task.euid,
|
||||||
|
exit = task.exit,
|
||||||
|
sleep = task.sleep,
|
||||||
|
ivs = task.ivs,
|
||||||
|
vs = task.vs,
|
||||||
|
children = children,
|
||||||
|
siblings = siblings,
|
||||||
|
parent = task.parent.pid,
|
||||||
|
cwd = task.cwd,
|
||||||
|
term = task.term,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.collect(pid)
|
||||||
|
local children = {}
|
||||||
|
for _, v in ipairs(kernel.currentTask.children) do children[#children+1] = v.pid end
|
||||||
|
|
||||||
|
local task = tasks[tostring(pid)]
|
||||||
|
if not task then
|
||||||
|
return false, "Task does not exist"
|
||||||
|
elseif not isEqualToAny(task.pid, table.unpack(children)) then
|
||||||
|
return false, "You do not own this task"
|
||||||
|
elseif task.status ~= "Z" then
|
||||||
|
return false, "Task must exit to collect status"
|
||||||
|
else
|
||||||
|
task.reapTime = 0
|
||||||
|
return true, task.exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
if ceuid ~= 0 and task.uid ~= (caller and caller.uid or kernel.uid) then
|
||||||
|
return false, "EPERM"
|
||||||
|
end
|
||||||
|
task.status = "Z"
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.stop(pid)
|
||||||
|
local task = tasks[tostring(pid)]
|
||||||
|
if not task then
|
||||||
|
return false, "Task does not exist"
|
||||||
|
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
|
||||||
|
if task.status == "S" then
|
||||||
|
task.status = "ST"
|
||||||
|
else
|
||||||
|
task.status = "T"
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.continue(pid)
|
||||||
|
local task = tasks[tostring(pid)]
|
||||||
|
if not task then
|
||||||
|
return false, "Task does not exist"
|
||||||
|
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
|
||||||
|
if task.status == "ST" then
|
||||||
|
task.status = "S"
|
||||||
|
else
|
||||||
|
task.status = "R"
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.getpid() return kernel.currentTask.pid end
|
||||||
|
function sys.getppid() return kernel.currentTask.parent.pid end
|
||||||
|
|
||||||
|
function sys.getTasks()
|
||||||
|
local ret = {}
|
||||||
|
for _, v in pairs(tasks) do ret[#ret+1] = v.pid end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.getEnviron(key) return kernel.currentTask.envars[key] end
|
||||||
|
function sys.setEnviron(key, val) kernel.currentTask.envars[key] = val end
|
||||||
|
|
||||||
|
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), "DBUG", 0x00FFFF)
|
||||||
|
else
|
||||||
|
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "DBUG", 0x00FFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tasks[tostring(task.pid)].status = "Z"
|
||||||
|
if type(code) == "number" then
|
||||||
|
tasks[tostring(task.pid)].exit = code
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.setuid(uid)
|
||||||
|
local task = kernel.currentTask
|
||||||
|
if task.euid ~= 0 and task.uid ~= uid then
|
||||||
|
error("EPERM")
|
||||||
|
end
|
||||||
|
task.uid = uid
|
||||||
|
task.euid = uid
|
||||||
|
kernel.uid = uid
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.geteuid()
|
||||||
|
return kernel.currentTask.euid
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
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.EFI:getEpochMs() + 30000
|
||||||
|
|
||||||
|
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"]
|
||||||
|
child.siblings = tasks["1"].children
|
||||||
|
table.insert(tasks["1"].children, child)
|
||||||
|
end
|
||||||
|
for i, sibling in ipairs(task.siblings) do
|
||||||
|
if sibling.pid == task.pid then
|
||||||
|
table.remove(task.siblings, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tasks[pid] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local alpha = 0.85
|
||||||
|
local C_target = 0.01
|
||||||
|
local Tmin = 0.0005
|
||||||
|
local Tmax = 0.5
|
||||||
|
local lambda_budget = 0.08
|
||||||
|
local lambda_clamp = 0.03
|
||||||
|
local lambda_var = 0.02
|
||||||
|
local k_min = 0.5
|
||||||
|
local k_max = 0.5
|
||||||
|
local B = 0.01
|
||||||
|
|
||||||
|
function kernel.main()
|
||||||
|
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
|
||||||
|
local totalTaskTime = 0
|
||||||
|
local taskTimes = {}
|
||||||
|
|
||||||
|
for pid, task in pairs(tasks) do
|
||||||
|
if kernel.exitMain then break end
|
||||||
|
kernel.currentTask = task
|
||||||
|
kernel.uid = task.euid or task.uid
|
||||||
|
kernel.process = task.name
|
||||||
|
|
||||||
|
if task.status == "S" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||||
|
task.status = "R"
|
||||||
|
task.sleep = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if task.status == "ST" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||||
|
task.status = "T"
|
||||||
|
task.sleep = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
|
||||||
|
task.status = "D"
|
||||||
|
task.sleep = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if task.status == "D" then
|
||||||
|
if task.ksh then
|
||||||
|
coroutine.resume(task.ksh)
|
||||||
|
if coroutine.status(task.ksh) == "dead" then
|
||||||
|
task.ksh = nil
|
||||||
|
if task.status == "D" then
|
||||||
|
task.status = "R"
|
||||||
|
end
|
||||||
|
|
||||||
|
if kernel.config.debugSyscalls then
|
||||||
|
kernel.log("Task " .. task.pid .. " IO wait completed", "DBUG", 0x00FFFF)
|
||||||
|
local sysret = task.syscallReturn
|
||||||
|
for i = 2, #sysret do
|
||||||
|
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
|
||||||
|
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if task.status == "R" then
|
||||||
|
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 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.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
|
||||||
|
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.EFI:getEpochMs() - startTime
|
||||||
|
task.lastTime = elapsed
|
||||||
|
task.totalTime = (task.totalTime or 0) + elapsed
|
||||||
|
task.numRuns = (task.numRuns or 0) + 1
|
||||||
|
|
||||||
|
taskTimes[#taskTimes+1] = elapsed
|
||||||
|
totalTaskTime = totalTaskTime + elapsed
|
||||||
|
|
||||||
|
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
|
||||||
|
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
|
||||||
|
|
||||||
|
if ret[1] == "error" or ret[1] == false then
|
||||||
|
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 0xFF0000)
|
||||||
|
task.status = "Z"
|
||||||
|
task.exit = "processHandlerException: " .. tostring(ret[2])
|
||||||
|
|
||||||
|
elseif ret[1] == "timeout" then
|
||||||
|
task.ivs = task.ivs + 1
|
||||||
|
task.syscallReturn = {}
|
||||||
|
|
||||||
|
elseif ret[1] == "success" or ret[1] == true then
|
||||||
|
task.vs = task.vs + 1
|
||||||
|
|
||||||
|
if ret[2] == "syscall" then
|
||||||
|
local scname = ret[3]
|
||||||
|
if kernel.syscalls[scname] then
|
||||||
|
if kernel.config.debugSyscalls then
|
||||||
|
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 0x00FFFF)
|
||||||
|
for i = 4, #ret do
|
||||||
|
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 0x00FFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local sysret = { xpcall(kernel.syscalls[scname], debug.traceback, table.unpack(ret, 4)) }
|
||||||
|
|
||||||
|
if kernel.config.debugSyscalls then
|
||||||
|
if not sysret[1] then
|
||||||
|
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", 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", 0x00FFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not sysret[1] then
|
||||||
|
task.syscallReturn = { false, sysret[2] }
|
||||||
|
else
|
||||||
|
task.syscallReturn = { true, table.unpack(sysret, 2) }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
task.syscallReturn = { false, "Unknown syscall: " .. tostring(scname) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
|
||||||
|
local T_prev_var = 0
|
||||||
|
for _, t in ipairs(taskTimes) do
|
||||||
|
T_prev_var = T_prev_var + (t - T_prev_avg) ^ 2
|
||||||
|
end
|
||||||
|
if N > 0 then T_prev_var = T_prev_var / N end
|
||||||
|
|
||||||
|
if N > 0 then
|
||||||
|
local f_clamp = k_min * (Tmin_hit / N) - k_max * (Tmax_hit / N)
|
||||||
|
local B_budget = (C_target * (N ^ (alpha - 1))) / math.max(T_prev_avg, 1e-8)
|
||||||
|
B = B + lambda_budget * (B_budget - B) + lambda_clamp * f_clamp - lambda_var * T_prev_var
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
local sysc = kernel.syscalls
|
||||||
|
sysc["spawn"] = sys.spawn
|
||||||
|
sysc["execspawn"] = sys.execspawn
|
||||||
|
sysc["exec"] = sys.exec
|
||||||
|
sysc["sleep"] = sys.sleep
|
||||||
|
sysc["getTask"] = sys.getTask
|
||||||
|
sysc["collect"] = sys.collect
|
||||||
|
sysc["kill"] = sys.kill
|
||||||
|
sysc["stop"] = sys.stop
|
||||||
|
sysc["continue"] = sys.continue
|
||||||
|
sysc["getpid"] = sys.getpid
|
||||||
|
sysc["getppid"] = sys.getppid
|
||||||
|
sysc["getTasks"] = sys.getTasks
|
||||||
|
sysc["setEnviron"] = sys.setEnviron
|
||||||
|
sysc["getEnviron"] = sys.getEnviron
|
||||||
|
sysc["exit"] = sys.exit
|
||||||
|
sysc["setuid"] = sys.setuid
|
||||||
|
sysc["getuid"] = sys.getuid
|
||||||
|
sysc["geteuid"] = sys.geteuid
|
||||||
|
|
||||||
|
kernel._G.sleep = function(...) coroutine.yield("syscall", "sleep", ...) end
|
||||||
|
|
||||||
|
kernel.tasks = tasks
|
||||||
|
kernel.hpv = sys
|
||||||
28
Src/Hyperion-kernel/data/lib/modules/hyperion/50_gpio.kmod
Normal file
28
Src/Hyperion-kernel/data/lib/modules/hyperion/50_gpio.kmod
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel=...
|
||||||
|
local sysc=kernel.syscalls
|
||||||
|
kernel.gpio={}
|
||||||
|
|
||||||
|
sysc["gpio_write"]=function(pin, data)
|
||||||
|
if kernel.gpio[pin] then
|
||||||
|
return kernel.gpio[pin]("w", data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
sysc["gpio_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
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
function print(...)
|
||||||
|
local args = {...}
|
||||||
|
local output = ""
|
||||||
|
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
||||||
|
output = output:sub(1, -2)
|
||||||
|
syscall.write(1, output.."\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function printf(fmt, ...)
|
||||||
|
local output = string.format(fmt, ...)
|
||||||
|
syscall.write(1, output.."\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function printInline(...)
|
||||||
|
local args = {...}
|
||||||
|
local output = ""
|
||||||
|
for i = 1, #args do output = output .. tostring(args[i]) .. "\t" end
|
||||||
|
output = output:sub(1, -2)
|
||||||
|
syscall.write(1, output)
|
||||||
|
end
|
||||||
54
Src/Hyperion-kernel/data/lib/modules/hyperion/90_init.kmod
Normal file
54
Src/Hyperion-kernel/data/lib/modules/hyperion/90_init.kmod
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
kernel.log("Loading init system...")
|
||||||
|
kernel.log("InitPath: " .. kernel.config.initPath)
|
||||||
|
|
||||||
|
local initOk, initErr = pcall(kernel.vfs.access, kernel.config.initPath, "rx")
|
||||||
|
if not initOk then
|
||||||
|
kernel.PANIC("Init binary not executable: " .. kernel.config.initPath .. " (" .. tostring(initErr) .. ")")
|
||||||
|
end
|
||||||
|
|
||||||
|
local handle = kernel.vfs.open(kernel.config.initPath, "r")
|
||||||
|
if not handle then kernel.panic("Failed to open "..kernel.config.initPath) end
|
||||||
|
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
|
||||||
|
if not handle then kernel.panic("Failed to read "..kernel.config.initPath) end
|
||||||
|
kernel.vfs.close(handle)
|
||||||
|
|
||||||
|
local initFunc, err = load(data, "@sysinit", "t", kernel._U)
|
||||||
|
if not initFunc then kernel.PANIC("Failed to load init system: " .. err) end
|
||||||
|
|
||||||
|
kernel.tasks["1"] = {
|
||||||
|
coro = coroutine.create(function()
|
||||||
|
local ok, err = xpcall(initFunc, debug.traceback, kernel)
|
||||||
|
if not ok then
|
||||||
|
kernel.panic("Init system crashed: " .. tostring(err))
|
||||||
|
else
|
||||||
|
kernel.panic("Init system exited: " .. tostring(err))
|
||||||
|
end
|
||||||
|
end),
|
||||||
|
|
||||||
|
name = "sysinit",
|
||||||
|
status = "R",
|
||||||
|
pid = 1,
|
||||||
|
tgid = 1,
|
||||||
|
uid = 0,
|
||||||
|
fd = {},
|
||||||
|
envars = {},
|
||||||
|
args = {},
|
||||||
|
exit = "",
|
||||||
|
sleep = 0,
|
||||||
|
ivs = 0,
|
||||||
|
vs = 0,
|
||||||
|
parent = kernel.kernelTask,
|
||||||
|
siblings = kernel.kernelTask.children,
|
||||||
|
children = {},
|
||||||
|
syscallReturn = {},
|
||||||
|
cwd = "/",
|
||||||
|
timeSlice = 0,
|
||||||
|
lastTime = 0,
|
||||||
|
totalTime = 0,
|
||||||
|
numRuns = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel.log("created init task with PID 1")
|
||||||
|
kernel.log("Initializing init system...")
|
||||||
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
|
||||||
10
Src/Hyperion-kernel/data/lib/modules/hyperion/91_login.kmod
Normal file
10
Src/Hyperion-kernel/data/lib/modules/hyperion/91_login.kmod
Normal file
@@ -0,0 +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", 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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local kernel = ...
|
||||||
|
kernel.allowGlobalOverwrites = false
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "HyperionOS",
|
|
||||||
"Version": "1.0.0",
|
|
||||||
"Publishers": ["HyperionOS Dev Team"],
|
|
||||||
"Description": "The install package for HyperionOS project.",
|
|
||||||
"Dependencies": [
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v1.0.0.pkg",
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v1.0.0.pkg",
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-ui-v1.0.0.pkg",
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-network-v1.0.0.pkg",
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-security-v1.0.0.pkg",
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-filesystem-v1.0.0.pkg",
|
|
||||||
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-bash-v1.0.0.pkg"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--:Minify:--
|
||||||
-- blake2s.lua
|
-- blake2s.lua
|
||||||
-- Pure Lua 5.2, 32-bit only, supports keyed hashing
|
-- Pure Lua 5.2, 32-bit only, supports keyed hashing
|
||||||
|
|
||||||
169
Src/coreutils/data/bin/login
Normal file
169
Src/coreutils/data/bin/login
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
--:Minify:--
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
local MAX_ATTEMPTS = 3
|
||||||
|
|
||||||
|
local function readLine(mask)
|
||||||
|
local input = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then
|
||||||
|
syscall.write(1, "\n")
|
||||||
|
return input
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #input > 0 then
|
||||||
|
input = input:sub(1, -2)
|
||||||
|
syscall.write(1, "\b \b")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
input = input .. ch
|
||||||
|
syscall.write(1, mask or ch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function firstBoot()
|
||||||
|
local shadow = ""
|
||||||
|
local _fd, _fderr = pcall(function()
|
||||||
|
local fd = syscall.open("/etc/shadow", "r")
|
||||||
|
shadow = syscall.read(fd, 65535) or ""
|
||||||
|
syscall.close(fd)
|
||||||
|
end)
|
||||||
|
if shadow:match("%S") then return end
|
||||||
|
|
||||||
|
syscall.devctl(1, "clear")
|
||||||
|
syscall.devctl(1, "spos", 1, 1)
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||||
|
syscall.write(1, "HyperionOS First Boot Setup\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
syscall.write(1, "No root password is set. Please create one now.\n\n")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
syscall.write(1, "New root password: ")
|
||||||
|
local pw1 = readLine("*")
|
||||||
|
syscall.write(1, "Confirm password: ")
|
||||||
|
local pw2 = readLine("*")
|
||||||
|
|
||||||
|
if pw1 ~= pw2 then
|
||||||
|
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||||
|
syscall.write(1, "Passwords do not match. Try again.\n\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
elseif #pw1 < 6 then
|
||||||
|
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||||
|
syscall.write(1, "Password too short (minimum 6 characters).\n\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
else
|
||||||
|
local ok, err = syscall.setpassword(0, pw1)
|
||||||
|
if ok then
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||||
|
syscall.write(1, "Root password set.\n\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
sleep(0.5)
|
||||||
|
break
|
||||||
|
else
|
||||||
|
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||||
|
syscall.write(1, "Error: " .. tostring(err) .. "\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function spawnShell(username, uid, shell, homedir)
|
||||||
|
local existsOk, existsErr = pcall(syscall.exists, shell)
|
||||||
|
if not existsOk or not existsErr then
|
||||||
|
syscall.write(1, "login: shell not found: " .. shell .. "\n")
|
||||||
|
sleep(2)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local accessOk, accessErr = pcall(syscall.access, shell, "rx")
|
||||||
|
|
||||||
|
syscall.setEnviron("HOME", homedir)
|
||||||
|
syscall.setEnviron("USER", username)
|
||||||
|
syscall.setEnviron("SHELL", shell)
|
||||||
|
syscall.setEnviron("PATH", "/bin/")
|
||||||
|
|
||||||
|
local setuidOk, setuidErr = pcall(syscall.setuid, uid)
|
||||||
|
if not setuidOk then
|
||||||
|
syscall.write(1, "login: setuid failed: " .. tostring(setuidErr) .. "\n")
|
||||||
|
sleep(2)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local chdirOk, chdirErr = pcall(syscall.chdir, homedir)
|
||||||
|
if not chdirOk then
|
||||||
|
pcall(syscall.chdir, "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = pcall(syscall.execspawn, shell, username .. ":shell")
|
||||||
|
if not ok then
|
||||||
|
syscall.write(1, "login: failed to launch shell: " .. tostring(err) .. "\n")
|
||||||
|
sleep(2)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
syscall.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function doLogin()
|
||||||
|
syscall.devctl(1, "clear")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
syscall.devctl(1, "sbgc", 0x000000)
|
||||||
|
syscall.devctl(1, "spos", 1, 1)
|
||||||
|
|
||||||
|
local hostname = syscall.getHostname() or "hyperion"
|
||||||
|
syscall.write(1, "HyperionOS\n")
|
||||||
|
syscall.write(1, hostname .. " login\n\n")
|
||||||
|
|
||||||
|
local attempts = 0
|
||||||
|
while attempts < MAX_ATTEMPTS do
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
syscall.write(1, "Username: ")
|
||||||
|
local username = readLine(nil)
|
||||||
|
|
||||||
|
if username ~= "" then
|
||||||
|
|
||||||
|
syscall.write(1, "Password: ")
|
||||||
|
local password = readLine("*")
|
||||||
|
local uid = syscall.getuidbyname(username)
|
||||||
|
|
||||||
|
local ok, err = syscall.login(uid, password)
|
||||||
|
if ok then
|
||||||
|
local uid = syscall.getuid()
|
||||||
|
local pwent = syscall.getpasswd(uid)
|
||||||
|
|
||||||
|
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||||
|
local homedir = (pwent and pwent.homedir) or "/"
|
||||||
|
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FF00)
|
||||||
|
syscall.write(1, "\nWelcome, " .. username .. "!\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
sleep(0.3)
|
||||||
|
|
||||||
|
spawnShell(username, uid, shell, homedir)
|
||||||
|
return -- back to login prompt
|
||||||
|
else
|
||||||
|
attempts = attempts + 1
|
||||||
|
sleep(1)
|
||||||
|
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||||
|
syscall.write(1, "Login incorrect.\n\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
syscall.devctl(1, "sfgc", 0xFF0000)
|
||||||
|
syscall.write(1, "Maximum login attempts exceeded.\n")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
sleep(5)
|
||||||
|
end
|
||||||
|
|
||||||
|
firstBoot()
|
||||||
|
while true do
|
||||||
|
doLogin()
|
||||||
|
end
|
||||||
19
Src/coreutils/data/bin/lsusers
Normal file
19
Src/coreutils/data/bin/lsusers
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local users = syscall.listusers()
|
||||||
|
if not users or #users == 0 then
|
||||||
|
print("No users found.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
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",0xFFFFFF)
|
||||||
|
|
||||||
|
for _, u in ipairs(users) do
|
||||||
|
local lock_marker = u.locked and " [locked]" or ""
|
||||||
|
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",0xFFFFFF) end
|
||||||
|
end
|
||||||
152
Src/coreutils/data/bin/mount
Normal file
152
Src/coreutils/data/bin/mount
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
--:Minify:--
|
||||||
|
-- Usage:
|
||||||
|
-- mount list all current mounts
|
||||||
|
-- mount <id> <mountpoint> mount loop device id at mountpoint
|
||||||
|
-- mount -o loop <src> <dest> attach <src> as loop device and mount at <dest>
|
||||||
|
-- mount --help
|
||||||
|
|
||||||
|
local name = syscall.getTask(syscall.getpid()).name
|
||||||
|
local args, opts = {}, { help=false, o=nil }
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
local rawArgs = {...}
|
||||||
|
while i <= #rawArgs do
|
||||||
|
local v = rawArgs[i]
|
||||||
|
if v:sub(1,2) == "--" then
|
||||||
|
local o = v:sub(3)
|
||||||
|
if o == "help" then opts.help = true
|
||||||
|
else print(name..": unrecognised option '"..v.."'")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return end
|
||||||
|
elseif v == "-o" then
|
||||||
|
i = i + 1
|
||||||
|
opts.o = rawArgs[i]
|
||||||
|
elseif v:sub(1,1) == "-" then
|
||||||
|
local rest = v:sub(2)
|
||||||
|
if rest:sub(1,1) == "o" then
|
||||||
|
if #rest > 1 then opts.o = rest:sub(2)
|
||||||
|
else i = i + 1; opts.o = rawArgs[i] end
|
||||||
|
else
|
||||||
|
print(name..": invalid option '"..v.."'")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(args, v)
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.help then
|
||||||
|
print("Usage: "..name)
|
||||||
|
print(" "..name.." <id> <mountpoint>")
|
||||||
|
print(" "..name.." -o loop <source> <mountpoint>")
|
||||||
|
print("")
|
||||||
|
print("Mount a loop device or filesystem.")
|
||||||
|
print("")
|
||||||
|
print(" (no args) list all active mount points")
|
||||||
|
print(" <id> <mountpoint> mount an already-attached loop device")
|
||||||
|
print(" -o loop <src> <dest> attach src as loop device and mount at dest")
|
||||||
|
print(" src can be a directory (bind) or .hfs image")
|
||||||
|
print("")
|
||||||
|
print("Requires root for all operations except listing.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if #args == 0 and not opts.o then
|
||||||
|
local ok, mounts = pcall(syscall.mounts or function()
|
||||||
|
error("ENOSYS")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local loDevs = {}
|
||||||
|
local lok, ld = pcall(syscall.lolist)
|
||||||
|
if lok then
|
||||||
|
for id, info in pairs(ld) do
|
||||||
|
local path = (type(info)=="table" and info.path) or tostring(info)
|
||||||
|
local mode = (type(info)=="table" and info.mode) or "bind"
|
||||||
|
loDevs[id] = { path=path, mode=mode }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if next(loDevs) == nil then
|
||||||
|
syscall.devctl(1, "sfgc", 0xFF00FF)
|
||||||
|
print("(no loop devices attached)")
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for id, info in pairs(loDevs) do
|
||||||
|
local colour = info.mode == "image" and 0x00FFFF or 0x0000FF
|
||||||
|
syscall.devctl(1, "sfgc", colour)
|
||||||
|
printInline(info.mode.." "..id)
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
print(" on "..info.path)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.o and opts.o:lower() == "loop" then
|
||||||
|
if #args < 2 then
|
||||||
|
print(name..": -o loop requires <source> and <mountpoint>")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
local src = args[1]
|
||||||
|
local dest = args[2]
|
||||||
|
|
||||||
|
if src:sub(1,1) ~= "/" then src = syscall.getcwd().."/"..src end
|
||||||
|
if dest:sub(1,1) ~= "/" then dest = syscall.getcwd().."/"..dest end
|
||||||
|
|
||||||
|
local ok, loopId = pcall(syscall.losetup, src)
|
||||||
|
if not ok then
|
||||||
|
local msg = tostring(loopId)
|
||||||
|
if msg:find("EPERM") then msg = "Permission denied"
|
||||||
|
elseif msg:find("EINVAL") then msg = "'"..args[1].."': not a directory or .hfs image"
|
||||||
|
elseif msg:find("EIO") then msg = "'"..args[1].."': I/O error reading image"
|
||||||
|
end
|
||||||
|
print(name..": losetup: "..msg); syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok2, merr = pcall(syscall.mount, dest, loopId)
|
||||||
|
if not ok2 then
|
||||||
|
pcall(syscall.lodetach, loopId)
|
||||||
|
local msg = tostring(merr)
|
||||||
|
if msg:find("EPERM") then msg = "Permission denied"
|
||||||
|
elseif msg:find("EBUSY") then msg = "'"..dest.."' is already a mount point"
|
||||||
|
elseif msg:find("ENODEV") then msg = "loop device not found (internal error)"
|
||||||
|
end
|
||||||
|
print(name..": mount: "..msg); syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||||
|
print(name..": "..loopId.." mounted at "..dest)
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if #args == 2 then
|
||||||
|
local loopId = args[1]
|
||||||
|
local dest = args[2]
|
||||||
|
if dest:sub(1,1) ~= "/" then dest = syscall.getcwd().."/"..dest end
|
||||||
|
|
||||||
|
local ok, err = pcall(syscall.mount, dest, loopId)
|
||||||
|
if not ok then
|
||||||
|
local msg = tostring(err)
|
||||||
|
if msg:find("EPERM") then msg = "Permission denied"
|
||||||
|
elseif msg:find("ENODEV") then msg = "'"..loopId.."': no such device - use losetup first"
|
||||||
|
elseif msg:find("EBUSY") then msg = "'"..dest.."' is already a mount point"
|
||||||
|
elseif msg:find("EINVAL") then msg = "invalid arguments"
|
||||||
|
end
|
||||||
|
print(name..": "..msg); syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||||
|
print(name..": "..loopId.." mounted at "..dest)
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print(name..": wrong number of arguments")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1)
|
||||||
80
Src/coreutils/data/bin/passwd
Normal file
80
Src/coreutils/data/bin/passwd
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
--:Minify:--
|
||||||
|
-- passwd: change a user's password
|
||||||
|
-- Usage: passwd [username] (default: current user)
|
||||||
|
|
||||||
|
local args = {...}
|
||||||
|
local targetName = args[1]
|
||||||
|
|
||||||
|
local currentUid = syscall.getuid()
|
||||||
|
|
||||||
|
local targetUid
|
||||||
|
if targetName then
|
||||||
|
targetUid = syscall.getuid()
|
||||||
|
if not targetUid then
|
||||||
|
print("passwd: user '" .. targetName .. "' does not exist")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
-- Only root can change another user's password
|
||||||
|
if currentUid ~= 0 and targetUid ~= currentUid then
|
||||||
|
print("passwd: permission denied")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
targetUid = currentUid
|
||||||
|
targetName = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Non-root must verify their current password first
|
||||||
|
if currentUid ~= 0 then
|
||||||
|
printInline("Current password: ")
|
||||||
|
local cur = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then syscall.write(1,"\n"); break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #cur > 0 then cur=cur:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
|
else cur=cur..ch; syscall.write(1,"*") end
|
||||||
|
end
|
||||||
|
local ok, err = syscall.login(targetUid, cur)
|
||||||
|
if not ok then
|
||||||
|
sleep(1)
|
||||||
|
print("passwd: authentication failure")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
printInline("New password: ")
|
||||||
|
local pw1 = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then syscall.write(1,"\n"); break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #pw1 > 0 then pw1=pw1:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
|
else pw1=pw1..ch; syscall.write(1,"*") end
|
||||||
|
end
|
||||||
|
|
||||||
|
printInline("Confirm password: ")
|
||||||
|
local pw2 = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then syscall.write(1,"\n"); break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #pw2 > 0 then pw2=pw2:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
|
else pw2=pw2..ch; syscall.write(1,"*") end
|
||||||
|
end
|
||||||
|
|
||||||
|
if pw1 ~= pw2 then
|
||||||
|
print("passwd: passwords do not match")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = syscall.setpassword(targetUid, pw1)
|
||||||
|
if not ok then
|
||||||
|
print("passwd: " .. tostring(err))
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
print("passwd: password updated for '" .. targetName .. "'")
|
||||||
65
Src/coreutils/data/bin/su
Normal file
65
Src/coreutils/data/bin/su
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local targetUser = ({ ... })[1]
|
||||||
|
local currentUid = syscall.getuid()
|
||||||
|
if syscall.geteuid()~=0 then
|
||||||
|
syscall.exec("/bin/su", {...})
|
||||||
|
end
|
||||||
|
local targetUid
|
||||||
|
if targetUser then
|
||||||
|
targetUid = syscall.getuidbyname(targetUser)
|
||||||
|
else
|
||||||
|
targetUid = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if not targetUid then
|
||||||
|
print("su: user '" .. targetUser .. "' does not exist")
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if currentUid ~= 0 then
|
||||||
|
printInline("Password: ")
|
||||||
|
local pw = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then
|
||||||
|
syscall.write(1, "\n")
|
||||||
|
break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #pw > 0 then pw = pw:sub(1, -2); syscall.write(1, "\b \b") end
|
||||||
|
else
|
||||||
|
pw = pw .. ch; syscall.write(1, "*")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = syscall.login(targetUid, pw)
|
||||||
|
if not ok then
|
||||||
|
sleep(1)
|
||||||
|
print("su: Authentication failure")
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
syscall.setuid(targetUid)
|
||||||
|
end
|
||||||
|
|
||||||
|
local pwent = syscall.getpasswd(targetUid)
|
||||||
|
local shell = (pwent and pwent.shell) or "/bin/hysh"
|
||||||
|
local homedir = (pwent and pwent.homedir) or "/"
|
||||||
|
local username= (pwent and pwent.username)or "Unknown"
|
||||||
|
|
||||||
|
local ok_cd, err_cd = pcall(syscall.chdir, homedir)
|
||||||
|
if not ok_cd then
|
||||||
|
homedir = "/"
|
||||||
|
syscall.chdir(homedir)
|
||||||
|
end
|
||||||
|
syscall.setEnviron("HOME", homedir)
|
||||||
|
syscall.setEnviron("USER", username)
|
||||||
|
syscall.setEnviron("SHELL", shell)
|
||||||
|
|
||||||
|
local ok, err = pcall(syscall.exec, shell)
|
||||||
|
if not ok then
|
||||||
|
print("su: cannot exec shell '" .. shell .. "': " .. tostring(err))
|
||||||
|
syscall.exit(1)
|
||||||
|
end
|
||||||
110
Src/coreutils/data/bin/sudo
Normal file
110
Src/coreutils/data/bin/sudo
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local fs = require("fs")
|
||||||
|
|
||||||
|
local cmdArgs = {...}
|
||||||
|
local targetUser = "root"
|
||||||
|
local i = 1
|
||||||
|
|
||||||
|
if cmdArgs[i] == "-u" then
|
||||||
|
i = i + 1
|
||||||
|
local uarg = cmdArgs[i] or "root"
|
||||||
|
local numUid = tonumber(uarg)
|
||||||
|
if numUid then
|
||||||
|
local pwent = syscall.getpasswd(numUid)
|
||||||
|
targetUser = (pwent and pwent.username) or uarg
|
||||||
|
else
|
||||||
|
targetUser = uarg
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = cmdArgs[i]
|
||||||
|
if not cmd or cmd == "" then
|
||||||
|
print("usage: sudo [-u user] <command> [args...]")
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local restArgs = {}
|
||||||
|
for j = i + 1, #cmdArgs do restArgs[#restArgs + 1] = cmdArgs[j] end
|
||||||
|
|
||||||
|
local currentUid = syscall.getuid()
|
||||||
|
local currentUser = syscall.getUsername(currentUid) or tostring(currentUid)
|
||||||
|
|
||||||
|
local targetUid = syscall.getuidbyname(targetUser)
|
||||||
|
if not targetUid then
|
||||||
|
print("sudo: user '" .. targetUser .. "' does not exist")
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if currentUid ~= 0 then
|
||||||
|
printInline("[sudo] password for root: ")
|
||||||
|
local pw = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then
|
||||||
|
syscall.write(1, "\n")
|
||||||
|
break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #pw > 0 then pw = pw:sub(1, -2); syscall.write(1, "\b \b") end
|
||||||
|
else
|
||||||
|
pw = pw .. ch
|
||||||
|
syscall.write(1, "*")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = syscall.login(0, pw)
|
||||||
|
if not ok then
|
||||||
|
sleep(1)
|
||||||
|
print("sudo: Authentication failure")
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if targetUid ~= currentUid then
|
||||||
|
syscall.setuid(targetUid)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if targetUid ~= currentUid then
|
||||||
|
syscall.setuid(targetUid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmdPath = ""
|
||||||
|
if cmd:find("/") then
|
||||||
|
if fs.exists(cmd) then cmdPath = cmd end
|
||||||
|
else
|
||||||
|
local paths = string.split(syscall.getEnviron("PATH") or "/bin/", ":")
|
||||||
|
for _, p in ipairs(paths) do
|
||||||
|
local full = p .. cmd
|
||||||
|
if fs.exists(full) then cmdPath = full; break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if cmdPath == "" then
|
||||||
|
print("sudo: command not found: " .. cmd)
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = fs.readAllText(cmdPath)
|
||||||
|
local program, loadErr = load(text, "@" .. cmdPath)
|
||||||
|
if not program then
|
||||||
|
print("sudo: cannot load " .. cmd .. ": " .. tostring(loadErr))
|
||||||
|
syscall.exit(1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local pwent = syscall.getpasswd(targetUid)
|
||||||
|
if pwent and pwent.homedir then
|
||||||
|
syscall.setEnviron("HOME", pwent.homedir)
|
||||||
|
end
|
||||||
|
syscall.setEnviron("USER", targetUser)
|
||||||
|
|
||||||
|
local ok, err = xpcall(program, debug.traceback, table.unpack(restArgs))
|
||||||
|
if not ok then
|
||||||
|
print("sudo: " .. cmd .. ": " .. tostring(err))
|
||||||
|
syscall.exit(1)
|
||||||
|
end
|
||||||
111
Src/coreutils/data/bin/umount
Normal file
111
Src/coreutils/data/bin/umount
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
--:Minify:--
|
||||||
|
-- Usage:
|
||||||
|
-- umount <mountpoint> unmount; auto-detach loop device if one is found
|
||||||
|
-- umount -l <id> detach loop device without unmounting (force)
|
||||||
|
-- umount --no-detach <mpt> unmount but leave loop device attached
|
||||||
|
-- umount --help
|
||||||
|
|
||||||
|
local name = syscall.getTask(syscall.getpid()).name
|
||||||
|
local args, opts = {}, { l=false, ["no-detach"]=false, help=false }
|
||||||
|
|
||||||
|
for _, v in ipairs({...}) do
|
||||||
|
if v:sub(1,2) == "--" then
|
||||||
|
local o = v:sub(3)
|
||||||
|
if opts[o] ~= nil then opts[o] = true
|
||||||
|
else print(name..": unrecognised option '"..v.."'")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return end
|
||||||
|
elseif v:sub(1,1) == "-" then
|
||||||
|
for i = 2, #v do
|
||||||
|
local c = v:sub(i,i)
|
||||||
|
if opts[c] ~= nil then opts[c] = true
|
||||||
|
else print(name..": invalid option '-"..c.."'")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(args, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.help then
|
||||||
|
print("Usage: "..name.." <mountpoint>")
|
||||||
|
print(" "..name.." --no-detach <mountpoint>")
|
||||||
|
print(" "..name.." -l <loopid>")
|
||||||
|
print("")
|
||||||
|
print("Unmount a filesystem mounted at <mountpoint>.")
|
||||||
|
print("")
|
||||||
|
print(" <mountpoint> unmount and auto-detach any loop device")
|
||||||
|
print(" --no-detach unmount but keep the loop device attached")
|
||||||
|
print(" -l <loopid> forcibly detach a loop device (no unmount)")
|
||||||
|
print("")
|
||||||
|
print("Requires root.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.l then
|
||||||
|
if #args < 1 then
|
||||||
|
print(name..": -l requires a loop device id")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
local id = args[1]
|
||||||
|
local ok, err = pcall(syscall.lodetach, id)
|
||||||
|
if not ok then
|
||||||
|
local msg = tostring(err)
|
||||||
|
if msg:find("EPERM") then msg = "Permission denied"
|
||||||
|
elseif msg:find("ENXIO") then msg = "no such loop device '"..id.."'"
|
||||||
|
elseif msg:find("EBUSY") then msg = "'"..id.."' is still mounted - unmount first or omit -l"
|
||||||
|
end
|
||||||
|
print(name..": "..msg); syscall.exit(1); return
|
||||||
|
end
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||||
|
print(name..": detached "..id)
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if #args < 1 then
|
||||||
|
print(name..": missing mount point operand")
|
||||||
|
print("try '"..name.." --help' for more information.")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
local mpt = args[1]
|
||||||
|
if mpt:sub(1,1) ~= "/" then mpt = syscall.getcwd().."/"..mpt end
|
||||||
|
|
||||||
|
local loopIdToDetach = nil
|
||||||
|
if not opts["no-detach"] then
|
||||||
|
local lok, devs = pcall(syscall.lolist)
|
||||||
|
if lok then
|
||||||
|
loopIdToDetach = {}
|
||||||
|
for id in pairs(devs) do
|
||||||
|
loopIdToDetach[#loopIdToDetach + 1] = id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, err = pcall(syscall.umount, mpt)
|
||||||
|
if not ok then
|
||||||
|
local msg = tostring(err)
|
||||||
|
if msg:find("EPERM") then msg = "Permission denied"
|
||||||
|
elseif msg:find("EINVAL") then msg = "'"..args[1].."' is not a mount point"
|
||||||
|
elseif msg:find("EBUSY") then msg = "'"..args[1].."' is busy - close open files first"
|
||||||
|
end
|
||||||
|
print(name..": "..msg); syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
syscall.devctl(1, "sfgc", 0x00FFFF)
|
||||||
|
print(name..": unmounted "..mpt)
|
||||||
|
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", 0xFF00FF)
|
||||||
|
print(name..": auto-detached "..id)
|
||||||
|
syscall.devctl(1, "sfgc", 0xFFFFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
67
Src/coreutils/data/bin/useradd
Normal file
67
Src/coreutils/data/bin/useradd
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
--:Minify:--
|
||||||
|
local args = {...}
|
||||||
|
local i = 1
|
||||||
|
local opt = { createHome = true }
|
||||||
|
|
||||||
|
while i <= #args do
|
||||||
|
local a = args[i]
|
||||||
|
if a == "-p" then i=i+1; opt.password = args[i]
|
||||||
|
elseif a == "-g" then i=i+1; opt.gid = tonumber(args[i])
|
||||||
|
elseif a == "-d" then i=i+1; opt.homedir = args[i]
|
||||||
|
elseif a == "-s" then i=i+1; opt.shell = args[i]
|
||||||
|
elseif a == "-M" then opt.createHome = false
|
||||||
|
elseif a:sub(1,1) ~= "-" then opt.username = a
|
||||||
|
else print("useradd: unknown option: " .. a); return end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if not opt.username then
|
||||||
|
print("Usage: useradd [-p password] [-g gid] [-d homedir] [-s shell] [-M] <username>")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
local password = opt.password
|
||||||
|
if not password then
|
||||||
|
printInline("New password: ")
|
||||||
|
password = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then syscall.write(1,"\n"); break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #password > 0 then password=password:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
|
else password=password..ch; syscall.write(1,"*") end
|
||||||
|
end
|
||||||
|
printInline("Confirm password: ")
|
||||||
|
local pw2 = ""
|
||||||
|
while true do
|
||||||
|
local ch = syscall.read(0)
|
||||||
|
if not ch or ch == "" then
|
||||||
|
elseif ch == "\n" then syscall.write(1,"\n"); break
|
||||||
|
elseif ch == "\b" then
|
||||||
|
if #pw2 > 0 then pw2=pw2:sub(1,-2); syscall.write(1,"\b \b") end
|
||||||
|
else pw2=pw2..ch; syscall.write(1,"*") end
|
||||||
|
end
|
||||||
|
if password ~= pw2 then
|
||||||
|
print("useradd: passwords do not match")
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local uid, err = syscall.newuser(
|
||||||
|
opt.username, password, opt.gid, opt.homedir, opt.shell or "/bin/hysh"
|
||||||
|
)
|
||||||
|
if not uid then
|
||||||
|
print("useradd: " .. tostring(err))
|
||||||
|
syscall.exit(1); return
|
||||||
|
end
|
||||||
|
|
||||||
|
if opt.createHome then
|
||||||
|
local home = opt.homedir or ("/home/" .. opt.username)
|
||||||
|
local ok, e = pcall(syscall.mkdir, home)
|
||||||
|
if not ok then
|
||||||
|
print("useradd: warning: could not create home " .. home .. ": " .. tostring(e))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print("useradd: created user '" .. opt.username .. "' with uid=" .. tostring(uid))
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user