func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
	t, ok := c.subscriber.ignore[path]
	return ok && t >= time.Now().UnixNano()
}
The ignore functions work around a wart in the systemd dbus interface.
Requesting the properties of an unloaded unit will cause systemd to send a
pair of UnitNew/UnitRemoved signals.  Because we need to get a unit's
properties on UnitNew (as that's the only indication of a new unit coming up
for the first time), we would enter an infinite loop if we did not attempt
to detect and ignore these spurious signals.  The signal themselves are
indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
unloaded unit's signals for a short time after requesting its properties.
This means that we will miss e.g. a transient unit being restarted
*immediately* upon failure and also a transient unit being started
immediately after requesting its status (with systemctl status, for example,
because this causes a UnitNew signal to be sent which then causes us to fetch
the properties).
package dbus
import (
	"errors"
	"time"
	"github.com/godbus/dbus"
)
const (
	cleanIgnoreInterval = int64(10 * time.Second)
	ignoreInterval      = int64(30 * time.Millisecond)
)
func (c *Conn) Subscribe() error {
	c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
	c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
	err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
	if err != nil {
		return err
	}
	return nil
}
func (c *Conn) Unsubscribe() error {
	err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
	if err != nil {
		return err
	}
	return nil
}
func (c *Conn) dispatch() {
	ch := make(chan *dbus.Signal, signalBuffer)
	c.sigconn.Signal(ch)
	go func() {
		for {
			signal, ok := <-ch
			if !ok {
				return
			}
			if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
				c.jobComplete(signal)
			}
			if c.subscriber.updateCh == nil {
				continue
			}
			var unitPath dbus.ObjectPath
			switch signal.Name {
			case "org.freedesktop.systemd1.Manager.JobRemoved":
				unitName := signal.Body[2].(string)
				c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
			case "org.freedesktop.systemd1.Manager.UnitNew":
				unitPath = signal.Body[1].(dbus.ObjectPath)
			case "org.freedesktop.DBus.Properties.PropertiesChanged":
				if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
					unitPath = signal.Path
				}
			}
			if unitPath == dbus.ObjectPath("") {
				continue
			}
			c.sendSubStateUpdate(unitPath)
		}
	}()
}
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
	return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
}
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
	old := make(map[string]*UnitStatus)
	statusChan := make(chan map[string]*UnitStatus, buffer)
	errChan := make(chan error, buffer)
	go func() {
		for {
			timerChan := time.After(interval)
			units, err := c.ListUnits()
			if err == nil {
				cur := make(map[string]*UnitStatus)
				for i := range units {
					if filterUnit != nil && filterUnit(units[i].Name) {
						continue
					}
					cur[units[i].Name] = &units[i]
				}
				changed := make(map[string]*UnitStatus)
				for n, u := range cur {
					if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
						changed[n] = u
					}
					delete(old, n)
				}
				for oldN := range old {
					changed[oldN] = nil
				}
				old = cur
				if len(changed) != 0 {
					statusChan <- changed
				}
			} else {
				errChan <- err
			}
			<-timerChan
		}
	}()
	return statusChan, errChan
}
type SubStateUpdate struct {
	UnitName string
	SubState string
}
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
	c.subscriber.Lock()
	defer c.subscriber.Unlock()
	c.subscriber.updateCh = updateCh
	c.subscriber.errCh = errCh
}
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
	c.subscriber.Lock()
	defer c.subscriber.Unlock()
	if c.shouldIgnore(path) {
		return
	}
	info, err := c.GetUnitProperties(string(path))
	if err != nil {
		select {
		case c.subscriber.errCh <- err:
		default:
		}
	}
	name := info["Id"].(string)
	substate := info["SubState"].(string)
	update := &SubStateUpdate{name, substate}
	select {
	case c.subscriber.updateCh <- update:
	default:
		select {
		case c.subscriber.errCh <- errors.New("update channel full!"):
		default:
		}
	}
	c.updateIgnore(path, info)
}