Skip to main content

Module hooks

Module hooks 

Source
Expand description

User-configurable shell commands that fire on scheduler events.

Hooks are off by default and gated behind a confirmation dialog (see scheduler::commands::hooks::set_hooks). The threat model is documented in docs/HOOKS.md — anyone with write access to settings.json can run arbitrary code as the user, so the master hooks_enabled toggle is the sole trust boundary.

Structs§

Hook
One configured hook: subscribe to event, run command when it fires (if enabled). command is POSIX-style argv that we split with shell-words — no shell is involved, so pipes / redirects need an explicit sh -c wrapper.
HookContext
Per-call context populated by the scheduler. Fields show up as $ENTRACTE_KIND, $ENTRACTE_DURATION_SECS, $ENTRACTE_OUTCOME when the hook child runs; empty when not applicable to the event.
HookTestOutcome
Result of a one-off hook test-run, surfaced in the Settings UI so a user can see what a command does before relying on it.

Enums§

HookEvent
The scheduler events a hook can subscribe to. Serialised as the lowercase snake-case name (also the value passed in $ENTRACTE_EVENT).

Constants§

HOOK_TEST_TIMEOUT
Timeout for a one-off hook test-run from the Settings UI. Shorter than the fire-path HOOK_TIMEOUT so a hung command doesn’t leave the user staring at a spinner.
HOOK_TIMEOUT 🔒
Hard cap on how long a hook child may run before it’s killed. Hooks are fire-and-forget, so a hung one — an accidental infinite loop, a read that blocks despite the null stdin — would otherwise live until the app exits. 30s is generous for the quick notify/log commands hooks are meant for while still bounding a runaway.
MAX_HOOKS_PER_EVENT
Hard cap on hooks fired per event. A misconfigured (or malicious) settings.json could otherwise register thousands of entries and fork-bomb the host on every break boundary. 32 is well above any realistic per-event subscription count.
MAX_TEST_OUTPUT_BYTES 🔒
Cap on captured stdout/stderr returned to the renderer, so a chatty command can’t balloon the IPC payload.

Functions§

build_env
Build the (key, value) env vars handed to the hook child: ENTRACTE_EVENT, ENTRACTE_KIND, ENTRACTE_DURATION_SECS, ENTRACTE_OUTCOME. Missing fields are empty strings so consumers can shell-test them uniformly.
kind_str 🔒
matching_hooks
Return the subset of hooks that should fire for event. Returns empty when the master hooks_enabled toggle is off, regardless of per-hook enabled flags.
parse_command 🔒
Split a hook command into (program, args) using shell-words (no shell is involved). Errors are the user-facing strings the test-run surfaces; the fire path logs them. Pure, so the parse rules are testable.
program_log_label 🔒
run_command_capture
Run command once with env, capturing stdout/stderr and exit code, killing it after timeout. Powers the Settings “Test” button. Like the fire path, no shell is involved — pipes/redirects need an explicit sh -c wrapper. The capture/spawn is the OS shim; the parsing (parse_command) and output handling (truncate_output) are pure and tested, and this whole function is exercised against real commands in the unit tests.
run_hooks
Fire every matching hook for event. Each child runs on its own std::thread with stdio set to /dev/null. We don’t capture output, but the thread does reap the child (and kill it after HOOK_TIMEOUT) so a fire-and-forget hook can’t leave a zombie or a runaway behind.
run_hooks_with
Same as run_hooks but delegates spawning to spawn. The callback receives each Hook (already filtered by event and enabled, and already truncated to MAX_HOOKS_PER_EVENT) plus the env vars that would be passed to its child. Used by tests to verify the cap without actually shelling out hundreds of processes.
sample_test_env
The env a test-run exposes, so a command using $ENTRACTE_* shows realistic values. Uses a representative break-start context.
spawn_hook 🔒
spawn_hook_with_timeout 🔒
truncate_output 🔒
Lossy-UTF8 a captured stream and cap it at max bytes (on a char boundary), flagging truncation. Pure.