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.
16 #include <sys/socket.h>
22 #include "alloc-util.h"
23 #include "errno-util.h"
25 #include "format-util.h"
27 #include "socket-util.h"
29 #include "udev-ctrl.h"
32 /* wire protocol magic must match */
33 #define UDEV_CTRL_MAGIC 0xdead1dea
35 struct udev_ctrl_msg_wire
{
38 enum udev_ctrl_msg_type type
;
39 union udev_ctrl_msg_value value
;
46 union sockaddr_union saddr
;
49 bool cleanup_socket
:1;
51 bool maybe_disconnected
:1;
53 sd_event_source
*event_source
;
54 sd_event_source
*event_source_connect
;
55 udev_ctrl_handler_t callback
;
59 int udev_ctrl_new_from_fd(struct udev_ctrl
**ret
, int fd
) {
60 _cleanup_close_
int sock
= -1;
61 struct udev_ctrl
*uctrl
;
67 sock
= socket(AF_LOCAL
, SOCK_SEQPACKET
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
69 return log_error_errno(errno
, "Failed to create socket: %m");
72 uctrl
= new(struct udev_ctrl
, 1);
76 *uctrl
= (struct udev_ctrl
) {
78 .sock
= fd
>= 0 ? fd
: TAKE_FD(sock
),
84 * FIXME: remove it as soon as we can depend on this:
85 * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949
87 r
= setsockopt_int(uctrl
->sock
, SOL_SOCKET
, SO_PASSCRED
, true);
89 log_warning_errno(r
, "Failed to set SO_PASSCRED: %m");
91 uctrl
->saddr
.un
= (struct sockaddr_un
) {
92 .sun_family
= AF_UNIX
,
93 .sun_path
= "/run/udev/control",
96 uctrl
->addrlen
= SOCKADDR_UN_LEN(uctrl
->saddr
.un
);
98 *ret
= TAKE_PTR(uctrl
);
102 int udev_ctrl_enable_receiving(struct udev_ctrl
*uctrl
) {
110 r
= bind(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
);
111 if (r
< 0 && errno
== EADDRINUSE
) {
112 (void) sockaddr_un_unlink(&uctrl
->saddr
.un
);
113 r
= bind(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
);
117 return log_error_errno(errno
, "Failed to bind udev control socket: %m");
119 if (listen(uctrl
->sock
, 0) < 0)
120 return log_error_errno(errno
, "Failed to listen udev control socket: %m");
123 uctrl
->cleanup_socket
= true;
128 static void udev_ctrl_disconnect(struct udev_ctrl
*uctrl
) {
132 uctrl
->event_source_connect
= sd_event_source_unref(uctrl
->event_source_connect
);
133 uctrl
->sock_connect
= safe_close(uctrl
->sock_connect
);
136 static struct udev_ctrl
*udev_ctrl_free(struct udev_ctrl
*uctrl
) {
139 udev_ctrl_disconnect(uctrl
);
141 sd_event_source_unref(uctrl
->event_source
);
142 safe_close(uctrl
->sock
);
144 sd_event_unref(uctrl
->event
);
148 DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl
, udev_ctrl
, udev_ctrl_free
);
150 int udev_ctrl_cleanup(struct udev_ctrl
*uctrl
) {
153 if (uctrl
->cleanup_socket
)
154 sockaddr_un_unlink(&uctrl
->saddr
.un
);
158 int udev_ctrl_attach_event(struct udev_ctrl
*uctrl
, sd_event
*event
) {
161 assert_return(uctrl
, -EINVAL
);
162 assert_return(!uctrl
->event
, -EBUSY
);
165 uctrl
->event
= sd_event_ref(event
);
167 r
= sd_event_default(&uctrl
->event
);
175 sd_event_source
*udev_ctrl_get_event_source(struct udev_ctrl
*uctrl
) {
178 return uctrl
->event_source
;
181 static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl
*uctrl
) {
182 udev_ctrl_disconnect(uctrl
);
183 udev_ctrl_unref(uctrl
);
184 (void) sd_event_source_set_enabled(uctrl
->event_source
, SD_EVENT_ON
);
187 DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl
*, udev_ctrl_disconnect_and_listen_again
);
189 static int udev_ctrl_connection_event_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
190 _cleanup_(udev_ctrl_disconnect_and_listen_againp
) struct udev_ctrl
*uctrl
= NULL
;
191 struct udev_ctrl_msg_wire msg_wire
;
192 struct iovec iov
= IOVEC_MAKE(&msg_wire
, sizeof(struct udev_ctrl_msg_wire
));
193 char cred_msg
[CMSG_SPACE(sizeof(struct ucred
))];
194 struct msghdr smsg
= {
197 .msg_control
= cred_msg
,
198 .msg_controllen
= sizeof(cred_msg
),
200 struct cmsghdr
*cmsg
;
206 /* When UDEV_CTRL_EXIT is received, manager unref udev_ctrl object.
207 * To avoid the object freed, let's increment the refcount. */
208 uctrl
= udev_ctrl_ref(userdata
);
210 size
= next_datagram_size_fd(fd
);
212 return log_error_errno(size
, "Failed to get size of message: %m");
214 return 0; /* Client disconnects? */
216 size
= recvmsg(fd
, &smsg
, 0);
219 return log_error_errno(errno
, "Failed to receive ctrl message: %m");
224 cmsg_close_all(&smsg
);
226 cmsg
= CMSG_FIRSTHDR(&smsg
);
228 if (!cmsg
|| cmsg
->cmsg_type
!= SCM_CREDENTIALS
) {
229 log_error("No sender credentials received, ignoring message");
233 cred
= (struct ucred
*) CMSG_DATA(cmsg
);
235 if (cred
->uid
!= 0) {
236 log_error("Invalid sender uid "UID_FMT
", ignoring message", cred
->uid
);
240 if (msg_wire
.magic
!= UDEV_CTRL_MAGIC
) {
241 log_error("Message magic 0x%08x doesn't match, ignoring message", msg_wire
.magic
);
245 if (msg_wire
.type
== _UDEV_CTRL_END_MESSAGES
)
249 (void) uctrl
->callback(uctrl
, msg_wire
.type
, &msg_wire
.value
, uctrl
->userdata
);
251 /* Do not disconnect and wait for next message. */
252 uctrl
= udev_ctrl_unref(uctrl
);
256 static int udev_ctrl_event_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
257 struct udev_ctrl
*uctrl
= userdata
;
258 _cleanup_close_
int sock
= -1;
264 sock
= accept4(fd
, NULL
, NULL
, SOCK_CLOEXEC
|SOCK_NONBLOCK
);
266 if (ERRNO_IS_ACCEPT_AGAIN(errno
))
269 return log_error_errno(errno
, "Failed to accept ctrl connection: %m");
272 /* check peer credential of connection */
273 r
= getpeercred(sock
, &ucred
);
275 log_error_errno(r
, "Failed to receive credentials of ctrl connection: %m");
280 log_error("Invalid sender uid "UID_FMT
", closing connection", ucred
.uid
);
284 /* enable receiving of the sender credentials in the messages */
285 r
= setsockopt_int(sock
, SOL_SOCKET
, SO_PASSCRED
, true);
287 log_warning_errno(r
, "Failed to set SO_PASSCRED, ignoring: %m");
289 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source_connect
, sock
, EPOLLIN
, udev_ctrl_connection_event_handler
, uctrl
);
291 log_error_errno(r
, "Failed to create event source for udev control connection: %m");
295 (void) sd_event_source_set_description(uctrl
->event_source_connect
, "udev-ctrl-connection");
297 /* Do not accept multiple connection. */
298 (void) sd_event_source_set_enabled(uctrl
->event_source
, SD_EVENT_OFF
);
300 uctrl
->sock_connect
= TAKE_FD(sock
);
304 int udev_ctrl_start(struct udev_ctrl
*uctrl
, udev_ctrl_handler_t callback
, void *userdata
) {
310 r
= udev_ctrl_attach_event(uctrl
, NULL
);
315 r
= udev_ctrl_enable_receiving(uctrl
);
319 uctrl
->callback
= callback
;
320 uctrl
->userdata
= userdata
;
322 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source
, uctrl
->sock
, EPOLLIN
, udev_ctrl_event_handler
, uctrl
);
326 (void) sd_event_source_set_description(uctrl
->event_source
, "udev-ctrl");
331 int udev_ctrl_send(struct udev_ctrl
*uctrl
, enum udev_ctrl_msg_type type
, int intval
, const char *buf
) {
332 struct udev_ctrl_msg_wire ctrl_msg_wire
= {
333 .version
= "udev-" STRINGIFY(PROJECT_VERSION
),
334 .magic
= UDEV_CTRL_MAGIC
,
338 if (uctrl
->maybe_disconnected
)
339 return -ENOANO
; /* to distinguish this from other errors. */
342 strscpy(ctrl_msg_wire
.value
.buf
, sizeof(ctrl_msg_wire
.value
.buf
), buf
);
344 ctrl_msg_wire
.value
.intval
= intval
;
346 if (!uctrl
->connected
) {
347 if (connect(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
) < 0)
349 uctrl
->connected
= true;
352 if (send(uctrl
->sock
, &ctrl_msg_wire
, sizeof(ctrl_msg_wire
), 0) < 0)
355 if (type
== UDEV_CTRL_EXIT
)
356 uctrl
->maybe_disconnected
= true;
361 static int udev_ctrl_wait_io_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
362 return sd_event_exit(sd_event_source_get_event(s
), 0);
365 int udev_ctrl_wait(struct udev_ctrl
*uctrl
, usec_t timeout
) {
366 _cleanup_(sd_event_source_unrefp
) sd_event_source
*source_io
= NULL
, *source_timeout
= NULL
;
373 if (!uctrl
->connected
)
376 if (!uctrl
->maybe_disconnected
) {
377 r
= udev_ctrl_send(uctrl
, _UDEV_CTRL_END_MESSAGES
, 0, NULL
);
386 r
= udev_ctrl_attach_event(uctrl
, NULL
);
391 r
= sd_event_add_io(uctrl
->event
, &source_io
, uctrl
->sock
, EPOLLIN
, udev_ctrl_wait_io_handler
, NULL
);
395 (void) sd_event_source_set_description(source_io
, "udev-ctrl-wait-io");
397 if (timeout
!= USEC_INFINITY
) {
398 r
= sd_event_add_time(uctrl
->event
, &source_timeout
, clock_boottime_or_monotonic(),
399 usec_add(now(clock_boottime_or_monotonic()), timeout
),
400 0, NULL
, INT_TO_PTR(-ETIMEDOUT
));
404 (void) sd_event_source_set_description(source_timeout
, "udev-ctrl-wait-timeout");
407 return sd_event_loop(uctrl
->event
);