Skip to main content

Module process_scope

Module process_scope 

Source
Expand description

Run child processes in their own systemd scope (Linux).

systemd-oomd, under memory pressure, kills a whole cgroup at once, choosing the one using the most swap. Without this, everything Synaps starts lives in the cgroup of whatever started Synaps — usually a terminal’s scope. A single runaway tool command (S348: a 270-file test run from the bash tool) then makes that whole scope the biggest, and systemd-oomd kills it: the command, the daemon, every session it hosts, and the terminal.

A scope gives each child a separate cgroup, not a memory cap. An ancestor cgroup OOM or global kernel OOM can still kill the daemon and other scopes. Windows isolation is not implemented.

systemd-run --user --scope registers a transient scope for its own pid and then execs the command in place: same pid, process group, session, environment, inherited fds and stdio. Group cleanup covers descendants that remain in that group. Each launch first performs a bounded fresh probe.

Command arguments are passed verbatim: where systemd-run supports it (systemd >= 254, from a cached --version), --expand-environment=no stops it from substituting ${NAME} in tool scripts before exec.

Used only when it is available (Linux, a reachable systemd user manager) and not turned off (process_scopes = off in config, or SYNAPS_PROCESS_SCOPES=off). Otherwise commands start exactly as before.

Slices: by default systemd puts each scope where the user manager puts transient units (usually app.slice, next to the desktop’s apps). The config keys tool_scope_slice (bash tool commands and PTY shells) and daemon_scope_slice (the daemon’s own scope) pass --slice=<name>, so an operator can cap agent tool work on its own slice. Synaps sets no limits; the operator sets them on the slice. The probe uses the same --slice: when it fails (no reachable user manager, or a slice it refuses) the command starts as it does without scopes, with one WARN per slice name. A slice that doesn’t exist is not refused: systemd creates it on the fly, without limits. So, once per slice name and on a background thread, Synaps asks the manager for the slice’s limits and WARNs if none is set (a typo, or a unit file not yet daemon-reloaded).

Structs§

ScopeLauncher
Turns program args… into systemd-run --user --scope … -- program args….

Enums§

ScopeKind
Which of Synaps’s scopes a launch makes. Each kind has its own slice key.

Functions§

inherited_env
Snapshot inherited environment before applying child-specific overrides.
launcher_for_effective_env
Fresh, bounded availability check. Synchronous callers (PTY and detached daemon spawn) may wait up to three seconds. Async callers must offload it. Probe failure selects direct launch. After success, wrapper failure is an error, NEVER a reason to replay the command: availability can race, and the wrapped command may already have executed. This is not a launch handshake. kind picks the slice key (tool_scope_slice / daemon_scope_slice), read from the config this call loads.
launcher_for_env
Compatibility convenience for inherited or fully rebuilt environments, for a tool launch.
resolve_program
Resolve before wrapping: systemd-run must not substitute its own executable search rules for the child’s PATH/cwd. Missing programs remain spawn errors.
set_env