1
0
mirror of https://github.com/20kdc/OC-KittenOS.git synced 2024-12-27 03:18:06 +11:00
OC-KittenOS/repository/docs/kn-refer

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.