72 Commits

Author SHA1 Message Date
f7b64c11b7 added seperate input and working on http / sockets 2026-05-27 17:01:26 -04:00
59e09a5995 Update LICENSE 2026-03-26 10:34:00 -04:00
ab0a10105c formating better for spm packages 2026-03-26 08:12:37 -04:00
22b5021e9d fixed boot crash 2026-03-21 01:10:47 -04:00
462c74686a fixed the installer 2026-03-21 01:04:27 -04:00
0072547beb updated package list 2026-03-21 00:59:46 -04:00
7deefc83ca made newer installer that does not require tar stuff 2026-03-21 00:54:39 -04:00
df62414229 made system use full salt+pepper 2026-03-19 14:10:17 -04:00
055dd4e606 fixed again 2026-03-19 12:10:31 -04:00
5755dd9cbe fixed 2026-03-19 11:32:48 -04:00
8e11faf9ec updated again 2026-03-19 11:23:24 -04:00
0ea42f9454 Updated installer 2026-03-19 11:21:08 -04:00
d0f26a937f made installer 2026-03-19 11:16:05 -04:00
9b338328f0 made unified colors and stuff 2026-03-19 08:33:47 -04:00
4f9eebade2 update tar 2026-03-12 20:54:27 -04:00
d08935b68a remove gotos 2026-03-12 19:31:37 -04:00
45b46cf3c4 Merge pull request 'main' (#10) from main into 1.2-dev
Reviewed-on: #10
2026-03-12 19:27:44 -04:00
aeea68bc9b Update install/data/tarbad 2026-03-12 12:12:48 -04:00
f983b13d56 Update install/installcc.lua
make it use right stuff
2026-03-12 12:10:37 -04:00
03c5b106c4 Update Src/Hyperion-kernel/boot/kernel.lua 2026-03-12 11:59:35 -04:00
08323e00ff Update install/data/tarbad 2026-03-12 11:56:58 -04:00
5e3cdbe40c Update install/installcc.lua 2026-03-12 11:51:55 -04:00
8762b8f022 Merge pull request '1.2-dev' (#9) from 1.2-dev into main
Reviewed-on: #9
2026-03-11 10:37:44 -04:00
677b2cccec user login fixed 2026-03-11 10:37:13 -04:00
a5e8624368 forgot to edit login 2026-03-11 08:54:47 -04:00
bbda3b3937 fixed elevate VULN 2026-03-11 08:52:41 -04:00
585d39bec2 Merge pull request '1.2-dev' (#8) from 1.2-dev into main
Reviewed-on: #8
2026-03-11 08:23:21 -04:00
b08b14763a i am a we bit stupid (fixed cct bug) 2026-03-11 08:22:53 -04:00
de6696003b fix capitaization part 2 2026-03-11 07:26:38 -04:00
9220281365 fix capitalization part 1 2026-03-11 07:25:52 -04:00
813ddabd9d fixed disk detection i think 2026-03-10 21:13:52 -04:00
5177639d71 Merge pull request 'update build.tar' (#7) from 1.2-dev into main
Reviewed-on: #7
i was dumb
2026-03-10 20:05:26 -04:00
528b4f31bd update build.tar 2026-03-10 20:04:53 -04:00
60162c7c57 Merge pull request '1.2.4' (#6) from 1.2-dev into main
Reviewed-on: #6
2026-03-10 20:02:20 -04:00
18f5c454bb fixed bug where cct drivers are inaccessable 2026-03-10 20:00:21 -04:00
849ecb7dd6 made procfs self 2026-03-10 19:41:54 -04:00
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
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
beebf01223 remove tar during install 2026-03-10 10:57:10 -04:00
2d4ea1bbf4 Update README.md 2026-03-10 10:39:01 -04:00
ea3a7e99a7 Update install/installcc.lua 2026-03-10 10:34:36 -04:00
be0fe5dc5a install works (i think) 2026-03-10 10:32:32 -04:00
12669d9f82 adde /proc fs and working on install 2026-03-10 09:17:21 -04:00
f12159bfb9 fixed build script 2026-03-09 11:31:31 -04:00
1590e1f3f7 more reorganizeing and $PKGCONFIG.ini files added B to ls -lh for "Bytes" 2026-03-09 11:28:09 -04:00
a69f945b91 did some reorganizing 2026-03-06 09:57:45 -05:00
7da67899db finished hyperion manifest :D 2026-03-05 10:36:43 -05:00
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
6fefa2d9ff cp should not copy perms 2026-03-03 12:25:25 -05:00
bb354cc706 mv syscallautofill 2026-03-03 11:45:05 -05:00
fabc061731 made lua not clear screen 2026-03-03 10:06:04 -05:00
82c3e2b346 fix ll 2026-03-03 08:57:45 -05:00
e2e1d5b8a5 making descriptions for syscalls 2026-03-03 08:52:55 -05:00
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
9a7db6c243 syscalls now autocomplete is vsc 2026-03-03 08:02:05 -05:00
c7545e6947 Update build.py 2026-03-03 07:31:59 -05:00
b7f52dd17b working on syscall manifest and fixed anther exploit 2026-03-02 22:25:37 -05:00
eb5bed0f09 fixed asyncsyscall5 2026-03-02 21:58:51 -05:00
1827a463eb added ll for ghxx 2026-03-02 21:56:05 -05:00
b532a63fc6 fixed stupid dumbass mistake i made me dumb 2026-03-02 21:32:56 -05:00
4e5a4172bf hopfully fixed it omfg 2026-03-02 21:29:46 -05:00
31ce894fda fixed build script 2026-03-02 21:26:50 -05:00
16c900de84 fixed ls links, modules writeable 2026-03-02 21:23:35 -05:00
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
a0a0ac69d4 remove old shell files 2026-03-02 07:27:45 -05:00
17453983ad Path traversal fixes, 26_tty removal, ctrl+key fixes 2026-03-01 00:21:02 -06:00
a6550aa069 fixed minify header and build script 2026-02-25 11:41:41 -05:00
02e7b3897c fixed tty naming 2026-02-25 09:03:32 -05:00
34b89a8e34 fixed build script 2026-02-25 08:04:53 -05:00
0eabfebd0f fixed new ghxx exploit 2026-02-25 08:01:28 -05:00
5b2e5eac65 added more libs and fixed build script 2026-02-24 17:54:09 -05:00
415064480a Patch the AsyncSyscall v4 exploit from working 2026-02-24 02:00:37 -06:00
153 changed files with 15149 additions and 3026 deletions

View File

@@ -5,6 +5,7 @@
"syscall", "syscall",
"printf", "printf",
"printInline", "printInline",
"toHex" "toHex",
"loadcstr"
] ]
} }

212
LICENSE
View File

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

View File

@@ -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.

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("")

View File

@@ -1 +0,0 @@
syscall.devctl(1,"clear")

View File

@@ -1,2 +0,0 @@
local args = {...}
print(table.concat(args, " "))

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

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

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)

View File

@@ -1 +0,0 @@
print(syscall.getcwd())

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))

View File

@@ -1 +0,0 @@
print((syscall.getUsername() or "Unknown"))

View File

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

View File

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

View File

@@ -1,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)

View File

@@ -1,6 +0,0 @@
local sys = {}
local fs = require("sys.fs")
return sys

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

View File

@@ -1,3 +0,0 @@
local ipc = {}
return ipc

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

View File

@@ -0,0 +1,344 @@
--:Minify:--
local args={...}
local bootdrive=args[1]
local gpu=components:getFirst("gpu")
local screenTextBuffer=nil
local cursorX,cursorY=0,0
local screenSizeX,screenSizeY=128,25
if gpu then
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
for t,v in components:list() do
if t == "screen" then
gpu:assignBuffer(screenTextBuffer, v)
end
end
end
local function write(text)
cursorX, cursorY = screenTextBuffer:pasteText(cursorX, cursorY, "SCROLL_SPILL_CLEAR", text)
cursorY = cursorY + 1
cursorX = 0
if cursorY >= screenSizeY then
cursorY = screenSizeY-1
screenTextBuffer:newline()
end
end
local function displaySuperBadError(err)
gpu:freeAllBuffers()
screenTextBuffer=gpu:newBuffer(screenSizeX,screenSizeY)
for t,v in components:list() do
if t == "screen" then
gpu:assignBuffer(screenTextBuffer, v)
end
end
term.setBackgroundColor(0x1)
term.setTextColor(0x4)
term.clear()
term.setCursorPos(1, 1)
term.write("A critical error occurred while loading the system:")
term.setCursorPos(1, 3)
write(err)
while true do end
end
local ok, err = xpcall(function()
local apis = {}
local lua = {
coroutine = true,
debug = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G = true
}
local debug = debug
for i, v in pairs(_G) do
if not lua[i] or lua[i] == nil then
apis[i] = v
_G[i] = nil
end
end
local function string_file(file)
local str = file:read()
local buf = {str or ""}
local pos = 1
local closed = false
local function content()
return table.concat(buf)
end
local function set_content(s)
buf = {s}
end
local function flush()
file.write(content())
end
local file = {}
function file.read(n)
assert(not closed, "file is closed")
local s = content()
local len = #s
if not n then
local out = s:sub(pos)
pos = len + 1
return out
end
local out = s:sub(pos, pos + n - 1)
pos = pos + #out
return out
end
function file.write(data)
assert(not closed, "file is closed")
local s = content()
local before = s:sub(1, pos - 1)
local after = s:sub(pos + #data)
set_content(before .. data .. after)
pos = pos + #data
return true
end
function file.seek(whence, offset)
assert(not closed, "file is closed")
local s = content()
local len = #s
whence = whence or "cur"
offset = offset or 0
if whence == "set" then
pos = offset + 1
elseif whence == "cur" then
pos = pos + offset
elseif whence == "end" then
pos = len + offset + 1
else
error("invalid whence")
end
if pos < 1 then pos = 1 end
if pos > len + 1 then pos = len + 1 end
return pos - 1
end
function file:close()
assert(not closed, "file is closed")
flush()
closed = true
end
function file:flush()
assert(not closed, "file is closed")
flush()
end
return file
end
local function getFile(path)
local file = bootdrive:open(path, "r")
if not file then
displaySuperBadError("Could not open file: " .. path)
end
local content = file:read()
return content
end
local Kernel = load(getFile("/boot/kernel.lua"),"@Kernel")
local initFs = load(getFile("/boot/ac/initdisks"),"@Init_disks")(apis)
local fs = load(getFile("/boot/initfs"), "@InitFs")()
if not Kernel then displaySuperBadError("Could not load kernel.") end
if not initFs then displaySuperBadError("Could not load initdisks.") end
if not fs then displaySuperBadError("Could not load initfs.") end
local computercomp=apis.components:getFirst("computer")
local uefi=apis.components:getFirst("uefi")
local computer = {
time = function() return apis.os.epoch("utc") end,
clock = function() return apis.os.clock() * 1000 end,
shutdown = apis.os.shutdown,
reboot = apis.os.reboot,
getMachineEvent = function()
return computercomp:getMachineEvent()
end,
getEEPROM = function() return uefi.data end,
setEEPROM = function(_, text)
uefi.data=text
end
}
local icolors = {
[0x1] = 1, -- #000000
[0x2] = 2, -- #FFFFFF
[0x4] = 3, -- #FF0000
[0x8] = 4, -- #00FF00
[0x10] = 5, -- #0000FF
[0x20] = 6, -- #00FFFF
[0x40] = 7, -- #FF00FF
[0x80] = 8, -- #FFFF00
[0x100] = 9, -- #FF6D00
[0x200] = 10, -- #6DFF55
[0x400] = 11, -- #24FFFF
[0x800] = 12, -- #924900
[0x1000] = 13, -- #6D6D55
[0x2000] = 14, -- #DBDBAA
[0x4000] = 15, -- #6D00FF
[0x8000] = 16 -- #B6FF00
}
local colors = {
0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
apis.term.setBackgroundColor(0x8000)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
local kernelCoro = coroutine.create(function()
---@diagnostic disable-next-line: param-type-mismatch
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
{
print = function(_, text) write(text .. "\n") end,
printInline = function(_, text) write(text) end,
clear = function()
apis.term.clear()
apis.term.setCursorPos(1, 1)
end,
setCursorPos = function(_, x, y)
apis.term.setCursorPos(x, y)
end,
getCursorPos = function() return apis.term.getCursorPos() end,
getSize = function() return apis.term.getSize() end,
setBackgroundColor = function(_, color)
apis.term.setBackgroundColor(colors[color])
end,
setTextColor = function(_, color)
apis.term.setTextColor(colors[color])
end,
getBackgroundColor = function()
return icolors[apis.term.getBackgroundColor()]
end,
getTextColor = function()
return icolors[apis.term.getTextColor()]
end
}, computer, fs, "$")
if not ok then displaySuperBadError(err) end
end)
function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time()
debug.sethook(co, function()
if computer.time() > startTime + timeout then
return coroutine.yield("timeout")
end
end, "", 1000)
local ret = {coroutine.resume(co, ...)}
if ret[1] and ret[2] == "timeout" then
return "timeout"
elseif ret[1] == false then
return "error", ret[2]
else
debug.sethook(co)
return "success", table.unpack(ret, 2)
end
end
write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n")
while true do
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
apis.os.queueEvent("NoSleep")
local exit = false
while not exit do
local event = {coroutine.yield()}
if event[1] == "key" then
queueEvent("keyPressed", 1, event[2])
if acekeys[event[2]] then
queueEvent("keyTyped", 1, acekeys[event[2]])
end
elseif event[1] == "char" then
queueEvent("keyTyped", 1, event[2])
elseif event[1] == "key_up" then
queueEvent("keyReleased", 1, event[2])
elseif event[1] == "disk" then
queueEvent("componentAdded", "disk")
elseif event[1] == "disk_eject" then
queueEvent("componentRemoved", "disk")
elseif event[1] == "modem_message" then
queueEvent("modem_message", table.unpack(event, 2))
elseif event[1] == "rednet_message" then
queueEvent("rednet_message", table.unpack(event, 2))
elseif event[1] == "http_success" then
queueEvent("http_success", table.unpack(event, 2))
elseif event[1] == "http_failure" then
queueEvent("http_failure", table.unpack(event, 2))
elseif event[1] == "NoSleep" then
exit = true
end
end
if status == "error" or coroutine.status(kernelCoro) == "dead" then
displaySuperBadError("Kernel error: " .. tostring(err))
coroutine.yield("key")
end
end
end, debug.traceback)
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
while true do coroutine.yield() end

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local apis = kernel.apis local apis = kernel.apis
local native = apis.peripheral local native = apis.peripheral
@@ -311,13 +311,24 @@ local fifo = kernel.newFifo()
kernel.processes.cctmond = function() kernel.processes.cctmond = function()
local timeout = false local timeout = false
while true do while true do
local event = {kernel.computer:getMachineEvent()} local event = {kernel.EFI:getMachineEvent()}
if event[1] then if event[1] then
local eventType = event[1] local eventType = event[1]
local charOrKey = event[3] local charOrKey = event[3]
-- Update modifier keys local ctrlKeyMap = {
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
[apis.keys.y]=25, [apis.keys.z]=26,
}
if eventType == "keyPressed" then if eventType == "keyPressed" then
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
ctrl = true ctrl = true
@@ -325,11 +336,31 @@ kernel.processes.cctmond = function()
alt = true alt = true
end end
-- Handle Ctrl+C if ctrl then
if ctrl and charOrKey == apis.keys.c then local ctrlByte = ctrlKeyMap[charOrKey]
if ctrlByte then
if ctrlByte == 3 then
for _, task in ipairs(syscall.getTasks()) do for _, task in ipairs(syscall.getTasks()) do
syscall.sigsend(task, 1) -- SIGINT syscall.sigsend(task, 1)
end end
else
fifo.push(string.char(ctrlByte))
end
end
else
local specialKeyMap = {
[apis.keys.up] = "",
[apis.keys.down] = "",
[apis.keys.right] = "",
[apis.keys.left] = "",
[apis.keys.home] = "",
[apis.keys["end"]] = "",
[apis.keys.pageUp] = "[5~",
[apis.keys.pageDown] = "[6~",
[apis.keys.delete] = "[3~",
}
local special = specialKeyMap[charOrKey]
if special then fifo.push(special) end
end end
elseif eventType == "keyReleased" then elseif eventType == "keyReleased" then
@@ -354,10 +385,10 @@ kernel.processes.cctmond = function()
end end
end end
newtty(apis.term, "TTY1", fifo.pop) newtty(apis.term, "1", fifo.pop)
for i,v in ipairs({peripheral.find("monitor")}) do for i,v in ipairs({peripheral.find("monitor")}) do
v.setTextScale(.5) v.setTextScale(.5)
v.write("Initializing...") v.write("Initializing...")
newtty(v,"TTY"..tostring(i+1),function () end) newtty(v,tostring(i+1),function () end)
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,26 +0,0 @@
local args={...}
local kernel=args[1]
local driver={}
driver.name="CCT Term Module"
driver.version="0.1.0"
driver.type="gpio"
driver.description="CCT redstone Module Kernel Module"
driver.arch="cct"
driver.author="HyperionOS Dev Team"
driver.license="MIT"
driver.api={}
function driver.load()
-- will
end
function driver.unload()
-- Nothing to unload
end
function driver.main()
-- Nothing to run
end
-- kernel.drivers.register(driver)

View File

@@ -1,3 +0,0 @@
U $;/
U devfs0000;/dev/
U tmpfs0000;/tmp/

View File

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

View File

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

View File

@@ -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,
} }

View File

@@ -1,4 +1,5 @@
-- :Minify:-- --:Minify:--
--- @diagnostic disable: duplicate-set-field
local kernel = ... local kernel = ...
kernel.allowGlobalOverwrites = true kernel.allowGlobalOverwrites = true
@@ -172,6 +173,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
@@ -207,8 +229,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
@@ -217,7 +260,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

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
local kernel = ... local kernel = ...
local vfs = {} local vfs = {}
kernel.vfs = vfs kernel.vfs = vfs
@@ -113,57 +113,25 @@ end
local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$" local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$"
local function normalizePath(path) local function tokenizePath(path)
local task = kernel.currentTask local isAbsolute = (path:sub(1,1) == "/")
local cwd = task.cwd or "/" local tokens = {}
for comp in (path .. "/"):gmatch("([^/]*)/") do
table.insert(tokens, comp)
end
return isAbsolute, tokens
end
if path:sub(1, 1) ~= "/" then local function validateComponent(comp)
path = cwd .. "/" .. path local lower = comp:lower()
if not lower:match(SAFE_COMPONENT_PATTERN) then
error("EINVAL: illegal characters in path component: " .. comp, 3)
end end
if lower == ".meta" then
local stack = {} error("EINVAL: reserved path component: .meta", 3)
local i = 1 elseif lower:sub(#lower) == "." then
local len = #path error("Cannot have empty name extention: " .. comp, 3)
while i <= len do
local j = path:find("/", i, true)
local comp
if j then
comp = path:sub(i, j - 1)
i = j + 1
else
comp = path:sub(i)
i = len + 1
end end
comp = comp:match("^%s*(.-)%s*$")
if comp == "" or comp == "." then
elseif comp == ".." then
if #stack > 0 then
table.remove(stack)
end
else
comp = comp:lower()
if not comp:match(SAFE_COMPONENT_PATTERN) then
error("EINVAL: illegal characters in path component: " .. comp, 2)
end
if comp == ".meta" then
error("EINVAL: reserved path component: " .. comp, 2)
end
table.insert(stack, comp)
end
end
local result = "/" .. table.concat(stack, "/")
local root = task and task.root
if root and root ~= "/" then
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
result = root
end
end
return result
end end
function vfs.splitPath(path) function vfs.splitPath(path)
@@ -222,47 +190,205 @@ local function readMetaEntry(disk, parentDiskPath, 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:lower()
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:lower())
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.."'")
@@ -271,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
@@ -361,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
@@ -398,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
@@ -406,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
@@ -444,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")
@@ -456,6 +666,21 @@ function vfs.open(path, mode)
local fd = allocFD(task) local fd = allocFD(task)
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if not disk then error("NODISK") end if not disk then error("NODISK") end
if (mode=="w" or mode=="a") and disk:isReadOnly() then error("ERDONLY") end
if kernel.unixSockets[namei(path)] then
local meta = kernel.unixSockets[namei(path)].meta
if kernel.uid~=0 or kernel.uid~=meta.owner then
local groups = (task and task.groups) or kernel.groups or {}
local access=false
for _, gid in ipairs(groups) do
if gid == meta.group then access=true end
end
if not access then error("EACCES") end
end
task[fd]=kernel.unixSockets[namei(path)]
return fd
end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath) local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
@@ -487,6 +712,7 @@ function vfs.open(path, mode)
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
@@ -535,8 +761,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()
@@ -554,10 +782,17 @@ function vfs.sendfile(outfd, infd, count)
end end
function vfs.stat(path) function vfs.stat(path)
local attrs
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path) local meta = getFileMeta(path)
local ok, attrs = pcall(disk.attributes, disk, diskPath) if meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 }
else
local ok
ok, attrs = pcall(disk.attributes, disk, diskPath)
if not ok then attrs = { size=0, modified=0, created=0 } end if not ok then attrs = { size=0, modified=0, created=0 } end
end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -572,15 +807,15 @@ end
function vfs.lstat(path) function vfs.lstat(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
local attrs local attrs
if meta.etype == 0x01 then if meta.etype == 0x01 or meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 } attrs = { size=0, modified=0, created=0 }
else else
local disk, diskPath = resolvePath(path, true) local disk, diskPath = resolvePath(path, true)
local ok, a = pcall(disk.attributes, disk, diskPath) local ok, a = pcall(disk.attributes, disk, diskPath)
attrs = ok and a or { size=0, modified=0, created=0 } attrs = ok and a or { size=0, modified=0, created=0 }
end end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -597,8 +832,15 @@ end
function vfs.fstat(fd) function vfs.fstat(fd)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
local disk, diskPath = resolvePath(file.path) local attrs
local attrs = disk:attributes(diskPath) if file.meta.etype == 0x02 then
attrs = { size=0, modified=0, created=0 }
else
local disk, diskPath = resolvePath(file.path, true)
local ok, a = pcall(disk.attributes, disk, diskPath)
attrs = ok and a or { size=0, modified=0, created=0 }
end
return { return {
size = attrs.size, size = attrs.size,
modified = attrs.modified, modified = attrs.modified,
@@ -627,6 +869,14 @@ function vfs.listdir(path)
end end
end end
for k,v in pairs(kernel.unixSockets) do
local p=normalizePath(path)
if k:match("^(.*)/[^/]+$")==p then
seen[v.name]=true
table.insert(out, v.name)
end
end
local mp local mp
if diskPath == "/" then if diskPath == "/" then
mp = ".meta" mp = ".meta"
@@ -655,6 +905,7 @@ function vfs.mkdir(path)
local parentMeta = getFileMeta(parent) local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w") checkperms(parentMeta, "w")
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if disk:isReadOnly() then error("ERDONLY") end
disk:makeDirectory(diskPath) disk:makeDirectory(diskPath)
local task = kernel.currentTask local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid local euid = (task and (task.euid or task.uid)) or kernel.uid
@@ -668,7 +919,7 @@ function vfs.mkdir(path)
end end
function vfs.remove(path) function vfs.remove(path)
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 parentMeta = getFileMeta(parent) local parentMeta = getFileMeta(parent)
@@ -676,16 +927,22 @@ function vfs.remove(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if kernel.unixSockets and kernel.unixSockets[path] then if kernel.unixSockets[namei(path)] then
kernel.unixSockets[path] = nil if kernel.uid ~= 0 then
if kernel.unixSockets[namei(path)].meta.owner~=kernel.uid then
error("EACCES")
end
end
kernel.unixSockets[namei(path)] = nil
end end
if meta.etype == 0x01 then if meta.etype == 0x01 then
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
@@ -702,6 +959,7 @@ function vfs.remove(path)
if f2.close then f2.close() end if f2.close then f2.close() end
else else
local disk, diskPath = resolvePath(path) local disk, diskPath = resolvePath(path)
if disk:isReadOnly() then error("ERDONLY") end
disk:remove(diskPath) disk:remove(diskPath)
end end
end end
@@ -710,6 +968,8 @@ function vfs.symlink(target, linkPath)
if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end if type(target) ~= "string" or type(linkPath) ~= "string" then error("EINVAL") end
local norm = normalizePath(linkPath) local norm = normalizePath(linkPath)
local parent = norm:match("^(.*)/[^/]+$") or "/" local parent = norm:match("^(.*)/[^/]+$") or "/"
local disk = resolveMount(linkPath)
if disk:isReadOnly() then error("ERDONLY") end
if parent == "" then parent = "/" end if parent == "" then parent = "/" end
local name = norm:match("[^/]+$") local name = norm:match("[^/]+$")
if not name then error("EINVAL") end if not name then error("EINVAL") end
@@ -739,7 +999,13 @@ function vfs.readlink(path)
end end
function vfs.access(path, mode) function vfs.access(path, mode)
local meta = getFileMeta(path) local meta
if kernel.unixSockets[normalizePath(path)] then
meta = kernel.unixSockets[normalizePath(path)].meta
else
meta = getFileMeta(path)
end
for i = 1, #mode do for i = 1, #mode do
checkperms(meta, mode:sub(i,i)) checkperms(meta, mode:sub(i,i))
end end
@@ -747,9 +1013,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
@@ -776,6 +1044,9 @@ local function updateMeta(path, fn, noFollow)
end end
function vfs.chmod(path, perms) function vfs.chmod(path, perms)
if kernel.unixSockets[namei(path)] then error("EINVAL") end
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
local meta = getFileMeta(path) local meta = getFileMeta(path)
local euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if euid ~= 0 and euid ~= meta.owner then error("EACCES") end if euid ~= 0 and euid ~= meta.owner then error("EACCES") end
@@ -785,10 +1056,14 @@ end
function vfs.fchmod(fd, perms) function vfs.fchmod(fd, perms)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
if file.meta.etype==0x02 then error("EINVAL") end
vfs.chmod(file.path, perms) vfs.chmod(file.path, perms)
end end
function vfs.chown(path, uid, gid) function vfs.chown(path, uid, gid)
if kernel.unixSockets[namei(path)] then error("EINVAL") end
local disk = resolveMount(path)
if disk:isReadOnly() then error("ERDONLY") end
local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid local _euid = (kernel.currentTask and (kernel.currentTask.euid or kernel.currentTask.uid)) or kernel.uid
if _euid ~= 0 then error("EPERM") end if _euid ~= 0 then error("EPERM") end
updateMeta(path, function(e) e.owner = uid; e.group = gid end) updateMeta(path, function(e) e.owner = uid; e.group = gid end)
@@ -797,20 +1072,29 @@ end
function vfs.fchown(fd, uid, gid) function vfs.fchown(fd, uid, gid)
local file = kernel.currentTask.fd[fd] local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end if not file then error("EBADF") end
if file.meta.etype==0x02 then error("EINVAL") end
vfs.chown(file.path, uid, gid) vfs.chown(file.path, uid, gid)
end end
function vfs.exists(path) function vfs.exists(path)
if kernel.unixSockets[namei(path)] then return true end
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if meta.etype == 0x01 then return true end if meta.etype == 0x01 or meta.etype == 0x02 then return true end
local ok, disk, diskPath = pcall(resolvePath, path) local ok, disk, diskPath = pcall(resolvePath, path)
if not ok then return false end if not ok then return false end
return disk:fileExists(diskPath) if disk:type(diskPath) then
return true
else
return false
end
end end
function vfs.type(path) function vfs.type(path)
local meta = getFileMeta(path, true) local meta = getFileMeta(path, true)
if meta.etype == 0x01 then return "symlink" end if meta.etype == 0x01 then return "symlink" end
if kernel.unixSockets[namei(path)] then
return "socket"
end
local ok, disk, diskPath = pcall(resolvePath, path) local ok, disk, diskPath = pcall(resolvePath, path)
if not ok then return nil end if not ok then return nil end
return disk:type(diskPath) return disk:type(diskPath)

View File

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

View File

@@ -0,0 +1,301 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "devfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("EACCES") end
proxy.remove = function() error("EACCES") end
proxy.setLabel = function() error("EACCES") end
proxy.getLabel = function() return "devfs" end
proxy.attributes = function(path) return {
size = 0,
modified = 0,
created = 0
} end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
error("ENFILE")
end
function proxy:type(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return "directory"
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
error("ENOENT")
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return table.keys(data)
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
error("ENOENT")
end
function proxy:fileExists(path)
local ok = pcall(function()
return self:type(path)
end)
return ok
end
function data.random(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(math.random(0, 255))
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.null(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount) end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.zero(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(0)
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
local function buildMeta(entries, opts)
opts = opts or {}
local uid = opts.uid or 0
local gid = opts.gid or 0
local perms = opts.perms or 0x3F -- default read/write for owner/group/world
local chunks = {}
table.insert(chunks, string.char(0x02)) -- version header
for path, target in pairs(entries) do
local name = path
local nameLen = #name
if nameLen > 255 then
error("Filename too long (>255 bytes): "..name)
end
-- Determine entry type: 0x01 = symlink if target ~= nil and target ~= ""
local entryType = 0x00
local cmeta = ""
if target and target ~= "" then
entryType = 0x01
cmeta = target
end
local cmetaLen = #cmeta
if cmetaLen > 255 then
error("cmeta too long (>255 bytes) for "..name)
end
-- Build entry as bytes
table.insert(chunks, string.char(nameLen)) -- name length
table.insert(chunks, name) -- name
table.insert(chunks, string.char(entryType)) -- entry type
table.insert(chunks, string.char(uid % 256, math.floor(uid/256) % 256)) -- uid
table.insert(chunks, string.char(gid % 256, math.floor(gid/256) % 256)) -- gid
table.insert(chunks, string.char(perms % 256, math.floor(perms/256) % 256)) -- perms
table.insert(chunks, string.char(cmetaLen)) -- cmeta length
if cmetaLen > 0 then
table.insert(chunks, cmeta)
end
end
return table.concat(chunks)
end
local function simpleFile(r,w)
return function(op, mode)
if op=="type" then
return "file"
elseif op=="open" then
if mode=="r" then
return {
read=r
}
elseif mode=="w" then
return {
write=w
}
end
end
end
end
local function strFile(str)
local dat=tostring(str)
local pos=1
return simpleFile(function(amount)
pos=pos+amount
return dat:sub(pos-amount, pos)
end,function() error("EACCES") end)
end
data[".meta"]=strFile(buildMeta({
stdin="/proc/self/fd/0",
stdout="/proc/self/fd/1",
log="/var/log/syslog.log"
}))
if kernel.EFI:getEEPROM() then
function data.eeprom(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
local ptr,eeprom=1,kernel.EFI:getEEPROM()
return {
read=function(amount)
ptr=ptr+amount
return eeprom:sub(ptr-amount, ptr)
end
}
elseif mode=="w" then
if kernel.uid~=0 then error("EACCES") end
local firstwrite=true
return {
write=function(data)
if firstwrite then
kernel.EFI:setEEPROM(data)
else
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
end
end
}
elseif mode=="a" then
if kernel.uid~=0 then error("EACCES") end
return {
write=function(data)
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
end
}
else error("EACCES")
end
end
end
end
if kernel.EFI:getNvram() then
function data.nvram(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
local ptr,nvram=1,kernel.EFI:getNvram()
return {
read=function(amount)
ptr=ptr+amount
return nvram:sub(ptr-amount, ptr)
end
}
elseif mode=="w" then
if kernel.uid~=0 then error("EACCES") end
local firstwrite=true
return {
write=function(data)
if firstwrite then
kernel.EFI:setNvram(data)
else
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
end
end
}
elseif mode=="a" then
if kernel.uid~=0 then error("EACCES") end
return {
write=function(data)
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
end
}
else error("EACCES")
end
end
end
end
data["disk"]={["by-id"]={}, ["by-path"]={}, ["by-label"]={}}
data["input"]={}
kernel.devfs={}
kernel.devfs.data=data
kernel.devfs.proxy=proxy
kernel.disks["devfs0000"]=proxy

View File

@@ -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

View File

@@ -0,0 +1,141 @@
--:Minify:--
local kernel = ...
kernel.vfs.remove("/tmp")
kernel.vfs.mkdir("/tmp")
if not kernel.config.tmpToDisk then
local proxy = {}
local data = {}
proxy.address = "tmpfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
if not step[steps[i]] then
step[steps[i]] = {}
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
end
proxy.remove = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
step = step[steps[i]]
if not step then error("ENOENT") end
end
step[steps[#steps]] = nil
end
proxy.setLabel = function(_, label) end
proxy.getLabel = function() return "tmpfs" end
proxy.attributes = function(_, path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
return {
size = type(step) == "string" and #step or 0,
modified = 0,
created = 0,
}
end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps-1 do
if not step[steps[i]] then
if mode == "w" then step[steps[i]] = {} else error("ENOENT") end
elseif type(step[steps[i]]) ~= "table" then
error("ENOTDIR")
end
step = step[steps[i]]
end
local filename = steps[#steps]
if mode == "r" then
if type(step[filename]) ~= "string" then error("ENOENT") end
local content = step[filename]
local pos = 1
return {
read = function(amount)
amount = amount or #content
local chunk = content:sub(pos, pos+amount-1)
pos = pos + #chunk
return chunk
end,
close = function() end,
}
elseif mode == "w" then
step[filename] = ""
local buf = {}
return {
write = function(str)
buf[#buf + 1] = str
end,
close = function()
step[filename] = table.concat(buf)
end,
}
elseif mode == "a" then
if type(step[filename]) ~= "string" then step[filename] = "" end
return {
write = function(str)
step[filename] = step[filename] .. str
end,
close = function() end,
}
else
error("EACCES")
end
end
function proxy:type(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then return "directory" end
for i=1,#steps do
step = step[steps[i]]
if not step then return false end
end
if type(step) == "table" then return "directory" end
if type(step) == "string" then return "file" end
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1,#steps do
step = step[steps[i]]
if not step then error("ENOENT") end
end
if type(step) ~= "table" then error("ENOTDIR") end
local keys = {}
for k,_ in pairs(step) do table.insert(keys, k) end
return keys
end
function proxy:fileExists(path)
local t = self:type(path)
return t == "file" or t == "directory"
end
kernel.disks["tmpfs0000"] = proxy
else
kernel.log("tmpToDisk enabled, skipping tmpfs module")
kernel.log("tmpfs0000 will passthrough to /tmp on the disk")
end

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
-- Loop device driver: -- Loop device driver:
-- --
-- BIND (directory) - re-routes VFS calls into a host directory subtree. -- BIND (directory) - re-routes VFS calls into a host directory subtree.

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
local args = {...} local args = {...}
local kernel = args[1] local kernel = args[1]
kernel._G = _G kernel._G = _G
@@ -55,3 +55,19 @@ local origLoad = load
kernel._U = readonly(kernel._G) kernel._U = readonly(kernel._G)
kernel._U._G = kernel._U kernel._U._G = kernel._U
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end 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

View File

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

View File

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

View File

@@ -11,15 +11,17 @@ 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)
@@ -39,11 +41,11 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if not ok then if not ok then
kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 2) kernel.log("Task " .. tostring(id) .. " exited with err: " .. tostring(err), "ERROR", 0xFF0000)
elseif err then elseif err then
kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "INFO") kernel.log("Task " .. tostring(id) .. " exited with code: " .. tostring(err), "DBUG", 0x00FFFF)
else else
kernel.log("Task " .. tostring(id) .. " exited without code", "INFO") kernel.log("Task " .. tostring(id) .. " exited without code", "DBUG", 0x00FFFF)
end end
end end
@@ -108,7 +110,7 @@ function sys.execspawn(path, name, envars, args, tgid)
kernel.log( kernel.log(
"execspawn: suid exec '" .. path .. "execspawn: suid exec '" .. path ..
"' caller_uid=" .. tostring(real_uid) .. "' caller_uid=" .. tostring(real_uid) ..
" -> euid=" .. tostring(euid), "INFO" " -> euid=" .. tostring(euid)
) )
end end
@@ -134,9 +136,9 @@ function sys.exec(path, args, envars)
local ok, err = xpcall(func, debug.traceback, table.unpack(task.args)) local ok, err = xpcall(func, debug.traceback, table.unpack(task.args))
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if not ok then if not ok then
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 2) kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' err: " .. tostring(err), "ERROR", 0xFF0000)
else else
kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "INFO") kernel.log("Task " .. tostring(task.pid) .. " exec '" .. path .. "' exited: " .. tostring(err), "DBUG", 0x00FFFF)
end end
end end
if type(err) == "number" then tasks[tostring(task.pid)].exit = err end if type(err) == "number" then tasks[tostring(task.pid)].exit = err end
@@ -153,8 +155,7 @@ end
function sys.sleep(s) function sys.sleep(s)
kernel.currentTask.status = "S" kernel.currentTask.status = "S"
kernel.currentTask.sleep = kernel.computer:time() + s * 1000 kernel.currentTask.sleep = kernel.EFI:getEpochMs() + s * 1000
coroutine.yield()
end end
function sys.getTask(pid) function sys.getTask(pid)
@@ -202,12 +203,16 @@ function sys.collect(pid)
end end
end end
function sys.kill(pid) function sys.kill(pid, force)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status == "Z" then elseif task.status == "Z" then
return false, "Task is already dead" return false, "Task is already dead"
elseif task.status == "D" and not force then
return false, "Cannot kill task waiting for IO"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
end end
local caller = kernel.currentTask local caller = kernel.currentTask
local ceuid = caller and (caller.euid or caller.uid) or kernel.uid local ceuid = caller and (caller.euid or caller.uid) or kernel.uid
@@ -222,10 +227,16 @@ function sys.stop(pid)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "R" then elseif task.status ~= "R" and task.status ~= "S" then
return false, "Cannot stop non-running task" return false, "Cannot stop non-running task"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else
if task.status == "S" then
task.status = "ST"
else else
task.status = "T" task.status = "T"
end
return true return true
end end
end end
@@ -234,10 +245,16 @@ function sys.continue(pid)
local task = tasks[tostring(pid)] local task = tasks[tostring(pid)]
if not task then if not task then
return false, "Task does not exist" return false, "Task does not exist"
elseif task.status ~= "T" then elseif task.status ~= "T" and task.status ~= "ST" then
return false, "Task is not stopped" return false, "Task is not stopped"
elseif task.euid ~= kernel.uid and kernel.uid ~= 0 then
return false, "Different user"
else
if task.status == "ST" then
task.status = "S"
else else
task.status = "R" task.status = "R"
end
return true return true
end end
end end
@@ -258,9 +275,9 @@ function sys.exit(code)
local task = kernel.currentTask local task = kernel.currentTask
if kernel.config.logTaskExit then if kernel.config.logTaskExit then
if code then if code then
kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "INFO") kernel.log("Task " .. tostring(task.pid) .. " exited with code: " .. tostring(code), "DBUG", 0x00FFFF)
else else
kernel.log("Task " .. tostring(task.pid) .. " exited without code", "INFO") kernel.log("Task " .. tostring(task.pid) .. " exited without code", "DBUG", 0x00FFFF)
end end
end end
tasks[tostring(task.pid)].status = "Z" tasks[tostring(task.pid)].status = "Z"
@@ -288,6 +305,7 @@ function sys.getuid() return kernel.currentTask.uid end
local function reapDeadTasks() local function reapDeadTasks()
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then if task.status == "Z" and not task.reapTime then
if task.pid == 1 then kernel.panic("Attempted to gc init!") end
task.coro = nil task.coro = nil
task.ivs = nil task.ivs = nil
task.vs = nil task.vs = nil
@@ -301,9 +319,9 @@ local function reapDeadTasks()
task.syscallReturn = nil task.syscallReturn = nil
task.sleep = nil task.sleep = nil
task.fd = nil task.fd = nil
task.reapTime = kernel.computer:time() + 30000 task.reapTime = kernel.EFI:getEpochMs() + 30000
elseif task.reapTime and kernel.computer:time() > task.reapTime elseif task.reapTime and kernel.EFI:getEpochMs() > task.reapTime
and task.status == "Z" then and task.status == "Z" then
for _, child in ipairs(task.children) do for _, child in ipairs(task.children) do
child.parent = tasks["1"] child.parent = tasks["1"]
@@ -333,7 +351,23 @@ local k_max = 0.5
local B = 0.01 local B = 0.01
function kernel.main() function kernel.main()
kernel.log("Starting main loop...")
kernel.saveLog()
local stopLog=5
local logTasks=100
while not kernel.exitMain do while not kernel.exitMain do
if kernel.config.logTasks then
if logTasks<0 then
kernel.log("Active Tasks:")
for i,v in pairs(tasks) do
kernel.log(v.name.." : "..v.status.." : "..tostring(v.pid).." : "..tostring(v.exit))
end
kernel.log("[END BLOCK]")
logTasks=100
end
logTasks=logTasks-1
end
local N = 0 local N = 0
local Tmin_hit = 0 local Tmin_hit = 0
local Tmax_hit = 0 local Tmax_hit = 0
@@ -341,40 +375,94 @@ function kernel.main()
local taskTimes = {} local taskTimes = {}
for pid, task in pairs(tasks) do for pid, task in pairs(tasks) do
if task.status == "S" and kernel.computer:time() >= task.sleep then if kernel.exitMain then break end
kernel.currentTask = task
kernel.uid = task.euid or task.uid
kernel.process = task.name
if task.status == "S" and kernel.EFI:getEpochMs() >= task.sleep then
task.status = "R" task.status = "R"
task.sleep = 0 task.sleep = 0
end end
if task.status == "R" then if task.status == "ST" and kernel.EFI:getEpochMs() >= task.sleep then
kernel.currentTask = task task.status = "T"
task.sleep = 0
end
kernel.uid = task.euid or task.uid if task.status == "DS" and kernel.EFI:getEpochMs() >= task.sleep then
kernel.process = task.name task.status = "D"
task.sleep = 0
end
if task.status == "D" then
if task.ksh then
coroutine.resume(task.ksh)
if coroutine.status(task.ksh) == "dead" then
task.ksh = nil
if task.status == "D" then
task.status = "R"
end
if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " IO wait completed", "DBUG", 0x00FFFF)
local sysret = task.syscallReturn
for i = 2, #sysret do
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
end
end
end
end
end
if task.status == "R" then
N = N + 1 N = N + 1
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha))) task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
if task.sigq and #task.sigq ~= 0 and task.sigh then if task.sigq and #task.sigq ~= 0 and task.sigh then
local coro = coroutine.create(task.sigh) local coro = coroutine.create(task.sigh)
local status,err
if kernel.config.preempt then if kernel.config.preempt then
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
if not task.debugger then
ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) }
else else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) } ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end end
else
ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) }
end
local elapsed = kernel.computer:time() - startTime local elapsed = kernel.EFI:getEpochMs() - startTime
task.lastTime = elapsed task.lastTime = elapsed
task.totalTime = (task.totalTime or 0) + elapsed task.totalTime = (task.totalTime or 0) + elapsed
task.numRuns = (task.numRuns or 0) + 1 task.numRuns = (task.numRuns or 0) + 1
@@ -386,7 +474,7 @@ function kernel.main()
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
if ret[1] == "error" or ret[1] == false then if ret[1] == "error" or ret[1] == false then
kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 2) kernel.log("processHandlerException: " .. tostring(ret[2]), "ERROR", 0xFF0000)
task.status = "Z" task.status = "Z"
task.exit = "processHandlerException: " .. tostring(ret[2]) task.exit = "processHandlerException: " .. tostring(ret[2])
@@ -401,9 +489,9 @@ function kernel.main()
local scname = ret[3] local scname = ret[3]
if kernel.syscalls[scname] then if kernel.syscalls[scname] then
if kernel.config.debugSyscalls then if kernel.config.debugSyscalls then
kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 5) kernel.log("Task " .. task.pid .. " syscall: " .. scname, "DBUG", 0x00FFFF)
for i = 4, #ret do for i = 4, #ret do
kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 5) kernel.log(" inval[" .. (i-3) .. "] = " .. tostring(ret[i]), "DBUG", 0x00FFFF)
end end
end end
@@ -411,12 +499,12 @@ function kernel.main()
if kernel.config.debugSyscalls then if kernel.config.debugSyscalls then
if not sysret[1] then if not sysret[1] then
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 2) kernel.log("Task " .. task.pid .. " syscall " .. scname .. " failed: " .. tostring(sysret[2]), "ERROR", 0xFF0000)
else else
kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 5) kernel.log("Task " .. task.pid .. " syscall " .. scname .. " ok, " .. (#sysret-1) .. " retvals", "DBUG", 0x00FFFF)
for i = 2, #sysret do for i = 2, #sysret do
local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i]) local v = type(sysret[i]) == "table" and table.serialize(sysret[i]) or tostring(sysret[i])
kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 5) kernel.log(" retval[" .. (i-1) .. "] = " .. v, "DBUG", 0x00FFFF)
end end
end end
end end
@@ -449,6 +537,11 @@ function kernel.main()
end end
reapDeadTasks() reapDeadTasks()
if stopLog > 0 then
kernel.log("Executed " .. N .. " tasks, avg time: " .. string.format("%.2f", T_prev_avg) .. "ms, var: " .. string.format("%.2f", T_prev_var) .. ", B: " .. string.format("%.5f", B))
stopLog = stopLog - 1
kernel.saveLog()
end
end end
end end

View File

@@ -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

View File

@@ -1,4 +1,4 @@
-- :Minify:-- --:Minify:--
local kernel = ... local kernel = ...
function print(...) function print(...)
local args = {...} local args = {...}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
--:Minify:--
local kernel = ...
kernel.allowGlobalOverwrites = false

View File

@@ -1,40 +0,0 @@
-- :Minify:--
local kernel = ...
local cache = {}
kernel.searchpaths = {
"/lib/?.lua", "/lib/?", "/usr/lib/?.lua", "/usr/lib/?",
"/usr/local/lib/?.lua", "/usr/local/lib/?", "?.lua", "?"
}
function require(module, ...)
if cache[module] then return cache[module] end
local modpath = module:gsub("%.", "/")
local failed = {}
for _, path in ipairs(kernel.searchpaths) do
local full_path = string.replace(path, "?", modpath)
if full_path:sub(1, 1) ~= "/" then
full_path = kernel.currentTask.cwd .. full_path
end
if kernel.vfs.exists(full_path) then
if kernel.vfs.type(full_path) == "directory" then
full_path = full_path .. "/init"
end
if kernel.vfs.exists(full_path) then
local handle = kernel.vfs.open(full_path, "r")
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
return
assert(load(file_content, full_path, "t", kernel._U))(...)
else
table.insert(failed, full_path)
end
else
table.insert(failed, full_path)
end
end
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
end

View File

@@ -1,147 +0,0 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "devfs0000"
proxy.isvirt = true
proxy.isReadOnly = function() return false end
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("EACCES") end
proxy.remove = function() error("EACCES") end
proxy.setLabel = function() error("EACCES") end
proxy.getLabel = function() return "devfs" end
proxy.attributes = function(path) return {
size = 0,
modified = 0,
created = 0,
} end
function proxy:open(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("open", mode)
end
error("ENFILE")
end
function proxy:type(path, mode)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return "directory"
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENFILE") end
step=dat
end
if type(step[steps[#steps]]) == "function" then
return step[steps[#steps]]("type", mode)
end
if type(step[steps[#steps]]) == "table" then
return "directory"
end
error("ENOENT")
end
function proxy:list(path)
local steps = kernel.vfs.splitPath(path)
local step = data
if #steps == 0 then
return table.keys(data)
end
for i=1, #steps-1 do
local dat = step[steps[i]]
if type(dat) ~= "table" then error("ENOENT") end
step=dat
end
if type(step[steps[#steps]]) == "table" then
return table.keys(step[steps[#steps]])
end
error("ENOENT")
end
function proxy:fileExists(path)
local ok = pcall(function()
return self:type(path)
end)
return ok
end
function data.random(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(math.random(0, 255))
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.null(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount) end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
function data.zero(op, mode)
if op=="type" then
return "character device"
elseif op=="open" then
if mode=="r" then
return {
read=function(amount)
local str = ""
for i=1, amount or 1 do
str=str..string.char(0)
end
return str
end
}
elseif mode=="w" or mode=="a" then
return {
write=function() end
}
else error("EACCES")
end
end
end
data["disk"]={}
kernel.devfs={}
kernel.devfs.data=data
kernel.devfs.proxy=proxy
kernel.disks["devfs0000"]=proxy

View File

@@ -1,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

View File

@@ -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

View File

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

View File

@@ -1,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

View File

@@ -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

View File

@@ -1,233 +0,0 @@
-- :Minify:--
local kernel = ...
local P = kernel.vfs.P
local PERM = kernel.vfs.PERM
local RW_R_R = P.OWNER_R + P.OWNER_W + P.GROUP_R + P.WORLD_R
local RWX_RX_RX = P.OWNER_R + P.OWNER_W + P.OWNER_X
+ P.GROUP_R + P.GROUP_X
+ P.WORLD_R + P.WORLD_X
local RW_R__ = P.OWNER_R + P.OWNER_W + P.GROUP_R
local RW____ = P.OWNER_R + P.OWNER_W
local RWXRWXRWX = PERM.RWXRWXRWX
local SUID_755 = PERM.SUID_755
local META_VERSION = 0x02
local rootDisk = kernel.disks["$"]
local function makeEntry(name, etype, owner, group, perms, cmeta)
cmeta = cmeta or ""
local plo = perms % 256
local phi = math.floor(perms / 256) % 256
local olo = (owner or 0) % 256
local ohi = math.floor((owner or 0) / 256) % 256
local glo = (group or 0) % 256
local ghi = math.floor((group or 0) / 256) % 256
return string.char(#name) .. name
.. string.char(etype, olo, ohi, glo, ghi, plo, phi)
.. string.char(#cmeta) .. cmeta
end
local REG = 0x00
local function mergeMeta(dir, entries)
local diskDir = dir
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
local existing = {}
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
if rok and rf then
local raw = rf.read(65535)
if rf.close then rf.close() end
existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {}
end
for _, e in ipairs(entries) do
local name = e[1]
local etype = e[2] or REG
local owner = e[3] or 0
local group = e[4] or 0
local perms = e[5] or RWX_RX_RX
local cmeta = e[6] or ""
existing[name] = {
etype = etype,
owner = owner,
group = group,
perms = perms,
cmeta = cmeta,
}
end
local data = string.char(META_VERSION)
for name, m in pairs(existing) do
data = data .. makeEntry(
name,
m.etype or REG,
m.owner or 0,
m.group or 0,
m.perms or RWX_RX_RX,
m.cmeta or ""
)
end
local ok, err = pcall(function()
local f = rootDisk:open(metaPath, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
end
end
kernel.log("Seeding filesystem permissions...", "INFO")
mergeMeta("/", {
{"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},
})
mergeMeta("/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},
})
mergeMeta("/boot/cct", {
{"boot.lua", REG, 0, 0, RW_R_R},
{"initdisks", REG, 0, 0, RW_R_R},
{"eeprom", REG, 0, 0, RW_R_R},
})
mergeMeta("/boot/oc", {
{"boot.lua", REG, 0, 0, RW_R_R},
{"initfs.lua",REG, 0, 0, RW_R_R},
{"eeprom", REG, 0, 0, RW_R_R},
})
mergeMeta("/sbin", {
{"init.lua", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/bin", {
{"cat", REG, 0, 0, RWX_RX_RX},
{"chattr", REG, 0, 0, RWX_RX_RX},
{"chgrp", REG, 0, 0, RWX_RX_RX},
{"chmod", REG, 0, 0, RWX_RX_RX},
{"chown", REG, 0, 0, RWX_RX_RX},
{"chroot", 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},
{"help", REG, 0, 0, RWX_RX_RX},
{"hysh", REG, 0, 0, RWX_RX_RX},
{"hyshex", REG, 0, 0, RWX_RX_RX},
{"id", REG, 0, 0, RWX_RX_RX},
{"install", REG, 0, 0, RWX_RX_RX},
{"ln", REG, 0, 0, RWX_RX_RX},
{"login", REG, 0, 0, SUID_755 },
{"loimgcreate", REG, 0, 0, RWX_RX_RX},
{"looptest", REG, 0, 0, RWX_RX_RX},
{"losetup", REG, 0, 0, RWX_RX_RX},
{"ls", REG, 0, 0, RWX_RX_RX},
{"lsusers", REG, 0, 0, RWX_RX_RX},
{"lua", REG, 0, 0, RWX_RX_RX},
{"luaold", REG, 0, 0, RWX_RX_RX},
{"micro", REG, 0, 0, RWX_RX_RX},
{"mkdir", REG, 0, 0, RWX_RX_RX},
{"mount", REG, 0, 0, RWX_RX_RX},
{"passwd", REG, 0, 0, RWX_RX_RX},
{"ps", REG, 0, 0, RWX_RX_RX},
{"pwd", REG, 0, 0, RWX_RX_RX},
{"readlink", REG, 0, 0, RWX_RX_RX},
{"sed", REG, 0, 0, RWX_RX_RX},
{"socktest", REG, 0, 0, RWX_RX_RX},
{"spm", REG, 0, 0, RWX_RX_RX},
{"startup", 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},
{"umount", REG, 0, 0, RWX_RX_RX},
{"useradd", REG, 0, 0, RWX_RX_RX},
{"userdel", REG, 0, 0, RWX_RX_RX},
{"usermod", REG, 0, 0, RWX_RX_RX},
{"whoami", REG, 0, 0, RWX_RX_RX},
{"yes", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/bin/startup", {
{"test.lua", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/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 },
})
mergeMeta("/lib/sys", {
{"fs", REG, 0, 0, RW_R_R},
{"hpv", REG, 0, 0, RW_R_R},
{"ipc", REG, 0, 0, RW_R_R},
{"term", REG, 0, 0, RW_R_R},
{"init", REG, 0, 0, RW_R_R},
})
mergeMeta("/lib/modules", {
{"hyperion", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/lib/modules/hyperion", {
{"01_stdlib.kmod", REG, 0, 0, RW_R_R},
{"10_vfs.kmod", REG, 0, 0, RW_R_R},
{"11_require.kmod", REG, 0, 0, RW_R_R},
{"12_devfs.kmod", REG, 0, 0, RW_R_R},
{"12_tmpfs.kmod", REG, 0, 0, RW_R_R},
{"13_loopdev.kmod", REG, 0, 0, RW_R_R},
{"14_keventd.kmod", REG, 0, 0, RW_R_R},
{"19_fstab.kmod", REG, 0, 0, RW_R_R},
{"20_signals.kmod", REG, 0, 0, RW_R_R},
{"20_socket.kmod", REG, 0, 0, RW_R_R},
{"26_tty.kmod", REG, 0, 0, RW_R_R},
{"30_userspace.kmod", REG, 0, 0, RW_R_R},
{"40_auth.kmod", REG, 0, 0, RW_R_R},
{"45_hypervisor.kmod", REG, 0, 0, RW_R_R},
{"47_dbg.kmod", REG, 0, 0, RW_R_R},
{"50_gpio.kmod", REG, 0, 0, RW_R_R},
{"70_stdlibadv.kmod", REG, 0, 0, RW_R_R},
{"90_init.kmod", REG, 0, 0, RW_R_R},
{"91_login.kmod", REG, 0, 0, RW_R_R},
{"92_permissions.kmod", REG, 0, 0, RW_R_R},
{"99_final.kmod", REG, 0, 0, RW_R_R},
})
mergeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R },
{"shadow", REG, 0, 0, RW____ },
{"pam.d", REG, 0, 0, RWX_RX_RX},
})
mergeMeta("/etc/pam.d", {
{"secret", REG, 0, 0, RW____},
})
kernel.log("Filesystem permissions seeded.", "INFO")

View File

@@ -1,2 +0,0 @@
local kernel = ...
kernel.allowGlobalOverwrites = false

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
--:Minify:-- --:Minify:--
local fs = require("sys.fs") local fs = require("fs")
local cmdArgs = {...} local cmdArgs = {...}
local targetUser = "root" local targetUser = "root"
@@ -55,7 +55,7 @@ if currentUid ~= 0 then
end end
end end
local ok, err = syscall.elevate("root", pw) local ok, err = syscall.login(0, pw)
if not ok then if not ok then
sleep(1) sleep(1)
print("sudo: Authentication failure") print("sudo: Authentication failure")

View File

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

View File

@@ -29,7 +29,7 @@ if not ok then
end end
if removeHome and pwent and pwent.homedir then if removeHome and pwent and pwent.homedir then
local fs = require("sys.fs") local fs = require("fs")
local ok2, err2 = pcall(function() local ok2, err2 = pcall(function()
local function rmdir(path) local function rmdir(path)
for _, f in ipairs(fs.list(path) or {}) do for _, f in ipairs(fs.list(path) or {}) do

View File

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

View File

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

View File

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

View File

@@ -1,21 +1,21 @@
--:Minify:-- --:Minify:--
syscall.open("/dev/tty/tty1","r") --stdin (Device 0)
syscall.open("/dev/tty/tty1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2)
local success, errorMsg = xpcall(function() local success, errorMsg = xpcall(function()
local fs = require("sys.fs") syscall.open("/dev/input/keyboard1","r") --stdin (Device 0)
syscall.open("/dev/tty/1","w") --stdout (Device 1)
syscall.open("/dev/null","w") --stderr (device 2)
local fs = require("fs")
syscall.devctl(1,"clear") syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.devctl(1,"spos",1,1) syscall.devctl(1,"spos",1,1)
print("HyperionOS hysh Shell") print("HyperionOS hysh Shell")
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown") local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
local commandHistory = {} local commandHistory = {}
local terminate = false local terminate = false
syscall.setEnviron("SHELL","rtbash") syscall.setEnviron("SHELL","hysh")
syscall.setEnviron("PATH","/bin/") syscall.setEnviron("PATH","/bin/")
local _home = syscall.getEnviron("HOME") local _home = syscall.getEnviron("HOME")
if _home and _home ~= "" then if _home and _home ~= "" then
@@ -26,8 +26,27 @@ else
end end
local oldWD = "" local oldWD = ""
local colors = {
0xFFFFFF,
0xFF0000,
0x00FF00,
0x0000FF,
0x00FFFF,
0xFF00FF,
0xFFFF00,
0xFF6D00,
0x6DFF55,
0x24FFFF,
0x924900,
0x6D6D55,
0xDBDBAA,
0x6D00FF,
0xB6FF00,
0x000000
}
for i = 1, 16 do for i = 1, 16 do
syscall.devctl(1,"sbgc",i); printInline(" ") syscall.devctl(1,"sbgc",colors[i]); printInline(" ")
end end
print("\n") print("\n")
@@ -133,8 +152,6 @@ local function copyfile(src, dst)
if not data then return false, err end if not data then return false, err end
local ok, err2 = writefile(dst, data) local ok, err2 = writefile(dst, data)
if not ok then return false, err2 end if not ok then return false, err2 end
local ok2, stat = pcall(syscall.stat, src)
if ok2 and stat and stat.perms then pcall(syscall.chmod, dst, stat.perms) end
return true return true
end end
@@ -223,7 +240,7 @@ end
builtinCmds.clear = function() builtinCmds.clear = function()
syscall.devctl(1,"clear") syscall.devctl(1,"clear")
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.devctl(1,"spos",1,1) syscall.devctl(1,"spos",1,1)
end end
@@ -431,7 +448,7 @@ builtinCmds.head = function(...)
local multi = #files > 1 local multi = #files > 1
local function dohead(text, label) local function dohead(text, label)
if multi then if multi then
syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0x0000FF); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",0xFFFFFF)
end end
local count = 0 local count = 0
for line in (text.."\n"):gmatch("([^\n]*)\n") do for line in (text.."\n"):gmatch("([^\n]*)\n") do
@@ -470,7 +487,7 @@ builtinCmds.tail = function(...)
local multi = #files > 1 local multi = #files > 1
local function dotail(text, label) local function dotail(text, label)
if multi then if multi then
syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0x0000FF); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",0xFFFFFF)
end end
local lines = splitlines(text) local lines = splitlines(text)
local start = math.max(1, #lines - n + 1) local start = math.max(1, #lines - n + 1)
@@ -810,14 +827,183 @@ builtinCmds.df = function(...)
end end
end end
builtinCmds.ops = function()
for i,v in pairs(math) do
print(i)
end
end
builtinCmds.reboot = function ()
syscall.reboot()
end
builtinCmds.shutdown = function ()
syscall.shutdown()
end
local function listDir(dir, prefix)
local ok, entries = pcall(syscall.listdir, dir)
if not ok or not entries then return {} end
local results = {}
for _, e in ipairs(entries) do
if prefix == "" or e:sub(1, #prefix) == prefix then
local fullpath = (dir == "/" and "/" or dir.."/")..e
local t = syscall.type(fullpath)
results[#results+1] = t == "directory" and (e.."/") or e
end
end
table.sort(results)
return results
end
local function listCommands(prefix)
local results = {}
local seen = {}
for name in pairs(builtinCmds) do
if prefix == "" or name:sub(1, #prefix) == prefix then
if not seen[name] then results[#results+1] = name; seen[name] = true end
end
end
local paths = string.split(syscall.getEnviron("PATH") or "/bin/", ":")
for _, p in ipairs(paths) do
local ok, entries = pcall(syscall.listdir, p)
if ok and entries then
for _, e in ipairs(entries) do
local fullpath = (p:sub(-1)=="/" and p or p.."/")..e
local xok = pcall(syscall.access, fullpath, "x")
if xok and (prefix == "" or e:sub(1, #prefix) == prefix) then
if not seen[e] then results[#results+1] = e; seen[e] = true end
end
end
end
end
table.sort(results)
return results
end
local function commonPrefix(list)
if #list == 0 then return "" end
local pre = list[1]
for i = 2, #list do
local s = list[i]
local j = 1
while j <= #pre and j <= #s and pre:sub(j,j) == s:sub(j,j) do j = j+1 end
pre = pre:sub(1, j-1)
if pre == "" then return "" end
end
return pre
end
local function showCompletions(completions)
syscall.write(1, "\n")
local W = 51
local maxlen = 0
for _, c in ipairs(completions) do if #c > maxlen then maxlen = #c end end
local colw = maxlen + 2
local cols = math.max(1, math.floor(W / colw))
local i = 0
for _, c in ipairs(completions) do
local padded = c .. string.rep(" ", colw - #c)
syscall.write(1, padded)
i = i + 1
if i % cols == 0 then syscall.write(1, "\n") end
end
if i % cols ~= 0 then syscall.write(1, "\n") end
end
local function parseForCompletion(input, cursorPos)
local sofar = input:sub(1, cursorPos - 1)
local tokens = {}
for tok in sofar:gmatch("%S+") do tokens[#tokens+1] = tok end
local partial = sofar:match("%S+$") or ""
local isFirst = (#tokens == 0) or (sofar:sub(-1) ~= " " and #tokens == 1)
local partialDir, partialBase
if partial:find("/") then
partialDir = partial:match("^(.*/)") or "/"
partialBase = partial:match("[^/]*$") or ""
else
partialDir = nil
partialBase = partial
end
return tokens, partial, isFirst, partialDir, partialBase
end
local tabState = { last = nil, idx = 0, list = {} }
local function doTabComplete(input, cursorPos)
local tokens, partial, isFirst, partialDir, partialBase = parseForCompletion(input, cursorPos)
local candidates
if isFirst then
if partialDir then
local dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
candidates = listDir(dir, partialBase)
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
else
candidates = listCommands(partialBase)
end
else
local dir, base
if partialDir then
dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
base = partialBase
else
dir = syscall.getcwd()
base = partialBase
end
candidates = listDir(dir, base)
if partialDir then
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
end
end
if #candidates == 0 then
return input, cursorPos, false
end
local context = input.."\0"..tostring(cursorPos)
if tabState.last ~= context then
tabState.last = context
tabState.idx = 0
tabState.list = candidates
end
if #candidates == 1 then
local completed = candidates[1]
local before = input:sub(1, cursorPos - 1 - #partial)
local after = input:sub(cursorPos)
local newInput = before .. completed .. after
local newCursor = #before + #completed + 1
tabState.last = nil
return newInput, newCursor, true
end
local pre = commonPrefix(candidates)
if #pre > #partial then
local before = input:sub(1, cursorPos - 1 - #partial)
local after = input:sub(cursorPos)
local newInput = before .. pre .. after
local newCursor = #before + #pre + 1
tabState.last = newInput.."\0"..tostring(newCursor)
tabState.list = candidates
return newInput, newCursor, true
else
showCompletions(candidates)
return input, cursorPos, true
end
end
local lastghost=""
local function getUserInput() local function getUserInput()
syscall.devctl(1,"sfgc",3) syscall.devctl(1,"sfgc",0x00FF00)
syscall.write(1, userhost) syscall.write(1, userhost)
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.write(1, ":") syscall.write(1, ":")
syscall.devctl(1,"sfgc",10) syscall.devctl(1,"sfgc",0x24FFFF)
syscall.write(1, syscall.getcwd()) syscall.write(1, syscall.getcwd())
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.write(1, "$ ") syscall.write(1, "$ ")
local curOffsetStr = syscall.devctl(1, "gpos") local curOffsetStr = syscall.devctl(1, "gpos")
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1)) local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
@@ -829,33 +1015,78 @@ local function getUserInput()
local history = 0 local history = 0
local dirty = true local dirty = true
local function getGhostSuffix()
if #input == 0 then return "" end
local _, partial, isFirst, partialDir, partialBase = parseForCompletion(input, cursorPos)
if cursorPos ~= #input + 1 then return "" end
local candidates
if isFirst then
if partialDir then
local dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
candidates = listDir(dir, partialBase)
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
else
candidates = listCommands(partialBase)
end
else
local dir, base
if partialDir then
dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
base = partialBase
else
dir = syscall.getcwd()
base = partialBase
end
candidates = listDir(dir, base)
if partialDir then
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
end
end
if #candidates == 0 then return "" end
local pre = commonPrefix(candidates)
if #pre > #partial then
return pre:sub(#partial + 1)
end
return ""
end
local function redraw() local function redraw()
syscall.devctl(1,"spos",curOffsetX,curOffsetY) syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, string.sub(input, 1, cursorPos-1)) syscall.write(1, string.sub(input, 1, cursorPos-1))
if blinkState then if blinkState then
syscall.devctl(1,"sfgc",16); syscall.devctl(1,"sbgc",1) syscall.devctl(1,"sfgc",0x000000); syscall.devctl(1,"sbgc",0xFFFFFF)
end end
if cursorPos > #input then if cursorPos > #input then
syscall.write(1, " ") syscall.write(1, " ")
else else
syscall.write(1, string.sub(input, cursorPos, cursorPos)) syscall.write(1, string.sub(input, cursorPos, cursorPos))
end end
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16) syscall.devctl(1,"sfgc",0xFFFFFF); syscall.devctl(1,"sbgc",0x000000)
syscall.write(1, string.sub(input, cursorPos+1) .. " ") local after = string.sub(input, cursorPos+1)
syscall.write(1, after)
local ghost = getGhostSuffix()
if #ghost > 0 then
syscall.devctl(1,"sfgc",0x6D00FF)
syscall.write(1, ghost)
syscall.devctl(1,"sfgc",0xFFFFFF)
syscall.write(1, " ")
else
syscall.write(1, " ")
end
end end
while true do while true do
local key = syscall.read(0) local key = syscall.read(0)
if key and key ~= "" then if key and key ~= "" then
if key=="\19" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end if key=="" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end
elseif key=="\20" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end elseif key=="" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end
elseif key=="\17" then elseif key=="" then
if history<#commandHistory then if history<#commandHistory then
history=history+1 history=history+1
input=commandHistory[#commandHistory-history+1] input=commandHistory[#commandHistory-history+1]
cursorPos=#input+1;dirty=true cursorPos=#input+1;dirty=true
end end
elseif key=="\18" then elseif key=="" then
if history>1 then if history>1 then
history=history-1 history=history-1
input=commandHistory[#commandHistory-history+1] input=commandHistory[#commandHistory-history+1]
@@ -863,17 +1094,49 @@ local function getUserInput()
elseif history==1 then elseif history==1 then
history=0;input="";cursorPos=1;dirty=true history=0;input="";cursorPos=1;dirty=true
end end
elseif key=="" then cursorPos=1;dirty=true
elseif key=="" then cursorPos=#input+1;dirty=true
elseif key=="[3~" then
if cursorPos<=#input then
input=string.sub(input,1,cursorPos-1)..string.sub(input,cursorPos+1)
dirty=true
end
elseif key=="\t" then
local newInput, newCursor, needsRedraw = doTabComplete(input, cursorPos)
if needsRedraw then
input = newInput; cursorPos = newCursor
local posStr = syscall.devctl(1, "gpos")
local sep = posStr:find(";")
local px = tonumber(posStr:sub(1, sep-1))
local py = tonumber(posStr:sub(sep+1))
if px > 1 then
syscall.devctl(1,"spos",1,py)
local tsz = syscall.devctl(1,"size") or "51;19"
local tw = tonumber(tsz:match("^(%d+)")) or 51
syscall.write(1, string.rep(" ", tw))
syscall.devctl(1,"spos",1,py)
end
syscall.devctl(1,"sfgc",0x00FF00); syscall.write(1, userhost)
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.write(1, ":")
syscall.devctl(1,"sfgc",0x00FFFF); syscall.write(1, syscall.getcwd())
syscall.devctl(1,"sfgc",0xFFFFFF); syscall.write(1, "$ ")
posStr = syscall.devctl(1, "gpos")
sep = posStr:find(";")
curOffsetX = tonumber(posStr:sub(1, sep-1))
curOffsetY = tonumber(posStr:sub(sep+1))
dirty = true
end
elseif key=="\b" then elseif key=="\b" then
if cursorPos>1 then if cursorPos>1 then
input=string.sub(input,1,cursorPos-2)..string.sub(input,cursorPos) input=string.sub(input,1,cursorPos-2)..string.sub(input,cursorPos)
cursorPos=cursorPos-1;dirty=true cursorPos=cursorPos-1;dirty=true
end end
elseif key=="\n" then elseif key=="\n" then
syscall.devctl(1,"sfgc",1);syscall.devctl(1,"sbgc",16) syscall.devctl(1,"sfgc",0xFFFFFF);syscall.devctl(1,"sbgc",0x000000)
syscall.devctl(1,"spos",curOffsetX,curOffsetY) syscall.devctl(1,"spos",curOffsetX,curOffsetY)
syscall.write(1, input.." \n") syscall.write(1, input.." \n")
return input return input
else elseif #key == 1 and key:byte(1) >= 32 and key:byte(1) < 127 then
input=string.sub(input,1,cursorPos-1)..key..string.sub(input,cursorPos) input=string.sub(input,1,cursorPos-1)..key..string.sub(input,cursorPos)
cursorPos=cursorPos+1;dirty=true cursorPos=cursorPos+1;dirty=true
end end
@@ -885,7 +1148,7 @@ local function getUserInput()
end end
local function printError(progName, msg) local function printError(progName, msg)
syscall.devctl(1,"sfgc",2) syscall.devctl(1,"sfgc",0xFF0000)
local s = tostring(msg) local s = tostring(msg)
local line, rest = s:match("%]:(%d+): (.+)$") local line, rest = s:match("%]:(%d+): (.+)$")
if not line then line, rest = s:match(":(%d+): (.+)$") end if not line then line, rest = s:match(":(%d+): (.+)$") end
@@ -894,12 +1157,12 @@ local function printError(progName, msg)
else else
print(progName..": "..s) print(progName..": "..s)
end end
syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFFFFFF)
end end
local function runCommand(command) local function runCommand(command)
do do
local func = load("return "..command, "@equation", "t", {}) local func = load("return "..command, "@equation", "t", math)
if func then if func then
local ok, result = pcall(func) local ok, result = pcall(func)
if ok and type(result)=="number" then print(result); return end if ok and type(result)=="number" then print(result); return end
@@ -942,27 +1205,20 @@ local function runCommand(command)
local xok, xerr = pcall(syscall.access, cmdPath, "x") local xok, xerr = pcall(syscall.access, cmdPath, "x")
if not xok then if not xok then
syscall.devctl(1,"sfgc",2); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",1) syscall.devctl(1,"sfgc",0xFF0000); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",0xFFFFFF)
return return
end end
local text = fs.readAllText(cmdPath) local proc = syscall.spawn(function()
local program, err = load(text, progName) -- Open standard fds so programs that don't do it themselves work correctly.
if not program then syscall.open("/dev/input/keyboard1", "r") -- fd 0 stdin
syscall.devctl(1,"sfgc",2) syscall.open("/dev/tty/1", "w") -- fd 1 stdout
local line, rest = tostring(err):match(":(%d+): (.+)$") syscall.open("/dev/null", "w") -- fd 2 stderr
if line then printInline(progName..": load error on line "..line..": "); print(rest) -- exec replaces this coroutine's code with a fresh isolated environment
else print(progName..": load error: "..tostring(err)) end -- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
syscall.devctl(1,"sfgc",1); return -- so the child cannot share any upvalue or syscall table state with hysh.
end syscall.exec(cmdPath, {table.unpack(args, 2)})
end, progName)
local proc = syscall.spawn(function(...)
syscall.open("/dev/tty/tty1","r")
syscall.open("/dev/tty/tty1","w")
syscall.open("/dev/null","w")
local ok2, msg = pcall(program, ...)
if not ok2 then printError(progName, msg) end
end, progName, nil, {table.unpack(args, 2)})
while true do while true do
local exited, code = syscall.collect(proc) local exited, code = syscall.collect(proc)
@@ -973,8 +1229,8 @@ local function runCommand(command)
if terminate then if terminate then
local ok2 = syscall.kill(proc) local ok2 = syscall.kill(proc)
if ok2 then if ok2 then
syscall.devctl(1,"sbgc",16); syscall.devctl(1,"sfgc",2) syscall.devctl(1,"sbgc",16); syscall.devctl(1,"sfgc",0xFF0000)
print("\nProgram Terminated."); syscall.devctl(1,"sfgc",1) print("\nProgram Terminated."); syscall.devctl(1,"sfgc",0xFFFFFF)
end end
terminate = false; break terminate = false; break
end end
@@ -996,8 +1252,8 @@ end, debug.traceback)
if not success then if not success then
syscall.log("Error running shell: "..errorMsg, "ERROR") syscall.log("Error running shell: "..errorMsg, "ERROR")
syscall.devctl(1,"sfgc",2) syscall.devctl(1,"sfgc",0xFF0000)
syscall.devctl(1,"sbgc",16) syscall.devctl(1,"sbgc",0x000000)
print() print()
print("Error running shell: ") print("Error running shell: ")
print(errorMsg) print(errorMsg)

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