Skip to content

[Bug] SetSubStateSubscriber floods channel with repeated events when unit is in "dead" state #502

@zandwang

Description

@zandwang

Describe the bug

When using SetSubStateSubscriber to monitor systemd unit state changes, the subscriber continuously receives repeated events for a unit that is in the "dead" SubState. This causes an infinite stream of events even though the unit's state hasn't actually changed.

To Reproduce

  1. Create a simple systemd service (e.g., hello-systemd.service)
  2. Subscribe using SetSubStateSubscriber
  3. Start and then stop the service: sudo systemctl stop hello-systemd
  4. Observe the channel receiving thousands of repeated events per second

Minimal reproduction code:

package main

import (
	"context"
	"fmt"
	"strings"

	"github.com/coreos/go-systemd/v22/dbus"
)

func main() {
	ctx := context.Background()

	conn, err := dbus.NewSystemdConnectionContext(ctx)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	if err := conn.Subscribe(); err != nil {
		panic(err)
	}
	defer conn.Unsubscribe()

	statusChan := make(chan *dbus.SubStateUpdate, 100)
	errChan := make(chan error, 10)
	conn.SetSubStateSubscriber(statusChan, errChan)

	eventCount := 0
	for update := range statusChan {
		if strings.HasSuffix(update.UnitName, ".service") {
			eventCount++
			fmt.Printf("event=%d unit=%s SubState=%s\n", eventCount, update.UnitName, update.SubState)
		}
	}
}

Steps:

# Terminal 1: Run the subscriber
go run main.go

# Terminal 2: Stop a service
sudo systemctl stop hello-systemd

# Observe Terminal 1 flooding with events like:
# event=1 unit=hello-systemd.service SubState=dead
# event=2 unit=hello-systemd.service SubState=dead
# event=3 unit=hello-systemd.service SubState=dead
# ... (thousands per second, never stops)

Expected behavior

SetSubStateSubscriber should only send an event when the SubState actually changes, not repeatedly for the same state. Once a unit transitions to "dead", it should send one event and then remain silent until the next state change.

Actual behavior

The subscriber continuously floods the channel with repeated "dead" state events, making it unusable for monitoring service state changes.

Environment

  • go-systemd version: v22.5.0
  • Go version: 1.24
  • OS: Ubuntu 24.04 (systemd 255)

Workaround

Using SetPropertiesSubscriber instead works correctly - it only fires when D-Bus actually sends a PropertiesChanged signal:

propsChan := make(chan *dbus.PropertiesUpdate, 100)
errChan := make(chan error, 10)
conn.SetPropertiesSubscriber(propsChan, errChan)

Additional context

Our use case is a security monitoring agent that needs to detect when systemd services start/stop. SetSubStateSubscriber seemed ideal but the flooding issue makes it unusable. We switched to SetPropertiesSubscriber which works correctly but requires filtering for ActiveState changes manually.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions