thox native process runtime API#

When started, a thox process has access to a given set of functions and constants; this document describes them.

Todo

When started, every process should have access to the native functions to the current Lua function, e.g. those described in the Lua 5.3 manual.

Maybe make a table and check which utilities are available or not.

Events#

class os.Event#

An event as seen by the user process.

type: string#

The event type; see derivative classes.

Events can be one of the following:

class os.CallEvent: Event#

An event emitted when receiving a call from another process through RPC.

type: string#

The event type, set to "call".

cid: number#

The Call IDentifier (CID) to which to answer.

ctx: number#

The context identifier from which the call was emitted.

name: string#

The name which was used to emit the call to the current process.

pid: number#

The Process IDentifier (PID) of the process which has emitted the call.

args: table#

The arguments to the function, as a sequence.

class os.AnswerEvent: Event#

An event emitted when receiving an answer to a call emitted previously.

type: string#

The event type, set to "answer".

cid: number#

The Call IDentifier (CID) of the answered call.

status_code: number#

The status code bundled with the answer; see Status codes for more information.

args: table#

The arguments returned by the process.

class os.OpenEvent: Event#

An event produced when a process opens a context owned by the current process (when not shared by the current process).

type: string#

The event type, set to "open".

ctx: number#

The identifier of the context which was shared.

pid: number#

The Process IDentifier (PID) of the process which has gained access to the context.

class os.CloseEvent: Event#

An event produced when a process closes a context owned by the current process (when not closed by the current process).

type: string#

The event type, set to "close".

ctx: number#

The identifier of the context which was shared.

pid: number#

The Process IDentifier (PID) of the process which has gained access to the context.

These events are produced by the following function:

os.pull([filter[, ...]])#

Pull the next event out of the event queue for the process.

The filters are tables containing expected values to filter the event with.

For example, the following snippet waits for either:

  • An answer for the calls 4 and 5;

  • A new call.

local event = os.pull(
    {type = "answer", cid = 4},
    {type = "answer", cid = 5},
    {type = "call"},
)

When requiring a specific event, other events will stay in the queue for the next call asking specifically for it in the queue.

Parameters:

filter (table) – The filter as a table with the expected values.

Returns:

The next event out of the event queue.

Return type:

An Event.

Asynchronous RPC#

os.call(ctx, name, ...)#

Emits a call to the given name on the given context.

If the name ends with an underscore (see Remote Procedure Names (RPN)), then the first returned value after the status code will either be nil or the number of a context shared with the current process.

Parameters:
  • ctx (number) – A valid context identifier.

  • name (str) – A valid procedure name.

Returns:

The generated Call IDentifier (CID), or nil if an error has occurred.

Return type:

number

os.answer(cid, status, ...)#

Answer a call with arguments.

If the name of the related call ends with an underscore (see Remote Procedure Names (RPN)), then the first returned value after the status code must be either nil or the number of the context to share.

The status code should be 0 in case of success or an other value in case of failure; see Status codes for more information.

Parameters:
  • cid (number) – A valid Call IDentifier (CID).

  • status (number) – The status code for the call.

os.transmit(ctx, name, cid[, pid])#

Transmit a call to another process bound to a given name on a given context.

This function is an optimization: instead of having to make the call to the parent process and waiting for the answer to retransmit it to the end process, this actually sends the same call event (with the context and name replaced to correspond to this function’s arguments) to the bound process, which will then answer directly to the client process; the CID is therefore closed from the current process’s point of view when the call is transmitted.

The pid parameter serves mostly when the call is not transmitted to the context owner specifically. This mostly serves for context-managing processes that support bindings, for them to transmit calls to children on a context they have created.

Todo

For now, we stay on the os.transmit logic, but soon we’ll see that this approach may be slow when there are multiple levels of sandboxing. So what we could do is, have a nftables-like approach where a process owning a context configures routing for calls on the context (based on origin, name, why not arguments as well?), and the kernel doesn’t need to call this process to route, whereas with the os.transmit we need to and this may be slow.

However this is the complicated approach, for now we’ll stick with the simpler approach as a PoC.

Parameters:
  • ctx (number) – A valid context identifier.

  • name (str) – A valid RPC name.

  • cid (number) – A valid Call IDentifier (CID).

  • pid (number) – The optional PID of the process to which the call should be transmitted on the given context.

Synchronous RPC#

class os.RPCProxy#

An RPC proxy object, which allows you to make synchronous RPC calls, on a given context. By calling this object, you emit a system call to the given name, and wait for an answer.

Examples usages of this object are the following:

local status, div, rem = os.rpc.math.div(10, 3)

-- is the equivalent of:

local status, div, rem

do
    local cid = os.call(0, "math.div", 10, 3)
    local status

    status, div, rem = os.pull(
        {type = "answer", cid = cid},
    )
end
any: RPCProxy#

Indexing the object gives you an RPC proxy object, on the same context and with a prefixed name, which you can call the same.

This class is instanciated as the following object:

class os.rpc: RPCProxy#

The basic RPC proxy object, with a blank namespace.

Other functions#

os.capture(co, ...)#

Equivalent of the coroutine.resume() function, but it captures system calls.

Parameters:

co (thread) – The coroutine to resume.

Returns:

The status of the coroutine, accompagnied with various data.