-
Notifications
You must be signed in to change notification settings - Fork 27
/
systembus.go
147 lines (128 loc) · 4.11 KB
/
systembus.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package testutils
import (
"context"
"errors"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"testing"
"github.com/godbus/dbus/v5"
"github.com/stretchr/testify/require"
)
var (
sdbus sync.Once
sdbusMU sync.Mutex
nbRunningTestsSdbus uint
stopDbus context.CancelFunc
dbusCmd *exec.Cmd
config string
savedDbusSystemAddress string
)
// StartLocalSystemBus allows to start and set environment variable to a local bus, preventing polluting system ones.
func StartLocalSystemBus() func() {
sdbusMU.Lock()
defer sdbusMU.Unlock()
nbRunningTestsSdbus++
sdbus.Do(func() {
dir, err := os.MkdirTemp("", "tests-dbus")
if err != nil {
log.Fatalf("Setup: can’t create dbus system directory: %v", err)
}
savedDbusSystemAddress = os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
config = filepath.Join(dir, "dbus.config")
err = os.WriteFile(config, []byte(`<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<type>system</type>
<keep_umask/>
<listen>unix:tmpdir=/tmp</listen>
<standard_system_servicedirs />
<policy context="default">
<allow send_destination="*" eavesdrop="true"/>
<allow eavesdrop="true"/>
<allow own="*"/>
</policy>
</busconfig>`), 0600)
if err != nil {
log.Fatalf("Setup: can’t create dbus configuration: %v", err)
}
var ctx context.Context
ctx, stopDbus = context.WithCancel(context.Background())
// #nosec G204 - this is only for tests, we are in control of the config
dbusCmd = exec.CommandContext(ctx, "dbus-daemon", "--print-address=1", "--config-file="+config)
dbusStdout, err := dbusCmd.StdoutPipe()
if err != nil {
_ = os.RemoveAll(dir)
log.Fatalf("couldn't get stdout of dbus-daemon: %v", err)
}
if err := dbusCmd.Start(); err != nil {
_ = os.RemoveAll(dir)
log.Fatalf("couldn't start dbus-daemon: %v", err)
}
dbusAddr := make([]byte, 256)
n, err := dbusStdout.Read(dbusAddr)
if err != nil {
_ = os.RemoveAll(dir)
log.Fatalf("couldn't get dbus address: %v", err)
}
dbusAddr = dbusAddr[:n]
if err := os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", string(dbusAddr)); err != nil {
_ = os.RemoveAll(dir)
log.Fatalf("couldn't set DBUS_SYSTEM_BUS_ADDRESS: %v", err)
}
})
return func() {
sdbusMU.Lock()
defer sdbusMU.Unlock()
nbRunningTestsSdbus--
if nbRunningTestsSdbus != 0 {
return
}
stopDbus()
// dbus command is killed
_ = dbusCmd.Wait()
if err := os.RemoveAll(filepath.Dir(config)); err != nil {
log.Fatalf("couldn't remove dbus configuration directory: %v", err)
}
if err := os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", savedDbusSystemAddress); err != nil {
log.Fatalf("couldn't restore DBUS_SYSTEM_BUS_ADDRESS: %v", err)
}
// Restore dbus system launcher
sdbus = sync.Once{}
}
}
// NewDbusConn returns a system dbus connection which automatically close on test shutdown.
func NewDbusConn(t *testing.T) *dbus.Conn {
t.Helper()
bus, err := dbus.SystemBusPrivate()
require.NoError(t, err, "Setup: can’t get a private system bus")
t.Cleanup(func() {
err = bus.Close()
require.NoError(t, err, "Teardown: can’t close system dbus connection")
})
err = bus.Auth(nil)
require.NoError(t, err, "Setup: can’t auth on private system bus")
err = bus.Hello()
require.NoError(t, err, "Setup: can’t send hello message on private system bus")
return bus
}
// GetSystemBusConnection returns a connection to the system bus with a safety check to avoid mistakenly connecting to the
// actual system bus.
func GetSystemBusConnection() (*dbus.Conn, error) {
if !isRunning() {
return nil, errors.New("system bus mock is not running. If that's intended, manually connect to the system bus instead of using this function")
}
conn, err := dbus.ConnectSystemBus()
if err != nil {
return nil, err
}
return conn, nil
}
// isRunning checks if the system bus mock is running.
func isRunning() bool {
return strings.HasPrefix(os.Getenv("DBUS_SYSTEM_BUS_ADDRESS"), "unix:abstract=/tmp") ||
strings.HasPrefix(os.Getenv("DBUS_SYSTEM_BUS_ADDRESS"), "unix:path=/tmp")
}