1
2
3
4
19
20 package systemd
21
22 import (
23 "fmt"
24 "os"
25 "path/filepath"
26 "syscall"
27 "time"
28
29 "github.com/godbus/dbus/v5"
30 "k8s.io/klog/v2"
31 )
32
33 const (
34 logindService = "org.freedesktop.login1"
35 logindObject = dbus.ObjectPath("/org/freedesktop/login1")
36 logindInterface = "org.freedesktop.login1.Manager"
37 )
38
39 type dBusConnector interface {
40 Object(dest string, path dbus.ObjectPath) dbus.BusObject
41 AddMatchSignal(options ...dbus.MatchOption) error
42 Signal(ch chan<- *dbus.Signal)
43 }
44
45
46 type DBusCon struct {
47 SystemBus dBusConnector
48 }
49
50 func NewDBusCon() (*DBusCon, error) {
51 conn, err := dbus.SystemBus()
52 if err != nil {
53 return nil, err
54 }
55
56 return &DBusCon{
57 SystemBus: conn,
58 }, nil
59 }
60
61
62 type InhibitLock uint32
63
64
65
66 func (bus *DBusCon) CurrentInhibitDelay() (time.Duration, error) {
67 obj := bus.SystemBus.Object(logindService, logindObject)
68 res, err := obj.GetProperty(logindInterface + ".InhibitDelayMaxUSec")
69 if err != nil {
70 return 0, fmt.Errorf("failed reading InhibitDelayMaxUSec property from logind: %w", err)
71 }
72
73 delay, ok := res.Value().(uint64)
74 if !ok {
75 return 0, fmt.Errorf("InhibitDelayMaxUSec from logind is not a uint64 as expected")
76 }
77
78
79 duration := time.Duration(delay) * time.Microsecond
80 return duration, nil
81 }
82
83
84
85 func (bus *DBusCon) InhibitShutdown() (InhibitLock, error) {
86 obj := bus.SystemBus.Object(logindService, logindObject)
87 what := "shutdown"
88 who := "kubelet"
89 why := "Kubelet needs time to handle node shutdown"
90 mode := "delay"
91
92 call := obj.Call("org.freedesktop.login1.Manager.Inhibit", 0, what, who, why, mode)
93 if call.Err != nil {
94 return InhibitLock(0), fmt.Errorf("failed creating systemd inhibitor: %w", call.Err)
95 }
96
97 var fd uint32
98 err := call.Store(&fd)
99 if err != nil {
100 return InhibitLock(0), fmt.Errorf("failed storing inhibit lock file descriptor: %w", err)
101 }
102
103 return InhibitLock(fd), nil
104 }
105
106
107 func (bus *DBusCon) ReleaseInhibitLock(lock InhibitLock) error {
108 err := syscall.Close(int(lock))
109
110 if err != nil {
111 return fmt.Errorf("unable to close systemd inhibitor lock: %w", err)
112 }
113
114 return nil
115 }
116
117
118 func (bus *DBusCon) ReloadLogindConf() error {
119 systemdService := "org.freedesktop.systemd1"
120 systemdObject := "/org/freedesktop/systemd1"
121 systemdInterface := "org.freedesktop.systemd1.Manager"
122
123 obj := bus.SystemBus.Object(systemdService, dbus.ObjectPath(systemdObject))
124 unit := "systemd-logind.service"
125 who := "all"
126 var signal int32 = 1
127
128 call := obj.Call(systemdInterface+".KillUnit", 0, unit, who, signal)
129 if call.Err != nil {
130 return fmt.Errorf("unable to reload logind conf: %w", call.Err)
131 }
132
133 return nil
134 }
135
136
137
138 func (bus *DBusCon) MonitorShutdown() (<-chan bool, error) {
139 err := bus.SystemBus.AddMatchSignal(dbus.WithMatchInterface(logindInterface), dbus.WithMatchMember("PrepareForShutdown"), dbus.WithMatchObjectPath("/org/freedesktop/login1"))
140
141 if err != nil {
142 return nil, err
143 }
144
145 busChan := make(chan *dbus.Signal, 1)
146 bus.SystemBus.Signal(busChan)
147
148 shutdownChan := make(chan bool, 1)
149
150 go func() {
151 for {
152 event, ok := <-busChan
153 if !ok {
154 close(shutdownChan)
155 return
156 }
157 if event == nil || len(event.Body) == 0 {
158 klog.ErrorS(nil, "Failed obtaining shutdown event, PrepareForShutdown event was empty")
159 continue
160 }
161 shutdownActive, ok := event.Body[0].(bool)
162 if !ok {
163 klog.ErrorS(nil, "Failed obtaining shutdown event, PrepareForShutdown event was not bool type as expected")
164 continue
165 }
166 shutdownChan <- shutdownActive
167 }
168 }()
169
170 return shutdownChan, nil
171 }
172
173 const (
174 logindConfigDirectory = "/etc/systemd/logind.conf.d/"
175 kubeletLogindConf = "99-kubelet.conf"
176 )
177
178
179 func (bus *DBusCon) OverrideInhibitDelay(inhibitDelayMax time.Duration) error {
180 err := os.MkdirAll(logindConfigDirectory, 0755)
181 if err != nil {
182 return fmt.Errorf("failed creating %v directory: %w", logindConfigDirectory, err)
183 }
184
185
186
187
188
189 inhibitOverride := fmt.Sprintf(`# Kubelet logind override
190 [Login]
191 InhibitDelayMaxSec=%.0f
192 `, inhibitDelayMax.Seconds())
193
194 logindOverridePath := filepath.Join(logindConfigDirectory, kubeletLogindConf)
195 if err := os.WriteFile(logindOverridePath, []byte(inhibitOverride), 0644); err != nil {
196 return fmt.Errorf("failed writing logind shutdown inhibit override file %v: %w", logindOverridePath, err)
197 }
198
199 return nil
200 }
201
View as plain text