@@ -70,7 +70,8 @@ func (c *Conn) dispatch() {
7070 c .jobComplete (signal )
7171 }
7272
73- if c .subscriber .updateCh == nil {
73+ if c .subStateSubscriber .updateCh == nil &&
74+ c .propertiesSubscriber .updateCh == nil {
7475 continue
7576 }
7677
@@ -84,6 +85,12 @@ func (c *Conn) dispatch() {
8485 case "org.freedesktop.DBus.Properties.PropertiesChanged" :
8586 if signal .Body [0 ].(string ) == "org.freedesktop.systemd1.Unit" {
8687 unitPath = signal .Path
88+
89+ if len (signal .Body ) >= 2 {
90+ if changed , ok := signal .Body [1 ].(map [string ]dbus.Variant ); ok {
91+ c .sendPropertiesUpdate (unitPath , changed )
92+ }
93+ }
8794 }
8895 }
8996
@@ -169,15 +176,19 @@ type SubStateUpdate struct {
169176// is full, it attempts to write an error to errCh; if errCh is full, the error
170177// passes silently.
171178func (c * Conn ) SetSubStateSubscriber (updateCh chan <- * SubStateUpdate , errCh chan <- error ) {
172- c .subscriber .Lock ()
173- defer c .subscriber .Unlock ()
174- c .subscriber .updateCh = updateCh
175- c .subscriber .errCh = errCh
179+ c .subStateSubscriber .Lock ()
180+ defer c .subStateSubscriber .Unlock ()
181+ c .subStateSubscriber .updateCh = updateCh
182+ c .subStateSubscriber .errCh = errCh
176183}
177184
178185func (c * Conn ) sendSubStateUpdate (unitPath dbus.ObjectPath ) {
179- c .subscriber .Lock ()
180- defer c .subscriber .Unlock ()
186+ c .subStateSubscriber .Lock ()
187+ defer c .subStateSubscriber .Unlock ()
188+
189+ if c .subStateSubscriber .updateCh == nil {
190+ return
191+ }
181192
182193 if c .shouldIgnore (unitPath ) {
183194 return
@@ -186,7 +197,7 @@ func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) {
186197 info , err := c .GetUnitPathProperties (unitPath )
187198 if err != nil {
188199 select {
189- case c .subscriber .errCh <- err :
200+ case c .subStateSubscriber .errCh <- err :
190201 default :
191202 }
192203 }
@@ -196,10 +207,10 @@ func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) {
196207
197208 update := & SubStateUpdate {name , substate }
198209 select {
199- case c .subscriber .updateCh <- update :
210+ case c .subStateSubscriber .updateCh <- update :
200211 default :
201212 select {
202- case c .subscriber .errCh <- errors .New ("update channel full!" ):
213+ case c .subStateSubscriber .errCh <- errors .New ("update channel full!" ):
203214 default :
204215 }
205216 }
@@ -222,7 +233,7 @@ func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) {
222233// the properties).
223234
224235func (c * Conn ) shouldIgnore (path dbus.ObjectPath ) bool {
225- t , ok := c .subscriber .ignore [path ]
236+ t , ok := c .subStateSubscriber .ignore [path ]
226237 return ok && t >= time .Now ().UnixNano ()
227238}
228239
@@ -231,20 +242,62 @@ func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
231242
232243 // unit is unloaded - it will trigger bad systemd dbus behavior
233244 if info ["LoadState" ].(string ) == "not-found" {
234- c .subscriber .ignore [path ] = time .Now ().UnixNano () + ignoreInterval
245+ c .subStateSubscriber .ignore [path ] = time .Now ().UnixNano () + ignoreInterval
235246 }
236247}
237248
238249// without this, ignore would grow unboundedly over time
239250func (c * Conn ) cleanIgnore () {
240251 now := time .Now ().UnixNano ()
241- if c .subscriber .cleanIgnore < now {
242- c .subscriber .cleanIgnore = now + cleanIgnoreInterval
252+ if c .subStateSubscriber .cleanIgnore < now {
253+ c .subStateSubscriber .cleanIgnore = now + cleanIgnoreInterval
243254
244- for p , t := range c .subscriber .ignore {
255+ for p , t := range c .subStateSubscriber .ignore {
245256 if t < now {
246- delete (c .subscriber .ignore , p )
257+ delete (c .subStateSubscriber .ignore , p )
247258 }
248259 }
249260 }
250261}
262+
263+ // PropertiesUpdate holds a map of a unit's changed properties
264+ type PropertiesUpdate struct {
265+ UnitName string
266+ Changed map [string ]dbus.Variant
267+ }
268+
269+ // SetPropertiesSubscriber writes to updateCh when any unit's properties
270+ // change. Every property change reported by systemd will be sent; that is, no
271+ // transitions will be "missed" (as they might be with SetSubStateSubscriber).
272+ // However, state changes will only be written to the channel with non-blocking
273+ // writes. If updateCh is full, it attempts to write an error to errCh; if
274+ // errCh is full, the error passes silently.
275+ func (c * Conn ) SetPropertiesSubscriber (updateCh chan <- * PropertiesUpdate , errCh chan <- error ) {
276+ c .propertiesSubscriber .Lock ()
277+ defer c .propertiesSubscriber .Unlock ()
278+ c .propertiesSubscriber .updateCh = updateCh
279+ c .propertiesSubscriber .errCh = errCh
280+ }
281+
282+ // we don't need to worry about shouldIgnore() here because
283+ // sendPropertiesUpdate doesn't call GetProperties()
284+ func (c * Conn ) sendPropertiesUpdate (unitPath dbus.ObjectPath , changedProps map [string ]dbus.Variant ) {
285+ c .propertiesSubscriber .Lock ()
286+ defer c .propertiesSubscriber .Unlock ()
287+
288+ if c .propertiesSubscriber .updateCh == nil {
289+ return
290+ }
291+
292+ update := & PropertiesUpdate {unitName (unitPath ), changedProps }
293+
294+ select {
295+ case c .propertiesSubscriber .updateCh <- update :
296+ default :
297+ select {
298+ case c .propertiesSubscriber .errCh <- errors .New ("update channel is full" ):
299+ default :
300+ }
301+ return
302+ }
303+ }
0 commit comments