]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/udev/udev-ctrl.c
ssh-generator: generate /etc/issue.d/ with VSOCK ssh info data (#37819)
[thirdparty/systemd.git] / src / udev / udev-ctrl.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <poll.h>
4#include <sys/un.h>
5#include <unistd.h>
6
7#include "sd-event.h"
8
9#include "alloc-util.h"
10#include "errno-util.h"
11#include "fd-util.h"
12#include "format-util.h"
13#include "iovec-util.h"
14#include "log.h"
15#include "socket-util.h"
16#include "strxcpyx.h"
17#include "udev-ctrl.h"
18
19/* wire protocol magic must match */
20#define UDEV_CTRL_MAGIC 0xdead1dea
21
22typedef struct UdevCtrlMessageWire {
23 char version[16];
24 unsigned magic;
25 UdevCtrlMessageType type;
26 UdevCtrlMessageValue value;
27} UdevCtrlMessageWire;
28
29struct UdevCtrl {
30 unsigned n_ref;
31 int sock;
32 int sock_connect;
33 union sockaddr_union saddr;
34 socklen_t addrlen;
35 bool bound;
36 bool connected;
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;
42};
43
44int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
45 _cleanup_close_ int sock = -EBADF;
46 UdevCtrl *uctrl;
47 int r;
48
49 assert(ret);
50
51 if (fd < 0) {
52 sock = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
53 if (sock < 0)
54 return log_error_errno(errno, "Failed to create socket: %m");
55 }
56
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
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
66 uctrl = new(UdevCtrl, 1);
67 if (!uctrl)
68 return -ENOMEM;
69
70 *uctrl = (UdevCtrl) {
71 .n_ref = 1,
72 .sock = fd >= 0 ? fd : TAKE_FD(sock),
73 .sock_connect = -EBADF,
74 .bound = fd >= 0,
75 };
76
77 uctrl->saddr.un = (struct sockaddr_un) {
78 .sun_family = AF_UNIX,
79 .sun_path = "/run/udev/control",
80 };
81
82 uctrl->addrlen = sockaddr_un_len(&uctrl->saddr.un);
83
84 *ret = TAKE_PTR(uctrl);
85 return 0;
86}
87
88int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
89 assert(uctrl);
90
91 if (uctrl->bound)
92 return 0;
93
94 (void) sockaddr_un_unlink(&uctrl->saddr.un);
95 if (bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
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;
102 return 0;
103}
104
105static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
106 if (!uctrl)
107 return;
108
109 uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect);
110 uctrl->sock_connect = safe_close(uctrl->sock_connect);
111}
112
113static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
114 assert(uctrl);
115
116 udev_ctrl_disconnect(uctrl);
117
118 sd_event_source_unref(uctrl->event_source);
119 safe_close(uctrl->sock);
120
121 sd_event_unref(uctrl->event);
122 return mfree(uctrl);
123}
124
125DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
126
127int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
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;
142}
143
144sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
145 assert(uctrl);
146
147 return uctrl->event_source;
148}
149
150static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
151 udev_ctrl_disconnect(uctrl);
152 udev_ctrl_unref(uctrl);
153 (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
154 /* We don't return NULL here because uctrl is not freed */
155}
156
157DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
158
159static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
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));
163 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
164 struct msghdr smsg = {
165 .msg_iov = &iov,
166 .msg_iovlen = 1,
167 .msg_control = &control,
168 .msg_controllen = sizeof(control),
169 };
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
179 size = recvmsg_safe(fd, &smsg, 0);
180 if (ERRNO_IS_NEG_TRANSIENT(size))
181 return 0;
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 }
190 if (size < 0)
191 return log_error_errno(size, "Failed to receive ctrl message: %m");
192
193 cmsg_close_all(&smsg);
194
195 if (size != sizeof(msg_wire)) {
196 log_warning("Received message with invalid length, ignoring");
197 return 0;
198 }
199
200 cred = CMSG_FIND_DATA(&smsg, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
201 if (!cred) {
202 log_warning("No sender credentials received, ignoring message");
203 return 0;
204 }
205
206 if (cred->uid != 0) {
207 log_warning("Invalid sender uid "UID_FMT", ignoring message", cred->uid);
208 return 0;
209 }
210
211 if (msg_wire.magic != UDEV_CTRL_MAGIC) {
212 log_warning("Message magic 0x%08x doesn't match, ignoring message", msg_wire.magic);
213 return 0;
214 }
215
216 if (msg_wire.type == _UDEV_CTRL_END_MESSAGES)
217 return 0;
218
219 if (uctrl->callback)
220 (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata);
221
222 /* Do not disconnect and wait for next message. */
223 uctrl = udev_ctrl_unref(uctrl);
224 return 0;
225}
226
227static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
228 UdevCtrl *uctrl = ASSERT_PTR(userdata);
229 _cleanup_close_ int sock = -EBADF;
230 struct ucred ucred;
231 int r;
232
233 sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
234 if (sock < 0) {
235 if (ERRNO_IS_ACCEPT_AGAIN(errno))
236 return 0;
237
238 return log_error_errno(errno, "Failed to accept ctrl connection: %m");
239 }
240
241 /* check peer credential of connection */
242 r = getpeercred(sock, &ucred);
243 if (r < 0) {
244 log_error_errno(r, "Failed to receive credentials of ctrl connection: %m");
245 return 0;
246 }
247
248 if (ucred.uid > 0) {
249 log_error("Invalid sender uid "UID_FMT", closing connection", ucred.uid);
250 return 0;
251 }
252
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");
260
261 /* Do not accept multiple connection. */
262 (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_OFF);
263
264 uctrl->sock_connect = TAKE_FD(sock);
265 return 0;
266}
267
268int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
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}
294
295int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data) {
296 UdevCtrlMessageWire ctrl_msg_wire = {
297 .version = "udev-" PROJECT_VERSION_STR,
298 .magic = UDEV_CTRL_MAGIC,
299 .type = type,
300 };
301
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);
307
308 if (!uctrl->connected) {
309 if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
310 return -errno;
311 uctrl->connected = true;
312 }
313
314 if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0)
315 return -errno;
316
317 return 0;
318}
319
320int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
321 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
322 int r;
323
324 assert(uctrl);
325
326 if (uctrl->sock < 0)
327 return 0;
328 if (!uctrl->connected)
329 return 0;
330
331 r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, NULL);
332 if (r < 0)
333 return r;
334
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
344 r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, NULL, INT_TO_PTR(0));
345 if (r < 0)
346 return r;
347
348 (void) sd_event_source_set_description(source_io, "udev-ctrl-wait-io");
349
350 if (timeout != USEC_INFINITY) {
351 r = sd_event_add_time_relative(
352 uctrl->event, &source_timeout, CLOCK_BOOTTIME,
353 timeout,
354 0, NULL, INT_TO_PTR(-ETIMEDOUT));
355 if (r < 0)
356 return r;
357
358 (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-timeout");
359 }
360
361 return sd_event_loop(uctrl->event);
362}