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