99 Commits
Author SHA1 Message Date
Astronand 7ddc527e00 Merge pull request '1.2 dev' (#21) from 1.2-dev into main
Reviewed-on: #21
2026-08-15 22:41:41 -04:00
Astronand 3b86113425 UPDATE HYPERVISOR 2026-08-15 22:40:25 -04:00
Astronand bb28d24674 update hypervisor 2026-08-15 22:38:35 -04:00
Astronand 3a33bd0264 Merge pull request 'fix spm files' (#20) from 1.2-dev into main
Reviewed-on: #20
2026-08-14 21:13:19 -04:00
Astronand 4ed82a23a1 fix spm files 2026-08-14 21:12:49 -04:00
Astronand 242762ab6b Merge pull request '1.2 dev' (#19) from 1.2-dev into main
Reviewed-on: #19
2026-08-14 20:47:25 -04:00
Astronand 10aa0c62ff spm works maybe? 2026-08-14 20:44:11 -04:00
Astronand 56646625c0 update 2026-07-28 20:13:50 -04:00
Astronand 49c9e5e37c finished http implementation working on spm and liveboot env 2026-07-23 14:33:16 -04:00
Astronand f7b64c11b7 added seperate input and working on http / sockets 2026-05-27 17:01:26 -04:00
Astronand 59e09a5995 Update LICENSE 2026-03-26 10:34:00 -04:00
Astronand ab0a10105c formating better for spm packages 2026-03-26 08:12:37 -04:00
Astronand 8c18d89489 Merge pull request '1.2-dev' (#18) from 1.2-dev into main
Reviewed-on: #18
2026-03-21 01:12:24 -04:00
Astronand 22b5021e9d fixed boot crash 2026-03-21 01:10:47 -04:00
Astronand 462c74686a fixed the installer 2026-03-21 01:04:27 -04:00
Astronand 0072547beb updated package list 2026-03-21 00:59:46 -04:00
Astronand 041663e1e8 Merge pull request 'made newer installer that does not require tar stuff' (#17) from 1.2-dev into main
Reviewed-on: #17
2026-03-21 00:57:16 -04:00
Astronand 7deefc83ca made newer installer that does not require tar stuff 2026-03-21 00:54:39 -04:00
Astronand fcac0ff8d8 Merge pull request 'made system use full salt+pepper' (#16) from 1.2-dev into main
Reviewed-on: #16
2026-03-19 14:11:24 -04:00
Astronand df62414229 made system use full salt+pepper 2026-03-19 14:10:17 -04:00
Astronand d98555328b Merge pull request '1.2-dev' (#15) from 1.2-dev into main
Reviewed-on: #15
2026-03-19 12:19:44 -04:00
Astronand 055dd4e606 fixed again 2026-03-19 12:10:31 -04:00
Astronand 5755dd9cbe fixed 2026-03-19 11:32:48 -04:00
Astronand 61cbb91db2 Merge branch '1.2-dev' 2026-03-19 11:28:56 -04:00
Astronand b79492dbb8 Merge branch 'main' of https://git.astronand.dev/Hyperion/HyperionOS 2026-03-19 11:28:29 -04:00
Astronand bc87c53427 Merge branch '1.2-dev' 2026-03-19 11:28:28 -04:00
Astronand 8e11faf9ec updated again 2026-03-19 11:23:24 -04:00
Astronand 0ea42f9454 Updated installer 2026-03-19 11:21:08 -04:00
Astronand d0f26a937f made installer 2026-03-19 11:16:05 -04:00
Astronand 9b338328f0 made unified colors and stuff 2026-03-19 08:33:47 -04:00
Astronand 030e5bfd96 Update install/data/tarbad 2026-03-12 20:59:36 -04:00
Astronand 099638c735 Merge pull request 'update tar' (#12) from 1.2-dev into main
Reviewed-on: #12
2026-03-12 20:55:22 -04:00
Astronand 4f9eebade2 update tar 2026-03-12 20:54:27 -04:00
Astronand 5172da1b5b Merge pull request '1.2-dev' (#11) from 1.2-dev into main
Reviewed-on: #11
2026-03-12 19:32:16 -04:00
Astronand d08935b68a remove gotos 2026-03-12 19:31:37 -04:00
Astronand 45b46cf3c4 Merge pull request 'main' (#10) from main into 1.2-dev
Reviewed-on: #10
2026-03-12 19:27:44 -04:00
Astronand aeea68bc9b Update install/data/tarbad 2026-03-12 12:12:48 -04:00
Astronand f983b13d56 Update install/installcc.lua
make it use right stuff
2026-03-12 12:10:37 -04:00
Astronand 03c5b106c4 Update Src/Hyperion-kernel/boot/kernel.lua 2026-03-12 11:59:35 -04:00
Astronand 08323e00ff Update install/data/tarbad 2026-03-12 11:56:58 -04:00
Astronand 5e3cdbe40c Update install/installcc.lua 2026-03-12 11:51:55 -04:00
Astronand 8762b8f022 Merge pull request '1.2-dev' (#9) from 1.2-dev into main
Reviewed-on: #9
2026-03-11 10:37:44 -04:00
Astronand 677b2cccec user login fixed 2026-03-11 10:37:13 -04:00
Astronand a5e8624368 forgot to edit login 2026-03-11 08:54:47 -04:00
Astronand bbda3b3937 fixed elevate VULN 2026-03-11 08:52:41 -04:00
Astronand 585d39bec2 Merge pull request '1.2-dev' (#8) from 1.2-dev into main
Reviewed-on: #8
2026-03-11 08:23:21 -04:00
Astronand b08b14763a i am a we bit stupid (fixed cct bug) 2026-03-11 08:22:53 -04:00
Astronand de6696003b fix capitaization part 2 2026-03-11 07:26:38 -04:00
Astronand 9220281365 fix capitalization part 1 2026-03-11 07:25:52 -04:00
Astronand 813ddabd9d fixed disk detection i think 2026-03-10 21:13:52 -04:00
Astronand 5177639d71 Merge pull request 'update build.tar' (#7) from 1.2-dev into main
Reviewed-on: #7
i was dumb
2026-03-10 20:05:26 -04:00
Astronand 528b4f31bd update build.tar 2026-03-10 20:04:53 -04:00
Astronand 60162c7c57 Merge pull request '1.2.4' (#6) from 1.2-dev into main
Reviewed-on: #6
2026-03-10 20:02:20 -04:00
Astronand 18f5c454bb fixed bug where cct drivers are inaccessable 2026-03-10 20:00:21 -04:00
Astronand 849ecb7dd6 made procfs self 2026-03-10 19:41:54 -04:00
Astronand e41bd6bee7 Merge pull request '1.2-dev merge' (#5) from 1.2-dev into main
Reviewed-on: #5
2026-03-10 12:27:08 -04:00
Astronand 359198c1ea Merge branch '1.2-dev' of https://git.astronand.dev/Hyperion/HyperionOS into 1.2-dev 2026-03-10 10:57:27 -04:00
Astronand beebf01223 remove tar during install 2026-03-10 10:57:10 -04:00
Astronand 2d4ea1bbf4 Update README.md 2026-03-10 10:39:01 -04:00
Astronand ea3a7e99a7 Update install/installcc.lua 2026-03-10 10:34:36 -04:00
Astronand be0fe5dc5a install works (i think) 2026-03-10 10:32:32 -04:00
Astronand 12669d9f82 adde /proc fs and working on install 2026-03-10 09:17:21 -04:00
Astronand f12159bfb9 fixed build script 2026-03-09 11:31:31 -04:00
Astronand 1590e1f3f7 more reorganizeing and $PKGCONFIG.ini files added B to ls -lh for "Bytes" 2026-03-09 11:28:09 -04:00
Astronand a69f945b91 did some reorganizing 2026-03-06 09:57:45 -05:00
Astronand 7da67899db finished hyperion manifest :D 2026-03-05 10:36:43 -05:00
Astronand 62e032e4c5 made sysinit in /usr/lib/sysinit and sped up micro added kernel.firstBoot in kernel table 2026-03-05 09:53:30 -05:00
Astronand 6fefa2d9ff cp should not copy perms 2026-03-03 12:25:25 -05:00
Astronand bb354cc706 mv syscallautofill 2026-03-03 11:45:05 -05:00
Astronand fabc061731 made lua not clear screen 2026-03-03 10:06:04 -05:00
Astronand 82c3e2b346 fix ll 2026-03-03 08:57:45 -05:00
Astronand e2e1d5b8a5 making descriptions for syscalls 2026-03-03 08:52:55 -05:00
Astronand 9342b9b2b3 Merge branch '1.2-dev' of https://git.astronand.dev/Hyperion/HyperionOS into 1.2-dev 2026-03-03 08:02:09 -05:00
Astronand 9a7db6c243 syscalls now autocomplete is vsc 2026-03-03 08:02:05 -05:00
Astronand c7545e6947 Update build.py 2026-03-03 07:31:59 -05:00
Astronand b7f52dd17b working on syscall manifest and fixed anther exploit 2026-03-02 22:25:37 -05:00
Astronand eb5bed0f09 fixed asyncsyscall5 2026-03-02 21:58:51 -05:00
Astronand 1827a463eb added ll for ghxx 2026-03-02 21:56:05 -05:00
Astronand b532a63fc6 fixed stupid dumbass mistake i made me dumb 2026-03-02 21:32:56 -05:00
Astronand 4e5a4172bf hopfully fixed it omfg 2026-03-02 21:29:46 -05:00
Astronand 31ce894fda fixed build script 2026-03-02 21:26:50 -05:00
Astronand 16c900de84 fixed ls links, modules writeable 2026-03-02 21:23:35 -05:00
Astronand 413afd96de Merge branch '1.2-dev' of https://git.astronand.dev/Hyperion/HyperionOS into 1.2-dev 2026-03-02 07:27:46 -05:00
Astronand a0a0ac69d4 remove old shell files 2026-03-02 07:27:45 -05:00
spsf 17453983ad Path traversal fixes, 26_tty removal, ctrl+key fixes 2026-03-01 00:21:02 -06:00
Astronand a6550aa069 fixed minify header and build script 2026-02-25 11:41:41 -05:00
Astronand 02e7b3897c fixed tty naming 2026-02-25 09:03:32 -05:00
Astronand 34b89a8e34 fixed build script 2026-02-25 08:04:53 -05:00
Astronand 0eabfebd0f fixed new ghxx exploit 2026-02-25 08:01:28 -05:00
Astronand 5b2e5eac65 added more libs and fixed build script 2026-02-24 17:54:09 -05:00
spsf 415064480a Patch the AsyncSyscall v4 exploit from working 2026-02-24 02:00:37 -06:00
spsf f00453f703 Merge pull request '1.2-dev merge' (#5) from 1.2-dev into main
Reviewed-on: #5
2026-02-24 02:12:36 -05:00
spsf ab1e847d1c Fix exploit with trailing whitespace 2026-02-24 00:48:20 -06:00
spsf 62a03bfe6b Potential windows case insensitive filesystem issue fix 2026-02-24 00:34:59 -06:00
spsf e77a8b3636 AsyncSyscall3 exploit fix 2026-02-24 00:01:39 -06:00
spsf 6bb7f03a3e file permissions fixes 2026-02-23 23:50:37 -06:00
spsf 8798a2f4fe 2 potential vulnerability fixes 2026-02-23 23:26:21 -06:00
spsf a6d2f6dca7 /home/user owned by user, user starts in cwd /home/user 2026-02-23 23:05:13 -06:00
spsf b015d5880a load vuln fixed, sudo fixed 2026-02-23 22:43:12 -06:00
283 changed files with 18121 additions and 3567 deletions
+6 -1
View File
@@ -5,6 +5,11 @@
"syscall", "syscall",
"printf", "printf",
"printInline", "printInline",
"toHex" "toHex",
"loadcstr",
"userinput"
],
"Lua.diagnostics.disable": [
"need-check-nil"
] ]
} }
+198 -14
View File
@@ -1,18 +1,202 @@
MIT License
Copyright (c) 2025 Astronand Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial 1. Definitions.
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT "License" shall mean the terms and conditions for use, reproduction,
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO and distribution as defined by Sections 1 through 9 of this document.
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE "Licensor" shall mean the copyright owner or entity authorized by
USE OR OTHER DEALINGS IN THE SOFTWARE. the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+10 -1
View File
@@ -1,3 +1,4 @@
[![Download on PineStore](https://raster.shields.io/badge/dynamic/json?url=https%3A%2F%2Fpinestore.cc%2Fapi%2Fproject%2F225&query=%24.project.downloads&suffix=%20downloads&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNzYuOTA0IiBoZWlnaHQ9Ijg5LjI5NSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDc2OS4wNCA4OTIuOTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI%2BCiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTQuNzQgLTQuNjgyNikiIGZpbGw9IiM5YWIyZjIiPgogIDxwYXRoIGQ9Im00MTAgODUxYzAtMTIgMjYtMjEgNTgtMjEgMTUgMCAyMiA0IDE3IDktMTQgMTItNzUgMjItNzUgMTJ6Ii8%2BCiAgPHBhdGggZD0ibTU4NSA3NDJjLTEtNDkgNC03MiAxNi04NSAyMi0yNCAzMC02OCAxNi04Ni0xMi0xNC0yNy0zOS00OC03OC0xMC0xOS05LTI2IDQtNDEgMjItMjQgMjEtNjctMi0xNDQtMjEtNjktMzktMTQ0LTQ4LTE5NS00LTI2LTItMzMgMTEtMzMgMzEgMCAxMTIgMzMgMTQxIDU4IDI4IDIzIDgxIDkyIDcxIDkyLTIgMCA1IDI2IDE2IDU3IDI4IDc5IDI5IDIyNCAzIDMwOC0xMCAzMy0xOSA2Mi0xOSA2NS00IDI2LTEzMiAxNTAtMTU1IDE1MC0zIDAtNi0zMC02LTY4eiIvPgogIDxwYXRoIGQ9Im02OCA2NzNjLTcyLTEwOS03MS0yNzggMy00MjMgMzYtNzEgNjItMTAwIDEyOC0xNDAgNDMtMjcgNjUtMzQgMTE4LTM2IDEwMC00IDk4IDExLTE5IDEzNi0zNCAzNy03OCA4OC05NiAxMTMtMjggMzktMzEgNDgtMjEgNjUgMTEgMTcgNiAyNy0zMyA3OS00MCA1My00NCA2Mi0zMiA3OCAxNyAyMyAxOCA1NyAyIDczLTYgNi0xNCAzMS0xNyA1NC02IDQyLTYgNDItMzMgMXoiLz4KIDwvZz4KIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNC43NCAtNC42ODI2KSIgZmlsbD0iIzU5YTY0ZiI%2BCiAgPHBhdGggZD0ibTM2NSA4MTNjLTUzLTYtMTM5LTMzLTE5Mi02MS02OC0zNS04My02Ny01OC0xMjIgMjYtNTkgNDAtNjcgNzgtNDkgNjggMzMgMTY3IDU4IDI2NiA2OSA1OCA1IDEwNiAxMiAxMDkgMTQgMiAzIDYgMzIgOSA2NSA4IDg1IDAgOTEtMTAxIDkwLTQ0LTEtOTQtNC0xMTEtNnoiLz4KICA8cGF0aCBkPSJtNDEwIDQ1OWMtNjctNy0xNjAtMjktMTk5LTQ4LTI3LTE0LTM0LTM2LTIwLTYzIDIxLTM4IDk3LTEzNiAxNTAtMTkzIDI1LTI3IDU4LTcxIDczLTk3IDI1LTQzIDMxLTQ3IDU0LTQyIDQwIDEwIDQyIDEyIDQyIDUyIDAgMjAgNiA1NyAxNCA4MiAyNCA3MyA1NCAxOTIgNjIgMjM2IDUgMzUgMyA0NS0xNSA2My0yMyAyMy0zNiAyNC0xNjEgMTB6Ii8%2BCiA8L2c%2BCiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTQuNzQgLTQuNjgyNikiIGZpbGw9IiM3ZWNiMjUiPgogIDxwYXRoIGQ9Im01NTggNjc0Yy0yLTItNTEtOS0xMDktMTQtMTAyLTExLTIwNC0zNy0yNjQtNjktMTYtOC0zMi0xNC0zNC0xMi00IDMtMzEtNDgtMzEtNjEgMC01IDIxLTMxIDQ2LTU4IDUxLTU0IDcxLTYwIDEzMC0zNSAxOSA4IDgzIDE5IDE0MiAyNSA1OCA2IDEwNyAxMiAxMDcgMTNzMTUgMjYgMzMgNTZjMjcgNDMgMzIgNjMgMzAgOTktMiAzNS04IDQ3LTI1IDUzLTExIDQtMjMgNi0yNSAzeiIvPgogPC9nPgogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE0Ljc0IC00LjY4MjYpIiBmaWxsPSIjZWNlZGVmIj4KICA8cGF0aCBkPSJtMjYwIDg5MGMtMzQtOC03MC00MS03MC02NSAwLTYtOS0yMC0yMC0zMHMtMjAtMjItMjAtMjctMTMtMjEtMzAtMzVjLTM1LTI5LTQxLTgzLTEzLTEyMiAxNS0yMiAxNS0yNi0xLTU2LTE4LTMzLTE4LTMzIDI3LTkxIDI4LTM2IDQyLTYzIDM2LTY4LTIzLTI1IDktNzggMTIwLTE5NyAzNi0zOCA3Mi04MSA4Mi05NiAxMC0xNCAyNS0zMCAzMy0zNSAzNi0yMCA3IDMyLTUzIDk3LTQ4IDUxLTEyNiAxNTAtMTQ5IDE4OS0xMCAxOC05IDI0IDEwIDQwIDIzIDE5IDIzIDE5LTI5IDcxLTUzIDUyLTUzIDUyLTM4IDgyIDE0IDI4IDE0IDMzLTEwIDc2LTMyIDU3LTIzIDgxIDQ2IDEyMCAzNCAxOSA0OSAzMyA0NSA0Mi0xNCAzNyAzNiA3NSA5OCA3NSAyNSAwIDQwLTcgNTQtMjUgMTgtMjMgMjctMjUgOTUtMjUgOTQgMCAxMDItOCA5My04OS02LTUzLTUtNTkgMTQtNjQgMzItOCAyNi02NC0xNS0xMzItMzUtNTgtMzUtNTgtOS04MiAyMS0xOSAyNC0yOSAxOS01Ni0xMC00Ny00NC0xNzUtNjEtMjI3LTgtMjUtMTQtNjItMTQtODMgMC0yNy01LTM5LTE3LTQzLTEwLTMtMjUtOC0zMy0xMC0xMi00LTEyLTYtMS0xNCAyNy0xNiA1NiA1IDY5IDUxIDM1IDExNyA0MyAxNDggNDYgMTcwIDIgMTMgMTEgNTEgMjEgODQgMjEgNzEgMjEgMTIxIDAgMTQ1LTE0IDE1LTEzIDE5IDUgNDMgMTEgMTQgMjAgMzAgMjAgMzVzNyAxNSAxNSAyMmMyMSAxNyAxNiA3NS0xMCAxMDItMTggMTktMjAgMzItMTcgNzkgNCA1MCAyIDU4LTE5IDcyLTEyIDktNTAgMTktODMgMjMtNDUgNS02NSAxMy04MyAzMi0yNiAyOC05MiAzOC0xNTMgMjJ6Ii8%2BCiA8L2c%2BCiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTQuNzQgLTQuNjgyNikiIGZpbGw9IiM3ZTY3NGQiPgogIDxwYXRoIGQ9Im0yNDggODU0Yy0zMC0xNi00Ny01OS0zMC03NiA4LTggMjMtNyA1NCAyIDI0IDcgNjEgMTQgODMgMTcgNTQgNyA1OSAxNSAzNSA0Ni0xOCAyMy0yOSAyNy02OCAyNy0yNi0xLTU5LTctNzQtMTZ6Ii8%2BCiA8L2c%2BCjwvc3ZnPgo%3D&label=PineStore)](https://pinestore.cc/projects/225/hyperionos)
# HyperionOS # HyperionOS
HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services. HyperionOS is a modular, hybrid kernel operating system written entirely in Lua. It features a custom task scheduler, virtual filesystem, syscall interface, and separates core functionality from user-space services.
@@ -14,8 +15,10 @@ See `building.md`.
- Functionality is split into kernel modules (`.kmod`) - Functionality is split into kernel modules (`.kmod`)
- Task-based lightweight thread/task preemptive scheduler with process isolation and IPC support - Task-based lightweight thread/task preemptive scheduler with process isolation and IPC support
- Virtual filesystem unified interface for disk, RAM, and virtual filesystems - Virtual filesystem
- TTY & Shell - TTY & Shell
- Sockets
-
--- ---
@@ -47,6 +50,12 @@ Add your name to `contributors.md` when your PR is merged.
--- ---
## Information
Trello: https://trello.com/b/8dryrlMi
---
## License ## License
This project is licensed under the MIT License. See the LICENSE file for details. This project is licensed under the MIT License. See the LICENSE file for details.
-33
View File
@@ -1,33 +0,0 @@
local args = {...}
local name = syscall.getTask(syscall.getpid()).name
local fs = require("sys.fs")
if not args[1] then
while true do
local content = syscall.read(0, 1024)
if not content or content == "" then break end
printInline(content)
end
print("")
return
end
for _, arg in ipairs(args) do
local filePath = arg
if filePath:sub(1,1) ~= "/" then
filePath = syscall.getcwd().."/"..filePath
end
if not fs.exists(filePath) then
print(name..": Cannot access '"..arg.."': No such file.")
else
local fd = syscall.open(filePath, "r")
while true do
local content = syscall.read(fd, 1024)
if not content or content == "" then break end
printInline(content)
end
syscall.close(fd)
end
end
print("")
-1
View File
@@ -1 +0,0 @@
syscall.devctl(1,"clear")
-2
View File
@@ -1,2 +0,0 @@
local args = {...}
print(table.concat(args, " "))
-94
View File
@@ -1,94 +0,0 @@
--:Minify:--
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","r")
syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1)
syscall.devctl(1,"spos",1,1)
print("HyperionOS hysh Shell")
local str=""
local stopInput=false
local proc=0
local fs=require("sys.fs")
local timeout=false
syscall.setEnviron("SHELL","simpleshell")
printInline("> ")
syscall.sigcatch(function(sig)
if sig==1 then
syscall.kill(proc)
print("Terminated")
printInline("> ")
stopInput=false
end
end)
while true do
if not stopInput then
local input=syscall.read(0)
if input then
if input=="\b" then
if #str>0 then
str=str:sub(1,#str-1)
printInline("\b")
end
elseif input=="\n" then
print("")
stopInput=true
if str == "" then
printInline("> ")
stopInput=false
else
local path=nil
local split=string.split(str, " ")
if fs.exists("/bin/"..split[1]) then
path="/bin/"..split[1]
elseif fs.exists("/bin/"..split[1]..".lua") then
path="/bin/"..split[1]..".lua"
end
if not path then
print("Program not found")
printInline("> ")
stopInput=false
else
local text = fs.readAllText(path)
local program, err = load(text, path)
if not program then
print(err)
printInline("> ")
end
proc = syscall.spawn(function(...)
syscall.open("/dev/tty/TTY1","r")
syscall.open("/dev/tty/TTY1","w")
syscall.open("/dev/null","w")
program(...)
end, path, nil, {table.unpack(split, 2)})
end
str=""
end
else
str=str..input
printInline(input)
end
timeout=false
else
timeout=true
end
else
local exited, code = syscall.collect(proc)
if exited then
if code then
print("\nTask exited with code:\n"..tostring(code))
end
printInline("> ")
stopInput=false
end
timeout=true
end
if timeout then
if stopInput then
sleep(.5)
else
sleep(.05)
end
end
end
-50
View File
@@ -1,50 +0,0 @@
--:Minify:--
print("HyperionOS lua")
local str=""
local stopInput=false
local timeout=false
local luaEnv=setmetatable({},{__index=_ENV})
printInline("> ")
while true do
local input=syscall.read(0)
if input then
if input=="\b" then
if #str>0 then
str=str:sub(1,#str-1)
printInline("\b")
end
elseif input=="\n" then
print("")
stopInput=true
if str == "" then
printInline("> ")
stopInput=false
elseif str == "exit()" then
break
else
local func=load(str,"@Lua","t",luaEnv)
local ok,err = xpcall(func, debug.traceback)
if not ok then
print(err)
end
printInline("\n> ")
str=""
end
str=""
else
str=str..input
printInline(input)
end
timeout=false
else
timeout=true
end
if timeout then
if stopInput then
sleep(.5)
else
sleep(.05)
end
end
end
-23
View File
@@ -1,23 +0,0 @@
local args = {...}
local name = syscall.getTask(syscall.getpid()).name
if #args == 0 then
print(name..": Missing operand.")
return
end
local fs = require("sys.fs")
local newDir = args[1]
if newDir:sub(1, 1) ~= "/" then
newDir = syscall.getcwd().."/"..newDir
end
if newDir:sub(#newDir, #newDir) ~= "/" then
newDir = newDir.."/"
end
if fs.isDir(newDir) then
print(name..": Cannot create directory '"..args[1].."': Directory already exists.")
return
end
fs.mkdir(newDir)
-1
View File
@@ -1 +0,0 @@
print(syscall.getcwd())
-5
View File
@@ -1,5 +0,0 @@
local syscalls=syscall.sysdump()
for i=1, #syscalls do
print(syscalls[i])
end
print("Total # of syscalls: "..tostring(#syscalls))
-1
View File
@@ -1 +0,0 @@
print((syscall.getUsername() or "Unknown"))
+2
View File
@@ -0,0 +1,2 @@
Hyperion
Astronand
+18
View File
@@ -0,0 +1,18 @@
--:Minify:--
return {
white=0xFFFFFF,
red=0xFF0000,
green=0x00FF00,
blue=0x0000FF,
cyan=0x00FFFF,
yellow=0xFFFF00,
purple=0xFF00FF,
black=0x000000,
gray=0x888888,
lightgrey=0xBBBBBB,
darkgrey=0x444444,
orange=0xFF8800,
mint=0x00FF88,
brown=0xa52a2a,
chocolate=0xd2691e
}
@@ -1,11 +1,6 @@
--:Minify:-- --:Minify:--
local fs={} local fs={}
-- "open" : open
-- "read" : read
-- "write" : write
-- "close" : close
function fs.open(path, mode) function fs.open(path, mode)
local fd=syscall.open(path,mode) local fd=syscall.open(path,mode)
local ret={ local ret={
@@ -140,4 +135,12 @@ function fs.isDir(path)
return syscall.type(path) == "directory" return syscall.type(path) == "directory"
end end
function fs.symlink(target, path)
return syscall.syslink(target, path)
end
function fs.readlink(path)
return syscall.readlink(path)
end
return fs return fs
+182
View File
@@ -0,0 +1,182 @@
--: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(response)
local headerBlock, body = response:match("^(.-)\r\n\r\n(.*)$")
if not headerBlock then
return nil, "invalid HTTP response"
end
local headers = {}
-- Parse status line
local statusLine, rest = headerBlock:match("^(.-)\r\n(.*)$")
if not statusLine then
statusLine = headerBlock
rest = ""
end
local version, code, message = statusLine:match("^(HTTP/%d+%.%d+)%s+(%d+)%s+(.*)$")
-- Parse headers
for line in (rest .. "\r\n"):gmatch("(.-)\r\n") do
local key, value = line:match("^([^:]+):%s*(.*)$")
if key then
headers[key:lower()] = value
end
end
return {
code=tonumber(code),
message=message,
headers=headers,
body=body
}
end
function http.request(req)
local raw, err =
buildRequest(req)
if not raw then
return nil, err
end
local fd =
syscall.socket()
if not fd then
return nil, "ESOCKET"
end
local host, path = parseUrl(req.url)
if not host then
syscall.close(fd)
return nil, path
end
local proto = req.url:match("^(https?://)")
local base = proto .. host
local ok, err = syscall.connect(fd, base)
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 out = {}
while true do
local chunk = syscall.read(fd, 4096)
if chunk == "" then
break
end
out[#out+1] = chunk
end
syscall.close(fd)
return parseResponse(table.concat(out))
end
function http.get(url, headers)
return http.request({
url = url,
method = "GET",
headers = headers
})
end
function http.post(url, body, headers)
return http.request({
url = url,
method = "POST",
body = body,
headers = headers
})
end
return http
@@ -1,5 +1,5 @@
local io = {} local io = {}
local fs = require("sys.fs") local fs = require("fs")
function io.open(path, mode) function io.open(path, mode)
return fs.open(path, mode) return fs.open(path, mode)
+1
View File
@@ -0,0 +1 @@
Core library for Hyperion
-6
View File
@@ -1,6 +0,0 @@
local sys = {}
local fs = require("sys.fs")
return sys
-5
View File
@@ -1,5 +0,0 @@
local sys = {}
sys.fs = require("sys.fs")
sys.hpv = require("sys.hpv")
sys.ipc = require("sys.ipc")
return sys
-3
View File
@@ -1,3 +0,0 @@
local ipc = {}
return ipc
-71
View File
@@ -1,71 +0,0 @@
local term = {}
function term.clear()
coroutine.yield("VFS_write", 1, "\27C\25")
end
function term.setCursorPos(x, y)
coroutine.yield("VFS_write", 1, "\27cs"..tostring(y)..";"..tostring(x).."\25")
end
function term.size()
coroutine.yield("VFS_write", 1, "\27ts\25")
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
if not ok then error("Failed to get terminal size") end
local x, y = string.match(data, "%R(%d+);(%d+)\25")
return tonumber(x), tonumber(y)
end
function term.getCursorPos()
coroutine.yield("VFS_write", 1, "\27gc\25")
local ok, data = coroutine.yield("VFS_read", 0, 16) -- read response
if not ok then error("Failed to get cursor position") end
local y, x = string.match(data, "%R(%d+);(%d+)\25")
return tonumber(x), tonumber(y)
end
function term.write(data)
coroutine.yield("VFS_write", 1, data)
end
function term.setTextColor(color)
local ok, err = coroutine.yield("VFS_type", 1)
if not ok then error(err) end
if ok ~= "tty" then return end
coroutine.yield("VFS_write", 1, "\27f"..tostring(color).."\25")
end
function term.setBackgroundColor(color)
local ok, err = coroutine.yield("VFS_type", 1)
if not ok then error(err) end
if ok ~= "tty" then return end
coroutine.yield("VFS_write", 1, "\27b"..tostring(color).."\25")
end
function term.isColor()
local ok, err = coroutine.yield("VFS_type", 1)
if not ok then error(err) end
return ok == "tty"
end
function term.scroll(n)
coroutine.yield("VFS_write", 1, "\27S"..tostring(n).."\25")
end
function term.setDefault(color, layer)
if layer then
coroutine.yield("VFS_write", 1, "\27F"..tostring(color).."\25")
else
coroutine.yield("VFS_write", 1, "\27B"..tostring(color).."\25")
end
end
function term.showCursor(show)
if show then
coroutine.yield("VFS_write", 1, "\27sc\25")
else
coroutine.yield("VFS_write", 1, "\27hc\25")
end
end
return term
-43
View File
@@ -1,43 +0,0 @@
--:Minify:--
local kernel=...
local fs=require("sys.fs")
for i,v in pairs(kernel.processes) do
kernel.log("Spawning kernel task "..i)
syscall.spawn(function()
local status, err = pcall(v)
if not status then
kernel.log("Error executing kernel task '" .. i .. "': " .. err, "ERROR")
else
kernel.log("Successfully executed kernel task: " .. i, "INFO")
end
end, i)
end
local files = fs.list("/bin/startup")
if not files then error("Failed to list /bin/startup") end
for i,v in ipairs(files) do
if v:sub(-4) == ".lua" then
local filepath = "/bin/startup/" .. v
kernel.log("Executing startup script: " .. filepath, "INFO")
local startupFunc, err = load(fs.readAllText(filepath), "@" .. filepath)
if not startupFunc then
kernel.log("Error loading startup script '" .. filepath .. "': " .. err, "ERROR")
else
syscall.spawn(function()
syscall.setuid(1)
local status, err = pcall(startupFunc)
if not status then
kernel.log("Error executing startup script '" .. filepath .. "': " .. err, "ERROR")
else
kernel.log("Successfully executed startup script: " .. filepath, "INFO")
end
end, "startup:" .. v)
end
end
end
while true do
sleep(1)
kernel.saveLog()
end
+1
View File
@@ -0,0 +1 @@
1.0.0
+3
View File
@@ -0,0 +1,3 @@
Hyperion
Astronand
GHXX (Advanced Computers)
@@ -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
+1
View File
@@ -0,0 +1 @@
firmware for Advanced Computers
+1
View File
@@ -0,0 +1 @@
1.0.0
@@ -0,0 +1,9 @@
--:Minify:--
local kernel=...
local kbs = {kernel.cct.peripheral.find("tm_keyboard")}
kernel.cct.eventhooks["tm_keyboard_char"]=function(...)
end
+2
View File
@@ -0,0 +1,2 @@
Hyperion
Astronand
@@ -71,7 +71,6 @@ local ok, err = xpcall(function()
collectgarbage = true, collectgarbage = true,
error = true, error = true,
gcinfo = true, gcinfo = true,
getfenv = true,
getmetatable = true, getmetatable = true,
ipairs = true, ipairs = true,
__inext = true, __inext = true,
@@ -85,7 +84,6 @@ local ok, err = xpcall(function()
rawlen = true, rawlen = true,
rawset = true, rawset = true,
select = true, select = true,
setfenv = true,
setmetatable = true, setmetatable = true,
string = true, string = true,
table = true, table = true,
@@ -154,17 +152,106 @@ local ok, err = xpcall(function()
if not initFs then displaySuperBadError("Could not load initdisks.") end if not initFs then displaySuperBadError("Could not load initdisks.") end
if not fs then displaySuperBadError("Could not load initfs.") end if not fs then displaySuperBadError("Could not load initfs.") end
if not apis.fs.exists("/nvram.dat") then
local file = apis.fs.open("/nvram.dat", "w")
file.write("Hello, World!")
file.close()
end
local eeprom
if apis.fs.exists("/startup.lua") then
eeprom="/startup.lua"
elseif apis.fs.exists("/eeprom") then
eeprom="/eeprom"
end
local eventQueue = {} local eventQueue = {}
local function queueEvent(event, ...) local function queueEvent(event, ...)
table.insert(eventQueue, {event, ...}) table.insert(eventQueue, {event, ...})
end end
local computer = { local colors = {
time = function() return apis.os.epoch("utc") end, [0x000000]=0x0001,
clock = function() return apis.os.clock() * 1000 end, [0xFFFFFF]=0x0002,
shutdown = apis.os.shutdown, [0xFF0000]=0x0004,
reboot = apis.os.reboot, [0x00FF00]=0x0008,
[0x0000FF]=0x0010,
[0x00FFFF]=0x0020,
[0xFF00FF]=0x0040,
[0xFFFF00]=0x0080,
[0xFF6D00]=0x0100,
[0x6DFF55]=0x0200,
[0x24FFFF]=0x0400,
[0x924900]=0x0800,
[0x6D6D55]=0x1000,
[0xDBDBAA]=0x2000,
[0x6D00FF]=0x4000,
[0xB6FF00]=0x8000
}
local fg,bg=0x6D6D55,0x000000
local l1f,l1d,l2,ops={},{},{},0
local function findClosest(tbl, target)
local closest = nil
local smallestDiff = math.huge
for k, _ in pairs(tbl) do
local diff = math.abs(k - target)
if diff < smallestDiff then
smallestDiff = diff
closest = k
end
end
return closest
end
local function aprox(c24)
ops = ops + 1
if ops % 1024 == 0 then
l1d = {}
l1f = {}
end
if ops % 8192 == 0 then
l2 = {}
end
if l2[c24] ~= nil then
return l2[c24]
end
if l1d[c24] ~= nil then
l1f[c24] = l1f[c24] + 1
if l1f[c24] >= 16 then
l2[c24] = l1d[c24]
l1d[c24] = nil
l1f[c24] = nil
return l2[c24]
end
return l1d[c24]
end
local closestKey = findClosest(colors, c24)
if not closestKey then return nil end
local value = colors[closestKey]
l1d[c24] = value
l1f[c24] = 1
return value
end
local EFI = {
getEpochMs = function() return apis.os.epoch("utc") end,
getUptime = function() return apis.os.clock() * 1000 end,
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
getMachineEvent = function() getMachineEvent = function()
if #eventQueue > 0 then if #eventQueue > 0 then
return table.unpack(table.remove(eventQueue, 1)) return table.unpack(table.remove(eventQueue, 1))
@@ -172,61 +259,15 @@ local ok, err = xpcall(function()
return nil return nil
end end
end, end,
getEEPROM = function() return getFile("/startup.lua") end, getEEPROM = function() return getFile(eeprom) end,
setEEPROM = function(_, text) setEEPROM = function(_, text)
local h = apis.fs.open("/startup.lua", "w") local h = apis.fs.open(eeprom, "w")
h.write(text) h.write(text)
h.close() h.close()
end end,
} initfs=fs,
disks=initFs,
local icolors = { screenCtl={
[0x1] = 1, -- #000000
[0x2] = 2, -- #FFFFFF
[0x4] = 3, -- #FF0000
[0x8] = 4, -- #00FF00
[0x10] = 5, -- #0000FF
[0x20] = 6, -- #00FFFF
[0x40] = 7, -- #FF00FF
[0x80] = 8, -- #FFFF00
[0x100] = 9, -- #FF6D00
[0x200] = 10, -- #6DFF55
[0x400] = 11, -- #24FFFF
[0x800] = 12, -- #924900
[0x1000] = 13, -- #6D6D55
[0x2000] = 14, -- #DBDBAA
[0x4000] = 15, -- #6D00FF
[0x8000] = 16 -- #B6FF00
}
local colors = {
0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
apis.term.setBackgroundColor(0x8000)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
local kernelCoro = coroutine.create(function()
---@diagnostic disable-next-line: param-type-mismatch
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
{
print = function(_, text) write(text .. "\n") end, print = function(_, text) write(text .. "\n") end,
printInline = function(_, text) write(text) end, printInline = function(_, text) write(text) end,
clear = function() clear = function()
@@ -239,25 +280,51 @@ local ok, err = xpcall(function()
getCursorPos = function() return apis.term.getCursorPos() end, getCursorPos = function() return apis.term.getCursorPos() end,
getSize = function() return apis.term.getSize() end, getSize = function() return apis.term.getSize() end,
setBackgroundColor = function(_, color) setBackgroundColor = function(_, color)
apis.term.setBackgroundColor(colors[color]) apis.term.setBackgroundColor(aprox(color))
end, end,
setTextColor = function(_, color) setTextColor = function(_, color)
apis.term.setTextColor(colors[color]) apis.term.setTextColor(aprox(color))
end, end,
getBackgroundColor = function() getBackgroundColor = function()
return icolors[apis.term.getBackgroundColor()] return bg
end, end,
getTextColor = function() getTextColor = function()
return icolors[apis.term.getTextColor()] return fg
end,
enable=function() end,
disable=function() end
},
architecture="cct",
getNvram = function() return getFile("/nvram.dat") end,
setNvram = function(_, text)
local h = apis.fs.open("/nvram.dat", "w")
h.write(text)
h.close()
end,
firmware=apis,
reboot=false
}
apis.term.setBackgroundColor(0x8000)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
local kernelCoro = coroutine.create(function()
---@diagnostic disable-next-line: param-type-mismatch
local ok, err = xpcall(Kernel, debug.traceback, EFI)
if not ok and not EFI.reboot then displaySuperBadError(err) end
if err then
apis.os.reboot()
else
apis.os.shutdown()
end end
}, computer, fs, "$")
if not ok then displaySuperBadError(err) end
end) end)
function coroutine.resumeWithTimeout(co, timeout, ...) function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time() local startTime = EFI.getEpochMs()
debug.sethook(co, function() debug.sethook(co, function()
if computer.time() > startTime + timeout then if EFI.getEpochMs() > startTime + timeout then
return coroutine.yield("timeout") return coroutine.yield("timeout")
end end
end, "", 1000) end, "", 1000)
@@ -306,11 +373,15 @@ local ok, err = xpcall(function()
end end
end end
if status == "error" or coroutine.status(kernelCoro) == "dead" then if status == "error" or coroutine.status(kernelCoro) == "dead" then
if EFI.reboot then
apis.os.reboot()
end
displaySuperBadError("Kernel error: " .. tostring(err)) displaySuperBadError("Kernel error: " .. tostring(err))
coroutine.yield("key") coroutine.yield("key")
end end
initFs:refresh()
end end
end, debug.traceback) end, debug.traceback)
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
while true do coroutine.yield() end while true do coroutine.yield("key") end
@@ -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,6 @@
--:Minify:--
local kernel=...
if kernel.gfx then
end
@@ -0,0 +1 @@
firmware for CraftOS PC
+1
View File
@@ -0,0 +1 @@
1.0.0
+2
View File
@@ -0,0 +1,2 @@
Hyperion
Astronand
@@ -1,155 +0,0 @@
-- :Minify:--
local apis = ({...})[1]
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getType(name)
if native.isPresent(name) then return native.getType(name) end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and
native.call(side, "isPresentRemote", name) then
return native.call(side, "getTypeRemote", name)
end
end
return nil
end
function peripheral.getNames()
local names = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then table.insert(names, side) end
if native.hasType(side, "peripheral_hub") then
local hubSides = native.call(side, "getConnectedSides")
for _, hubSide in ipairs(hubSides) do
table.insert(names, hubSide)
end
end
end
return names
end
local disks = {}
local internal = {}
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address = id, isReadOnly = function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal() return fs.getCapacity(basePath) end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then
return nil, "not directory"
end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:type(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then
return nil
elseif fs.isDir(p) then
return "directory"
else
return "file"
end
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label) periph.setLabel(label) end
function disk:getLabel(label) return periph.getLabel() end
function disk:attributes(path)
local p = fs.combine(basePath, path)
return fs.attributes(p)
end
function disk:open(path, mode)
local p = fs.combine(basePath, path)
return fs.open(p, mode)
end
return disk
end
internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
setLabel = function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel = function()
local h = fs.open("/.label", "r")
if not h then return "$" end
local label = h.readAll()
h.close()
return label
end
})
local function refresh()
for id, _ in pairs(disks) do
if not peripheral.getType(id) then disks[id] = nil end
end
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "disk" then
if not disks[name] then
local mount = disk.getMountPath(name)
if mount then
disks[name] = createDisk(name, mount, false, disk)
end
end
end
end
end
local function iter()
refresh()
local combined = {}
for id, obj in pairs(internal) do combined[id] = obj end
for id, obj in pairs(disks) do combined[id] = obj end
return pairs(combined)
end
return {refresh = refresh, list = iter}
@@ -0,0 +1,589 @@
--: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)
coroutine.yield("key")
end
term.setCursorBlink(false)
local ok, err = xpcall(function()
--p
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
--p
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
--p
local p={}
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
function p.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 p.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 p.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 p.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 p.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 p.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 p.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 p.wrap(name)
local methods = p.getMethods(name)
if not methods then
return nil
end
local types = { p.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 p.call(name, method, ...)
end
end
return result
end
function p.find(ty, filter)
local results = {}
for _, name in ipairs(p.getNames()) do
if p.hasType(name, ty) then
local wrapped = p.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
--p
local allscreens = {p.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 callAsyncQueue = {}
local callAsyncReturn = {}
apis.callAsyncReturn = callAsyncReturn
local callidx=0
function apis.callAsync(func, ...)
callidx=callidx+1
callAsyncQueue[#callAsyncQueue+1] = {callidx, func, ...}
return callidx
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)
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()
for i=1, #allscreens do
allscreens[i].clear()
allscreens[i].setCursorPos(1, 1)
end
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()
--pf
---@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")
--p
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(table.unpack(event))
queueEvent("keyPressed", 1, event[2])
if acekeys[event[2]] then
queueEvent("keyTyped", 1, acekeys[event[2]])
end
elseif event[1] == "char" then
queueEvent(table.unpack(event))
queueEvent("keyTyped", 1, event[2])
elseif event[1] == "key_up" then
queueEvent(table.unpack(event))
queueEvent("keyReleased", 1, event[2])
elseif event[1] == "NoSleep" then
exit = true
else
queueEvent(table.unpack(event))
end
end
while #callAsyncQueue>0 do
local bundle = table.remove(callAsyncQueue, 1)
local ret = {xpcall(bundle[2], debug.traceback, table.unpack(bundle, 3))}
if not ret[1] then
callAsyncReturn[bundle[1]]={false, ret[2]}
else
callAsyncReturn[bundle[1]]={true, table.unpack(ret,2)}
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")
elseif status == "success" then
if EFI.reboot then
apis.os.reboot()
end
displaySuperBadError("Kernel error: Attempted to yield main thread\n"..debug.traceback(kernelCoro, "Attempted to yield main thread"))
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
@@ -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
@@ -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,195 @@
--:Minify:--
-- CCT driver peripheral helper
local kernel=...
kernel.cct={}
kernel.cct.peripheral={}
kernel.cct.callAsync=kernel.apis.callAsync
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
local id = kernel.cct.callAsync(native.call, name, method, ...)
kernel.currentTask.io=function()
if kernel.apis.callAsyncReturn[id] then
local r=kernel.apis.callAsyncReturn[id]
if not r[1] then error(r[2]) end
kernel.apis.callAsyncReturn[id]=nil
return table.unpack(r,2)
end
end
kernel.currentTask.status="D"
return
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
local id = kernel.cct.callAsync(native.call, side, "callRemote", name, method, ...)
kernel.currentTask.io=function()
if kernel.apis.callAsyncReturn[id] then
local r=kernel.apis.callAsyncReturn[id]
if not r[1] then error(r[2]) end
kernel.apis.callAsyncReturn[id]=nil
return table.unpack(r,2)
end
end
kernel.currentTask.status="D"
return
end
end
return nil
end
function peripheral.callnative(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, wrap)
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,
})
if wrap then
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
else
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.callnative(name, method, ...)
end
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,26 @@
--:Minify:--
local kernel=...
local peripheral=kernel.cct.peripheral
local function wrap(p)
return function(op, mode)
if op=="type" then
return "device"
elseif op=="open" then
if kernel.uid~=0 then error("EACCES") end
return peripheral.wrap(p, true)
end
end
end
local peripherals=peripheral.getNames()
for i=1, #peripherals do
local p=peripherals[i]
local typ=peripheral.getType(p)
local file=wrap(p)
kernel.devfs.data["raw"][p]=file
local typefolder=kernel.devfs.data["raw"]["by-type"][typ]
if not typefolder then typefolder={} end
typefolder[tostring(#table.keys(typefolder)+1)] = file
kernel.devfs.data["raw"]["by-type"][typ]=typefolder
end
@@ -0,0 +1,207 @@
--: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, address)
local headerEnd = raw:find("\r\n\r\n", 1, true)
local headerPart
local body = ""
if headerEnd then
headerPart = raw:sub(1, headerEnd - 1)
body = raw:sub(headerEnd + 4)
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 split=string.split(lines[1], " ")
if #split~=3 then
return nil, "EINVAL"
end
local method, path, ver = table.unpack(split)
local headers = {}
for i = 2, #lines do
local k, v = lines[i]:match("^([^:]+):%s*(.*)$")
if k then
headers[k] = v
end
end
if path:sub(1,1)~="/" then
return nil, "EINVAL"
end
local url = address..path
local req = {
url = url,
method = method,
headers = headers
}
if body ~= "" then
req.body = body
end
return req, url
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 ""
resp.close()
local out = {
"HTTP/1.1 " ..
tostring(code) ..
" " ..
tostring(msg or "")
}
for i,v in pairs(headers) do
out[#out+1] = i..": "..v
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.handle.write = function(raw)
local req, url = parseRawRequest(raw, address)
if not req then
return nil, url
end
kernel.cct.httpqueue[url] = true
local ok, err = http.request(req)
if not ok then
kernel.cct.httpqueue[url] = nil
return nil, err
end
if fdo.socket.active then
fdo.socket.content=nil
fdo.socket.file.close()
fdo.socket.file=nil
end
fdo.socket.active=true
fdo.socket.url=url
fdo.socket.loaded=false
return true
end
fdo.handle.read = function(count)
if not fdo.socket.active then
return
end
if not kernel.cct.httpqueue[fdo.socket.url] and not fdo.socket.loaded then
fdo.socket.file=kernel.sfile(buildResponse(kernel.cct.httpresponse[fdo.socket.url]))
kernel.cct.httpqueue[fdo.socket.url]=nil
kernel.cct.httpresponse[fdo.socket.url]=nil
fdo.socket.loaded=true
end
if not fdo.socket.file then
kernel.currentTask.io=function()
if not kernel.cct.httpqueue[fdo.socket.url] then
fdo.socket.file=kernel.sfile(buildResponse(kernel.cct.httpresponse[fdo.socket.url]))
kernel.cct.httpqueue[fdo.socket.url]=nil
kernel.cct.httpresponse[fdo.socket.url]=nil
fdo.socket.loaded=true
return fdo.socket.file.read(count)
end
end
kernel.currentTask.status="D"
return
else
return fdo.socket.file.read(count)
end
end
fdo.handle.seek = function(whence, offset)
if not fdo.socket.active then
return
end
if not kernel.cct.httpqueue[fdo.socket.url] and not fdo.socket.loaded then
fdo.socket.file=kernel.sfile(buildResponse(kernel.cct.httpresponse[fdo.socket.url]))
kernel.cct.httpqueue[fdo.socket.url]=nil
kernel.cct.httpresponse[fdo.socket.url]=nil
fdo.socket.loaded=true
end
if not fdo.socket.file then
kernel.currentTask.io=function()
if not kernel.cct.httpqueue[fdo.socket.url] then
fdo.socket.file=kernel.sfile(buildResponse(kernel.cct.httpresponse[fdo.socket.url]))
kernel.cct.httpqueue[fdo.socket.url]=nil
kernel.cct.httpresponse[fdo.socket.url]=nil
fdo.socket.loaded=true
return fdo.socket.file.seek(whence, offset)
end
end
kernel.currentTask.status="D"
return
else
return fdo.socket.file.seek(whence, offset)
end
end
fdo.handle.close = function()
fdo.socket.file.close()
fdo.socket.active=false
kernel.cct.httpqueue[fdo.socket.url] = nil
kernel.cct.httpresponse[fdo.socket.url] = nil
kernel.cct.httperror[fdo.socket.url] = nil
return true
end
return true
end
kernel.socket.registerProtocal(
"http://",
handler
)
kernel.socket.registerProtocal(
"https://",
handler
)
@@ -0,0 +1,283 @@
--: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.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,
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,
gctrl=function()
return serializeBool(kernel.cct.ctrl)..";"..serializeBool(kernel.cct.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
@@ -0,0 +1,108 @@
--:Minify:--
local kernel=...
local keys=kernel.apis.keys
kernel.cct.eventhooks={}
kernel.processes.cctdaemon = function()
local timeout = false
kernel.log("CCT daemon started")
while true do
local event = {kernel.EFI:getMachineEvent()}
if event[1] then
local eventType = event[1]
local charOrKey = event[3]
local ctrlKeyMap = {
[keys.a]=1, [keys.b]=2, [keys.c]=3,
[keys.d]=4, [keys.e]=5, [keys.f]=6,
[keys.g]=7, [keys.h]=8, [keys.i]=9,
[keys.j]=10, [keys.k]=11, [keys.l]=12,
[keys.m]=13, [keys.n]=14, [keys.o]=15,
[keys.p]=16, [keys.q]=17, [keys.r]=18,
[keys.s]=19, [keys.t]=20, [keys.u]=21,
[keys.v]=22, [keys.w]=23, [keys.x]=24,
[keys.y]=25, [keys.z]=26,
}
if eventType == "keyPressed" then
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
kernel.cct.ctrl = true
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
kernel.cct.alt = true
end
if kernel.cct.ctrl then
local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1)
end
else
kernel.cct.fifo.push(string.char(ctrlByte))
end
end
else
local specialKeyMap = {
[keys.up] = "",
[keys.down] = "",
[keys.right] = "",
[keys.left] = "",
[keys.home] = "",
[keys["end"]] = "",
[keys.pageUp] = "[5~",
[keys.pageDown] = "[6~",
[keys.delete] = "[3~",
}
local special = specialKeyMap[charOrKey]
if special then kernel.cct.fifo.push(special) end
end
elseif eventType == "keyReleased" then
if charOrKey == keys.leftCtrl or charOrKey == keys.rightCtrl then
kernel.cct.ctrl = false
elseif charOrKey == keys.leftAlt or charOrKey == keys.rightAlt then
kernel.cct.alt = false
end
elseif eventType == "keyTyped" then
if charOrKey then kernel.cct.fifo.push(charOrKey) end
elseif eventType == "mouse_scroll" then
if event[2] == 1 then
kernel.cct.fifo.push("S")
else
kernel.cct.fifo.push("T")
end
elseif eventType == "http_success" then
if kernel.cct.httpqueue[event[2]] then
kernel.cct.httpqueue[event[2]]=nil
kernel.cct.httpresponse[event[2]]=event[3]
end
elseif eventType == "http_failure" then
if kernel.cct.httpqueue[event[2]] then
kernel.cct.httpqueue[event[2]]=nil
kernel.cct.httperror[event[2]]=event[3]
kernel.cct.httpresponse[event[2]]=event[4]
end
else
if kernel.cct.eventhooks[eventType] then
local ok,err = xpcall(kernel.cct.eventhooks[eventType], debug.traceback, table.unpack(event))
if not ok then
kernel.log("Error on eventhook "..eventType.."\n"..err)
end
end
end
timeout = false
else
timeout = true
end
if timeout then
sleep(0.05)
end
end
end
kernel.log("CCT daemon 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
@@ -0,0 +1 @@
firmware for ComputerCraft Tweaked
@@ -1,363 +0,0 @@
-- :Minify:--
local kernel = ...
local apis = kernel.apis
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
local peripheral={}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then -- Peripheral name passed
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 -- Peripheral name passed
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.computer:getMachineEvent()}
if event[1] then
local eventType = event[1]
local charOrKey = event[3]
-- Update modifier keys
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
-- Handle Ctrl+C
if ctrl and charOrKey == apis.keys.c then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1) -- SIGINT
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, "TTY1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5)
v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end)
end
@@ -1,26 +0,0 @@
local args={...}
local kernel=args[1]
local driver={}
driver.name="CCT Term Module"
driver.version="0.1.0"
driver.type="gpio"
driver.description="CCT redstone Module Kernel Module"
driver.arch="cct"
driver.author="HyperionOS Dev Team"
driver.license="MIT"
driver.api={}
function driver.load()
-- will
end
function driver.unload()
-- Nothing to unload
end
function driver.main()
-- Nothing to run
end
-- kernel.drivers.register(driver)
+1
View File
@@ -0,0 +1 @@
1.3.0
+1
View File
@@ -0,0 +1 @@
Hyperion
+1
View File
@@ -0,0 +1 @@
firmware for Opencomputers
+1
View File
@@ -0,0 +1 @@
0.1.0
+3
View File
@@ -0,0 +1,3 @@
Hyperion
Astronand
Spsf
-3
View File
@@ -1,3 +0,0 @@
U $;/
U devfs0000;/dev/
U tmpfs0000;/tmp/
-273
View File
@@ -1,273 +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 kernel = {}
kernel.LOG_Text=""
kernel.version="HyperionOS V1.2.0"
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._G=_G
kernel.sleep=sleep
_G.sleep=nil
local windowsExp = false
function kernel.log(msg, level, c)
c=c or 12
kernel.LOG_Text = kernel.LOG_Text..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
if kernel.status == "start" then
screen:setTextColor(c)
screen:print(string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
elseif kernel.status == "term" then
kernel.standbyTask=kernel.currentTask
kernel.currentTask=kernel.kernelTask
kernel.vfs.devctl(1,"sfgc",c)
kernel.vfs.write(1,string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n")
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(16)
screen:clear()
screen:setCursorPos(1,1)
screen:print(kernel.LOG_Text)
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
screen:print("Press any key to continue...")
kernel.exitMain = true
end
while true do
local event={computer:getMachineEvent()}
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", 2)
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", kernel.LOG_Text)
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
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,
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["time"]=function() return kernel.computer:time() end
kernel.syscalls["log"]=kernel.log
kernel.syscalls["getUptime"]=function() return kernel.computer:clock() 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["test"]=function() return true end
kernel.log("Running modules")
for _,p in ipairs(modules) do
for _,v in ipairs(p) do
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 5) end
local code=ifs.readAllText(v)
if not code then
kernel.log("ModuReadErr: "..v, "WARN", 8)
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
if kernel.config.showModLoad then kernel.log("Loaded module "..v, "DBUG", 5) 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")
+4
View File
@@ -0,0 +1,4 @@
U $;/
U devfs0000;/dev/
U tmpfs0000;/tmp/
U procfs0000;/proc/
+451
View File
@@ -0,0 +1,451 @@
--:Minify:--
local EFI=...
--pf
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"
kernel.tag="1.2.4"
kernel.process = "Kernel"
kernel.users={[0]="root",[1000]="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
function kernel.sfile(str, writeable)
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()
str=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
if writeable then
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
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
function file.content()
return str
end
return file
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]=i.."/"..v
end
end
end
end
kernel.ifs=ifs
--pf
kernel.apis=EFI.firmware
--pf
kernel.EFI=EFI
kernel.arch=arch
kernel.initdisks=disks
kernel.screen=screen
kernel.processes={}
kernel.fstab=fstab
kernel.denied={}
kernel.loadingModule="kernel"
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
function kernel.deny(moduleid)
kernel.denied[moduleid]=true
if kernel.config.showModLoad then kernel.log("Module "..kernel.loadingModule.." denied "..moduleid, "DBUG", 0xFF0000) end
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.syscalls["saveLog"]=function()
if kernel.uid==0 or kernel.groups.wheel then
kernel.saveLog()
end
end
kernel.saveLog()
kernel.log("Running modules")
for _,p in ipairs(modules) do
for _,v in ipairs(p) do
kernel.loadingModule=v
if not kernel.denied[v] then
if kernel.config.showModLoad then kernel.log("Loading module "..v, "DBUG", 0x00FFFF) end
local code=ifs.readAllText("/lib/modules/"..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
if kernel.config.moreLogsaves then kernel.saveLog() end
else
if kernel.config.showModLoad then kernel.log("Denied module "..v, "DBUG", 0xFF0000) end
end
end
end
kernel.log("Kernel initialized successfully.")
kernel.saveLog()
kernel.status="running"
kernel.loadingModule=nil
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,4 +1,8 @@
--:Minify:-- --:Minify:--
--- @diagnostic disable: duplicate-set-field
local kernel = ...
kernel.allowGlobalOverwrites = true
function string.hasSuffix(str, suffix) function string.hasSuffix(str, suffix)
return string.sub(str, #suffix + 1) == suffix return string.sub(str, #suffix + 1) == suffix
end end
@@ -19,6 +23,7 @@ function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now") assert(#delim == 1, "only delim len 1 supported for now")
if not str then return false end if not str then return false end
maxResultCountOrNil = (maxResultCountOrNil or 0) - 1 maxResultCountOrNil = (maxResultCountOrNil or 0) - 1
if str == "" then return {} end
local rv = {} local rv = {}
local buf = "" local buf = ""
for i = 1, #str do for i = 1, #str do
@@ -68,30 +73,24 @@ end
local function serialize(tbl, seen) local function serialize(tbl, seen)
seen = seen or {} seen = seen or {}
-- If we've seen this table before, return a placeholder to prevent infinite loops
if seen[tbl] then return '"[Circular Reference]"' end if seen[tbl] then return '"[Circular Reference]"' end
-- Mark this table as seen
seen[tbl] = true seen[tbl] = true
local output = "{" local output = "{"
local first = true local first = true
for i, v in pairs(tbl) do for i, v in pairs(tbl) do
-- Handle comma placement more cleanly
if not first then output = output .. "," end if not first then output = output .. "," end
first = false first = false
-- Serialize Key
if type(i) == "string" then if type(i) == "string" then
output = output .. "[\"" .. i .. "\"]=" output = output .. "[\"" .. i .. "\"]="
elseif type(i) == "number" then elseif type(i) == "number" then
output = output .. "[" .. tostring(i) .. "]=" output = output .. "[" .. tostring(i) .. "]="
end end
-- Serialize Value
if type(v) == "table" then if type(v) == "table" then
-- Pass the 'seen' table down to the recursive call
output = output .. serialize(v, seen) output = output .. serialize(v, seen)
elseif type(v) == "string" then elseif type(v) == "string" then
output = output .. "[=[" .. v .. "]=]" output = output .. "[=[" .. v .. "]=]"
@@ -175,6 +174,27 @@ function table.indexOf(t, value)
return -1 return -1
end end
function table.merge(...)
local args={...}
local out = {}
local outi = {}
for _,t in ipairs(args) do
for i,v in pairs(t) do
out[i]=v
end
for i,v in ipairs(t) do
outi[#outi+1]=v
end
end
for i,v in ipairs(outi) do
out[i]=v
end
return out
end
function string.replace(s, target, repl) function string.replace(s, target, repl)
local result = {} local result = {}
local i = 1 local i = 1
@@ -210,8 +230,29 @@ function toHex(num)
return string.format("%X", num) return string.format("%X", num)
end end
syscall = setmetatable({}, { 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) __index = function(self, name)
local raw = rawget(self, name)
if raw ~= nil then return raw end
return function(...) return function(...)
local res = table.pack(coroutine.yield("syscall", name, ...)) local res = table.pack(coroutine.yield("syscall", name, ...))
if res[1] then if res[1] then
@@ -220,7 +261,16 @@ syscall = setmetatable({}, {
error(res[2], 2) error(res[2], 2)
end end
end end
end end,
__newindex = function(self, k, v)
rawset(self, k, v)
end,
__metatable=false
}) })
end
syscall = makeSyscallProxy()
_makeSyscallProxy = makeSyscallProxy
table.serialize = serialize table.serialize = serialize
@@ -5,14 +5,14 @@ kernel.vfs = vfs
vfs.mounts = {["$"] = "/"} vfs.mounts = {["$"] = "/"}
vfs.disks = kernel.disks vfs.disks = kernel.disks
-- Metafile format (version 1) -- Metafile format (version 2)
-- File header: 1 byte = version (0x01) -- File header: 1 byte = version (0x02)
-- Per-entry: -- Per-entry:
-- 1 byte = name length -- 1 byte = name length
-- N bytes = name -- N bytes = name
-- 1 byte = entry type (0x00 = regular, 0x01 = symlink) -- 1 byte = entry type (0x00 = regular, 0x01 = symlink)
-- 1 byte = owner uid -- 2 bytes = owner uid (little-endian uint16)
-- 1 byte = group gid -- 2 bytes = group gid (little-endian uint16)
-- 2 bytes = perms (little-endian uint16) -- 2 bytes = perms (little-endian uint16)
-- bit 0 = world-write bit 1 = world-read -- bit 0 = world-write bit 1 = world-read
-- bit 2 = group-write bit 3 = group-read -- bit 2 = group-write bit 3 = group-read
@@ -24,12 +24,16 @@ vfs.disks = kernel.disks
-- 1 byte = cmeta length -- 1 byte = cmeta length
-- N bytes = cmeta (for symlinks: the link target path) -- N bytes = cmeta (for symlinks: the link target path)
-- --
-- Version 1:
-- 1 byte name len, N bytes name, 1 byte etype, 1 byte owner,
-- 1 byte group, 2 bytes perms (little-endian), 1 byte cmeta len, N bytes cmeta
--
-- Version 0: -- Version 0:
-- No file header. Per-entry: -- No file header. Per-entry:
-- 1 byte name len, N bytes name, 1 byte owner, 1 byte group, -- 1 byte name len, N bytes name, 1 byte owner, 1 byte group,
-- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta -- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta
local META_VERSION = 0x01 local META_VERSION = 0x02
local function bit_is_set(num, bit) local function bit_is_set(num, bit)
return math.floor(num / (2 ^ bit)) % 2 == 1 return math.floor(num / (2 ^ bit)) % 2 == 1
@@ -41,8 +45,9 @@ local function parseMetafile(raw)
local p = 1 local p = 1
local version = 0 local version = 0
if raw:byte(1) == META_VERSION then local firstByte = raw:byte(1)
version = META_VERSION if firstByte == 0x02 or firstByte == 0x01 then
version = firstByte
p = 2 p = 2
end end
@@ -54,7 +59,13 @@ local function parseMetafile(raw)
local etype, owner, group, perms, cmeta local etype, owner, group, perms, cmeta
if version == META_VERSION then if version == 0x02 then
if p + 6 > #raw then break end
etype = raw:byte(p); p = p + 1
owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
group = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2
elseif version == 0x01 then
if p + 4 > #raw then break end if p + 4 > #raw then break end
etype = raw:byte(p); p = p + 1 etype = raw:byte(p); p = p + 1
owner = raw:byte(p); p = p + 1 owner = raw:byte(p); p = p + 1
@@ -87,35 +98,40 @@ local function makeMetafile(meta)
for name, m in pairs(meta) do for name, m in pairs(meta) do
local plo = m.perms % 256 local plo = m.perms % 256
local phi = math.floor(m.perms / 256) % 256 local phi = math.floor(m.perms / 256) % 256
local olo = (m.owner or 0) % 256
local ohi = math.floor((m.owner or 0) / 256) % 256
local glo = (m.group or 0) % 256
local ghi = math.floor((m.group or 0) / 256) % 256
out = out out = out
.. string.char(#name) .. name .. string.char(#name) .. name
.. string.char(m.etype or 0x00) .. string.char(m.etype or 0x00)
.. string.char(m.owner, m.group, plo, phi) .. string.char(olo, ohi, glo, ghi, plo, phi)
.. string.char(#m.cmeta) .. m.cmeta .. string.char(#m.cmeta) .. m.cmeta
end end
return out return out
end end
local function normalizePath(path) local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$"
local task = kernel.currentTask
local cwd = task.cwd or "/" local function tokenizePath(path)
if path:sub(1,1) ~= "/" then path = cwd .. "/" .. path end local isAbsolute = (path:sub(1,1) == "/")
local parts = {} local tokens = {}
for part in path:gmatch("[^/]+") do for comp in (path .. "/"):gmatch("([^/]*)/") do
if part == ".." then table.insert(tokens, comp)
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
table.insert(parts, part)
end end
return isAbsolute, tokens
end end
local result = "/" .. table.concat(parts, "/")
local root = task and task.root local function validateComponent(comp)
if root and root ~= "/" then local lower = comp:lower()
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then if not comp:match(SAFE_COMPONENT_PATTERN) then
result = root error("EINVAL: illegal characters in path component: " .. comp, 3)
end end
if lower == ".meta" then
error("EINVAL: reserved path component: .meta", 3)
elseif lower:sub(#lower) == "." then
error("Cannot have empty name extention: " .. comp, 3)
end end
return result
end end
function vfs.splitPath(path) function vfs.splitPath(path)
@@ -146,8 +162,10 @@ local function resolveMount(normalPath)
return vfs.disks[mountId], diskPath return vfs.disks[mountId], diskPath
end end
vfs._parseMetafile = parseMetafile
local function readMetaEntry(disk, parentDiskPath, filename) local function readMetaEntry(disk, parentDiskPath, filename)
if filename == ".meta" then error("Cannot open metafile") end if filename == ".meta" then error("EACCES: Cannot open metafile") end
local mp local mp
if parentDiskPath == "/" then if parentDiskPath == "/" then
mp = ".meta" mp = ".meta"
@@ -159,52 +177,218 @@ local function readMetaEntry(disk, parentDiskPath, filename)
if not ok or not f then return nil end if not ok or not f then return nil end
local raw = f.read(65535) local raw = f.read(65535)
if f.close then f.close() end if f.close then f.close() end
if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then
local upgraded = makeMetafile(parseMetafile(raw))
local wok, wf = pcall(function() return disk:open(mp, "w") end)
if wok and wf then wf.write(upgraded); if wf.close then wf.close() end end
raw = upgraded
end
local parsed = parseMetafile(raw) local parsed = parseMetafile(raw)
return parsed[filename] return parsed[filename]
end end
local MAX_SYMLINK = 16 local MAX_SYMLINK = 16
local function resolveSymlinks(path, noFollow, _depth)
_depth = _depth or 0
if _depth > MAX_SYMLINK then error("ELOOP") end
path = normalizePath(path)
local parts = {} local function namei(path, noFollow, symDepth)
for p in path:gmatch("[^/]+") do table.insert(parts, p) end symDepth = symDepth or 0
if symDepth > MAX_SYMLINK then error("ELOOP") end
local resolved = "" local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid
local groups = (task and task.groups) or kernel.groups or {}
local root = (task and task.root) or "/"
local cwd = (task and task.cwd) or "/"
for i, part in ipairs(parts) do if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
local candidate = resolved == "" and ("/" .. part) or (resolved .. "/" .. part)
if noFollow and i == #parts then local function canTraverse(entry)
resolved = candidate if euid == 0 then return true end
break if not entry then return true end
local bits = entry.perms
if euid == entry.owner and bit_is_set(bits, 9) then return true end
if entry.group then
for _, gid in ipairs(groups) do
if gid == entry.group and bit_is_set(bits, 8) then return true end
end
end
return bit_is_set(bits, 7)
end end
local disk, parentDisk = resolveMount(resolved == "" and "/" or resolved) local isAbsolute, tokens = tokenizePath(path)
local entry = readMetaEntry(disk, parentDisk, part)
local stack = {}
if isAbsolute then
stack = {}
else
for seg in cwd:gmatch("[^/]+") do table.insert(stack, seg) end
end
local i = 1
while i <= #tokens do
local comp = tokens[i]
i = i + 1
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
local currentPath = "/" .. table.concat(stack, "/")
local jailStack = {}
if root ~= "/" then
for seg in root:gmatch("[^/]+") do table.insert(jailStack, seg) end
end
if #stack <= #jailStack then
stack = {}
for _, seg in ipairs(jailStack) do table.insert(stack, seg) end
else
local exitName = stack[#stack]
local parentPath = "/" .. table.concat(stack, "/", 1, #stack - 1)
if parentPath == "/" then parentPath = "/" end
local okM, diskM, dpM = pcall(resolveMount, parentPath == "" and "/" or parentPath)
if okM and diskM then
local entry = readMetaEntry(diskM, dpM, exitName)
if entry then
if entry.etype ~= 0x00 then
error("ENOTDIR: not a directory: " .. currentPath)
end
if not canTraverse(entry) then
error("EACCES: permission denied traversing " .. currentPath)
end
else
local okD, diskD, dpD = pcall(resolveMount, currentPath)
if okD and diskD then
local dtype = diskD:type(dpD)
if dtype ~= nil and dtype ~= "directory" then
error("ENOTDIR: not a directory: " .. currentPath)
end
end
end
end
table.remove(stack)
end
else
validateComponent(comp)
local lname = comp
local curPath = "/" .. table.concat(stack, "/")
local okM, diskM, dpM = pcall(resolveMount, curPath == "/" and "/" or curPath)
local entry = nil
if okM and diskM then
entry = readMetaEntry(diskM, dpM, lname)
end
local isFinal = (i > #tokens)
if entry and entry.etype == 0x01 then if entry and entry.etype == 0x01 then
if isFinal and noFollow then
table.insert(stack, lname)
else
symDepth = symDepth + 1
if symDepth > MAX_SYMLINK then error("ELOOP") end
local target = entry.cmeta local target = entry.cmeta
if target:sub(1,1) ~= "/" then if not target or target == "" then
target = (resolved == "" and "/" or resolved) .. "/" .. target error("ENOENT: empty symlink target")
end
if i < #parts then
target = target .. "/" .. table.concat(parts, "/", i+1, #parts)
end
return resolveSymlinks(normalizePath(target), noFollow, _depth + 1)
end end
resolved = candidate local symIsAbs, symTokens = tokenizePath(target)
if symIsAbs then
stack = {}
if root ~= "/" then
for seg in root:gmatch("[^/]+") do table.insert(stack, seg) end
end
end end
if resolved == "" then resolved = "/" end local fresh = {}
return resolved for j = 1, i - 2 do table.insert(fresh, tokens[j]) end
local insertAt = #fresh + 1
for _, t in ipairs(symTokens) do table.insert(fresh, t) end
for j = i, #tokens do table.insert(fresh, tokens[j]) end
tokens = fresh
i = insertAt
end
else
table.insert(stack, lname)
if not isFinal then
local newPath = "/" .. table.concat(stack, "/")
local okD, diskD, dpD = pcall(resolveMount, newPath)
if okD and diskD then
local dtype = diskD:type(dpD)
if dtype ~= nil and dtype ~= "directory" then
error("ENOTDIR: not a directory: " .. newPath)
end
end
if not canTraverse(entry) then
error("EACCES: permission denied traversing " .. newPath)
end
end
end
end
end
local result = "/" .. table.concat(stack, "/")
if root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root
end
end
return result
end
local function normalizePath(path)
local task = kernel.currentTask
local cwd = (task and task.cwd) or "/"
local root = (task and task.root) or "/"
if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
local isAbsolute, tokens = tokenizePath(path)
local stack = {}
if not isAbsolute then
for seg in cwd:gmatch("[^/]+") do table.insert(stack, seg) end
end
local jailStack = {}
if root ~= "/" then
for seg in root:gmatch("[^/]+") do table.insert(jailStack, seg) end
end
for _, comp in ipairs(tokens) do
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
if #stack > #jailStack then
table.remove(stack)
end
else
table.insert(stack, comp)
end
end
local result = "/" .. table.concat(stack, "/")
if root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root
end
end
return result
end end
local function resolvePath(path, noFollow) local function resolvePath(path, noFollow)
local real = resolveSymlinks(path, noFollow) local real = namei(path, noFollow)
local disk, diskPath = resolveMount(real) local disk, diskPath = resolveMount(real)
if kernel.config.logPathResolution then if kernel.config.logPathResolution then
kernel.log("resolvePath '"..path.."' -> '"..real.."' diskPath '"..diskPath.."'") kernel.log("resolvePath '"..path.."' -> '"..real.."' diskPath '"..diskPath.."'")
@@ -213,24 +397,42 @@ local function resolvePath(path, noFollow)
end end
local function getFileMeta(path, noFollow) local function getFileMeta(path, noFollow)
local real = resolveSymlinks(path, noFollow) local real = namei(path, noFollow)
local parts = {} if kernel.unixSockets[real] then
for p in real:gmatch("[^/]+") do table.insert(parts, p) end return kernel.unixSockets[real].meta
end
local default = { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" } if real == "/" then
if #parts == 0 then return default end return { etype = 0x00, owner = 0, group = 0, perms = 62, cmeta = "" }
end
local parentNorm = "/" .. table.concat(parts, "/", 1, #parts - 1) local cur = real
if parentNorm == "" then parentNorm = "/" end
local disk, parentDiskPath = resolveMount(parentNorm) -- FML i hated implementing this - Astronand
local entry = readMetaEntry(disk, parentDiskPath, parts[#parts]) while true do
if entry then return entry end local parent, name = cur:match("^(.*)/([^/]+)$")
return default if not parent or parent == "" then parent = "/" end
local disk, parentDiskPath = resolveMount(parent)
local entry = readMetaEntry(disk, parentDiskPath, name)
if entry then
return entry
end
if parent == "/" or cur == "/" then
break
end
cur = parent
end
return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
end end
local function writeMetaEntry(path, name, entry, noFollow) local function writeMetaEntry(path, name, entry, noFollow)
local real = resolveSymlinks(path, noFollow) local real = namei(path, noFollow)
local disk, diskPath = resolveMount(real) local disk, diskPath = resolveMount(real)
local mp local mp
@@ -303,7 +505,7 @@ local function checkperms(meta, mode)
local bitmap = { local bitmap = {
r = {owner = 5, group = 3, everyone = 1}, r = {owner = 5, group = 3, everyone = 1},
w = {owner = 4, group = 2, everyone = 0}, w = {owner = 4, group = 2, everyone = 0},
a = {owner = 4, group = 2, everyone = 0}, a = {owner = 4, group = 2, everyone = 0}
} }
local m = bitmap[mode] local m = bitmap[mode]
if not m then error("EINVAL") end if not m then error("EINVAL") end
@@ -340,6 +542,7 @@ local function allocFD(task)
if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end if fd >= kernel.config.maxFilesPerTask then error("ENFILE") end
return fd return fd
end end
local function checkSystemLimit() local function checkSystemLimit()
if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end if total >= kernel.config.maxOpenFiles - 16 then error("ENFILE") end
end end
@@ -348,32 +551,93 @@ local function newFileObj(handle, mode, path, meta, ftype)
end end
function vfs.newfd(fdobj) function vfs.newfd(fdobj)
checkSystemLimit(); total = total + 1
local fd = allocFD(kernel.currentTask) local fd = allocFD(kernel.currentTask)
kernel.currentTask.fd[fd] = fdobj kernel.currentTask.fd[fd] = fdobj
return fd return fd
end end
function vfs.mount(target, diskOrId) function vfs.mount(target, diskOrId, bind)
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if _euid ~= 0 then error("EPERM") end if _euid ~= 0 then error("EPERM") end
if not target then error("EINVAL") end if not target then error("EINVAL") end
target = normalizeMountPoint(target) target = normalizeMountPoint(target)
if not vfs.exists(target) then vfs.mkdir(target) end local drive, path = resolvePath(target)
if vfs.type(target) ~= "directory" then error("EINVAL") end if not drive:directoryExists(path) then
drive:makeDirectory(path)
end
if drive:type(path) ~= "directory" then
error("EINVAL")
end
local disk, id local disk, id
-- bind mount
if bind then
local src = normalizeMountPoint(diskOrId)
local srcDrive = resolvePath(src)
if not srcDrive then error("ENOENT") end
id = "bind:" .. src
disk = {
address = id,
isBind = true,
source = srcDrive,
exists = function(_, p)
return srcDrive:exists(p)
end,
type = function(_, p)
return srcDrive:type(p)
end,
list = function(_, p)
return srcDrive:list(p)
end,
open = function(_, ...)
return srcDrive:open(...)
end,
remove = function(_, p)
return srcDrive:remove(p)
end,
makeDirectory = function(_, p)
return srcDrive:makeDirectory(p)
end,
rename = function(_, a, b)
return srcDrive:rename(a, b)
end,
size = function(_, p)
return srcDrive:size(p)
end,
lastModified = function(_, p)
return srcDrive:lastModified(p)
end,
isReadOnly = function()
return srcDrive:isReadOnly()
end,
spaceTotal = function()
return srcDrive:spaceTotal()
end,
spaceUsed = function()
return srcDrive:spaceUsed()
end
}
vfs.disks[id] = disk
else
if type(diskOrId) == "string" then if type(diskOrId) == "string" then
disk = kernel.disks[diskOrId] disk = vfs.disks[diskOrId]
if not disk then error("ENODEV") end if not disk then error("ENODEV") end
checkDisk(disk); id = diskOrId checkDisk(disk)
id = diskOrId
elseif type(diskOrId) == "table" then elseif type(diskOrId) == "table" then
checkDisk(diskOrId); disk = diskOrId checkDisk(diskOrId)
id = disk.address; vfs.disks[id] = disk disk = diskOrId
else error("EINVAL") end id = disk.address
vfs.disks[id] = disk
else
error("EINVAL")
end
end
if vfs.mounts[id] then error("EBUSY") end if vfs.mounts[id] then error("EBUSY") end
for _, mp in pairs(vfs.mounts) do if mp == target then error("EBUSY") end end for _, mp in pairs(vfs.mounts) do
if mp == target then
error("EBUSY")
end
end
vfs.mounts[id] = target vfs.mounts[id] = target
return true return true
end end
@@ -386,7 +650,11 @@ function vfs.umount(target)
for id, mp in pairs(vfs.mounts) do for id, mp in pairs(vfs.mounts) do
if mp == target then if mp == target then
if id == "$" then error("EBUSY") end if id == "$" then error("EBUSY") end
vfs.mounts[id] = nil; return true vfs.mounts[id] = nil
if vfs.disks[id] and vfs.disks[id].isBind then
vfs.disks[id] = nil
end
return true
end end
end end
error("EINVAL") error("EINVAL")
@@ -398,20 +666,55 @@ function vfs.open(path, mode)
local fd = allocFD(task) local fd = allocFD(task)
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if not disk then error("NODISK") end if not disk then error("NODISK") end
if (mode=="w" or mode=="a") and disk:isReadOnly() then error("ERDONLY") end
if kernel.unixSockets[namei(path)] then
local meta = kernel.unixSockets[namei(path)].meta
if kernel.uid~=0 or kernel.uid~=meta.owner then
local groups = (task and task.groups) or kernel.groups or {}
local access=false
for _, gid in ipairs(groups) do
if gid == meta.group then access=true end
end
if not access then error("EACCES") end
end
task[fd]=kernel.unixSockets[namei(path)]
return fd
end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
checkperms(meta, mode == "r" and "r" or "w") checkperms(meta, mode == "r" and "r" or "w")
local handle local handle
if disk:type(diskPath) ~= "directory" then if disk:type(diskPath) ~= "directory" then
handle = disk:open(diskPath, mode) handle = disk:open(diskPath, mode)
if type(handle) ~= "table" then error("ENFILE") end if type(handle, true) ~= "table" then
error("ENFILE")
end
end
if isNew then
local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0
local norm = normalizePath(path)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local name = norm:match("[^/]+$")
if name then
local entry = { etype=0x00, owner=euid, group=egid,
perms=vfs.PERM.RW_R_R, cmeta="" }
pcall(writeMetaEntry, parent, name, entry, false)
meta = entry
end
end end
local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath)) local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath))
if mode == "r" and bit_is_set(meta.perms, 6) then if mode == "r" and bit_is_set(meta.perms, 6) then
fobj.suid_owner = meta.owner fobj.suid_owner = meta.owner
end end
if disk.isvirt then fobj.isvirt=true end
task.fd[fd] = fobj task.fd[fd] = fobj
if not disk.isvirt then total = total + 1 end if not disk.isvirt then total = total + 1 end
return fd return fd
@@ -460,8 +763,10 @@ function vfs.close(fd)
local task = kernel.currentTask local task = kernel.currentTask
local file = task.fd[fd] local file = task.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
task.fd[fd] = nil if not task.fd[fd].isvirt then
total = total - 1 total = total - 1
end
task.fd[fd] = nil
file.refcount = file.refcount - 1 file.refcount = file.refcount - 1
if file.refcount <= 0 and file.handle and file.handle.close then if file.refcount <= 0 and file.handle and file.handle.close then
file.handle.close() file.handle.close()
@@ -479,10 +784,17 @@ function vfs.sendfile(outfd, infd, count)
end end
function vfs.stat(path) function vfs.stat(path)
local attrs
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path) local meta = getFileMeta(path)
local ok, attrs = pcall(disk.attributes, disk, diskPath) if meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 }
else
local ok
ok, attrs = pcall(disk.attributes, disk, diskPath)
if not ok then attrs = { size=0, modified=0, created=0 } end if not ok then attrs = { size=0, modified=0, created=0 } end
end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -497,15 +809,15 @@ end
function vfs.lstat(path) function vfs.lstat(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
local attrs local attrs
if meta.etype == 0x01 then if meta.etype == 0x01 or meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 } attrs = { size=0, modified=0, created=0 }
else else
local disk, diskPath = resolvePath(path, true) local disk, diskPath = resolvePath(path, true)
local ok, a = pcall(disk.attributes, disk, diskPath) local ok, a = pcall(disk.attributes, disk, diskPath)
attrs = ok and a or { size=0, modified=0, created=0 } attrs = ok and a or { size=0, modified=0, created=0 }
end end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -522,8 +834,15 @@ end
function vfs.fstat(fd) function vfs.fstat(fd)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
local disk, diskPath = resolvePath(file.path) local attrs
local attrs = disk:attributes(diskPath) if file.meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 }
else
local disk, diskPath = resolvePath(file.path, true)
local ok, a = pcall(disk.attributes, disk, diskPath)
attrs = ok and a or { size=0, modified=0, created=0 }
end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -552,6 +871,14 @@ function vfs.listdir(path)
end end
end end
for k,v in pairs(kernel.unixSockets) do
local p=normalizePath(path)
if k:match("^(.*)/[^/]+$")==p then
seen[v.name]=true
table.insert(out, v.name)
end
end
local mp local mp
if diskPath == "/" then if diskPath == "/" then
mp = ".meta" mp = ".meta"
@@ -574,26 +901,50 @@ function vfs.listdir(path)
end end
function vfs.mkdir(path) function vfs.mkdir(path)
local norm = normalizePath(path)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w")
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path) if disk:isReadOnly() then error("ERDONLY") end
checkperms(meta, "w")
disk:makeDirectory(diskPath) disk:makeDirectory(diskPath)
local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0
local name = norm:match("[^/]+$")
if name then
local entry = { etype=0x00, owner=euid, group=egid,
perms=vfs.PERM.RWX_RX, cmeta="" }
pcall(writeMetaEntry, parent, name, entry, false)
end
end end
function vfs.remove(path) function vfs.remove(path)
local meta = getFileMeta(path, true) local norm = namei(path, true)
checkperms(meta, "w") local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w")
if kernel.unixSockets and kernel.unixSockets[path] then local meta = getFileMeta(path, true)
kernel.unixSockets[path] = nil
if kernel.unixSockets[namei(path)] then
if kernel.uid ~= 0 then
if kernel.unixSockets[namei(path)].meta.owner~=kernel.uid then
error("EACCES")
end
end
kernel.unixSockets[namei(path)] = nil
end end
if meta.etype == 0x01 then if meta.etype == 0x01 then
local norm = resolveSymlinks(path, true) local norm = namei(path, true)
local parent = norm:match("^(.*)/[^/]+$") or "/" local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
local disk, parentDiskPath = resolveMount(parent) local disk, parentDiskPath = resolveMount(parent)
if disk:isReadOnly() then error("ERDONLY") end
local mp local mp
if parentDiskPath == "/" then mp = ".meta" if parentDiskPath == "/" then mp = ".meta"
else mp = parentDiskPath:gsub("^/+", "") .. "/.meta" end else mp = parentDiskPath:gsub("^/+", "") .. "/.meta" end
@@ -610,6 +961,7 @@ function vfs.remove(path)
if f2.close then f2.close() end if f2.close then f2.close() end
else else
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if disk:isReadOnly() then error("ERDONLY") end
disk:remove(diskPath) disk:remove(diskPath)
end end
end end
@@ -618,6 +970,8 @@ function vfs.symlink(target, linkPath)
if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end
local norm = normalizePath(linkPath) local norm = normalizePath(linkPath)
local parent = norm:match("^(.*)/[^/]+$") or "/" local parent = norm:match("^(.*)/[^/]+$") or "/"
local disk = resolveMount(linkPath)
if disk:isReadOnly() then error("ERDONLY") end
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
if not name then error("EINVAL") end if not name then error("EINVAL") end
@@ -647,7 +1001,13 @@ function vfs.readlink(path)
end end
function vfs.access(path, mode) function vfs.access(path, mode)
local meta = getFileMeta(path) local meta
if kernel.unixSockets[normalizePath(path)] then
meta = kernel.unixSockets[normalizePath(path)].meta
else
meta = getFileMeta(path)
end
for i = 1, #mode do for i = 1, #mode do
checkperms(meta, mode:sub(i,i)) checkperms(meta, mode:sub(i,i))
end end
@@ -655,9 +1015,11 @@ function vfs.access(path, mode)
end end
local function updateMeta(path, fn, noFollow) local function updateMeta(path, fn, noFollow)
local real = resolveSymlinks(path, noFollow) local real = namei(path, noFollow)
local norm = real local norm = real
local parent = norm:match("^(.*)/[^/]+$") or "/" local parent = norm:match("^(.*)/[^/]+$") or "/"
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
if not name then error("EINVAL") end if not name then error("EINVAL") end
@@ -684,6 +1046,9 @@ local function updateMeta(path, fn, noFollow)
end end
function vfs.chmod(path, perms) function vfs.chmod(path, perms)
if kernel.unixSockets[namei(path)] then error("EINVAL") end
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if euid ~= 0 and euid ~= meta.owner then error("EACCES") end if euid ~= 0 and euid ~= meta.owner then error("EACCES") end
@@ -693,10 +1058,14 @@ end
function vfs.fchmod(fd, perms) function vfs.fchmod(fd, perms)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
if file.meta.etype==0x02 then error("EINVAL") end
vfs.chmod(file.path, perms) vfs.chmod(file.path, perms)
end end
function vfs.chown(path, uid, gid) function vfs.chown(path, uid, gid)
if kernel.unixSockets[namei(path)] then error("EINVAL") end
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if _euid ~= 0 then error("EPERM") end if _euid ~= 0 then error("EPERM") end
updateMeta(path, function(e) e.owner = uid; e.group = gid end) updateMeta(path, function(e) e.owner = uid; e.group = gid end)
@@ -705,20 +1074,29 @@ end
function vfs.fchown(fd, uid, gid) function vfs.fchown(fd, uid, gid)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
if file.meta.etype==0x02 then error("EINVAL") end
vfs.chown(file.path, uid, gid) vfs.chown(file.path, uid, gid)
end end
function vfs.exists(path) function vfs.exists(path)
if kernel.unixSockets[namei(path)] then return true end
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if meta.etype == 0x01 then return true end if meta.etype == 0x01 or meta.etype == 0x02 then return true end
local ok, disk, diskPath = pcall(resolvePath, path) local ok, disk, diskPath = pcall(resolvePath, path)
if not ok then return false end if not ok then return false end
return disk:fileExists(diskPath) if disk:type(diskPath) then
return true
else
return false
end
end end
function vfs.type(path) function vfs.type(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if meta.etype == 0x01 then return "symlink" end if meta.etype == 0x01 then return "symlink" end
if kernel.unixSockets[namei(path)] then
return "socket"
end
local ok, disk, diskPath = pcall(resolvePath, path) local ok, disk, diskPath = pcall(resolvePath, path)
if not ok then return nil end if not ok then return nil end
return disk:type(diskPath) return disk:type(diskPath)
@@ -771,6 +1149,12 @@ function vfs.devctl(fd, method, ...)
return kernel.currentTask.fd[fd].handle[method](...) return kernel.currentTask.fd[fd].handle[method](...)
end end
function vfs.devlst(fd)
if not kernel.currentTask.fd[fd] then error("EBADF") end
if not kernel.currentTask.fd[fd] then error("EINVAL") end
return table.keys(kernel.currentTask.fd[fd].handle)
end
vfs.resolveMount = resolveMount vfs.resolveMount = resolveMount
local sys = kernel.syscalls local sys = kernel.syscalls
@@ -803,6 +1187,7 @@ sys["chroot"] = vfs.chroot
sys["dup"] = vfs.dup sys["dup"] = vfs.dup
sys["dup2"] = vfs.dup2 sys["dup2"] = vfs.dup2
sys["devctl"] = vfs.devctl sys["devctl"] = vfs.devctl
sys["devlst"] = vfs.devlst
sys["symlink"] = vfs.symlink sys["symlink"] = vfs.symlink
sys["readlink"] = vfs.readlink sys["readlink"] = vfs.readlink
sys["access"] = vfs.access sys["access"] = vfs.access
@@ -0,0 +1,47 @@
--: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
local args = {...}
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
error("Module not found: "..module)
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
end
kernel.vfs.close(fd)
local data = table.concat(chunks)
local func, err = load(data, "@"..module, "t", kernel._U)
if not func then
error("Error loading module "..module..": "..tostring(err))
end
local ok, ret = xpcall(func, debug.traceback, table.unpack(args))
if not ok then
error("Error running module "..module..": "..tostring(ret))
end
cache[module] = {ret = ret, expires = kernel.EFI:getEpochMs() + 120000}
return ret
end
coroutine.yield()
end
error("Module not found: "..module)
end
_G.require = function(...) return require(...) end
@@ -0,0 +1,309 @@
--: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",
stderr="/proc/self/2",
fd="/proc/self/fd",
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 kernel.uid~=0 then error("EACCES") end
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
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
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 kernel.uid~=0 then error("EACCES") end
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
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
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"]={}}
-- disks
for i,v in pairs(kernel.vfs.mounts) do
end
data["raw"]={["by-type"]={}}
data["gfx"]={}
data["input"]={}
data["tty"]={}
kernel.devfs={}
kernel.devfs.data=data
kernel.devfs.proxy=proxy
kernel.disks["devfs0000"]=proxy
@@ -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
@@ -0,0 +1,141 @@
--:Minify:--
local kernel = ...
kernel.vfs.remove("/tmp")
kernel.vfs.mkdir("/tmp")
if not kernel.config.tmpToDisk then
local proxy = {}
local data = {}
proxy.address = "tmpfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
if not step[steps[i]] then
step[steps[i]] = {}
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
end
proxy.remove = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
step = step[steps[i]]
if not step then error("ENOENT") end
end
step[steps[#steps]] = nil
end
proxy.setLabel = function(_, label) end
proxy.getLabel = function() return "tmpfs" end
proxy.attributes = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
return {
size = type(step) == "string" and #step or 0,
modified = 0,
created = 0,
}
end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
if not step[steps[i]] then
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
local filename = steps[#steps]
if mode == "r" then
if type(step[filename]) ~= "string" then error("ENOENT") end
local content = step[filename]
local pos = 1
return {
read = function(amount)
amount = amount or #content
local chunk = content:sub(pos, pos+amount-1)
pos = pos + #chunk
return chunk
end,
close = function() end,
}
elseif mode == "w" then
step[filename] = ""
local buf = {}
return {
write = function(str)
buf[#buf + 1] = str
end,
close = function()
step[filename] = table.concat(buf)
end,
}
elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end
return {
write = function(str)
step[filename] = step[filename] .. str
end,
close = function() end,
}
else
error("EACCES")
end
end
function proxy:type(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then return "directory" end
for i=1,#steps do
step = step[steps[i]]
if not step then return false end
end
if type(step) == "table" then return "directory" end
if type(step) == "string" then return "file" end
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
if type(step) ~= "table" then error("ENOTDIR") end
local keys = {}
for k,_ in pairs(step) do table.insert(keys, k) end
return keys
end
function proxy:fileExists(path)
local t = self:type(path)
return t == "file" or t == "directory"
end
kernel.disks["tmpfs0000"] = proxy
else
kernel.log("tmpToDisk enabled, skipping tmpfs module")
kernel.log("tmpfs0000 will passthrough to /tmp on the disk")
end
@@ -21,14 +21,15 @@ for _, line in ipairs(string.split(kernel.fstab, "\n")) do
end end
if not semicolon_pos or semicolon_pos == 3 then if not semicolon_pos or semicolon_pos == 3 then
kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 8) kernel.log("Invalid fstab line: "..line.." ... Skipping.", "WARN", 0xFF8800)
else else
local id = line:sub(3, semicolon_pos - 1) local id = line:sub(3, semicolon_pos - 1)
local path = trim(line:sub(semicolon_pos + 1)) local path = trim(line:sub(semicolon_pos + 1))
kernel.log("Mounted "..id.." to "..path) kernel.log("Mounting '"..id.."' to '"..path.."'")
if id ~= "$" then if id ~= "$" then
kernel.vfs.mount(path, id) kernel.vfs.mount(path, id)
end end
kernel.log("Mounted "..id.." to "..path)
end end
end end
end end
@@ -12,13 +12,24 @@ function signal.sigsend(pid, sig)
end end
function signal.sigcatch(handler) function signal.sigcatch(handler)
kernel.currentTask.sigh=handler local task=kernel.currentTask
if not kernel.currentTask.sigq then kernel.currentTask.sigq={} end task.sigh=handler
if not task.sigq then task.sigq={} end
local handle={
error="",
active=true
}
if task.sigd then task.sigd.active=false; end
task.sigd=handle
return handle
end end
function signal.sigignore() function signal.sigignore()
kernel.currentTask.sigh=nil local task=kernel.currentTask
kernel.currentTask.sigq=nil task.sigh=nil
task.sigq=nil
if task.sigd then task.sigd.active=false end
task.sigd=nil
end end
local s=kernel.syscalls local s=kernel.syscalls
@@ -0,0 +1,199 @@
--: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()
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 nil, "ENOTCONN"
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
function socket.socketendpoints()
return table.keys(socket.handlers)
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
sys.socketendpoints = socket.socketendpoints
kernel.log("Loaded socket module")
@@ -0,0 +1,4 @@
--:Minify:--
local kernel=...
kernel.unixSockets={}
local socket=kernel.socket
@@ -3,6 +3,7 @@ local args = {...}
local kernel = args[1] local kernel = args[1]
kernel._G = _G kernel._G = _G
local function readonly(tbl) local function readonly(tbl)
return setmetatable({}, { return setmetatable({}, {
__index = function(_, key) __index = function(_, key)
@@ -20,7 +21,7 @@ local function readonly(tbl)
error("Attempt to modify global variable '" .. k .. "'", 2) error("Attempt to modify global variable '" .. k .. "'", 2)
end, end,
__pairs = function() __pairs = function(self)
local function iter(_, key) local function iter(_, key)
local nextKey, value = next(tbl, key) local nextKey, value = next(tbl, key)
if type(value) == "table" then if type(value) == "table" then
@@ -28,7 +29,7 @@ local function readonly(tbl)
end end
return nextKey, value return nextKey, value
end end
return iter, tbl, nil return iter, self, nil
end, end,
__ipairs = function() __ipairs = function()
@@ -49,8 +50,24 @@ local function readonly(tbl)
__metatable = false __metatable = false
}) })
end end
local origLoad = load
kernel._U = readonly(kernel._G) kernel._U = readonly(kernel._G)
kernel.allowGlobalOverwrites = true
kernel._U._G = kernel._U kernel._U._G = kernel._U
kernel.allowGlobalOverwrites = false 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
@@ -0,0 +1,129 @@
--:Minify:--
local kernel = ...
local vterms = {}
local function createVt(id, width, height)
local vt = {
id = id,
width = width,
height = height,
buffer = {},
cursorX = 1,
cursorY = 1,
fgColor = 0xFFFFFF,
bgColor = 0x000000,
obj = {}
}
for y = 1, height do
vt.buffer[y] = {}
for x = 1, width do
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
end
local function scroll(lines)
for _ = 1, lines do
table.remove(vt.buffer, 1)
vt.buffer[vt.height] = {}
for x = 1, vt.width do
vt.buffer[vt.height][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
end
end
function vt.obj:write(content)
local x, y = vt.cursorX, vt.cursorY
if x>vt.width then return end
if y>vt.height then return end
for i = 1, #content do
local c = content:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
for _ = 1, spaces do
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
x = x + 1
if x > vt.width then
x = 1
y = y + 1
end
end
elseif c == "\b" then
if x > 1 then
x = x - 1
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
else
if x <= vt.width and y <= vt.height then
vt.buffer[y][x] = {char = c, fgColor = vt.fgColor, bgColor = vt.bgColor}
x = x + 1
end
end
if x > vt.width then
x = 1
y = y + 1
end
if y > vt.height then
scroll(1)
y = vt.height
end
end
vt.cursorX, vt.cursorY = x, y
end
function vt.obj:spos(x, y)
vt.cursorX = tonumber(x)
vt.cursorY = tonumber(y)
end
function vt.obj:gpos()
return tostring(vt.cursorX)..";"..tostring(vt.cursorY)
end
function vt.obj:sfgc(color)
vt.fgColor = tonumber(color)
end
function vt.obj:sbgc(color)
vt.bgColor = tonumber(color)
end
function vt.obj:gfgc()
return vt.fgColor
end
function vt.obj:gbgc()
return vt.bgColor
end
function vt.obj:gplt()
return 24
end
function vt.obj:clear()
for y = 1, vt.height do
for x = 1, vt.width do
vt.buffer[y][x] = {char = " ", fgColor = vt.fgColor, bgColor = vt.bgColor}
end
end
vt.cursorX, vt.cursorY = 1, 1
end
function vt.obj:isvirt()
return true
end
function vt.obj:gctrl()
return serializeBool(vt.ctrl)..";"..serializeBool(vt.alt)
end
return vt
end
@@ -139,7 +139,7 @@ end
if not blake2s then error("Failed to load blake2s") end if not blake2s then error("Failed to load blake2s") end
if not kernel.vfs.exists("/etc/pam.d/secret") then if not kernel.vfs.exists("/etc/pam.d/secret") then
kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT") kernel.log("PAM SECRET REGENERATING PLEASE USE ROOT", "WARN", 0xFF8800)
local key = "" local key = ""
for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end for i = 1, 256 do key = key .. string.char(math.random(0, 255)) end
local handle = kernel.vfs.open("/etc/pam.d/secret", "w") local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
@@ -150,16 +150,11 @@ end
local pepper = getFile("/etc/pam.d/secret") local pepper = getFile("/etc/pam.d/secret")
local function genSalt() local function genSalt()
local chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" return toHex(math.random(0,2^32))
local s = ""
for i = 1, 16 do
s = s .. chars:sub(math.random(1, #chars), math.random(1, #chars))
end
return s
end end
local function hashPassword(password, salt) local function hashPassword(password, salt)
local key = (pepper .. salt):sub(1, 32) local key = (pepper .. salt)
return blake2s(password, key) return blake2s(password, key)
end end
@@ -236,19 +231,18 @@ local function nextUID()
return max + 1 return max + 1
end end
function auth.login(username, password) function auth.login(uid, password)
if type(username) ~= "string" or type(password) ~= "string" then if type(uid) ~= "number" or type(password) ~= "string" then
return nil, "Authentication failure" return nil, "Authentication failure"
end end
local entry = getPasswdByUsername(username) local entry = getPasswdByUID(uid)
if not entry then if not entry then
-- timing attack resistance -- timing attack resistance
hashPassword(password, "aaaaaaaaaaaaaaaa") hashPassword(password, "aaaaaaaaaaaaaaaa")
return nil, "Authentication failure" return nil, "Authentication failure"
end end
local uid = tonumber(entry[1])
local sEntry = getShadowByUID(uid) local sEntry = getShadowByUID(uid)
if not sEntry then if not sEntry then
hashPassword(password, "aaaaaaaaaaaaaaaa") hashPassword(password, "aaaaaaaaaaaaaaaa")
@@ -273,7 +267,7 @@ function auth.login(username, password)
_task.egid = tonumber(entry[2]) or uid _task.egid = tonumber(entry[2]) or uid
end end
kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. username .. ")") kernel.log("AUTH: login uid=" .. tostring(uid) .. " (" .. getPasswdByUID(uid)[3] .. ")")
return true return true
end end
@@ -394,6 +388,8 @@ function auth.newUser(username, password, gid, homedir, shell)
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
kernel.vfs.mkdir(homedir) kernel.vfs.mkdir(homedir)
-- Homedir must be owned by the new user, not root
pcall(kernel.vfs.chown, homedir, uid, uid)
end end
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid)) kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))
@@ -591,13 +587,13 @@ function auth.elevate(targetUsername, password)
local task = kernel.currentTask local task = kernel.currentTask
local prevUid = task.uid local prevUid = task.uid
task.uid = uid task.uid = 0
task.euid = uid task.euid = 0
task.gid = tonumber(entry[2]) or uid task.gid = 0
task.egid = tonumber(entry[2]) or uid task.egid = 0
kernel.uid = uid kernel.uid = 0
kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")") kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")")
return true, uid return true, uid
end end
@@ -5,19 +5,23 @@ local sys = {}
local nextpid = 2 local nextpid = 2
kernel.exitMain = false kernel.exitMain = false
local resumeWithTimeout = coroutine.resumeWithTimeout
local function bit_is_set(num, bit) local function bit_is_set(num, bit)
return math.floor(num / (2 ^ bit)) % 2 == 1 return math.floor(num / (2 ^ bit)) % 2 == 1
end end
local function loadExecutable(path, env) local function loadExecutable(path)
kernel.vfs.access(path, "rx") kernel.vfs.access(path, "rx")
local fd = kernel.vfs.open(path, "r") local fd = kernel.vfs.open(path, "r")
local data = kernel.vfs.read(fd, 1024 * 1024 * 4) local data = kernel.vfs.read(fd, 1024 * 1024 * 4)
kernel.vfs.close(fd) kernel.vfs.close(fd)
local func, err = load(data, "@" .. path, "t", env or kernel._U) local env = kernel.freshUserEnv()
if not func then error("ENOEXEC: " .. tostring(err)) end
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 meta = kernel.vfs.lstat(path)
local suid_set = bit_is_set(meta.perms, 6) local suid_set = bit_is_set(meta.perms, 6)
@@ -27,21 +31,29 @@ local function loadExecutable(path, env)
return func, euid, suid_set return func, euid, suid_set
end end
local function createTask(func, name, envars, args, tgid, real_uid, eff_uid) local function createTask(func, name, envars, args, tgid, real_uid, eff_uid, clearFds)
local id = nextpid local id = nextpid
nextpid = nextpid + 1 nextpid = nextpid + 1
local fds={}
if not clearFds then
for id, file in pairs(kernel.currentTask.fd) do
file.refcount=file.refcount+1
fds[id]=file
end
end
tasks[tostring(id)] = { tasks[tostring(id)] = {
coro = coroutine.create(function() coro = coroutine.create(function()
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {})) local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if not ok then if not ok then
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 2) kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 0xFF0000)
elseif err then elseif err then
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "INFO") kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "DBUG", 0x00FFFF)
else else
kernel.log("Task " .. tostring(id) .. " exited without code", "INFO") kernel.log("Task " .. tostring(id) .. " exited without code", "DBUG", 0x00FFFF)
end end
end end
@@ -67,7 +79,7 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
euid = eff_uid, euid = eff_uid,
gid = (kernel.currentTask and kernel.currentTask.gid) or 0, gid = (kernel.currentTask and kernel.currentTask.gid) or 0,
groups = (kernel.currentTask and kernel.currentTask.groups) or {}, groups = (kernel.currentTask and kernel.currentTask.groups) or {},
fd = {}, fd = fds,
sleep = 0, sleep = 0,
ivs = 0, ivs = 0,
vs = 0, vs = 0,
@@ -89,14 +101,14 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
return id return id
end end
function sys.spawn(func, name, envars, args, tgid) function sys.spawn(func, name, envars, args, tgid, clearFds)
local caller = kernel.currentTask local caller = kernel.currentTask
local real_uid = caller and caller.uid or kernel.uid local real_uid = caller and caller.uid or kernel.uid
local eff_uid = caller and caller.euid or real_uid local eff_uid = caller and caller.euid or real_uid
return createTask(func, name, envars, args, tgid, real_uid, eff_uid) return createTask(func, name, envars, args, tgid, real_uid, eff_uid, clearFds)
end end
function sys.execspawn(path, name, envars, args, tgid) function sys.execspawn(path, name, envars, args, tgid, keepFds)
local func, euid, suid_active = loadExecutable(path, kernel._U) local func, euid, suid_active = loadExecutable(path, kernel._U)
local caller = kernel.currentTask local caller = kernel.currentTask
@@ -106,11 +118,11 @@ function sys.execspawn(path, name, envars, args, tgid)
kernel.log( kernel.log(
"execspawn: suid exec '" .. path .. "execspawn: suid exec '" .. path ..
"' caller_uid=" .. tostring(real_uid) .. "' caller_uid=" .. tostring(real_uid) ..
" -> euid=" .. tostring(euid), "INFO" " -> euid=" .. tostring(euid)
) )
end end
return createTask(func, name or path, envars, args, tgid, real_uid, euid) return createTask(func, name or path, envars, args, tgid, real_uid, euid, not keepFds)
end end
function sys.exec(path, args, envars) function sys.exec(path, args, envars)
@@ -132,9 +144,9 @@ function sys.exec(path, args, envars)
local ok, err = xpcall(func, debug.traceback, table.unpack(task.args)) local ok, err = xpcall(func, debug.traceback, table.unpack(task.args))
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if not ok then if not ok then
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 2) kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 0xFF0000)
else else
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "INFO") kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "DBUG", 0x00FFFF)
end end
end end
if type(err) == "number" then tasks[tostring(task.pid)].exit = err end if type(err) == "number" then tasks[tostring(task.pid)].exit = err end
@@ -146,13 +158,11 @@ function sys.exec(path, args, envars)
tasks[tostring(task.pid)].status = "Z" tasks[tostring(task.pid)].status = "Z"
end) end)
task.syscallReturn = {} task.syscallReturn = {}
coroutine.yield()
end end
function sys.sleep(s) function sys.sleep(s)
kernel.currentTask.status = "S" kernel.currentTask.status = "S"
kernel.currentTask.sleep = kernel.computer:time() + s * 1000 kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
coroutine.yield()
end end
function sys.getTask(pid) function sys.getTask(pid)
@@ -200,26 +210,40 @@ function sys.collect(pid)
end end
end end
function sys.kill(pid) function sys.kill(pid, force)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status == "Z" then elseif task.status == "Z" then
return false, "Task is already dead" return false, "Task is already dead"
else 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" task.status = "Z"
return true return true
end end
end
function sys.stop(pid) function sys.stop(pid)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "R" then elseif task.status ~= "R" and task.status ~= "S" then
return false, "Cannot stop non-running task" return false, "Cannot stop non-running task"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else
if task.status == "S" then
task.status = "ST"
else else
task.status = "T" task.status = "T"
end
return true return true
end end
end end
@@ -228,10 +252,16 @@ function sys.continue(pid)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "T" then elseif task.status ~= "T" and task.status ~= "ST" then
return false, "Task is not stopped" return false, "Task is not stopped"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else
if task.status == "ST" then
task.status = "S"
else else
task.status = "R" task.status = "R"
end
return true return true
end end
end end
@@ -252,9 +282,9 @@ function sys.exit(code)
local task = kernel.currentTask local task = kernel.currentTask
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if code then if code then
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "INFO") kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "DBUG", 0x00FFFF)
else else
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "INFO") kernel.log("Task " .. tostring(task.pid) .. " exited without code", "DBUG", 0x00FFFF)
end end
end end
tasks[tostring(task.pid)].status = "Z" tasks[tostring(task.pid)].status = "Z"
@@ -282,6 +312,7 @@ function sys.getuid() return kernel.currentTask.uid end
local function reapDeadTasks() local function reapDeadTasks()
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then if task.status == "Z" and not task.reapTime then
if task.pid == 1 then kernel.panic("Attempted to gc init!") end
task.coro = nil task.coro = nil
task.ivs = nil task.ivs = nil
task.vs = nil task.vs = nil
@@ -295,9 +326,9 @@ local function reapDeadTasks()
task.syscallReturn = nil task.syscallReturn = nil
task.sleep = nil task.sleep = nil
task.fd = nil task.fd = nil
task.reapTime = kernel.computer:time() + 30000 task.reapTime = kernel.EFI:getEpochMs() + 30000
elseif task.reapTime and kernel.computer:time() > task.reapTime elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
and task.status == "Z" then and task.status == "Z" then
for _, child in ipairs(task.children) do for _, child in ipairs(task.children) do
child.parent = tasks["1"] child.parent = tasks["1"]
@@ -327,7 +358,23 @@ local k_max = 0.5
local B = 0.01 local B = 0.01
function kernel.main() function kernel.main()
kernel.log("Starting main loop...")
kernel.saveLog()
local stopLog=5
local logTasks=100
while not kernel.exitMain do while not kernel.exitMain do
if kernel.config.logTasks then
if logTasks<0 then
kernel.log("Active Tasks:")
for i,v in pairs(tasks) do
kernel.log(v.name.." : "..v.status.." : "..tostring(v.pid).." : "..tostring(v.exit))
end
kernel.log("[END BLOCK]")
logTasks=100
end
logTasks=logTasks-1
end
local N = 0 local N = 0
local Tmin_hit = 0 local Tmin_hit = 0
local Tmax_hit = 0 local Tmax_hit = 0
@@ -335,40 +382,88 @@ function kernel.main()
local taskTimes = {} local taskTimes = {}
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "S" and kernel.computer:time() >= task.sleep then if kernel.exitMain then break end
kernel.currentTask = task
kernel.uid = task.euid or task.uid
kernel.process = task.name
if task.status == "S" and kernel.EFI:getEpochMs() >= task.sleep then
task.status = "R" task.status = "R"
task.sleep = 0 task.sleep = 0
end end
if task.status == "R" then if task.status == "ST" and kernel.EFI:getEpochMs() >= task.sleep then
kernel.currentTask = task task.status = "T"
task.sleep = 0
end
kernel.uid = task.euid or task.uid if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
kernel.process = task.name task.status = "D"
task.sleep = 0
end
if task.status == "D" then
if task.io then
local ret = {xpcall(task.io, debug.traceback)}
if not ret[1] then
task.syscallReturn = {false, table.unpack(ret, 2)}
task.status = "R"
task.io=nil
elseif #ret>1 then
task.syscallReturn = {true, table.unpack(ret, 2)}
task.status = "R"
task.io=nil
end
end
end
if task.status == "R" then
N = N + 1 N = N + 1
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha))) task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
if task.sigq and #task.sigq ~= 0 and task.sigh then if task.sigq and #task.sigq ~= 0 and task.sigh then
local coro = coroutine.create(task.sigh) local coro = coroutine.create(task.sigh)
local status,err
if kernel.config.preempt then if kernel.config.preempt then
coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1)) status,err=coroutine.resumeWithTimeout(coro, 100, table.remove(task.sigq, 1))
else else
coroutine.resume(coro, table.remove(task.sigq, 1)) 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
end end
if task.status == "R" then if task.status == "R" then
local startTime = kernel.computer:time() local startTime = kernel.EFI:getEpochMs()
local ret local ret
if stopLog > 0 then
kernel.log("Running task " .. tostring(task.pid) .. " (" .. task.name .. ") with time slice " .. string.format("%.3f", task.timeSlice) .. "s", "DBUG", 0x00FFFF)
end
if kernel.config.preempt then if kernel.config.preempt then
ret = { coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } if not task.debugger then
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end
else else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) } ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end end
local elapsed = kernel.computer:time() - startTime local elapsed = kernel.EFI:getEpochMs() - startTime
task.lastTime = elapsed task.lastTime = elapsed
task.totalTime = (task.totalTime or 0) + elapsed task.totalTime = (task.totalTime or 0) + elapsed
task.numRuns = (task.numRuns or 0) + 1 task.numRuns = (task.numRuns or 0) + 1
@@ -380,7 +475,7 @@ function kernel.main()
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
if ret[1] == "error" or ret[1] == false then if ret[1] == "error" or ret[1] == false then
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 2) kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 0xFF0000)
task.status = "Z" task.status = "Z"
task.exit = "processHandlerException: " .. tostring(ret[2]) task.exit = "processHandlerException: " .. tostring(ret[2])
@@ -395,9 +490,9 @@ function kernel.main()
local scname = ret[3] local scname = ret[3]
if kernel.syscalls[scname] then if kernel.syscalls[scname] then
if kernel.config.debugSyscalls then if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 5) kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 0x00FFFF)
for i = 4, #ret do for i = 4, #ret do
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 5) kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 0x00FFFF)
end end
end end
@@ -405,12 +500,12 @@ function kernel.main()
if kernel.config.debugSyscalls then if kernel.config.debugSyscalls then
if not sysret[1] then if not sysret[1] then
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 2) kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 0xFF0000)
else else
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 5) kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 0x00FFFF)
for i = 2, #sysret do for i = 2, #sysret do
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i]) local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 5) kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
end end
end end
end end
@@ -443,6 +538,11 @@ function kernel.main()
end end
reapDeadTasks() reapDeadTasks()
if stopLog > 0 then
kernel.log("Executed " .. N .. " tasks, avg time: " .. string.format("%.2f", T_prev_avg) .. "ms, var: " .. string.format("%.2f", T_prev_var) .. ", B: " .. string.format("%.5f", B))
stopLog = stopLog - 1
kernel.saveLog()
end
end end
end end
@@ -1,3 +1,4 @@
--:Minify:--
local kernel=... local kernel=...
local sysc=kernel.syscalls local sysc=kernel.syscalls
kernel.gpio={} kernel.gpio={}
@@ -8,8 +9,20 @@ sysc["gpio_write"]=function(pin, data)
end end
end end
sysc["gpio_writeAnalog"]=function(pin, data)
if kernel.gpio[pin] then
return kernel.gpio[pin]("wa", data)
end
end
sysc["gpio_read"]=function(pin) sysc["gpio_read"]=function(pin)
if kernel.gpio[pin] then if kernel.gpio[pin] then
return kernel.gpio[pin]("r") return kernel.gpio[pin]("r")
end end
end end
sysc["gpio_readAnalog"]=function(pin)
if kernel.gpio[pin] then
return kernel.gpio[pin]("ra")
end
end
@@ -20,3 +20,24 @@ function printInline(...)
output = output:sub(1, -2) output = output:sub(1, -2)
syscall.write(1, output) syscall.write(1, output)
end end
function userinput(fd, helpstr, replacestr, fdo)
local str = ""
if helpstr then print(helpstr) end
while true do
local inp = syscall.read(fd)
if inp~="" and inp~=nil then
if inp~="\n" then
if replacestr then
syscall.write(fdo, replacestr)
else
syscall.write(fdo, inp)
end
str = inp
else
break
end
end
end
return str
end
@@ -9,11 +9,13 @@ if not initOk then
end end
local handle = kernel.vfs.open(kernel.config.initPath, "r") local handle = kernel.vfs.open(kernel.config.initPath, "r")
if not handle then kernel.panic("Failed to open "..kernel.config.initPath) end
local data = kernel.vfs.read(handle, 1024 * 1024 * 4) local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
if not handle then kernel.panic("Failed to read "..kernel.config.initPath) end
kernel.vfs.close(handle) kernel.vfs.close(handle)
local initFunc, err = load(data, "@sysinit", "t", kernel._U) local initFunc, err = load(data, "@sysinit", "t", kernel._U)
if not initFunc then error("Failed to load init system: " .. err) end if not initFunc then kernel.PANIC("Failed to load init system: " .. err) end
kernel.tasks["1"] = { kernel.tasks["1"] = {
coro = coroutine.create(function() coro = coroutine.create(function()
@@ -23,6 +25,7 @@ kernel.tasks["1"] = {
else else
kernel.panic("Init system exited: " .. tostring(err)) kernel.panic("Init system exited: " .. tostring(err))
end end
kernel.saveLog()
end), end),
name = "sysinit", name = "sysinit",
@@ -0,0 +1,12 @@
--:Minify:--
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
sleep(5)
end
end
@@ -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
View File
@@ -0,0 +1 @@
Hyperion kernel package
@@ -1,40 +0,0 @@
-- :Minify:--
local kernel = ...
local cache = {}
kernel.searchpaths = {
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
}
function require(module, ...)
if cache[module] then return cache[module] end
local modpath = module:gsub("%.", "/")
local failed = {}
for _, path in ipairs(kernel.searchpaths) do
local full_path = string.replace(path, "?", modpath)
if full_path:sub(1, 1) ~= "/" then
full_path = kernel.currentTask.cwd .. full_path
end
if kernel.vfs.exists(full_path) then
if kernel.vfs.type(full_path) == "directory" then
full_path = full_path .. "/init"
end
if kernel.vfs.exists(full_path) then
local handle = kernel.vfs.open(full_path, "r")
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
return
assert(load(file_content, full_path, "t", kernel._U))(...)
else
table.insert(failed, full_path)
end
else
table.insert(failed, full_path)
end
end
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
end
@@ -1,147 +0,0 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "devfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("EACCES") end
proxy.remove = function() error("EACCES") end
proxy.setLabel = function() error("EACCES") end
proxy.getLabel = function() return "devfs" end
proxy.attributes = function(path) return {
size = 0,
modified = 0,
created = 0,
} end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
error("ENFILE")
end
function proxy:type(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return "directory"
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
error("ENOENT")
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return table.keys(data)
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
error("ENOENT")
end
function proxy:fileExists(path)
local ok = pcall(function()
return self:type(path)
end)
return ok
end
function data.random(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(math.random(0, 255))
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.null(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount) end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.zero(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(0)
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
data["disk"]={}
kernel.devfs={}
kernel.devfs.data=data
kernel.devfs.proxy=proxy
kernel.disks["devfs0000"]=proxy
@@ -1,133 +0,0 @@
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "tmpfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
if not step[steps[i]] then
step[steps[i]] = {}
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
end
proxy.remove = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
step = step[steps[i]]
if not step then error("ENOENT") end
end
step[steps[#steps]] = nil
end
proxy.setLabel = function(_, label) end
proxy.getLabel = function() return "tmpfs" end
proxy.attributes = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
return {
size = type(step) == "string" and #step or 0,
modified = 0,
created = 0,
}
end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
if not step[steps[i]] then
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
local filename = steps[#steps]
if mode == "r" then
if type(step[filename]) ~= "string" then error("ENOENT") end
local content = step[filename]
local pos = 1
return {
read = function(amount)
amount = amount or #content
local chunk = content:sub(pos, pos+amount-1)
pos = pos + #chunk
return chunk
end,
close = function() end,
}
elseif mode == "w" then
step[filename] = ""
local buf = {}
return {
write = function(str)
buf[#buf + 1] = str
end,
close = function()
step[filename] = table.concat(buf)
end,
}
elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end
return {
write = function(str)
step[filename] = step[filename] .. str
end,
close = function() end,
}
else
error("EACCES")
end
end
function proxy:type(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then return "directory" end
for i=1,#steps do
step = step[steps[i]]
if not step then return false end
end
if type(step) == "table" then return "directory" end
if type(step) == "string" then return "file" end
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
if type(step) ~= "table" then error("ENOTDIR") end
local keys = {}
for k,_ in pairs(step) do table.insert(keys, k) end
return keys
end
function proxy:fileExists(path)
local t = self:type(path)
return t == "file" or t == "directory"
end
kernel.disks["tmpfs0000"] = proxy
@@ -1,22 +0,0 @@
---- :Minify:--
--local kernel = ...
--
--local timeout = false
--kernel.processes.keventd = function()
-- while true do
-- local event = {kernel.computer:getMachineEvent()}
-- if event[1] then
-- if event[1] == "keyTyped" then
-- if event[3] == "\x1b^s" then
-- kernel.shutdown()
-- elseif event[3] == "\x1b^r" then
-- kernel.reboot()
-- end
-- end
-- timeout = false
-- else
-- timeout = true
-- end
-- if timeout then sleep(.05) end
-- end
--end
@@ -1,556 +0,0 @@
-- :Minify:--
-- Supports:
-- AF_UNIX - local IPC via /var/run/*.sock paths
-- AF_INET - network sockets with three backends:
-- rednet://0.0.B.C or rednet+PROTO://0.0.B.C -> CC rednet (computer B*256+C)
-- modem://0.0.B.C -> raw CC modem frames
-- http://host/path or https://... -> HTTP via CC http API
-- A.B.C.D (dotted quad, non-zero A) -> HTTP
--
-- Socket lifecycle:
-- fd = syscall.socket(domain, socktype) -- "unix"/"inet", "stream"/"dgram"
-- syscall.bind(fd, address) -- server: claim address
-- syscall.listen(fd, backlog) -- server: mark as listening
-- cfd = syscall.accept(fd) -- server: get connected client fd (blocking poll)
-- syscall.connect(fd, address) -- client: connect to server
-- syscall.send(fd, data) -- send bytes
-- syscall.recv(fd, len) -- receive bytes (blocking poll, returns "" on nothing)
-- syscall.sockshutdown(fd) -- half-close send side
-- -- normal vfs.close(fd) closes the socket
local kernel = ...
local sockets = {}
local unixSocks = {}
local nextSockId = 1
local function allocSockId()
local id = nextSockId
nextSockId = nextSockId + 1
return id
end
local function parseAddress(addr)
if not addr then error("EINVAL") end
if addr:sub(1,1) == "/" or addr:sub(1,5) == "unix:" then
local path = addr:sub(1,5) == "unix:" and addr:sub(6) or addr
return { backend="unix", path=path }
end
local rproto, raddr = addr:match("^rednet%+?([^:/]*)://(.+)$")
if raddr then
local a,b,c,d = raddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
if not a then error("EINVAL: bad rednet address " .. raddr) end
local compId = tonumber(c)*256 + tonumber(d)
return { backend="rednet", compId=compId,
protocol=(rproto ~= "" and rproto or "hyperion") }
end
local maddr = addr:match("^modem://(.+)$")
if maddr then
local a,b,c,d = maddr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
if not a then error("EINVAL: bad modem address " .. maddr) end
local compId = tonumber(c)*256 + tonumber(d)
local port = tonumber(maddr:match(":(%d+)$")) or 0
return { backend="modem", compId=compId, port=port }
end
local scheme, rest = addr:match("^(https?)://(.+)$")
if scheme then
return { backend=scheme, url=addr }
end
local a,b,c,d = addr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)")
if a and tonumber(a) ~= 0 then
return { backend="http", url="http://" .. addr }
end
error("EINVAL: unrecognised address format: " .. tostring(addr))
end
local rednetOpen = false
local function ensureRednet()
if rednetOpen then return end
local rn = kernel.apis and kernel.apis.rednet
if not rn then error("ENODEV: no rednet API available") end
local peripheral = kernel.apis.peripheral
if peripheral then
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
if peripheral.getType(name) == "modem" then
pcall(rn.open, name)
end
end
end
rednetOpen = true
end
local function getModem()
local peripheral = kernel.apis and kernel.apis.peripheral
if not peripheral then error("ENODEV") end
for _, name in ipairs(peripheral.getNames and peripheral.getNames() or {}) do
if peripheral.getType(name) == "modem" then
local m = peripheral.wrap(name)
if m then return m, name end
end
end
error("ENODEV: no modem peripheral found")
end
local function pumpEvents()
local ev = kernel.computer:getMachineEvent()
while ev do
if ev == "rednet_message" then
for _, sock in pairs(sockets) do
if sock.backend == "rednet" and sock.bound then
if sock.address.protocol == tostring(select(4, table.unpack({ev}))) or
sock.address.protocol == "hyperion" then
end
end
end
end
ev = kernel.computer:getMachineEvent()
end
end
local function pollEvent()
local results = table.pack(kernel.computer:getMachineEvent())
if results.n == 0 or results[1] == nil then return nil end
return results
end
local function dispatchEvent(ev)
if not ev then return end
local evtype = ev[1]
if evtype == "rednet_message" then
local senderId = ev[2]
local message = ev[3]
local protocol = ev[4] or "hyperion"
for _, sock in pairs(sockets) do
if sock.backend == "rednet" and (sock.listening or sock.connected) then
if sock.address and sock.address.protocol == protocol then
table.insert(sock.rxbuf, { from=senderId, data=message })
end
end
end
elseif evtype == "modem_message" then
local channel = ev[3]
local msg = ev[5]
local fromCh = ev[4]
for _, sock in pairs(sockets) do
if sock.backend == "modem" and sock.modemChannel == channel then
table.insert(sock.rxbuf, { from=fromCh, data=msg })
end
end
elseif evtype == "http_success" then
local url = ev[2]
local handle = ev[3]
for _, sock in pairs(sockets) do
if sock.backend == "http" or sock.backend == "https" then
if sock.pendingUrl == url then
local body = handle.readAll and handle.readAll() or ""
handle.close()
table.insert(sock.rxbuf, { data=body, done=true })
sock.pendingUrl = nil
sock.connected = true
end
end
end
elseif evtype == "http_failure" then
local url = ev[2]
local err = ev[3]
for _, sock in pairs(sockets) do
if (sock.backend == "http" or sock.backend == "https") and
sock.pendingUrl == url then
sock.error = err
sock.pendingUrl = nil
end
end
end
end
local function pumpAll()
local ev = pollEvent()
while ev do
dispatchEvent(ev)
ev = pollEvent()
end
end
local function newSocket(domain, socktype)
local sock = {
id = allocSockId(),
domain = domain, -- "unix" | "inet"
socktype = socktype, -- "stream" | "dgram"
backend = nil,
state = "idle", -- idle | bound | listening | connected | closed
rxbuf = {},
txbuf = {},
backlog = {},
address = nil,
peer = nil,
modemChannel = nil,
modem = nil,
pendingUrl = nil,
bound = false,
listening = false,
connected = false,
error = nil,
}
sockets[sock.id] = sock
return sock
end
local sockSend, sockClose
local function socketToFd(sock)
return {
isSocket = true,
sockId = sock.id,
mode = "rw",
meta = { etype=0, owner=0, group=0, perms=0x1FF, cmeta="" },
type = "socket",
refcount = 1,
handle = {
read = function(count)
pumpAll()
if #sock.rxbuf == 0 then return "" end
local item = table.remove(sock.rxbuf, 1)
local data = type(item) == "table" and (item.data or "") or tostring(item)
if count and #data > count then
table.insert(sock.rxbuf, 1, { data=data:sub(count+1), from=item.from })
data = data:sub(1, count)
end
return data
end,
write = function(data)
if sock.state == "closed" then error("EBADF") end
return sockSend(sock, data)
end,
close = function()
sockClose(sock)
end,
}
}
end
sockSend = function(sock, data)
if sock.backend == "unix" then
local peer = sock.peer
if not peer then error("ENOTCONN") end
table.insert(peer.rxbuf, { data=data })
return #data
elseif sock.backend == "rednet" then
ensureRednet()
local rn = kernel.apis.rednet
rn.send(sock.address.compId, data, sock.address.protocol)
return #data
elseif sock.backend == "modem" then
local modem = sock.modem
if not modem then error("ENOTCONN") end
modem.transmit(sock.address.port, sock.modemChannel or 0, data)
return #data
elseif sock.backend == "http" or sock.backend == "https" then
local http = kernel.apis and kernel.apis.http
if not http then error("ENODEV: no http API") end
local url = sock.address.url
local ok, err = pcall(http.request, url, data, {
["Content-Type"] = "application/octet-stream"
})
if not ok then error("ENETDOWN: " .. tostring(err)) end
sock.pendingUrl = url
return #data
end
error("EPROTONOSUPPORT")
end
sockClose = function(sock)
if sock.state == "closed" then return end
sock.state = "closed"
if sock.backend == "unix" then
if sock.peer then
sock.peer.peer = nil
sock.peer.state = "closed"
end
if sock.bound and sock.address and sock.address.path then
unixSocks[sock.address.path] = nil
end
elseif sock.backend == "modem" and sock.modem and sock.modemChannel then
pcall(sock.modem.close, sock.modemChannel)
elseif sock.backend == "rednet" then
end
sockets[sock.id] = nil
end
kernel.syscalls["socket"] = function(domain, socktype)
domain = domain or "inet"
socktype = socktype or "stream"
if domain ~= "unix" and domain ~= "inet" then error("EAFNOSUPPORT") end
if socktype ~= "stream" and socktype ~= "dgram" then error("EPROTOTYPE") end
local sock = newSocket(domain, socktype)
local fdobj = socketToFd(sock)
local fd = kernel.vfs.newfd(fdobj)
return fd
end
kernel.syscalls["bind"] = function(fd, address)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if sock.bound then error("EINVAL") end
local parsed = parseAddress(address)
if parsed.backend == "unix" then
local existing = unixSocks[parsed.path]
if existing then
if existing.state == "closed" then
unixSocks[parsed.path] = nil
else
error("EADDRINUSE")
end
end
sock.backend = "unix"
sock.address = parsed
sock.bound = true
sock.state = "bound"
unixSocks[parsed.path] = sock
elseif parsed.backend == "rednet" then
ensureRednet()
sock.backend = "rednet"
sock.address = parsed
sock.bound = true
sock.state = "bound"
elseif parsed.backend == "modem" then
local modem, side = getModem()
sock.backend = "modem"
sock.address = parsed
sock.modem = modem
sock.modemChannel = parsed.port
sock.bound = true
sock.state = "bound"
modem.open(parsed.port)
else
error("EOPNOTSUPP: cannot bind to " .. parsed.backend .. " address")
end
end
kernel.syscalls["listen"] = function(fd, backlog)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if not sock.bound then error("EDESTADDRREQ") end
sock.listening = true
sock.state = "listening"
sock.maxBacklog = backlog or 5
end
kernel.syscalls["accept"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if not sock.listening then error("EINVAL") end
local deadline = kernel.computer:time() + 30000
while #sock.backlog == 0 do
pumpAll()
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
coroutine.yield()
end
local clientSock = table.remove(sock.backlog, 1)
local cfdobj = socketToFd(clientSock)
local newfd = kernel.vfs.newfd(cfdobj)
return newfd
end
kernel.syscalls["connect"] = function(fd, address)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
if sock.connected then error("EISCONN") end
local parsed = parseAddress(address)
sock.address = parsed
sock.backend = parsed.backend
if parsed.backend == "unix" then
local server = unixSocks[parsed.path]
if not server then error("ECONNREFUSED") end
if not server.listening then error("ECONNREFUSED") end
if #server.backlog >= (server.maxBacklog or 5) then error("ECONNREFUSED") end
local serverPeer = newSocket("unix", sock.socktype)
serverPeer.backend = "unix"
serverPeer.connected = true
serverPeer.state = "connected"
serverPeer.peer = sock
sock.peer = serverPeer
sock.connected = true
sock.state = "connected"
table.insert(server.backlog, serverPeer)
elseif parsed.backend == "rednet" then
ensureRednet()
sock.connected = true
sock.state = "connected"
elseif parsed.backend == "modem" then
local modem, side = getModem()
local replyChannel = math.random(1024, 65534)
sock.modem = modem
sock.modemChannel = replyChannel
sock.connected = true
sock.state = "connected"
modem.open(replyChannel)
elseif parsed.backend == "http" or parsed.backend == "https" then
sock.connected = true
sock.state = "connected"
else
error("EAFNOSUPPORT")
end
end
kernel.syscalls["send"] = function(fd, data)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
return sockSend(sock, data)
end
kernel.syscalls["recv"] = function(fd, maxlen, timeout_ms)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
local deadline = kernel.computer:time() + (timeout_ms or 10000)
while #sock.rxbuf == 0 do
pumpAll()
if #sock.rxbuf > 0 then break end
if sock.state == "closed" or sock.error then
if sock.error then error("ECONNRESET: " .. tostring(sock.error)) end
return ""
end
if kernel.computer:time() > deadline then return "" end
coroutine.yield()
end
local item = table.remove(sock.rxbuf, 1)
local data = type(item) == "table" and (item.data or "") or tostring(item)
if maxlen and #data > maxlen then
table.insert(sock.rxbuf, 1, { data=data:sub(maxlen+1), from=item and item.from })
data = data:sub(1, maxlen)
end
return data
end
kernel.syscalls["sockshutdown"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if sock then sockClose(sock) end
end
kernel.syscalls["getpeername"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock or not sock.connected then error("ENOTCONN") end
if sock.address then return sock.address end
return nil
end
kernel.syscalls["getsockname"] = function(fd)
local task = kernel.currentTask
local fdobj = task.fd[fd]
if not fdobj or not fdobj.isSocket then error("ENOTSOCK") end
local sock = sockets[fdobj.sockId]
if not sock then error("EBADF") end
return sock.address
end
kernel.syscalls["httpget"] = function(url, headers)
local http = kernel.apis and kernel.apis.http
if not http then error("ENODEV: no http API") end
local ok, err = pcall(http.request, url, nil, headers)
if not ok then error("ENETDOWN: " .. tostring(err)) end
local deadline = kernel.computer:time() + 15000
while true do
local ev = pollEvent()
if ev then
if ev[1] == "http_success" and ev[2] == url then
local handle = ev[3]
local body = handle.readAll and handle.readAll() or ""
handle.close()
return body
elseif ev[1] == "http_failure" and ev[2] == url then
error("ECONNREFUSED: " .. tostring(ev[3]))
else
dispatchEvent(ev)
end
end
if kernel.computer:time() > deadline then error("ETIMEDOUT") end
coroutine.yield()
end
end
kernel.syscalls["resolve"] = function(hostname)
if hostname:match("^%d+%.%d+%.%d+%.%d+$") then return hostname end
local a,b,c,d = hostname:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
if a and tonumber(a) == 0 and tonumber(b) == 0 then
return hostname
end
local http = kernel.apis and kernel.apis.http
if not http then error("ENODEV: no http API for DNS") end
local url = "https://cloudflare-dns.com/dns-query?name=" .. hostname .. "&type=A"
local body = kernel.syscalls["httpget"](url, {
["Accept"] = "application/dns-json"
})
local ip = body:match('"type":1[^}]*"data":"([%d%.]+)"')
if not ip then error("ENOENT: could not resolve " .. hostname) end
return ip
end
kernel.sockets = sockets
kernel.unixSockets = unixSocks
kernel.log("Loaded socket module")
@@ -1,395 +0,0 @@
-- :Minify:--
local kernel = ...
local apis = kernel.apis
local native = apis.peripheral
local sides = {"top", "bottom", "left", "right", "front", "back"}
local peripheral={}
function peripheral.getNames()
local results = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.hasType(side, "peripheral_hub") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
end
end
end
end
return results
end
function peripheral.isPresent(name)
if native.isPresent(name) then
return true
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return true
end
end
return false
end
function peripheral.getType(peripheral)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "getTypeRemote", peripheral)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return table.unpack(mt.types)
end
end
function peripheral.hasType(peripheral, peripheral_type)
if type(peripheral) == "string" then
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
function peripheral.getMethods(name)
if native.isPresent(name) then
return native.getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getMethodsRemote", name)
end
end
return nil
end
function peripheral.getName(peripheral)
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.name
end
function peripheral.call(name, method, ...)
if native.isPresent(name) then
return native.call(name, method, ...)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "callRemote", name, method, ...)
end
end
return nil
end
function peripheral.wrap(name)
local methods = peripheral.getMethods(name)
if not methods then
return nil
end
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
return peripheral.call(name, method, ...)
end
end
return result
end
function peripheral.find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)
end
end
end
return table.unpack(results)
end
local icolors = {
[0x1] = 1, -- #000000
[0x2] = 2, -- #FFFFFF
[0x4] = 3, -- #FF0000
[0x8] = 4, -- #00FF00
[0x10] = 5, -- #0000FF
[0x20] = 6, -- #00FFFF
[0x40] = 7, -- #FF00FF
[0x80] = 8, -- #FFFF00
[0x100] = 9, -- #FF6D00
[0x200] = 10, -- #6DFF55
[0x400] = 11, -- #24FFFF
[0x800] = 12, -- #924900
[0x1000] = 13, -- #6D6D55
[0x2000] = 14, -- #DBDBAA
[0x4000] = 15, -- #6D00FF
[0x8000] = 16 -- #B6FF00
}
local colors = {
0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
local function write(text, term)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
if x > w then
x = 1
y = y + 1
end
if y - 1 >= h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
kernel.devfs.data.tty={}
local ctrl,alt = false, false
local function serializeBool(bool)
if bool then
return "T"
else
return "F"
end
end
local function newtty(obj, id, ev)
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()
local ctrlLetterKeys = nil
local specialKeys = nil
local function buildKeyMaps()
if ctrlLetterKeys then return end
local k = apis.keys
ctrlLetterKeys = {}
local letters = {
{k.a,1},{k.b,2},{k.c,3},{k.d,4},{k.e,5},{k.f,6},{k.g,7},
{k.h,8}, {k.j,10},{k.k,11},{k.l,12},{k.m,13},
{k.n,14},{k.o,15},{k.p,16},
{k.u,21},{k.v,22},{k.w,23},{k.x,24},{k.y,25},{k.z,26},
}
for _, pair in ipairs(letters) do
ctrlLetterKeys[pair[1]] = string.char(pair[2])
end
specialKeys = {
[k.home] = "\1",
[k.delete] = "\4",
[k["end"]] = "\5",
[k.pageUp] = "\2",
[k.pageDown]= "\12",
}
end
kernel.processes.cctmond = function()
local timeout = false
while true do
local event = {kernel.computer:getMachineEvent()}
if event[1] then
local eventType = event[1]
local charOrKey = event[3]
buildKeyMaps()
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 and charOrKey == apis.keys.c then
for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1)
end
end
if ctrl and ctrlLetterKeys[charOrKey] then
fifo.push(ctrlLetterKeys[charOrKey])
end
if specialKeys[charOrKey] then
fifo.push(specialKeys[charOrKey])
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, "TTY1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5)
v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end)
end
@@ -1,9 +0,0 @@
-- :Minify:--
local kernel = ...
kernel.processes.login = function()
local ok, err = pcall(syscall.execspawn, "/bin/login", "login")
if not ok then
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
end
end
@@ -1,142 +0,0 @@
-- :Minify:--
local kernel = ...
local P = kernel.vfs.P
local PERM = kernel.vfs.PERM
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
+ P.GROUP_R + P.GROUP_X
+ P.WORLD_R + P.WORLD_X
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755
local META_VERSION = 0x01
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
return string.char(#name) .. name
.. string.char(etype, owner, group, plo, phi)
.. string.char(#cmeta) .. cmeta
end
local function writeMeta(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 data = string.char(META_VERSION)
for _, e in ipairs(entries) do
data = data .. makeEntry(e[1], e[2] or 0x00, e[3], e[4], e[5], e[6])
end
local ok, err = pcall(function()
local f = rootDisk:open(metaPath, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
end
end
local REG = 0x00
if rootDisk:fileExists(".meta") then
kernel.log("Permissions already seeded, skipping.", "INFO")
else
kernel.log("Seeding filesystem permissions...", "INFO")
-- /
writeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX},
{"dev", REG, 0, 0, RWX_RX_RX},
{"etc", REG, 0, 0, RWX_RX_RX},
{"home", REG, 0, 0, RWX_RX_RX},
{"lib", REG, 0, 0, RWX_RX_RX},
{"root", REG, 0, 0, RW____ },
{"sbin", REG, 0, 0, RWX_RX_RX},
{"tmp", REG, 0, 0, RWXRWXRWX},
{"usr", REG, 0, 0, RWX_RX_RX},
{"var", REG, 0, 0, RWX_RX_RX},
})
-- /bin
writeMeta("/bin", {
{"cat", REG, 0, 0, RWX_RX_RX},
{"clear", REG, 0, 0, RWX_RX_RX},
{"echo", REG, 0, 0, RWX_RX_RX},
{"hfetch", REG, 0, 0, RWX_RX_RX},
{"hysh", REG, 0, 0, RWX_RX_RX},
{"hyshex", REG, 0, 0, RWX_RX_RX},
{"install", REG, 0, 0, RWX_RX_RX},
{"login", REG, 0, 0, SUID_755 },
{"ls", REG, 0, 0, RWX_RX_RX},
{"lua", REG, 0, 0, RWX_RX_RX},
{"luaold", REG, 0, 0, RWX_RX_RX},
{"mkdir", REG, 0, 0, RWX_RX_RX},
{"ps", REG, 0, 0, RWX_RX_RX},
{"pwd", REG, 0, 0, RWX_RX_RX},
{"spm", REG, 0, 0, RWX_RX_RX},
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
{"sysdump", REG, 0, 0, RWX_RX_RX},
{"whoami", REG, 0, 0, RWX_RX_RX},
{"yes", REG, 0, 0, RWX_RX_RX},
{"startup", REG, 0, 0, RWX_RX_RX},
{"ln", REG, 0, 0, RWX_RX_RX},
{"readlink", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/bin/startup", {
{"test.lua", REG, 0, 0, RWX_RX_RX},
})
-- /etc
writeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R},
{"shadow", REG, 0, 0, RW____},
{"pam.d", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/etc/pam.d", {
{"secret", REG, 0, 0, RW____},
})
-- /sbin
writeMeta("/sbin", {
{"init.lua", REG, 0, 0, RWX_RX_RX},
})
-- /boot
writeMeta("/boot", {
{"kernel.lua", REG, 0, 0, RW_R_R },
{"boot.cfg", REG, 0, 0, RW_R_R },
{"safeboot.cfg", REG, 0, 0, RW_R_R },
{"fstab", REG, 0, 0, RW_R_R },
{"initfs", REG, 0, 0, RW_R_R },
{"cct", REG, 0, 0, RWX_RX_RX},
{"oc", REG, 0, 0, RWX_RX_RX},
})
-- /lib
writeMeta("/lib", {
{"sys", REG, 0, 0, RWX_RX_RX},
{"modules", REG, 0, 0, RWX_RX_RX},
{"crypto", REG, 0, 0, RWX_RX_RX},
{"store", REG, 0, 0, RWX_RX_RX},
{"snip", REG, 0, 0, RW_R_R },
{"io", REG, 0, 0, RW_R_R },
{"bit32", REG, 0, 0, RW_R_R },
})
kernel.log("Filesystem permissions seeded.", "INFO")
end
kernel.log("Permission module loaded.", "INFO")
+1
View File
@@ -0,0 +1 @@
1.5.0
+2
View File
@@ -0,0 +1,2 @@
Hyperion
Lua

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