1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
13 #include "alloc-util.h"
14 #include "errno-util.h"
16 #include "format-util.h"
18 #include "socket-util.h"
20 #include "udev-ctrl.h"
23 /* wire protocol magic must match */
24 #define UDEV_CTRL_MAGIC 0xdead1dea
26 typedef struct UdevCtrlMessageWire
{
29 UdevCtrlMessageType type
;
30 UdevCtrlMessageValue value
;
31 } UdevCtrlMessageWire
;
37 union sockaddr_union saddr
;
41 bool maybe_disconnected
;
43 sd_event_source
*event_source
;
44 sd_event_source
*event_source_connect
;
45 udev_ctrl_handler_t callback
;
49 int udev_ctrl_new_from_fd(UdevCtrl
**ret
, int fd
) {
50 _cleanup_close_
int sock
= -1;
56 sock
= socket(AF_LOCAL
, SOCK_SEQPACKET
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
58 return log_error_errno(errno
, "Failed to create socket: %m");
61 uctrl
= new(UdevCtrl
, 1);
67 .sock
= fd
>= 0 ? fd
: TAKE_FD(sock
),
72 uctrl
->saddr
.un
= (struct sockaddr_un
) {
73 .sun_family
= AF_UNIX
,
74 .sun_path
= "/run/udev/control",
77 uctrl
->addrlen
= SOCKADDR_UN_LEN(uctrl
->saddr
.un
);
79 *ret
= TAKE_PTR(uctrl
);
83 int udev_ctrl_enable_receiving(UdevCtrl
*uctrl
) {
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");
93 if (listen(uctrl
->sock
, 0) < 0)
94 return log_error_errno(errno
, "Failed to listen udev control socket: %m");
100 static void udev_ctrl_disconnect(UdevCtrl
*uctrl
) {
104 uctrl
->event_source_connect
= sd_event_source_disable_unref(uctrl
->event_source_connect
);
105 uctrl
->sock_connect
= safe_close(uctrl
->sock_connect
);
108 static UdevCtrl
*udev_ctrl_free(UdevCtrl
*uctrl
) {
111 udev_ctrl_disconnect(uctrl
);
113 sd_event_source_disable_unref(uctrl
->event_source
);
114 safe_close(uctrl
->sock
);
116 sd_event_unref(uctrl
->event
);
120 DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl
, udev_ctrl
, udev_ctrl_free
);
122 int udev_ctrl_attach_event(UdevCtrl
*uctrl
, sd_event
*event
) {
125 assert_return(uctrl
, -EINVAL
);
126 assert_return(!uctrl
->event
, -EBUSY
);
129 uctrl
->event
= sd_event_ref(event
);
131 r
= sd_event_default(&uctrl
->event
);
139 sd_event_source
*udev_ctrl_get_event_source(UdevCtrl
*uctrl
) {
142 return uctrl
->event_source
;
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 */
152 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl
*, udev_ctrl_disconnect_and_listen_again
, NULL
);
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
= {
162 .msg_control
= &control
,
163 .msg_controllen
= sizeof(control
),
165 struct cmsghdr
*cmsg
;
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
);
175 size
= next_datagram_size_fd(fd
);
177 return log_error_errno(size
, "Failed to get size of message: %m");
179 return 0; /* Client disconnects? */
181 size
= recvmsg_safe(fd
, &smsg
, 0);
185 return log_error_errno(size
, "Failed to receive ctrl message: %m");
187 cmsg_close_all(&smsg
);
189 cmsg
= CMSG_FIRSTHDR(&smsg
);
191 if (!cmsg
|| cmsg
->cmsg_type
!= SCM_CREDENTIALS
) {
192 log_error("No sender credentials received, ignoring message");
196 cred
= (struct ucred
*) CMSG_DATA(cmsg
);
198 if (cred
->uid
!= 0) {
199 log_error("Invalid sender uid "UID_FMT
", ignoring message", cred
->uid
);
203 if (msg_wire
.magic
!= UDEV_CTRL_MAGIC
) {
204 log_error("Message magic 0x%08x doesn't match, ignoring message", msg_wire
.magic
);
208 if (msg_wire
.type
== _UDEV_CTRL_END_MESSAGES
)
212 (void) uctrl
->callback(uctrl
, msg_wire
.type
, &msg_wire
.value
, uctrl
->userdata
);
214 /* Do not disconnect and wait for next message. */
215 uctrl
= udev_ctrl_unref(uctrl
);
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;
227 sock
= accept4(fd
, NULL
, NULL
, SOCK_CLOEXEC
|SOCK_NONBLOCK
);
229 if (ERRNO_IS_ACCEPT_AGAIN(errno
))
232 return log_error_errno(errno
, "Failed to accept ctrl connection: %m");
235 /* check peer credential of connection */
236 r
= getpeercred(sock
, &ucred
);
238 log_error_errno(r
, "Failed to receive credentials of ctrl connection: %m");
243 log_error("Invalid sender uid "UID_FMT
", closing connection", ucred
.uid
);
247 /* enable receiving of the sender credentials in the messages */
248 r
= setsockopt_int(sock
, SOL_SOCKET
, SO_PASSCRED
, true);
250 log_warning_errno(r
, "Failed to set SO_PASSCRED, ignoring: %m");
252 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source_connect
, sock
, EPOLLIN
, udev_ctrl_connection_event_handler
, uctrl
);
254 log_error_errno(r
, "Failed to create event source for udev control connection: %m");
258 (void) sd_event_source_set_description(uctrl
->event_source_connect
, "udev-ctrl-connection");
260 /* Do not accept multiple connection. */
261 (void) sd_event_source_set_enabled(uctrl
->event_source
, SD_EVENT_OFF
);
263 uctrl
->sock_connect
= TAKE_FD(sock
);
267 int udev_ctrl_start(UdevCtrl
*uctrl
, udev_ctrl_handler_t callback
, void *userdata
) {
273 r
= udev_ctrl_attach_event(uctrl
, NULL
);
278 r
= udev_ctrl_enable_receiving(uctrl
);
282 uctrl
->callback
= callback
;
283 uctrl
->userdata
= userdata
;
285 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source
, uctrl
->sock
, EPOLLIN
, udev_ctrl_event_handler
, uctrl
);
289 (void) sd_event_source_set_description(uctrl
->event_source
, "udev-ctrl");
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
,
301 if (uctrl
->maybe_disconnected
)
302 return -ENOANO
; /* to distinguish this from other errors. */
304 if (type
== UDEV_CTRL_SET_ENV
) {
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
);
310 if (!uctrl
->connected
) {
311 if (connect(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
) < 0)
313 uctrl
->connected
= true;
316 if (send(uctrl
->sock
, &ctrl_msg_wire
, sizeof(ctrl_msg_wire
), 0) < 0)
319 if (type
== UDEV_CTRL_EXIT
)
320 uctrl
->maybe_disconnected
= true;
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
;
333 if (!uctrl
->connected
)
336 if (!uctrl
->maybe_disconnected
) {
337 r
= udev_ctrl_send(uctrl
, _UDEV_CTRL_END_MESSAGES
, NULL
);
346 r
= udev_ctrl_attach_event(uctrl
, NULL
);
351 r
= sd_event_add_io(uctrl
->event
, &source_io
, uctrl
->sock
, EPOLLIN
, NULL
, INT_TO_PTR(0));
355 (void) sd_event_source_set_description(source_io
, "udev-ctrl-wait-io");
357 if (timeout
!= USEC_INFINITY
) {
358 r
= sd_event_add_time_relative(
359 uctrl
->event
, &source_timeout
, clock_boottime_or_monotonic(),
361 0, NULL
, INT_TO_PTR(-ETIMEDOUT
));
365 (void) sd_event_source_set_description(source_timeout
, "udev-ctrl-wait-timeout");
368 return sd_event_loop(uctrl
->event
);