]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udev-ctrl.c
ci: enable arm64 runner for build/unit jobs
[thirdparty/systemd.git] / src / udev / udev-ctrl.c
CommitLineData
ebd04379 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
d59f11e1 2
cf0fbc49 3#include <poll.h>
d59f11e1 4#include <sys/un.h>
cf0fbc49 5#include <unistd.h>
d59f11e1 6
d02c6f54
YW
7#include "sd-event.h"
8
b5efdb8a 9#include "alloc-util.h"
4ff9bc2e 10#include "errno-util.h"
3ffd4af2 11#include "fd-util.h"
f97b34a6 12#include "format-util.h"
bd1ae178 13#include "iovec-util.h"
93a1f792 14#include "log.h"
3ffd4af2 15#include "socket-util.h"
7d68eb1b
YW
16#include "strxcpyx.h"
17#include "udev-ctrl.h"
d59f11e1 18
540f4669 19/* wire protocol magic must match */
912541b0 20#define UDEV_CTRL_MAGIC 0xdead1dea
d59f11e1 21
e0d61dac 22typedef struct UdevCtrlMessageWire {
912541b0 23 char version[16];
14cb109d 24 unsigned magic;
e0d61dac
YW
25 UdevCtrlMessageType type;
26 UdevCtrlMessageValue value;
27} UdevCtrlMessageWire;
d59f11e1 28
e0d61dac 29struct UdevCtrl {
8f71a0d1 30 unsigned n_ref;
912541b0 31 int sock;
d02c6f54 32 int sock_connect;
6421348d 33 union sockaddr_union saddr;
912541b0 34 socklen_t addrlen;
481f24d1 35 bool bound;
481f24d1 36 bool connected;
d02c6f54
YW
37 sd_event *event;
38 sd_event_source *event_source;
39 sd_event_source *event_source_connect;
40 udev_ctrl_handler_t callback;
41 void *userdata;
d59f11e1
KS
42};
43
e0d61dac 44int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
5bb1d7fb 45 _cleanup_close_ int sock = -EBADF;
e0d61dac 46 UdevCtrl *uctrl;
58f3be82 47 int r;
912541b0 48
100bc5bf 49 assert(ret);
912541b0
KS
50
51 if (fd < 0) {
618b3642 52 sock = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
100bc5bf
YW
53 if (sock < 0)
54 return log_error_errno(errno, "Failed to create socket: %m");
912541b0 55 }
25568304 56
58f3be82
MY
57 /* enable receiving of the sender credentials in the messages */
58 r = setsockopt_int(fd >= 0 ? fd : sock, SOL_SOCKET, SO_PASSCRED, true);
59 if (r < 0)
60 log_warning_errno(r, "Failed to set SO_PASSCRED, ignoring: %m");
61
85352c09
MY
62 r = setsockopt_int(fd >= 0 ? fd : sock, SOL_SOCKET, SO_PASSRIGHTS, false);
63 if (r < 0)
64 log_debug_errno(r, "Failed to turn off SO_PASSRIGHTS, ignoring: %m");
65
e0d61dac 66 uctrl = new(UdevCtrl, 1);
100bc5bf
YW
67 if (!uctrl)
68 return -ENOMEM;
69
e0d61dac 70 *uctrl = (UdevCtrl) {
100bc5bf
YW
71 .n_ref = 1,
72 .sock = fd >= 0 ? fd : TAKE_FD(sock),
5bb1d7fb 73 .sock_connect = -EBADF,
100bc5bf
YW
74 .bound = fd >= 0,
75 };
76
44ed5214
LP
77 uctrl->saddr.un = (struct sockaddr_un) {
78 .sun_family = AF_UNIX,
79 .sun_path = "/run/udev/control",
80 };
81
a0233fcd 82 uctrl->addrlen = sockaddr_un_len(&uctrl->saddr.un);
fc1de713 83
100bc5bf
YW
84 *ret = TAKE_PTR(uctrl);
85 return 0;
d59f11e1
KS
86}
87
e0d61dac 88int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
53bba2fb 89 assert(uctrl);
912541b0 90
53bba2fb
YW
91 if (uctrl->bound)
92 return 0;
912541b0 93
bdb492b2
YW
94 (void) sockaddr_un_unlink(&uctrl->saddr.un);
95 if (bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
53bba2fb
YW
96 return log_error_errno(errno, "Failed to bind udev control socket: %m");
97
98 if (listen(uctrl->sock, 0) < 0)
99 return log_error_errno(errno, "Failed to listen udev control socket: %m");
100
101 uctrl->bound = true;
912541b0 102 return 0;
d59f11e1
KS
103}
104
e0d61dac 105static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
d02c6f54
YW
106 if (!uctrl)
107 return;
108
72151060 109 uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect);
d02c6f54
YW
110 uctrl->sock_connect = safe_close(uctrl->sock_connect);
111}
112
e0d61dac 113static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
8f71a0d1 114 assert(uctrl);
35927d13 115
d02c6f54
YW
116 udev_ctrl_disconnect(uctrl);
117
72151060 118 sd_event_source_unref(uctrl->event_source);
8f71a0d1 119 safe_close(uctrl->sock);
d02c6f54
YW
120
121 sd_event_unref(uctrl->event);
8f71a0d1 122 return mfree(uctrl);
d59f11e1
KS
123}
124
e0d61dac 125DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
d59f11e1 126
e0d61dac 127int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
d02c6f54
YW
128 int r;
129
130 assert_return(uctrl, -EINVAL);
131 assert_return(!uctrl->event, -EBUSY);
132
133 if (event)
134 uctrl->event = sd_event_ref(event);
135 else {
136 r = sd_event_default(&uctrl->event);
137 if (r < 0)
138 return r;
139 }
140
141 return 0;
d59f11e1
KS
142}
143
e0d61dac 144sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
d02c6f54
YW
145 assert(uctrl);
146
147 return uctrl->event_source;
148}
149
e0d61dac 150static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
d02c6f54
YW
151 udev_ctrl_disconnect(uctrl);
152 udev_ctrl_unref(uctrl);
153 (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
75db809a 154 /* We don't return NULL here because uctrl is not freed */
d02c6f54
YW
155}
156
e0d61dac 157DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
d02c6f54
YW
158
159static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
e0d61dac
YW
160 _cleanup_(udev_ctrl_disconnect_and_listen_againp) UdevCtrl *uctrl = NULL;
161 UdevCtrlMessageWire msg_wire;
162 struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(UdevCtrlMessageWire));
fb29cdbe 163 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
d02c6f54
YW
164 struct msghdr smsg = {
165 .msg_iov = &iov,
166 .msg_iovlen = 1,
fb29cdbe
LP
167 .msg_control = &control,
168 .msg_controllen = sizeof(control),
d02c6f54 169 };
d02c6f54
YW
170 struct ucred *cred;
171 ssize_t size;
172
173 assert(userdata);
174
175 /* When UDEV_CTRL_EXIT is received, manager unref udev_ctrl object.
176 * To avoid the object freed, let's increment the refcount. */
177 uctrl = udev_ctrl_ref(userdata);
178
3691bcf3 179 size = recvmsg_safe(fd, &smsg, 0);
ad501930 180 if (ERRNO_IS_NEG_TRANSIENT(size))
d02c6f54 181 return 0;
ad501930
MY
182 if (size == -ECHRNG) {
183 log_warning_errno(size, "Got message with truncated control data (unexpected fds sent?), ignoring.");
184 return 0;
185 }
186 if (size == -EXFULL) {
187 log_warning_errno(size, "Got message with truncated payload data, ignoring.");
188 return 0;
189 }
3691bcf3
LP
190 if (size < 0)
191 return log_error_errno(size, "Failed to receive ctrl message: %m");
d02c6f54
YW
192
193 cmsg_close_all(&smsg);
194
ad501930 195 if (size != sizeof(msg_wire)) {
3ad7f790
MY
196 log_warning("Received message with invalid length, ignoring");
197 return 0;
198 }
199
ca918f63
LP
200 cred = CMSG_FIND_DATA(&smsg, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
201 if (!cred) {
3ad7f790 202 log_warning("No sender credentials received, ignoring message");
d02c6f54
YW
203 return 0;
204 }
205
d02c6f54 206 if (cred->uid != 0) {
3ad7f790 207 log_warning("Invalid sender uid "UID_FMT", ignoring message", cred->uid);
d02c6f54
YW
208 return 0;
209 }
210
211 if (msg_wire.magic != UDEV_CTRL_MAGIC) {
3ad7f790 212 log_warning("Message magic 0x%08x doesn't match, ignoring message", msg_wire.magic);
d02c6f54
YW
213 return 0;
214 }
215
78467aeb
YW
216 if (msg_wire.type == _UDEV_CTRL_END_MESSAGES)
217 return 0;
218
d02c6f54
YW
219 if (uctrl->callback)
220 (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata);
221
78467aeb
YW
222 /* Do not disconnect and wait for next message. */
223 uctrl = udev_ctrl_unref(uctrl);
d02c6f54
YW
224 return 0;
225}
226
227static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 228 UdevCtrl *uctrl = ASSERT_PTR(userdata);
5bb1d7fb 229 _cleanup_close_ int sock = -EBADF;
d02c6f54 230 struct ucred ucred;
eff05270 231 int r;
912541b0 232
d02c6f54
YW
233 sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
234 if (sock < 0) {
4ff9bc2e
LP
235 if (ERRNO_IS_ACCEPT_AGAIN(errno))
236 return 0;
237
238 return log_error_errno(errno, "Failed to accept ctrl connection: %m");
912541b0
KS
239 }
240
241 /* check peer credential of connection */
d02c6f54 242 r = getpeercred(sock, &ucred);
eff05270 243 if (r < 0) {
572909a3 244 log_error_errno(r, "Failed to receive credentials of ctrl connection: %m");
d02c6f54 245 return 0;
912541b0 246 }
d02c6f54 247
912541b0 248 if (ucred.uid > 0) {
d02c6f54
YW
249 log_error("Invalid sender uid "UID_FMT", closing connection", ucred.uid);
250 return 0;
912541b0
KS
251 }
252
d02c6f54
YW
253 r = sd_event_add_io(uctrl->event, &uctrl->event_source_connect, sock, EPOLLIN, udev_ctrl_connection_event_handler, uctrl);
254 if (r < 0) {
255 log_error_errno(r, "Failed to create event source for udev control connection: %m");
256 return 0;
257 }
258
259 (void) sd_event_source_set_description(uctrl->event_source_connect, "udev-ctrl-connection");
ff2c503d 260
d02c6f54
YW
261 /* Do not accept multiple connection. */
262 (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_OFF);
35927d13 263
d02c6f54
YW
264 uctrl->sock_connect = TAKE_FD(sock);
265 return 0;
ff2c503d
KS
266}
267
e0d61dac 268int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
d02c6f54
YW
269 int r;
270
271 assert(uctrl);
272
273 if (!uctrl->event) {
274 r = udev_ctrl_attach_event(uctrl, NULL);
275 if (r < 0)
276 return r;
277 }
278
279 r = udev_ctrl_enable_receiving(uctrl);
280 if (r < 0)
281 return r;
282
283 uctrl->callback = callback;
284 uctrl->userdata = userdata;
285
286 r = sd_event_add_io(uctrl->event, &uctrl->event_source, uctrl->sock, EPOLLIN, udev_ctrl_event_handler, uctrl);
287 if (r < 0)
288 return r;
289
290 (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl");
291
292 return 0;
293}
8f71a0d1 294
f9da11ef 295int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data) {
e0d61dac 296 UdevCtrlMessageWire ctrl_msg_wire = {
8c823a03 297 .version = "udev-" PROJECT_VERSION_STR,
b9da6a09
ZJS
298 .magic = UDEV_CTRL_MAGIC,
299 .type = type,
300 };
912541b0 301
f9da11ef
YW
302 if (type == UDEV_CTRL_SET_ENV) {
303 assert(data);
304 strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), data);
305 } else if (IN_SET(type, UDEV_CTRL_SET_LOG_LEVEL, UDEV_CTRL_SET_CHILDREN_MAX))
306 ctrl_msg_wire.value.intval = PTR_TO_INT(data);
912541b0
KS
307
308 if (!uctrl->connected) {
b9da6a09
ZJS
309 if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
310 return -errno;
912541b0
KS
311 uctrl->connected = true;
312 }
78467aeb 313
b9da6a09
ZJS
314 if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0)
315 return -errno;
912541b0 316
78467aeb 317 return 0;
ff2c503d
KS
318}
319
e0d61dac 320int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
9612da36 321 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
78467aeb 322 int r;
d59f11e1 323
78467aeb 324 assert(uctrl);
d59f11e1 325
78467aeb
YW
326 if (uctrl->sock < 0)
327 return 0;
328 if (!uctrl->connected)
329 return 0;
d59f11e1 330
a859f794
DT
331 r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, NULL);
332 if (r < 0)
333 return r;
d59f11e1 334
78467aeb
YW
335 if (timeout == 0)
336 return 0;
337
338 if (!uctrl->event) {
339 r = udev_ctrl_attach_event(uctrl, NULL);
340 if (r < 0)
341 return r;
342 }
343
cbda8bd5 344 r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, NULL, INT_TO_PTR(0));
78467aeb
YW
345 if (r < 0)
346 return r;
347
60ccab09 348 (void) sd_event_source_set_description(source_io, "udev-ctrl-wait-io");
78467aeb
YW
349
350 if (timeout != USEC_INFINITY) {
39cf0351 351 r = sd_event_add_time_relative(
ba4e0427 352 uctrl->event, &source_timeout, CLOCK_BOOTTIME,
39cf0351
LP
353 timeout,
354 0, NULL, INT_TO_PTR(-ETIMEDOUT));
78467aeb
YW
355 if (r < 0)
356 return r;
357
f6e8ba81 358 (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-timeout");
78467aeb 359 }
d59f11e1 360
78467aeb 361 return sd_event_loop(uctrl->event);
bb38678e 362}