8.2 Emitting events
int ;
Emits a single event. event_type is a length-counted UTF-8 event type such as "org.example.backup.snapshot.created", named as PGSS §6.3 requires — not NUL-terminated, and its length must be non-zero. payload is payload_len bytes of MessagePack (one well-formed value). The kernel validates the payload (one well-formed MessagePack value within the configured size and nesting limits) and stamps origin_class = userspace. Returns 0, or -1 with errno:
| errno | Cause |
|---|---|
EPERM | No SeAuditPrivilege. |
EINVAL | Zero-length type, or a malformed payload. |
ENOSPC | Payload exceeds the size caps. |
EAGAIN | Rate-limited. |
EFAULT | Bad pointer. |
Since the kernel's payload check matches peios_mp_validate, you can validate in userspace first and turn a would-be EINVAL into a check you control.
/* Build a payload, then emit. */
peios_mp_writer *w = ;
; /* {outcome: {success: true}} */
;
;
; ;
static const char type = "org.example.backup.snapshot.created";
const void *buf; ssize_t n = ;
if
;
;
8.2.0.1 The emission policy #
typedef struct peios_event_policy peios_event_policy;
peios_event_policy *;
void ;
int ;
An emitter must not write an event whose type the emission policy has switched off (PGSS §6.9), and should find that out before it builds the payload. peios_event_policy_enabled answers it: 1 means build and emit, 0 means build nothing. tier is the tier the type declares in its fragment. An essential type is always 1, without reading anything.
peios_event_emit does not check the policy itself. By the time it is called the payload is built, and skipping that work is the point of asking.
The handle caches each type's answer and watches Machine\Generic\Events, so a change an administrator commits applies to your next decision. If that key does not exist yet, it watches for its creation; if nothing can be watched, it reads the policy again at least once a second. If the registry cannot be read at all, the answer is the tier's default: on for standard, off for verbose and debug.
peios_event_policy_open fails only with ENOMEM; it does not need the registry to be there. peios_event_policy_enabled returns -1 with EINVAL only for caller error: a NULL handle or type, a malformed type (empty, not UTF-8, an empty segment, or a \, / or NUL in a segment), or an unknown tier. Registry failures never make it fail.
Use a handle from one thread at a time, and don't use it across fork(): the child shares the parent's watch descriptor and would take the parent's notifications. A child opens its own. The Rust binding's peios::event::EventPolicy adds a lock so it can be shared between threads. libp-go has its own implementation, event.Policy.
8.2.0.2 Batch emit #
;
int ;
peios_event_emit_batch emits several events in one call, amortising the per-call overhead — a single timestamp capture, identity capture, and consumer wake cover the whole batch. count is in [1, KMES_BATCH_MAX_ENTRIES]. It returns 0 if all count were emitted, or -1 with the errno of the first entry that failed, with *emitted_out (if non-NULL) set to how many entries preceded the failure — so you know exactly where to resume. Rate-limiting is all-or-nothing here: an EAGAIN emits none of the batch.