Skip to content

Systems Programming for OS Work

Warmwind patches Sway (C), Chromium (C++), and builds tooling in Rust. This article covers memory safety tradeoffs, IPC patterns, file descriptor passing (the foundation of Wayland buffer sharing), process management, build systems used across the stack, and contributing upstream.

Memory Safety: Rust vs C

Aspect C (Sway, wlroots) Rust (new tooling)
Memory management Manual (malloc/free) Ownership + borrow checker
Use-after-free Runtime crash or exploit Compile-time error
Data races Possible, use mutexes Prevented by Send/Sync traits
FFI Native extern "C" + unsafe blocks
Ecosystem wlroots, Mesa, kernel wayland-rs, smithay, nix crate

Rust's ownership model in 30 seconds:

fn main() {
    let buffer = vec![0u8; 4096];  // buffer owns heap allocation
    let view = &buffer[..];        // immutable borrow -- buffer can't be freed
    process(view);
    // buffer dropped here -- memory freed automatically
}

fn process(data: &[u8]) {          // borrows, doesn't own
    // can read data, can't free or resize it
}

C equivalent requires discipline:

void process(const uint8_t *data, size_t len);

int main(void) {
    uint8_t *buffer = malloc(4096);
    process(buffer, 4096);
    free(buffer);  // forget this = leak, double this = corruption
    return 0;
}

IPC Patterns

Mechanism Use case in Warmwind stack
Unix sockets Wayland protocol, Sway IPC
D-Bus systemd control, desktop notifications
Shared memory Wayland wl_shm buffers, memfd_create
Pipes Process supervision, log forwarding
dma-buf (fd passing) GPU buffer sharing between processes

Unix Domain Sockets

// Server (compositor side)
int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
snprintf(addr.sun_path, sizeof(addr.sun_path),
    "%s/wayland-0", getenv("XDG_RUNTIME_DIR"));
bind(server_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(server_fd, 128);

D-Bus

# Introspect systemd's manager interface
busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1

# Programmatically restart a service
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 \
    org.freedesktop.systemd1.Manager RestartUnit ss "sway.service" "replace"

File Descriptor Passing (SCM_RIGHTS)

The foundation of Wayland buffer sharing. One process sends an fd over a Unix socket; the receiving process gets a new fd referencing the same kernel object.

// Send an fd over a Unix socket
void send_fd(int socket, int fd_to_send) {
    struct msghdr msg = {0};
    struct cmsghdr *cmsg;
    char buf[CMSG_SPACE(sizeof(int))];
    struct iovec iov = { .iov_base = "x", .iov_len = 1 };

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_control = buf;
    msg.msg_controllen = sizeof(buf);

    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int));

    sendmsg(socket, &msg, 0);
}

This is how Wayland clients pass dma-buf fds to the compositor. The compositor imports the fd as an EGLImage for texturing -- zero copies.

Process Management

fork/exec Pattern

pid_t pid = fork();
if (pid == 0) {
    // Child: become Chromium
    execvp("chromium", (char *[]){"chromium", "--kiosk", NULL});
    _exit(127);  // exec failed
}
// Parent: store pid, set up signal handler

Signal Handling

// Reap zombies without blocking
void sigchld_handler(int sig) {
    (void)sig;
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

struct sigaction sa = {
    .sa_handler = sigchld_handler,
    .sa_flags = SA_RESTART | SA_NOCLDSTOP
};
sigaction(SIGCHLD, &sa, NULL);

Zombie Prevention

A zombie is a terminated child whose parent hasn't called waitpid(). In a compositor managing child processes (Chromium, WayVNC), always:

  1. Handle SIGCHLD with waitpid(-1, &status, WNOHANG) in a loop.
  2. Or use prctl(PR_SET_CHILD_SUBREAPER, 1) to adopt orphaned descendants.
  3. Or set SIGCHLD to SIG_IGN (Linux-specific: auto-reaps).

Build Systems

Project Build system Key commands
wlroots/Sway Meson + Ninja meson setup build && ninja -C build
FreeRDP CMake + Ninja/Make cmake -B build -G Ninja && ninja -C build
Chromium GN + Ninja gn gen out/Release && autoninja -C out/Release
Rust projects Cargo cargo build --release
Linux kernel Kbuild (Make) make defconfig && make -j$(nproc)

Meson example (wlroots development):

meson setup build -Dxwayland=disabled -Dbackends=drm,headless
ninja -C build
sudo ninja -C build install

Contributing Upstream

The posting says "open-source collaboration, upstream contributions." Here is the workflow for wlroots/Sway (GitLab MR) and Chromium (Gerrit CL):

wlroots/Sway (GitLab):

git clone https://gitlab.freedesktop.org/wlroots/wlroots.git
git checkout -b fix-screencopy-damage
# Make changes, test
git commit -s  # -s adds Signed-off-by (DCO requirement)
# Push to fork, open Merge Request

Chromium (Gerrit):

git new-branch fix-ozone-wayland
# Make changes
git cl upload  # uploads to Gerrit for code review
# Respond to review comments, update patch
git cl upload  # re-uploads amended commit

Key practices:

  • One logical change per commit/MR. Separate refactoring from behavior changes.
  • Include test coverage. wlroots uses custom test harness; Chromium uses gtest.
  • Follow existing code style exactly (clang-format for C/C++, rustfmt for Rust).
  • Engage constructively with review feedback. Upstream maintainers are volunteers.
  • Reference the issue/bug number in the commit message.

Glossary

Ownership (Rust)
Each value has exactly one owner. When the owner goes out of scope, the value is dropped.
Borrow checker
Rust compiler pass enforcing that references don't outlive their referents and exclusive access is exclusive.
SCM_RIGHTS
Ancillary data type for sendmsg()/recvmsg() that transfers file descriptors between processes.
Zombie process
Terminated process whose exit status hasn't been collected by its parent via wait().
DCO (Developer Certificate of Origin)
Lightweight CLA alternative. Signed-off-by: in commit message certifies right to contribute.
Meson
Modern build system used by wlroots, Sway, Mesa, and many GNOME/freedesktop projects.
GN (Generate Ninja)
Chromium's meta-build system. Processes .gn and BUILD.gn files to emit Ninja build files.
CL (Change List)
Chromium/Gerrit term for a patch submitted for code review. Equivalent to a pull request.