1 /* SPDX-License-Identifier: LGPL-2.1+
3 * libudev - interface to udev device information
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
21 #include "alloc-util.h"
22 #include "errno-util.h"
24 #include "format-util.h"
26 #include "socket-util.h"
28 #include "udev-ctrl.h"
31 /* wire protocol magic must match */
32 #define UDEV_CTRL_MAGIC 0xdead1dea
34 struct udev_ctrl_msg_wire
{
37 enum udev_ctrl_msg_type type
;
38 union udev_ctrl_msg_value value
;
45 union sockaddr_union saddr
;
48 bool cleanup_socket
:1;
50 bool maybe_disconnected
:1;
52 sd_event_source
*event_source
;
53 sd_event_source
*event_source_connect
;
54 udev_ctrl_handler_t callback
;
58 int udev_ctrl_new_from_fd(struct udev_ctrl
**ret
, int fd
) {
59 _cleanup_close_
int sock
= -1;
60 struct udev_ctrl
*uctrl
;
65 sock
= socket(AF_LOCAL
, SOCK_SEQPACKET
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
67 return log_error_errno(errno
, "Failed to create socket: %m");
70 uctrl
= new(struct udev_ctrl
, 1);
74 *uctrl
= (struct udev_ctrl
) {
76 .sock
= fd
>= 0 ? fd
: TAKE_FD(sock
),
81 uctrl
->saddr
.un
= (struct sockaddr_un
) {
82 .sun_family
= AF_UNIX
,
83 .sun_path
= "/run/udev/control",
86 uctrl
->addrlen
= SOCKADDR_UN_LEN(uctrl
->saddr
.un
);
88 *ret
= TAKE_PTR(uctrl
);
92 int udev_ctrl_enable_receiving(struct udev_ctrl
*uctrl
) {
100 r
= bind(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
);
101 if (r
< 0 && errno
== EADDRINUSE
) {
102 (void) sockaddr_un_unlink(&uctrl
->saddr
.un
);
103 r
= bind(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
);
107 return log_error_errno(errno
, "Failed to bind udev control socket: %m");
109 if (listen(uctrl
->sock
, 0) < 0)
110 return log_error_errno(errno
, "Failed to listen udev control socket: %m");
113 uctrl
->cleanup_socket
= true;
118 static void udev_ctrl_disconnect(struct udev_ctrl
*uctrl
) {
122 uctrl
->event_source_connect
= sd_event_source_unref(uctrl
->event_source_connect
);
123 uctrl
->sock_connect
= safe_close(uctrl
->sock_connect
);
126 static struct udev_ctrl
*udev_ctrl_free(struct udev_ctrl
*uctrl
) {
129 udev_ctrl_disconnect(uctrl
);
131 sd_event_source_unref(uctrl
->event_source
);
132 safe_close(uctrl
->sock
);
134 sd_event_unref(uctrl
->event
);
138 DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl
, udev_ctrl
, udev_ctrl_free
);
140 int udev_ctrl_cleanup(struct udev_ctrl
*uctrl
) {
143 if (uctrl
->cleanup_socket
)
144 sockaddr_un_unlink(&uctrl
->saddr
.un
);
148 int udev_ctrl_attach_event(struct udev_ctrl
*uctrl
, sd_event
*event
) {
151 assert_return(uctrl
, -EINVAL
);
152 assert_return(!uctrl
->event
, -EBUSY
);
155 uctrl
->event
= sd_event_ref(event
);
157 r
= sd_event_default(&uctrl
->event
);
165 sd_event_source
*udev_ctrl_get_event_source(struct udev_ctrl
*uctrl
) {
168 return uctrl
->event_source
;
171 static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl
*uctrl
) {
172 udev_ctrl_disconnect(uctrl
);
173 udev_ctrl_unref(uctrl
);
174 (void) sd_event_source_set_enabled(uctrl
->event_source
, SD_EVENT_ON
);
177 DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl
*, udev_ctrl_disconnect_and_listen_again
);
179 static int udev_ctrl_connection_event_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
180 _cleanup_(udev_ctrl_disconnect_and_listen_againp
) struct udev_ctrl
*uctrl
= NULL
;
181 struct udev_ctrl_msg_wire msg_wire
;
182 struct iovec iov
= IOVEC_MAKE(&msg_wire
, sizeof(struct udev_ctrl_msg_wire
));
183 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred
))) control
;
184 struct msghdr smsg
= {
187 .msg_control
= &control
,
188 .msg_controllen
= sizeof(control
),
190 struct cmsghdr
*cmsg
;
196 /* When UDEV_CTRL_EXIT is received, manager unref udev_ctrl object.
197 * To avoid the object freed, let's increment the refcount. */
198 uctrl
= udev_ctrl_ref(userdata
);
200 size
= next_datagram_size_fd(fd
);
202 return log_error_errno(size
, "Failed to get size of message: %m");
204 return 0; /* Client disconnects? */
206 size
= recvmsg_safe(fd
, &smsg
, 0);
210 return log_error_errno(size
, "Failed to receive ctrl message: %m");
212 cmsg_close_all(&smsg
);
214 cmsg
= CMSG_FIRSTHDR(&smsg
);
216 if (!cmsg
|| cmsg
->cmsg_type
!= SCM_CREDENTIALS
) {
217 log_error("No sender credentials received, ignoring message");
221 cred
= (struct ucred
*) CMSG_DATA(cmsg
);
223 if (cred
->uid
!= 0) {
224 log_error("Invalid sender uid "UID_FMT
", ignoring message", cred
->uid
);
228 if (msg_wire
.magic
!= UDEV_CTRL_MAGIC
) {
229 log_error("Message magic 0x%08x doesn't match, ignoring message", msg_wire
.magic
);
233 if (msg_wire
.type
== _UDEV_CTRL_END_MESSAGES
)
237 (void) uctrl
->callback(uctrl
, msg_wire
.type
, &msg_wire
.value
, uctrl
->userdata
);
239 /* Do not disconnect and wait for next message. */
240 uctrl
= udev_ctrl_unref(uctrl
);
244 static int udev_ctrl_event_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
245 struct udev_ctrl
*uctrl
= userdata
;
246 _cleanup_close_
int sock
= -1;
252 sock
= accept4(fd
, NULL
, NULL
, SOCK_CLOEXEC
|SOCK_NONBLOCK
);
254 if (ERRNO_IS_ACCEPT_AGAIN(errno
))
257 return log_error_errno(errno
, "Failed to accept ctrl connection: %m");
260 /* check peer credential of connection */
261 r
= getpeercred(sock
, &ucred
);
263 log_error_errno(r
, "Failed to receive credentials of ctrl connection: %m");
268 log_error("Invalid sender uid "UID_FMT
", closing connection", ucred
.uid
);
272 /* enable receiving of the sender credentials in the messages */
273 r
= setsockopt_int(sock
, SOL_SOCKET
, SO_PASSCRED
, true);
275 log_warning_errno(r
, "Failed to set SO_PASSCRED, ignoring: %m");
277 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source_connect
, sock
, EPOLLIN
, udev_ctrl_connection_event_handler
, uctrl
);
279 log_error_errno(r
, "Failed to create event source for udev control connection: %m");
283 (void) sd_event_source_set_description(uctrl
->event_source_connect
, "udev-ctrl-connection");
285 /* Do not accept multiple connection. */
286 (void) sd_event_source_set_enabled(uctrl
->event_source
, SD_EVENT_OFF
);
288 uctrl
->sock_connect
= TAKE_FD(sock
);
292 int udev_ctrl_start(struct udev_ctrl
*uctrl
, udev_ctrl_handler_t callback
, void *userdata
) {
298 r
= udev_ctrl_attach_event(uctrl
, NULL
);
303 r
= udev_ctrl_enable_receiving(uctrl
);
307 uctrl
->callback
= callback
;
308 uctrl
->userdata
= userdata
;
310 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source
, uctrl
->sock
, EPOLLIN
, udev_ctrl_event_handler
, uctrl
);
314 (void) sd_event_source_set_description(uctrl
->event_source
, "udev-ctrl");
319 int udev_ctrl_send(struct udev_ctrl
*uctrl
, enum udev_ctrl_msg_type type
, int intval
, const char *buf
) {
320 struct udev_ctrl_msg_wire ctrl_msg_wire
= {
321 .version
= "udev-" STRINGIFY(PROJECT_VERSION
),
322 .magic
= UDEV_CTRL_MAGIC
,
326 if (uctrl
->maybe_disconnected
)
327 return -ENOANO
; /* to distinguish this from other errors. */
330 strscpy(ctrl_msg_wire
.value
.buf
, sizeof(ctrl_msg_wire
.value
.buf
), buf
);
332 ctrl_msg_wire
.value
.intval
= intval
;
334 if (!uctrl
->connected
) {
335 if (connect(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
) < 0)
337 uctrl
->connected
= true;
340 if (send(uctrl
->sock
, &ctrl_msg_wire
, sizeof(ctrl_msg_wire
), 0) < 0)
343 if (type
== UDEV_CTRL_EXIT
)
344 uctrl
->maybe_disconnected
= true;
349 int udev_ctrl_wait(struct udev_ctrl
*uctrl
, usec_t timeout
) {
350 _cleanup_(sd_event_source_unrefp
) sd_event_source
*source_io
= NULL
, *source_timeout
= NULL
;
357 if (!uctrl
->connected
)
360 if (!uctrl
->maybe_disconnected
) {
361 r
= udev_ctrl_send(uctrl
, _UDEV_CTRL_END_MESSAGES
, 0, NULL
);
370 r
= udev_ctrl_attach_event(uctrl
, NULL
);
375 r
= sd_event_add_io(uctrl
->event
, &source_io
, uctrl
->sock
, EPOLLIN
, NULL
, INT_TO_PTR(0));
379 (void) sd_event_source_set_description(source_io
, "udev-ctrl-wait-io");
381 if (timeout
!= USEC_INFINITY
) {
382 r
= sd_event_add_time_relative(
383 uctrl
->event
, &source_timeout
, clock_boottime_or_monotonic(),
385 0, NULL
, INT_TO_PTR(-ETIMEDOUT
));
389 (void) sd_event_source_set_description(source_timeout
, "udev-ctrl-wait-timeout");
392 return sd_event_loop(uctrl
->event
);