How witr Detects the Source of systemd, launchd, and rc.d Services

witr determines which init system started a process by walking the process ancestry tree and applying platform-specific detectors that return a structured model.Source containing the service type, unit name, and metadata.

The pranshuparmar/witr repository implements a unified detection architecture that identifies whether a running process originated from systemd, launchd, or traditional BSD rc.d initialization systems. By combining process tree traversal with filesystem and API inspection, witr maps each process back to its defining service unit.

The Detection Pipeline

witr orchestrates detection through internal/source/detect.go. The system first constructs a process ancestry slice by walking from the target PID up to PID 1, collecting model.Process objects. It then executes platform-specific detectors in priority order—systemd, launchd, and bsdrc—returning the first non-nil *model.Source result. Each detector populates the universal Source struct defined in pkg/model/source.go, which standardizes fields for Type, Name, Description, UnitFile, and a Details map.

Systemd Detection on Linux

The Linux systemd detector resides in internal/source/systemd_linux.go and implements the detectSystemd function.

Verifying systemd as the Init System

Before querying service metadata, the detector confirms systemd is the active init system. The IsSystemdRunning() function checks for the existence of /run/systemd/system (the canonical test used by sd_booted()) and verifies that PID 1 exists within the collected ancestry (lines 24–42).

Extracting the Unit Name

Once validated, witr derives the systemd unit by inspecting the control group (cgroup) of the target process. The getUnitNameFromCgroup helper (line 50) parses the cgroup path to extract the service identifier.

Enriching via D-Bus

The enrichFromSystemd() function (lines 61–98) establishes a D-Bus connection using sd.NewSystemConnectionContext and queries:

  • Unit properties for the description and fragment path
  • Service properties for restart counts
  • Timer properties for execution schedules

This enrichment is best-effort; any D-Bus failures result in empty fields rather than detection errors.

// Conceptual implementation based on internal/source/systemd_linux.go
source := &model.Source{
    Type: model.SourceSystemd,
    Name: unitName,
}

if conn, err := sd.NewSystemConnectionContext(ctx); err == nil {
    source.Description = getUnitDescription(conn, unitName)
    source.UnitFile = getFragmentPath(conn, unitName)
    source.Details["NRestarts"] = getRestartCount(conn, unitName)
}

Launchd Detection on macOS

For macOS systems, internal/source/launchd_darwin.go provides the detectLaunchd function.

Identifying launchd in the Ancestry

The detector scans the process ancestry for an entry where PID == 1 and Command == "launchd" (lines 13–19). This confirms the process tree originates from the Darwin init system.

Querying Service Metadata

Upon confirmation, witr invokes launchd.GetLaunchdInfo(target.PID) to retrieve the service label, comment, and plist configuration (lines 32–40). The detector populates:

  • Name: The service label (e.g., com.openssh.sshd)
  • Description: The comment field from the plist
  • UnitFile: The path to the plist file
  • Details: Domain description (Agent vs Daemon), schedule strings, trigger conditions, and KeepAlive status
// Based on internal/source/launchd_darwin.go
if info, err := launchd.GetLaunchdInfo(target.PID); err == nil {
    source := &model.Source{
        Type:        model.SourceLaunchd,
        Name:        info.Label,
        Description: info.Comment,
        UnitFile:    info.PlistPath,
    }
    source.Details["type"] = info.DomainDescription
    source.Details["keepalive"] = info.KeepAlive
}

rc.d Detection for BSD-Style Init

Traditional BSD rc.d services are handled by internal/source/bsdrc_linux.go via the detectBsdRc function.

Confirming BSD-Style Init

The detector verifies two conditions to avoid false positives on systemd systems. First, it checks if PID 1's command starts with init using strings.HasPrefix(p.Command, "init") (around line 15). Second, it confirms the existence of /etc/rc.d or /etc/init.d directories via os.Stat (around line 22).

Resolving Service Scripts

witr walks the process ancestry to match parent processes against filenames in the rc.d directories. When a match is found, it reads the init script to extract the # Description: comment (around line 50), constructing a source with:

  • Type: SourceBsdRc
  • Name: The script basename (e.g., sshd)
  • UnitFile: The full path (e.g., /etc/rc.d/sshd)

Unlike the systemd implementation, this detector does not use D-Bus; it relies entirely on static filesystem inspection and process command-line metadata.

Source Model and Orchestration

The Source struct in pkg/model/source.go provides the universal interface for all detection results. It contains:

  • Type: Enumeration (SourceSystemd, SourceLaunchd, SourceBsdRc)
  • Name: The service identifier (unit name, label, or script name)
  • Description: Human-readable summary
  • UnitFile: Path to the service definition
  • Details: Map for platform-specific metadata

The orchestration layer in internal/source/detect.go executes detectors sequentially, ensuring that on systems with multiple init systems present, the most specific and informative source is reported.

Summary

  • witr detects service sources by walking the process ancestry from the target PID to PID 1.
  • The systemd detector validates against /run/systemd/system and enriches data via D-Bus queries for unit properties in internal/source/systemd_linux.go.
  • The launchd detector identifies macOS init systems by checking for launchd as PID 1 and queries the launchd API for plist metadata in internal/source/launchd_darwin.go.
  • The rc.d detector identifies BSD-style init by verifying PID 1 is init and /etc/rc.d exists, then maps processes to their corresponding init scripts in internal/source/bsdrc_linux.go.
  • All detectors return a standardized model.Source struct defined in pkg/model/source.go.

Frequently Asked Questions

How does witr distinguish between systemd and traditional SysV init on Linux?

witr first checks for the presence of /run/systemd/system using IsSystemdRunning(). If this directory exists and PID 1 appears in the process ancestry, it treats the system as systemd-managed. If not, it falls back to the rc.d detector, which looks for init as PID 1 and the existence of /etc/init.d or /etc/rc.d directories to identify SysV-style initialization.

Can witr detect services started by launchd on older macOS versions?

Yes, as long as the system uses launchd as PID 1, the detector in internal/source/launchd_darwin.go identifies the init system by verifying the process name. However, the richness of metadata depends on the launchd.GetLaunchdInfo function's ability to query the specific macOS version's APIs for service details.

What happens if witr encounters a process started manually rather than by an init system?

If none of the detectors—systemd, launchd, or rc.d—can associate the process with a known service unit or script, the detection returns nil. The orchestration logic in internal/source/detect.go handles this by continuing through the detector chain until exhausted, resulting in no source being reported for manually executed binaries.

Does witr require root privileges to detect service sources?

Root privileges are not strictly required for the ancestry walk, but they significantly improve detection accuracy. Accessing D-Bus interfaces for systemd, querying certain process attributes, and reading all init scripts in /etc/rc.d may require elevated permissions depending on system security policies. Without root, witr may return partial or empty source information.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →