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