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