Linx.Sysctl reads and writes the kernel's tunable parameters — the /proc/sys/
knobs sysctl(8) exposes — on the host or, crucially, inside another process's
namespaces.
Sysctls are the kernel's ~1500 named scalar tunables, spanning networking, the
VM, the filesystem layer, IPC, and kernel-wide policy. Each is a file under
/proc/sys/, with dots in the name mapping to slashes in the path
(net.ipv4.ip_forward → /proc/sys/net/ipv4/ip_forward). Linx.Sysctl is a
typed wrapper over that procfs surface: read/1 and the parsing helpers
(read_int/1, read_ints/1) on one side, write/2 taking integers, strings,
and integer lists on the other, plus list/1 to walk a subtree.
The legacy sysctl(2) syscall was removed from Linux in 5.5; procfs is the only
API, and the right one. Linx supplies primitives — read, write, list — not a
sysctl.conf applier; parsing /etc/sysctl.d and reload policy belong to a
consumer. Single-shot reconciliation of a desired %{key => value} map is
mechanism, and lives in Linx.Sysctl.Reconcile.
Where it fits
The defining fact about a sysctl is that the kernel routes each read and write
through the calling task's namespace context. net.* resolves against the
network namespace, kernel.hostname against UTS, the kernel.shm*/msg* IPC
limits against IPC. Reading net.ipv4.ip_forward from inside a container does
not yield the host's value — it yields the container's. Traversing
/proc/<pid>/root/proc/sys/... does not reach another namespace's value;
the kernel resolves against the reader, not the path.
So Linx.Sysctl carries the same in: :self | {:pid, n} | {:path, p} option
as Linx.Mount. For {:pid, n} it opens the target's full namespace stack
(user, mount, UTS, IPC, net), skips any namespace already shared with the BEAM,
and setns(2)s into the rest on a throwaway pthread before doing the file I/O.
This is a checkpoint subsystem like the others: set a container's hostname and
net.* knobs between :ready and proceed/1, before the workload's first
instruction — or against a fully running namespace afterward. The coupling to
Linx.Process is only the shared window.
Flow
flowchart LR
call["Sysctl.read/write<br/>in: {:pid, host_pid}"] --> filter{"share this ns<br/>with the BEAM?"}
filter -->|"yes — skip it"| host["/proc/sys/… in the BEAM's ns"]
filter -->|"no"| setns["throwaway pthread:<br/>setns(2) into target's<br/>user · mount · uts · ipc · net"]
setns --> io["/proc/sys/… resolved in<br/>the container's namespaces"]
io --> exit["thread exits — BEAM schedulers<br/>never entered the target ns"]Learn more
- API —
Linx.Sysctl(withLinx.Sysctl.Entryfor walked rows,Linx.Sysctl.Reconcile, andLinx.Sysctl.Error) - Examples — sysctl-examples.md: reading/writing, walking the tree,
the
:inoption, and reconciliation - References — sysctl-references.md: the
/proc/sys/and per-namespace kernel docs and man pages