Environment Variable Discovery

The kernel provides sockets, tmpfs, and credentials. But it has no concept of "display server" or "message bus." Environment variables are the discovery layer — the glue that tells userspace processes where to find each other’s kernel-managed endpoints.

Environment Variables: The Kernel-Userspace Bridge

The Architectural Gap

The kernel provides mechanisms — sockets, tmpfs, credential verification. But the kernel has no concept of "display server" or "message bus." Those are userspace conventions. Environment variables are the discovery layer — they tell userspace processes where to find each other’s kernel-managed IPC endpoints.

Variable Points To Kernel Mechanism

XDG_RUNTIME_DIR

/run/user/$UID/ (tmpfs mount)

tmpfs filesystem (mm/shmem.c)

WAYLAND_DISPLAY

Socket filename inside XDG_RUNTIME_DIR

AF_UNIX socket (net/unix/af_unix.c)

DBUS_SESSION_BUS_ADDRESS

D-Bus socket path

AF_UNIX socket + SO_PEERCRED (net/core/sock.c)

How fork() and execve() Propagate Environment

When a shell spawns a process:

  1. fork() — kernel duplicates the parent process (copies task_struct, shares page tables via COW)

  2. The child inherits the parent’s entire environment (stored in the process’s user-space stack/heap)

  3. execve() — kernel replaces the process image but preserves the environment (passed in the envp array)

This means environment variables propagate automatically through the Unix process tree — from the login session to every child process. No IPC needed; it is baked into the process creation syscalls.

Where tmux Breaks the Chain

tmux inserts itself into this chain as a session manager. It creates new panes via fork() + execve(), but it deliberately filters the environment through update-environment for session isolation:

set -g update-environment "DISPLAY WAYLAND_DISPLAY XDG_RUNTIME_DIR ..."

When a variable is listed in update-environment:

  • tmux copies the variable from the attaching client’s environment into the session

  • New panes inherit it

When a variable is not listed:

  • tmux does not propagate it

  • New panes get whatever was in the session environment when the server started

  • If the server started without the variable (e.g., from a systemd unit), it is simply absent

This is why the clipboard broke: XDG_RUNTIME_DIR was absent from update-environment, so new panes never received it. The kernel’s connect() syscall on the Wayland socket failed because the path was (null)/wayland-1.

The Live Workaround Pattern

update-environment only takes effect for new sessions. For a running server with active sessions you cannot restart:

# Inject variables into the running tmux server
tmux set-environment WAYLAND_DISPLAY "$WAYLAND_DISPLAY"
tmux set-environment XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR"
tmux set-environment DBUS_SESSION_BUS_ADDRESS "$DBUS_SESSION_BUS_ADDRESS"

New panes created after this immediately inherit the variables. Existing panes retain their original environment — only new panes pick up set-environment changes. This is a reusable pattern for any update-environment fix applied to a live tmux server.

Three Subsystems, One Bug

The tmux clipboard bug touched three kernel subsystems simultaneously:

Subsystem Kernel Source Role in the Bug

tmpfs

mm/shmem.c

Provides /run/user/$UID — the ephemeral namespace where sockets live

AF_UNIX sockets

net/unix/af_unix.c

The transport layer for both Wayland and D-Bus communication

Credential passing

net/core/scm.c

SCM_RIGHTS (fd passing for Wayland clipboard) and SO_PEERCRED (identity for D-Bus)

Tracing at the Kernel Level

To see the kernel side of what environment variable discovery enables:

# Trace the full syscall chain: socket creation, connect, message passing
strace -e socket,connect,sendmsg,recvmsg wl-copy <<< "test"

# Compare: what happens when XDG_RUNTIME_DIR is missing
unset XDG_RUNTIME_DIR && strace -e socket,connect wl-copy <<< "test" 2>&1 | tail -5

# Watch environment inheritance through fork/exec
strace -f -e execve,clone bash -c 'echo $XDG_RUNTIME_DIR' 2>&1 | grep -E 'clone|execve|XDG'

Key Takeaways

  1. Userspace IPC is built on AF_UNIX sockets — Wayland, D-Bus, PipeWire, PulseAudio all use the same kernel primitive

  2. SCM_RIGHTS enables zero-copy IPC — processes pass file descriptors, not data, through the kernel

  3. SO_PEERCRED provides kernel-enforced identity — D-Bus security depends on unforgeable credentials from task_struct

  4. tmpfs provides the namespace — per-user, RAM-backed, permission-enforced, ephemeral

  5. Environment variables are the discovery mechanism — the kernel provides mechanisms, userspace conventions provide the addresses

  6. fork()/execve() propagate environment automatically — but session managers like tmux can filter the chain

  7. Debugging userspace IPC often means tracing syscalls — strace bridges the gap between application behavior and kernel code paths