]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-ctrl.c
udev: use sd_event_source_disable_unref()
[thirdparty/systemd.git] / src / udev / udev-ctrl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <poll.h>
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/un.h>
9 #include <unistd.h>
10
11 #include "sd-event.h"
12
13 #include "alloc-util.h"
14 #include "errno-util.h"
15 #include "fd-util.h"
16 #include "format-util.h"
17 #include "io-util.h"
18 #include "socket-util.h"
19 #include "strxcpyx.h"
20 #include "udev-ctrl.h"
21 #include "util.h"
22
23 /* wire protocol magic must match */
24 #define UDEV_CTRL_MAGIC 0xdead1dea
25
26 typedef struct UdevCtrlMessageWire {
27 char version[16];
28 unsigned magic;
29 UdevCtrlMessageType type;
30 UdevCtrlMessageValue value;
31 } UdevCtrlMessageWire;
32
33 struct UdevCtrl {
34 unsigned n_ref;
35 int sock;
36 int sock_connect;
37 union sockaddr_union saddr;
38 socklen_t addrlen;
39 bool bound;
40 bool connected;
41 bool maybe_disconnected;
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;
47 };
48
49 int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
50 _cleanup_close_ int sock = -1;
51 UdevCtrl *uctrl;
52
53 assert(ret);
54
55 if (fd < 0) {
56 sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
57 if (sock < 0)
58 return log_error_errno(errno, "Failed to create socket: %m");
59 }
60
61 uctrl = new(UdevCtrl, 1);
62 if (!uctrl)
63 return -ENOMEM;
64
65 *uctrl = (UdevCtrl) {
66 .n_ref = 1,
67 .sock = fd >= 0 ? fd : TAKE_FD(sock),
68 .sock_connect = -1,
69 .bound = fd >= 0,
70 };
71
72 uctrl->saddr.un = (struct sockaddr_un) {
73 .sun_family = AF_UNIX,
74 .sun_path = "/run/udev/control",
75 };
76
77 uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un);
78
79 *ret = TAKE_PTR(uctrl);
80 return 0;
81 }
82
83 int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
84 assert(uctrl);
85
86 if (uctrl->bound)
87 return 0;
88
89 (void) sockaddr_un_unlink(&uctrl->saddr.un);
90 if (bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
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;
97 return 0;
98 }
99
100 static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
101 if (!uctrl)
102 return;
103
104 uctrl->event_source_connect = sd_event_source_disable_unref(uctrl->event_source_connect);
105 uctrl->sock_connect = safe_close(uctrl->sock_connect);
106 }
107
108 static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
109 assert(uctrl);
110
111 udev_ctrl_disconnect(uctrl);
112
113 sd_event_source_disable_unref(uctrl->event_source);
114 safe_close(uctrl->sock);
115
116 sd_event_unref(uctrl->event);
117 return mfree(uctrl);
118 }
119
120 DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
121
122 int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
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;
137 }
138
139 sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
140 assert(uctrl);
141
142 return uctrl->event_source;
143 }
144
145 static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
146 udev_ctrl_disconnect(uctrl);
147 udev_ctrl_unref(uctrl);
148 (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
149 /* We don't return NULL here because uctrl is not freed */
150 }
151
152 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
153
154 static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
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));
158 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
159 struct msghdr smsg = {
160 .msg_iov = &iov,
161 .msg_iovlen = 1,
162 .msg_control = &control,
163 .msg_controllen = sizeof(control),
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
181 size = recvmsg_safe(fd, &smsg, 0);
182 if (size == -EINTR)
183 return 0;
184 if (size < 0)
185 return log_error_errno(size, "Failed to receive ctrl message: %m");
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
208 if (msg_wire.type == _UDEV_CTRL_END_MESSAGES)
209 return 0;
210
211 if (uctrl->callback)
212 (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata);
213
214 /* Do not disconnect and wait for next message. */
215 uctrl = udev_ctrl_unref(uctrl);
216 return 0;
217 }
218
219 static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
220 UdevCtrl *uctrl = userdata;
221 _cleanup_close_ int sock = -1;
222 struct ucred ucred;
223 int r;
224
225 assert(uctrl);
226
227 sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
228 if (sock < 0) {
229 if (ERRNO_IS_ACCEPT_AGAIN(errno))
230 return 0;
231
232 return log_error_errno(errno, "Failed to accept ctrl connection: %m");
233 }
234
235 /* check peer credential of connection */
236 r = getpeercred(sock, &ucred);
237 if (r < 0) {
238 log_error_errno(r, "Failed to receive credentials of ctrl connection: %m");
239 return 0;
240 }
241
242 if (ucred.uid > 0) {
243 log_error("Invalid sender uid "UID_FMT", closing connection", ucred.uid);
244 return 0;
245 }
246
247 /* enable receiving of the sender credentials in the messages */
248 r = setsockopt_int(sock, SOL_SOCKET, SO_PASSCRED, true);
249 if (r < 0)
250 log_warning_errno(r, "Failed to set SO_PASSCRED, ignoring: %m");
251
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");
259
260 /* Do not accept multiple connection. */
261 (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_OFF);
262
263 uctrl->sock_connect = TAKE_FD(sock);
264 return 0;
265 }
266
267 int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
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 }
293
294 int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data) {
295 UdevCtrlMessageWire ctrl_msg_wire = {
296 .version = "udev-" STRINGIFY(PROJECT_VERSION),
297 .magic = UDEV_CTRL_MAGIC,
298 .type = type,
299 };
300
301 if (uctrl->maybe_disconnected)
302 return -ENOANO; /* to distinguish this from other errors. */
303
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);
309
310 if (!uctrl->connected) {
311 if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
312 return -errno;
313 uctrl->connected = true;
314 }
315
316 if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0)
317 return -errno;
318
319 if (type == UDEV_CTRL_EXIT)
320 uctrl->maybe_disconnected = true;
321
322 return 0;
323 }
324
325 int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
326 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
327 int r;
328
329 assert(uctrl);
330
331 if (uctrl->sock < 0)
332 return 0;
333 if (!uctrl->connected)
334 return 0;
335
336 if (!uctrl->maybe_disconnected) {
337 r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, NULL);
338 if (r < 0)
339 return r;
340 }
341
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
351 r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, NULL, INT_TO_PTR(0));
352 if (r < 0)
353 return r;
354
355 (void) sd_event_source_set_description(source_io, "udev-ctrl-wait-io");
356
357 if (timeout != USEC_INFINITY) {
358 r = sd_event_add_time_relative(
359 uctrl->event, &source_timeout, clock_boottime_or_monotonic(),
360 timeout,
361 0, NULL, INT_TO_PTR(-ETIMEDOUT));
362 if (r < 0)
363 return r;
364
365 (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-timeout");
366 }
367
368 return sd_event_loop(uctrl->event);
369 }