# How witr Uses Systemd Socket Activation to Resolve Ports

> Learn how witr uses systemd socket activation to resolve ports by querying systemd for unit info. Discover port ownership without inspecting file descriptors.

- Repository: [Pranshu Parmar/witr](https://github.com/pranshuparmar/witr)
- Tags: internals
- Published: 2026-08-10

---

**witr determines port ownership by walking process ancestry to PID 1, then querying systemd over D-Bus for unit information when PID 1 is systemd, enabling resolution of socket-activated ports without inspecting raw file descriptors.**

The witr open-source tool solves a common Linux debugging problem: identifying what service owns a listening port. When systemd uses **socket activation**, the listening socket is created by PID 1, making traditional `lsof` or `netstat` output misleading. This article explains how witr integrates systemd socket activation for port resolution by mapping processes back to their systemd units through cgroup and D-Bus inspection.

## How Socket Activation Complicates Port Resolution

Systemd's socket activation allows services to be started on demand when a connection arrives. The socket is created early by PID 1, then handed to the actual service when it starts. This means `ss -tlnp` or similar tools show PID 1 as the owner, not the real service.

witr handles this by detecting when PID 1 is systemd, then querying systemd's own bookkeeping to find the true unit responsible for the port.

## The Systemd Detection Pipeline in witr

The port resolution logic lives in [`internal/source/systemd_linux.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/systemd_linux.go). The `detectSystemd` function implements a seven-step workflow:

| Step | Action | Source Location |
|------|--------|---------------|
| 1 | Verify systemd is the init system (`IsSystemdRunning`) | `systemd_linux.go:L22-L27` |
| 2 | Walk process ancestry and confirm PID 1 presence | `systemd_linux.go:L36-L45` |
| 3 | Extract unit name from cgroup (`getUnitNameFromCgroup`) | `systemd_linux.go:L47-L52` |
| 4 | Open D-Bus connection with 2-second timeout | `systemd_linux.go:L52-L58` |
| 5 | Query unit properties via D-Bus | `systemd_linux.go:L81-L88` |
| 6 | Check for companion timer unit if service | `systemd_linux.go:L90-L98` |
| 7 | Return populated `model.Source` with unit metadata | `detectSystemd` return value |

Each step validates preconditions before proceeding, ensuring witr only attempts D-Bus queries when systemd socket activation is actually in play.

## Verifying Systemd as the Init System

Before any D-Bus communication, witr confirms systemd is running. The `IsSystemdRunning` function uses the canonical test from libsystemd's `sd_booted()`:

```go
// internal/source/systemd_linux.go
func IsSystemdRunning() bool {
    // /run/systemd/system is created by systemd when it starts
    _, err := os.Stat("/run/systemd/system")
    return err == nil
}

```

This check prevents false positives on non-systemd systems like Alpine Linux or containers using other init systems.

## Walking Process Ancestry and Validating PID 1

witr validates that the target process descends from PID 1, a prerequisite for socket activation. The ancestry walk in `systemd_linux.go:L36-L45` aborts the systemd path if PID 1 isn't found in the chain.

This optimization avoids expensive D-Bus calls when the process clearly wasn't started through systemd's mechanisms.

## Resolving Units from Cgroups

Once PID 1 is confirmed, witr extracts the systemd unit name from the process's cgroup path:

```go
// internal/source/systemd_linux.go
func getUnitNameFromCgroup(pid int) (string, error) {
    // Parses /proc/<pid>/cgroup to find systemd controller entry
    // Returns unit name like "nginx.service" or "myapp.socket"
}

```

The cgroup filesystem exposes systemd's unit hierarchy, allowing witr to determine which unit owns the process without querying D-Bus.

## Querying Unit Properties via D-Bus

With the unit name identified, witr enriches the `model.Source` record through D-Bus:

```go
// Connection with timeout respecting context
conn, err := sd.NewSystemConnectionContext(ctx)
if err != nil {
    return nil, fmt.Errorf("dbus connection: %w", err)
}
defer conn.Close()

// Fetch all unit properties
props, err := conn.GetUnitPropertiesContext(ctx, unitName)

```

From the D-Bus response, witr extracts:

- **Description** — human-readable service description
- **FragmentPath** — location of the unit file (e.g., `/etc/systemd/system/myservice.service`)
- **NRestarts** — restart count for failure analysis

## Detecting Companion Timer Units

For service units, witr performs a secondary query to find associated timer units:

```go
// internal/source/systemd_linux.go:L90-L98
if strings.HasSuffix(unitName, ".service") {
    timerUnit := strings.TrimSuffix(unitName, ".service") + ".timer"
    schedule, err := timerSchedule(ctx, conn, timerUnit)
    if err == nil {
        src.TimerSchedule = schedule
    }
}

```

This captures calendar or monotonic timer specifications, useful for understanding when socket-activated services might next trigger.

## The Complete Resolution Flow

Here's how witr resolves a socket-activated port in practice:

```go
// Example: Resolve the owner of port 8080
port := 8080
src, err := witr.ResolvePort(port) // internal logic walks the process tree
if err != nil {
    log.Fatal(err)
}
if src.Type == model.SourceSystemd {
    fmt.Printf("Port %d is owned by systemd unit %s (%s)\n",
        port, src.Name, src.Description)
}

```

The `SourceSystemd` type in [`pkg/model/source.go`](https://github.com/pranshuparmar/witr/blob/main/pkg/model/source.go) signals that this port was resolved through systemd's socket-activation bookkeeping rather than direct process ownership.

## Minimal Implementation Pattern

The core pattern witr uses can be adapted for other tools:

```go
// Minimal snippet that mirrors witr's systemd detection
if source.IsSystemdRunning() && hasPID1(ancestry) {
    unit := source.getUnitNameFromCgroup(pid)
    conn, _ := sd.NewSystemConnectionContext(ctx)
    props, _ := conn.GetUnitPropertiesContext(ctx, unit)
    fmt.Println("Systemd unit:", unit)
    fmt.Println("Description :", stringProp(props, "Description"))
}

```

Note the defensive programming: each step validates preconditions before the next, with appropriate error handling at each layer.

## Key Source Files and Their Roles

| File | Purpose |
|------|---------|
| [`internal/source/systemd_linux.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/systemd_linux.go) | Core detection, D-Bus queries, `model.Source` enrichment |
| [`pkg/model/source.go`](https://github.com/pranshuparmar/witr/blob/main/pkg/model/source.go) | Defines `SourceSystemd` constant and data structures |
| [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go) | Orchestrates systemd, container, SSH, and other detectors |
| [`README.md`](https://github.com/pranshuparmar/witr/blob/main/README.md) | Documents the "Port → Container fallback" feature mentioning socket activation |

The architecture cleanly separates detection concerns: [`detect.go`](https://github.com/pranshuparmar/witr/blob/main/detect.go) selects the appropriate resolver, while [`systemd_linux.go`](https://github.com/pranshuparmar/witr/blob/main/systemd_linux.go) handles systemd-specific logic in isolation.

## Summary

- **witr detects systemd socket activation** by walking process ancestry to PID 1 and verifying `/run/systemd/system` exists
- **Unit resolution uses cgroups**, not socket file descriptor inspection, making it robust across kernel versions
- **D-Bus queries enrich port metadata** with human-readable descriptions, unit file locations, and timer schedules
- **Two-second timeouts prevent hangs** on misconfigured or overloaded systems
- **The `SourceSystemd` type** allows upstream code to distinguish socket-activated ports from directly-owned ports

## Frequently Asked Questions

### How does witr distinguish socket activation from regular systemd services?

witr doesn't fundamentally distinguish them—it treats any process owned by a systemd unit as potentially socket-activated. The key indicator is **PID 1 in the ancestry** combined with a valid unit name from cgroups. Whether the service was started by socket activation or manually is transparent to witr's resolution logic; both cases resolve to the same unit information.

### Why check `/run/systemd/system` instead of reading `/proc/1/comm`?

The `/run/systemd/system` check mirrors `sd_booted()` from libsystemd, the official systemd API. This is more reliable than checking process names because: PID 1 might be renamed, container environments may show misleading names, and the presence of systemd files confirms the system was booted with systemd—not just that a systemd binary exists.

### What happens if D-Bus is unavailable or slow?

witr's D-Bus connection in `systemd_linux.go:L52-L58` respects a **2-second timeout** defined by `dbusTimeout`. If the connection fails or queries timeout, the error propagates up and witr falls back to other detection methods (containers, SSH, etc.) or returns an error. This prevents indefinite hangs on systems with D-Bus issues.

### Can witr resolve ports for systemd user services, not just system services?

The current implementation in [`internal/source/systemd_linux.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/systemd_linux.go) uses `sd.NewSystemConnectionContext`, which connects to the **system bus**. User services on the session bus would require `NewUserConnectionContext` and additional logic to determine which user session owns the process. This is not currently implemented in witr.