mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-23 10:58:06 +11:00
372 lines
10 KiB
Plaintext
372 lines
10 KiB
Plaintext
This is a full reference on those
|
|
functions and fields exposed by the
|
|
kernel to processes and libraries.
|
|
|
|
Firstly, it is important to note that
|
|
a process runs within a coroutine.
|
|
|
|
This allows a highly "traditional"
|
|
form of mixing async and synchronous
|
|
code with event-loop nesting and
|
|
such designs. If this is not to your
|
|
taste then you can just use one, not
|
|
nested event loop.
|
|
|
|
As it runs in a coroutine, events are
|
|
received via coroutine.yield() -
|
|
sandboxers beware! You may have to
|
|
use coroutine.running() in order to
|
|
successfully hide the implementation
|
|
details of your sandbox (also events
|
|
and potentially accesses headed in
|
|
its direction...)
|
|
|
|
NOTE regarding security of this!
|
|
|
|
For efficiency, APIs are generally
|
|
used "directly". This allows read
|
|
access to all events, including any
|
|
security responses.
|
|
|
|
The assumption made here is that if
|
|
you're communicating with an app you
|
|
don't trust, you will wrap access to
|
|
it in a coroutine shell, and perform
|
|
ensureType usage on everything that
|
|
it spews out.
|
|
|
|
In particular, this is a good way to
|
|
isolate yourself from any effects,
|
|
including timeout, of a function you
|
|
know to be environment-sandboxed:
|
|
|
|
coroutine.resume(coroutine.create(
|
|
functionIDontTrust))
|
|
|
|
An example KittenOS NEO program,
|
|
solely using kernel APIs,
|
|
that you will likely have to kill:
|
|
|
|
neo.scheduleTimer(os.uptime() + 1)
|
|
while true do
|
|
local ev = coroutine.yield()
|
|
if ev == "k.timer" then
|
|
neo.emergency("Hello...")
|
|
neo.scheduleTimer(os.uptime() + 1)
|
|
end
|
|
end
|
|
|
|
This will say "Hello..." via the
|
|
neo.emergency mechanism once every
|
|
second, independently of anything
|
|
else on the system.
|
|
|
|
While this is obviously not a sane
|
|
sys-init for actual use, if you have
|
|
a disk that you can copy the kernel
|
|
to and a copy of this, it might make
|
|
a fun experiment.
|
|
|
|
The way to exit the program is to
|
|
return from your process's main
|
|
function.
|
|
|
|
The first field to note is:
|
|
|
|
_VERSION: _VERSION from the host.
|
|
|
|
The following are just wrapMeta'd
|
|
host libraries (*: altered):
|
|
|
|
math, table, string, unicode*,
|
|
coroutine, os*, debug, utf8, bit32
|
|
|
|
unicode is extended with:
|
|
safeTextFormat(s, p):
|
|
Takes a string s, and a position p,
|
|
(the position is optional, and is
|
|
assumed to be 1 otherwise)
|
|
and returns a space-padded string,
|
|
with a space after each wide char
|
|
to make unicode.len & co. act in
|
|
screen units, along with the
|
|
position translated.
|
|
undoSafeTextFormat(s):
|
|
Takes a string in padded-widechar
|
|
format, and gets rid of the pad.
|
|
Note that if padding is *missing*,
|
|
wide characters become spaces.
|
|
This leaves a string that's usually
|
|
safe to pass to a GPU without any
|
|
odd graphical glitches.
|
|
|
|
The KittenOS NEO kernel also reserves
|
|
the ability to take advantage of any
|
|
full de-UTF16'd support for Unicode
|
|
available on the system, but will
|
|
not include such support as a shim
|
|
for memory usage reasons.
|
|
|
|
Programs that thus try to work around
|
|
this problem should delegate this
|
|
task to a library, in a separate
|
|
package, which can then be updated
|
|
as-needed if and when the issue is
|
|
resolved.
|
|
|
|
os is extended with:
|
|
totalMemory = computer.totalMemory,
|
|
freeMemory = computer.freeMemory,
|
|
energy = computer.energy,
|
|
maxEnergy = computer.maxEnergy,
|
|
uptime = computer.uptime,
|
|
address = computer.address
|
|
|
|
The following are just host functions
|
|
(*: wrapped for security):
|
|
|
|
assert, ipairs, load*, next*,
|
|
pairs, pcall, xpcall, select,
|
|
type, error, tonumber, tostring,
|
|
setmetatable, getmetatable*,
|
|
rawset*, rawget, rawlen, rawequal
|
|
|
|
(Apparently load, if not given an
|
|
argument, uses the global metatable.
|
|
This is of course a security hole.
|
|
A very big one. So it ended up
|
|
getting wrapped as of R3.)
|
|
|
|
"require" and "neo" are the parts of
|
|
the environment where a NEO-specific
|
|
nature presents itself.
|
|
|
|
require takes a string, and returns
|
|
the value returned by the library at
|
|
"libs/" .. str .. ".lua" on the
|
|
primary disk.
|
|
Since R2, the value is automatically
|
|
wrapMeta'd, just in case.
|
|
Before R2, libraries did this on
|
|
their own, but this caused NEO-only
|
|
code to crop up in libraries that
|
|
did not need NEO-only code.
|
|
|
|
The library name must be a valid path
|
|
component, and the library path must
|
|
also be valid - see
|
|
ensurePathComponent, ensurePath for
|
|
more info.
|
|
|
|
The "neo" table is where most of the
|
|
NEO-specificness is hiding, which is
|
|
probably shown by its name.
|
|
|
|
It is also where libraries differ to
|
|
processes, as libraries get a subset
|
|
of the table.
|
|
|
|
For libraries, it contains:
|
|
emergency: Equals ocemu.log, if
|
|
available on the system. Else, NOP.
|
|
readBufSize: The readBufSize kernel
|
|
configuration value. Default: 2048.
|
|
Adjusting this in the kernel allows
|
|
adjusting how much the system will
|
|
read at any given time, which can
|
|
have non-obvious memory usage
|
|
effects.
|
|
Do note, following this limit is
|
|
not a requirement and is not
|
|
enforced - it's not a security
|
|
matter, just optimization/memory.
|
|
wrapMeta(v): A function that takes a
|
|
value, and wraps it in such a way
|
|
as to be immutable, returning the
|
|
wrapped value.
|
|
This is the first line of defense
|
|
against memory use - by using this
|
|
to protect a table, the result can
|
|
be shared between untrusted code.
|
|
listProcs(): A function that returns
|
|
an ipairs-friendly process list.
|
|
Values are:
|
|
{pid, pkg, cpuUsageInSeconds}
|
|
listApps(): Returns an
|
|
ipairs-friendly list of
|
|
applications on the system, like:
|
|
{"app-test", "svc-liliput"}
|
|
listLibs(): Returns an
|
|
ipairs-friendly list of libraries
|
|
on the system, such as:
|
|
{"fmttext",
|
|
"braille"}
|
|
usAccessExists(s):
|
|
Returns true if the specified
|
|
access has been registered from
|
|
userspace using the related "r."
|
|
access.
|
|
totalIdleTime(): Returns the current
|
|
kernel idle time total, useful for
|
|
measuring current CPU usage, and in
|
|
turn comparing to application CPU
|
|
time to get various statistics.
|
|
ensurePath(s, root):
|
|
Attempts to verify the
|
|
safety of a path, and errors if any
|
|
aspect seems incorrect.
|
|
The root must be a prefix to the
|
|
path, and the path must follow a
|
|
strict standardized form that is
|
|
guaranteed to always be supported
|
|
and handled in the same way on any
|
|
OC system.
|
|
Essentially, "//" must not occur,
|
|
and all "[^/]+" matches must be
|
|
valid path components.
|
|
ensurePathComponent(s):
|
|
Ensures that a string is a safe
|
|
filename via a character list and
|
|
some special filename checks, for
|
|
".." and ".".
|
|
Rather permissive right now, but
|
|
don't go relying on that.
|
|
ensureType(v, ts):
|
|
Checks that a value is of a given
|
|
type, and errors otherwise. If the
|
|
type is "table", it also errors if
|
|
a metatable exists.
|
|
|
|
The additional things available to
|
|
processes are those things that
|
|
require a process to use:
|
|
|
|
pid: A field that specifies the
|
|
process ID of this process.
|
|
Harmless, but not entirely useful.
|
|
pkg: A field that specifies the
|
|
package name of this process.
|
|
Useful if you're worried about
|
|
your app getting renamed.
|
|
dead: Actually a field, that isn't
|
|
set at first, but is set later to
|
|
indicate deadness. Useful if your
|
|
process does anything that might
|
|
lead to functions being called in
|
|
the afterlife, such as providing an
|
|
API.
|
|
executeAsync: Function that takes
|
|
an app name (aka: pkg), and a
|
|
set of arguments to give it.
|
|
NOTE: sys- apps cannot be started
|
|
from non sys- apps no matter how
|
|
hard you try, without k.root
|
|
alterations to runProgramPolicy.
|
|
Your process pkg and ID is
|
|
prepended to the arguments.
|
|
NOTE: This uses the result, err
|
|
return format, except for security
|
|
errors in which case it uses a
|
|
full error, because you might just
|
|
ignore the return value.
|
|
A successful result is the PID.
|
|
executeExt: Like executeAsync, but
|
|
firstly, synchronous, and secondly,
|
|
with an extra first parameter that
|
|
contains a function to call on
|
|
events encountered during the time.
|
|
As for the return values, it tries
|
|
to emulate os.execute, so it
|
|
returns -1 & reason on load error,
|
|
and 0 & death-reason otherwise.
|
|
execute: executeExt, but with the
|
|
first parameter set to a blank
|
|
function.
|
|
requestAccessAsync: A function that
|
|
takes an access ID (aka 'perm') as
|
|
a string (see kn-perms for info),
|
|
and starts a security request that
|
|
is responded to with a
|
|
k.securityresponse such as:
|
|
"k.securityresponse", perm, obj
|
|
requestAccess(perm[, handler]):
|
|
Runs requestAccessAsync, then sends
|
|
events to handler (if any) while
|
|
waiting for the response.
|
|
sys-icecap is responsible for any
|
|
automatic starting of services
|
|
that may occur.
|
|
requireAccess(perm, reason): requestAccess, but
|
|
(perm, reason) - the reason is used
|
|
in an error if the access cannot
|
|
be gained.
|
|
scheduleTimer: Given an os.uptime
|
|
value, creates a timer and returns
|
|
a completely meaningless table that
|
|
is never touched by the kernel
|
|
directly, called the "tag".
|
|
The resulting event:
|
|
"k.timer", tag, time
|
|
These events are ONLY EVER sent as
|
|
a consequence of this function,
|
|
and this can be relied on safely.
|
|
NOTE: Setting timers too far in the
|
|
future has effects on system
|
|
stability. So does using memory,
|
|
and there's no way for me to stop
|
|
that, either. So long as the timer
|
|
is reached, alive or dead, things
|
|
will work, but spamming timers has
|
|
the consequence of memory use,
|
|
and timers stick around after the
|
|
process that owns them is dead.
|
|
|
|
The list of events, tacked on at the
|
|
end here:
|
|
|
|
k.procnew(pkg, pid, ppkg, ppid):
|
|
New process creation, with parent
|
|
information (for seat tracking)
|
|
This is not given to the process
|
|
being created, as all of this gets
|
|
given to it anyway on main function
|
|
start.
|
|
|
|
k.procdie(pkg, pid, reason, cpuTime):
|
|
Process death.
|
|
|
|
k.registration(uid):
|
|
Registration of an access.
|
|
|
|
k.deregistration(uid):
|
|
Deregistration of an access.
|
|
|
|
k.securityresponse(perm, obj):
|
|
Response to a security request made
|
|
with neo.requestAccess or such.
|
|
|
|
k.timer(tag, time):
|
|
A timer. Includes the planned uptime
|
|
for comparison.
|
|
|
|
h.*(...):
|
|
Hardware signals, by type, such
|
|
as "h.key_up"
|
|
|
|
h._kosneo_syslog("kernel", ...):
|
|
System log entry. This is actually
|
|
generated by the kernel as part of
|
|
the emergency function processing.
|
|
Note the "kernel" component address.
|
|
The other parameters are the values
|
|
given to the emergency function.
|
|
You should tostring all of these.
|
|
|
|
With that, I hope I have documented
|
|
the kernel's interface to programs.
|
|
|
|
-- This is released into
|
|
the public domain.
|
|
-- No warranty is provided,
|
|
implied or otherwise.
|