coreos / go-systemd

Go bindings to systemd socket activation, journal, D-Bus, and unit files
Apache License 2.0
2.43k stars 306 forks source link

No asynchronous response when service is stopped #416

Open ndeubert opened 1 year ago

ndeubert commented 1 year ago

I noticed when using this code:

func isChanged(prev *dbus.UnitStatus, curr *dbus.UnitStatus) bool {
    if prev.ActiveState != curr.ActiveState {
        return true
    }
    return false
}

func (o *Object) filterUnit(unit string) bool {
    if unit == o.cfg.PlatCfg.ServiceName1|| unit == o.cfg.PlatCfg.ServiceName2{
        return false
    }
    return true
}
...
o.asyncstatus, o.asyncerr = o.conn.SubscribeUnitsCustom(1*time.Second, 1, isChanged, o.filterUnit)

I do not get any events from the asyncstatus channel when the service is stopped in a terminal with sudo systemctl stop service1 like I do when I start a service with id, err := o.conn.StartUnitContext(o.ctx, svcName, "replace", o.async) or sudo systemctl start service1

Even if I stop the service programmatically with id, usererr := o.conn.StopUnitContext(o.ctx, o.cfg.PlatCfg.ServiceName1, "replace", o.async) I only see "done" on the async channel.

If I want to find out about these events do I need to use the dbus interface directly? Do I need to poll myself or use o.asyncstatus, o.asyncerr = o.conn.SubscribeUnits(1 * time.Second) and check if it goes away?. If I kill the process manually then I do get a "failed" event on the asyncstatus channel. I didn't see any mention of this being expected behavior in the documentation, but it seems odd not to be supported given the rest of the behavior. Thanks.

vlw commented 6 months ago

I have same issue :(

    updates, errors := conn.SubscribeUnitsCustom(0, 10, isChanged, filterUnit)

    go func() {
        for err := range errors {
            log.Printf("Error: %s", err)
        }
    }()

    for update := range updates {
        for sName, status := range update {
            if status != nil {
                log.Printf("Service %s ActiveState changed to: %s\n", sName, status.ActiveState)
            } else {
                log.Printf("Service %s: no status\n", sName)
            }
        }
    }

log when I start/stop service many times:

2024/03/19 01:39:34 Service s-kek-1.service ActiveState changed to: active 2024/03/19 01:39:57 Service s-kek-1.service ActiveState changed to: deactivating 2024/03/19 01:39:57 Service s-kek-1.service: no status 2024/03/19 01:40:02 Service s-kek-1.service ActiveState changed to: active 2024/03/19 01:40:07 Service s-kek-1.service: no status 2024/03/19 01:40:12 Service s-kek-1.service ActiveState changed to: active

sometimes I see "deactivating" but always see "no status" when service stopped

BTW I see all logs in dbus monitor: dbus-monitor --system "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/systemd1/unit/s_2dkek_2d1_2eservice'" | awk '/string "ActiveState"/ { getline; print $0 }' terminal output:

         variant             string "active"
         variant             string "active"
         variant             string "deactivating"
         variant             string "inactive"
         variant             string "inactive"
         variant             string "inactive"
         variant             string "active"
         variant             string "active"
         variant             string "active"
vlw commented 6 months ago

@ndeubert solution found!

package main

import (
    "fmt"
    "github.com/godbus/dbus/v5"
    "log"
    "strings"
)

func main() {
    conn, err := dbus.SystemBus()
    if err != nil {
        log.Fatalf("Failed to connect to System Bus: %v", err)
    }

    var rules = "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0namespace='org.freedesktop.systemd1.Unit'"
    conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rules)
    fmt.Println("Subscribed to PropertiesChanged signals for systemd units")

    c := make(chan *dbus.Signal, 10)
    conn.Signal(c)

    for v := range c {
        handleSignal(v)
    }
}

func handleSignal(v *dbus.Signal) {
    if len(v.Body) >= 3 {
        var unitPath dbus.ObjectPath
        var changedProps map[string]dbus.Variant
        //var iface string

        //iface = v.Body[0].(string)
        changedProps = v.Body[1].(map[string]dbus.Variant)
        unitPath = v.Path

        //fmt.Printf("Signal from interface %s for unit %s\n", iface, unitPath)

        for propName, propValue := range changedProps {
            if propName == "ActiveState" {
                //fmt.Println("ActiveState changed")
                unitName := strings.Replace(string(unitPath), "/org/freedesktop/systemd1/unit/", "", 1)
                unitName = strings.Replace(unitName, "_2e", ".", -1)
                unitName = strings.Replace(unitName, "_2d", "-", -1)
                unitName = strings.Replace(unitName, "_5f", "_", -1)
                fmt.Printf("Unit: %s, ActiveState: %s\n", unitName, propValue.Value().(string))
            }
        }
    }
}

Output:

Subscribed to PropertiesChanged signals for systemd units
Unit: php8.3-fpm.service, ActiveState: active
Unit: php8.3-fpm.service, ActiveState: active
Unit: php8.3-fpm.service, ActiveState: active
Unit: php8.3-fpm.service, ActiveState: active
Unit: php8.3-fpm.service, ActiveState: active
Unit: php8.3-fpm.service, ActiveState: active
Unit: user-1157.slice, ActiveState: inactive
Unit: user-1157.slice, ActiveState: active
Unit: user-runtime-dir_401157.service, ActiveState: inactive
Unit: user-runtime-dir_401157.service, ActiveState: activating
Unit: user-1157.slice, ActiveState: active
Unit: user-runtime-dir_401157.service, ActiveState: active
Unit: user-runtime-dir_401157.service, ActiveState: active
Unit: user_401157.service, ActiveState: inactive
Unit: user_401157.service, ActiveState: activating
Unit: user_401157.service, ActiveState: activating
Unit: user_401157.service, ActiveState: active
Unit: user_401157.service, ActiveState: active
Unit: user_401157.service, ActiveState: active
Unit: session-18886.scope, ActiveState: inactive
Unit: session-18886.scope, ActiveState: active
Unit: session-18886.scope, ActiveState: active
Unit: session-18886.scope, ActiveState: inactive
Unit: user-1158.slice, ActiveState: inactive
Unit: user-1158.slice, ActiveState: active
Unit: user-runtime-dir_401158.service, ActiveState: inactive
Unit: user-runtime-dir_401158.service, ActiveState: activating
Unit: user-1158.slice, ActiveState: active
Unit: user-runtime-dir_401158.service, ActiveState: active
Unit: user-runtime-dir_401158.service, ActiveState: active
Unit: user_401158.service, ActiveState: inactive
ndeubert commented 6 months ago

Ah cool. thank you for following up