Skip to content

Commit b5b3eb9

Browse files
committed
dbus: block on jobs only if provided non-nil channel
1 parent 7a81740 commit b5b3eb9

File tree

5 files changed

+72
-45
lines changed

5 files changed

+72
-45
lines changed

dbus/dbus.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ type Conn struct {
7373
sigobj *dbus.Object
7474

7575
jobListener struct {
76-
jobs map[dbus.ObjectPath]chan string
76+
jobs map[dbus.ObjectPath]chan<- string
7777
sync.Mutex
7878
}
7979
subscriber struct {
@@ -115,7 +115,7 @@ func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
115115
}
116116

117117
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
118-
c.jobListener.jobs = make(map[dbus.ObjectPath]chan string)
118+
c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string)
119119

120120
// Setup the listeners on jobs so that we can get completions
121121
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,

dbus/methods.go

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package dbus
1818

1919
import (
2020
"errors"
21+
"path"
22+
"strconv"
2123

2224
"github.com/godbus/dbus"
2325
)
@@ -37,26 +39,26 @@ func (c *Conn) jobComplete(signal *dbus.Signal) {
3739
c.jobListener.Unlock()
3840
}
3941

40-
func (c *Conn) startJob(job string, args ...interface{}) (<-chan string, error) {
41-
c.jobListener.Lock()
42-
defer c.jobListener.Unlock()
42+
func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
43+
if ch != nil {
44+
c.jobListener.Lock()
45+
defer c.jobListener.Unlock()
46+
}
4347

44-
ch := make(chan string, 1)
45-
var path dbus.ObjectPath
46-
err := c.sysobj.Call(job, 0, args...).Store(&path)
48+
var p dbus.ObjectPath
49+
err := c.sysobj.Call(job, 0, args...).Store(&p)
4750
if err != nil {
48-
return nil, err
51+
return 0, err
4952
}
50-
c.jobListener.jobs[path] = ch
51-
return ch, nil
52-
}
5353

54-
func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
55-
respCh, err := c.startJob(job, args...)
56-
if err != nil {
57-
return "", err
54+
if ch != nil {
55+
c.jobListener.jobs[p] = ch
5856
}
59-
return <-respCh, nil
57+
58+
// ignore error since 0 is fine if conversion fails
59+
jobID, _ := strconv.Atoi(path.Base(string(p)))
60+
61+
return jobID, nil
6062
}
6163

6264
// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
@@ -74,59 +76,67 @@ func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
7476
// requirement dependencies. It is not recommended to make use of the latter
7577
// two options.
7678
//
77-
// Result string: one of done, canceled, timeout, failed, dependency, skipped.
79+
// If the provided channel is non-nil, a result string will be sent to it upon
80+
// job completion: one of done, canceled, timeout, failed, dependency, skipped.
7881
// done indicates successful execution of a job. canceled indicates that a job
7982
// has been canceled before it finished execution. timeout indicates that the
8083
// job timeout was reached. failed indicates that the job failed. dependency
8184
// indicates that a job this job has been depending on failed and the job hence
8285
// has been removed too. skipped indicates that a job was skipped because it
8386
// didn't apply to the units current state.
84-
func (c *Conn) StartUnit(name string, mode string) (string, error) {
85-
return c.runJob("org.freedesktop.systemd1.Manager.StartUnit", name, mode)
87+
//
88+
// If no error occurs, the ID of the underlying systemd job will be returned. There
89+
// does exist the possibility for no error to be returned, but for the returned job
90+
// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
91+
// should not be considered authoritative.
92+
//
93+
// If an error does occur, it will be returned to the user alongside a job ID of 0.
94+
func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
95+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
8696
}
8797

8898
// StopUnit is similar to StartUnit but stops the specified unit rather
8999
// than starting it.
90-
func (c *Conn) StopUnit(name string, mode string) (string, error) {
91-
return c.runJob("org.freedesktop.systemd1.Manager.StopUnit", name, mode)
100+
func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
101+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
92102
}
93103

94104
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
95-
func (c *Conn) ReloadUnit(name string, mode string) (string, error) {
96-
return c.runJob("org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
105+
func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
106+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
97107
}
98108

99109
// RestartUnit restarts a service. If a service is restarted that isn't
100110
// running it will be started.
101-
func (c *Conn) RestartUnit(name string, mode string) (string, error) {
102-
return c.runJob("org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
111+
func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
112+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
103113
}
104114

105115
// TryRestartUnit is like RestartUnit, except that a service that isn't running
106116
// is not affected by the restart.
107-
func (c *Conn) TryRestartUnit(name string, mode string) (string, error) {
108-
return c.runJob("org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
117+
func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
118+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
109119
}
110120

111121
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
112122
// otherwise.
113-
func (c *Conn) ReloadOrRestartUnit(name string, mode string) (string, error) {
114-
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
123+
func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
124+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
115125
}
116126

117127
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
118128
// flavored restart otherwise.
119-
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error) {
120-
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
129+
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
130+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
121131
}
122132

123133
// StartTransientUnit() may be used to create and start a transient unit, which
124134
// will be released as soon as it is not running or referenced anymore or the
125135
// system is rebooted. name is the unit name including suffix, and must be
126136
// unique. mode is the same as in StartUnit(), properties contains properties
127137
// of the unit.
128-
func (c *Conn) StartTransientUnit(name string, mode string, properties ...Property) (string, error) {
129-
return c.runJob("org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
138+
func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
139+
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
130140
}
131141

132142
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's

dbus/methods_test.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func findFixture(target string, t *testing.T) string {
4646

4747
func setupUnit(target string, conn *Conn, t *testing.T) {
4848
// Blindly stop the unit in case it is running
49-
conn.StopUnit(target, "replace")
49+
conn.StopUnit(target, "replace", nil)
5050

5151
// Blindly remove the symlink in case it exists
5252
targetRun := filepath.Join("/run/systemd/system/", target)
@@ -81,11 +81,13 @@ func TestStartStopUnit(t *testing.T) {
8181
linkUnit(target, conn, t)
8282

8383
// 2. Start the unit
84-
job, err := conn.StartUnit(target, "replace")
84+
reschan := make(chan string)
85+
_, err := conn.StartUnit(target, "replace", reschan)
8586
if err != nil {
8687
t.Fatal(err)
8788
}
8889

90+
job := <-reschan
8991
if job != "done" {
9092
t.Fatal("Job is not done:", job)
9193
}
@@ -108,11 +110,14 @@ func TestStartStopUnit(t *testing.T) {
108110
}
109111

110112
// 3. Stop the unit
111-
job, err = conn.StopUnit(target, "replace")
113+
_, err = conn.StopUnit(target, "replace", reschan)
112114
if err != nil {
113115
t.Fatal(err)
114116
}
115117

118+
// wait for StopUnit job to complete
119+
<-reschan
120+
116121
units, err = conn.ListUnits()
117122

118123
unit = nil
@@ -260,11 +265,13 @@ func TestStartStopTransientUnit(t *testing.T) {
260265
target := fmt.Sprintf("testing-transient-%d.service", rand.Int())
261266

262267
// Start the unit
263-
job, err := conn.StartTransientUnit(target, "replace", props...)
268+
reschan := make(chan string)
269+
_, err := conn.StartTransientUnit(target, "replace", props, reschan)
264270
if err != nil {
265271
t.Fatal(err)
266272
}
267273

274+
job := <-reschan
268275
if job != "done" {
269276
t.Fatal("Job is not done:", job)
270277
}
@@ -287,11 +294,14 @@ func TestStartStopTransientUnit(t *testing.T) {
287294
}
288295

289296
// 3. Stop the unit
290-
job, err = conn.StopUnit(target, "replace")
297+
_, err = conn.StopUnit(target, "replace", reschan)
291298
if err != nil {
292299
t.Fatal(err)
293300
}
294301

302+
// wait for StopUnit job to complete
303+
<-reschan
304+
295305
units, err = conn.ListUnits()
296306

297307
unit = nil
@@ -315,16 +325,21 @@ func TestConnJobListener(t *testing.T) {
315325

316326
jobSize := len(conn.jobListener.jobs)
317327

318-
_, err := conn.StartUnit(target, "replace")
328+
reschan := make(chan string)
329+
_, err := conn.StartUnit(target, "replace", reschan)
319330
if err != nil {
320331
t.Fatal(err)
321332
}
322333

323-
_, err = conn.StopUnit(target, "replace")
334+
<-reschan
335+
336+
_, err = conn.StopUnit(target, "replace", reschan)
324337
if err != nil {
325338
t.Fatal(err)
326339
}
327340

341+
<-reschan
342+
328343
currentJobSize := len(conn.jobListener.jobs)
329344
if jobSize != currentJobSize {
330345
t.Fatal("JobListener jobs leaked")

dbus/subscription_set_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ func TestSubscriptionSetUnit(t *testing.T) {
2727
setupUnit(target, conn, t)
2828
linkUnit(target, conn, t)
2929

30-
job, err := conn.StartUnit(target, "replace")
30+
reschan := make(chan string)
31+
_, err = conn.StartUnit(target, "replace", reschan)
3132
if err != nil {
3233
t.Fatal(err)
3334
}
3435

36+
job := <-reschan
3537
if job != "done" {
3638
t.Fatal("Couldn't start", target)
3739
}

dbus/subscription_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ func TestSubscribeUnit(t *testing.T) {
4949
setupUnit(target, conn, t)
5050
linkUnit(target, conn, t)
5151

52-
job, err := conn.StartUnit(target, "replace")
52+
reschan := make(chan string)
53+
_, err = conn.StartUnit(target, "replace", reschan)
5354
if err != nil {
5455
t.Fatal(err)
5556
}
5657

58+
job := <-reschan
5759
if job != "done" {
5860
t.Fatal("Couldn't start", target)
5961
}
@@ -87,5 +89,3 @@ func TestSubscribeUnit(t *testing.T) {
8789
success:
8890
return
8991
}
90-
91-

0 commit comments

Comments
 (0)