...
1 package systemd
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "sync"
8
9 systemdDbus "github.com/coreos/go-systemd/v22/dbus"
10 dbus "github.com/godbus/dbus/v5"
11 )
12
13 var (
14 dbusC *systemdDbus.Conn
15 dbusMu sync.RWMutex
16 dbusInited bool
17 dbusRootless bool
18 )
19
20 type dbusConnManager struct{}
21
22
23 func newDbusConnManager(rootless bool) *dbusConnManager {
24 dbusMu.Lock()
25 defer dbusMu.Unlock()
26 if dbusInited && rootless != dbusRootless {
27 panic("can't have both root and rootless dbus")
28 }
29 dbusInited = true
30 dbusRootless = rootless
31 return &dbusConnManager{}
32 }
33
34
35 func (d *dbusConnManager) getConnection() (*systemdDbus.Conn, error) {
36
37
38
39 dbusMu.RLock()
40 if conn := dbusC; conn != nil {
41 dbusMu.RUnlock()
42 return conn, nil
43 }
44 dbusMu.RUnlock()
45
46
47
48
49 dbusMu.Lock()
50 defer dbusMu.Unlock()
51 if conn := dbusC; conn != nil {
52 return conn, nil
53 }
54
55 conn, err := d.newConnection()
56 if err != nil {
57
58
59
60 return nil, fmt.Errorf("failed to connect to dbus (hint: for rootless containers, maybe you need to install dbus-user-session package, see https://github.com/opencontainers/runc/blob/master/docs/cgroup-v2.md): %w", err)
61 }
62 dbusC = conn
63 return conn, nil
64 }
65
66 func (d *dbusConnManager) newConnection() (*systemdDbus.Conn, error) {
67 if dbusRootless {
68 return newUserSystemdDbus()
69 }
70 return systemdDbus.NewWithContext(context.TODO())
71 }
72
73
74
75 func (d *dbusConnManager) resetConnection(conn *systemdDbus.Conn) {
76 dbusMu.Lock()
77 defer dbusMu.Unlock()
78 if dbusC != nil && dbusC == conn {
79 dbusC.Close()
80 dbusC = nil
81 }
82 }
83
84
85
86
87 func (d *dbusConnManager) retryOnDisconnect(op func(*systemdDbus.Conn) error) error {
88 for {
89 conn, err := d.getConnection()
90 if err != nil {
91 return err
92 }
93 err = op(conn)
94 if err == nil {
95 return nil
96 }
97 if !errors.Is(err, dbus.ErrClosed) {
98 return err
99 }
100 d.resetConnection(conn)
101 }
102 }
103
View as plain text