1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "errno-util.h"
12 #include "format-util.h"
13 #include "iovec-util.h"
15 #include "socket-util.h"
17 #include "udev-ctrl.h"
19 /* wire protocol magic must match */
20 #define UDEV_CTRL_MAGIC 0xdead1dea
22 typedef struct UdevCtrlMessageWire
{
25 UdevCtrlMessageType type
;
26 UdevCtrlMessageValue value
;
27 } UdevCtrlMessageWire
;
33 union sockaddr_union saddr
;
38 sd_event_source
*event_source
;
39 sd_event_source
*event_source_connect
;
40 udev_ctrl_handler_t callback
;
44 int udev_ctrl_new_from_fd(UdevCtrl
**ret
, int fd
) {
45 _cleanup_close_
int sock
= -EBADF
;
52 sock
= socket(AF_UNIX
, SOCK_SEQPACKET
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
54 return log_error_errno(errno
, "Failed to create socket: %m");
57 /* enable receiving of the sender credentials in the messages */
58 r
= setsockopt_int(fd
>= 0 ? fd
: sock
, SOL_SOCKET
, SO_PASSCRED
, true);
60 log_warning_errno(r
, "Failed to set SO_PASSCRED, ignoring: %m");
62 r
= setsockopt_int(fd
>= 0 ? fd
: sock
, SOL_SOCKET
, SO_PASSRIGHTS
, false);
64 log_debug_errno(r
, "Failed to turn off SO_PASSRIGHTS, ignoring: %m");
66 uctrl
= new(UdevCtrl
, 1);
72 .sock
= fd
>= 0 ? fd
: TAKE_FD(sock
),
73 .sock_connect
= -EBADF
,
77 uctrl
->saddr
.un
= (struct sockaddr_un
) {
78 .sun_family
= AF_UNIX
,
79 .sun_path
= "/run/udev/control",
82 uctrl
->addrlen
= sockaddr_un_len(&uctrl
->saddr
.un
);
84 *ret
= TAKE_PTR(uctrl
);
88 int udev_ctrl_enable_receiving(UdevCtrl
*uctrl
) {
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");
98 if (listen(uctrl
->sock
, 0) < 0)
99 return log_error_errno(errno
, "Failed to listen udev control socket: %m");
105 static void udev_ctrl_disconnect(UdevCtrl
*uctrl
) {
109 uctrl
->event_source_connect
= sd_event_source_unref(uctrl
->event_source_connect
);
110 uctrl
->sock_connect
= safe_close(uctrl
->sock_connect
);
113 static UdevCtrl
*udev_ctrl_free(UdevCtrl
*uctrl
) {
116 udev_ctrl_disconnect(uctrl
);
118 sd_event_source_unref(uctrl
->event_source
);
119 safe_close(uctrl
->sock
);
121 sd_event_unref(uctrl
->event
);
125 DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl
, udev_ctrl
, udev_ctrl_free
);
127 int udev_ctrl_attach_event(UdevCtrl
*uctrl
, sd_event
*event
) {
130 assert_return(uctrl
, -EINVAL
);
131 assert_return(!uctrl
->event
, -EBUSY
);
134 uctrl
->event
= sd_event_ref(event
);
136 r
= sd_event_default(&uctrl
->event
);
144 sd_event_source
*udev_ctrl_get_event_source(UdevCtrl
*uctrl
) {
147 return uctrl
->event_source
;
150 static 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 */
157 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl
*, udev_ctrl_disconnect_and_listen_again
, NULL
);
159 static 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
= {
167 .msg_control
= &control
,
168 .msg_controllen
= sizeof(control
),
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
);
179 size
= recvmsg_safe(fd
, &smsg
, 0);
180 if (ERRNO_IS_NEG_TRANSIENT(size
))
182 if (size
== -ECHRNG
) {
183 log_warning_errno(size
, "Got message with truncated control data (unexpected fds sent?), ignoring.");
186 if (size
== -EXFULL
) {
187 log_warning_errno(size
, "Got message with truncated payload data, ignoring.");
191 return log_error_errno(size
, "Failed to receive ctrl message: %m");
193 cmsg_close_all(&smsg
);
195 if (size
!= sizeof(msg_wire
)) {
196 log_warning("Received message with invalid length, ignoring");
200 cred
= CMSG_FIND_DATA(&smsg
, SOL_SOCKET
, SCM_CREDENTIALS
, struct ucred
);
202 log_warning("No sender credentials received, ignoring message");
206 if (cred
->uid
!= 0) {
207 log_warning("Invalid sender uid "UID_FMT
", ignoring message", cred
->uid
);
211 if (msg_wire
.magic
!= UDEV_CTRL_MAGIC
) {
212 log_warning("Message magic 0x%08x doesn't match, ignoring message", msg_wire
.magic
);
216 if (msg_wire
.type
== _UDEV_CTRL_END_MESSAGES
)
220 (void) uctrl
->callback(uctrl
, msg_wire
.type
, &msg_wire
.value
, uctrl
->userdata
);
222 /* Do not disconnect and wait for next message. */
223 uctrl
= udev_ctrl_unref(uctrl
);
227 static 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
;
233 sock
= accept4(fd
, NULL
, NULL
, SOCK_CLOEXEC
|SOCK_NONBLOCK
);
235 if (ERRNO_IS_ACCEPT_AGAIN(errno
))
238 return log_error_errno(errno
, "Failed to accept ctrl connection: %m");
241 /* check peer credential of connection */
242 r
= getpeercred(sock
, &ucred
);
244 log_error_errno(r
, "Failed to receive credentials of ctrl connection: %m");
249 log_error("Invalid sender uid "UID_FMT
", closing connection", ucred
.uid
);
253 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source_connect
, sock
, EPOLLIN
, udev_ctrl_connection_event_handler
, uctrl
);
255 log_error_errno(r
, "Failed to create event source for udev control connection: %m");
259 (void) sd_event_source_set_description(uctrl
->event_source_connect
, "udev-ctrl-connection");
261 /* Do not accept multiple connection. */
262 (void) sd_event_source_set_enabled(uctrl
->event_source
, SD_EVENT_OFF
);
264 uctrl
->sock_connect
= TAKE_FD(sock
);
268 int udev_ctrl_start(UdevCtrl
*uctrl
, udev_ctrl_handler_t callback
, void *userdata
) {
274 r
= udev_ctrl_attach_event(uctrl
, NULL
);
279 r
= udev_ctrl_enable_receiving(uctrl
);
283 uctrl
->callback
= callback
;
284 uctrl
->userdata
= userdata
;
286 r
= sd_event_add_io(uctrl
->event
, &uctrl
->event_source
, uctrl
->sock
, EPOLLIN
, udev_ctrl_event_handler
, uctrl
);
290 (void) sd_event_source_set_description(uctrl
->event_source
, "udev-ctrl");
295 int 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
,
302 if (type
== UDEV_CTRL_SET_ENV
) {
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
);
308 if (!uctrl
->connected
) {
309 if (connect(uctrl
->sock
, &uctrl
->saddr
.sa
, uctrl
->addrlen
) < 0)
311 uctrl
->connected
= true;
314 if (send(uctrl
->sock
, &ctrl_msg_wire
, sizeof(ctrl_msg_wire
), 0) < 0)
320 int udev_ctrl_wait(UdevCtrl
*uctrl
, usec_t timeout
) {
321 _cleanup_(sd_event_source_disable_unrefp
) sd_event_source
*source_io
= NULL
, *source_timeout
= NULL
;
328 if (!uctrl
->connected
)
331 r
= udev_ctrl_send(uctrl
, _UDEV_CTRL_END_MESSAGES
, NULL
);
339 r
= udev_ctrl_attach_event(uctrl
, NULL
);
344 r
= sd_event_add_io(uctrl
->event
, &source_io
, uctrl
->sock
, EPOLLIN
, NULL
, INT_TO_PTR(0));
348 (void) sd_event_source_set_description(source_io
, "udev-ctrl-wait-io");
350 if (timeout
!= USEC_INFINITY
) {
351 r
= sd_event_add_time_relative(
352 uctrl
->event
, &source_timeout
, CLOCK_BOOTTIME
,
354 0, NULL
, INT_TO_PTR(-ETIMEDOUT
));
358 (void) sd_event_source_set_description(source_timeout
, "udev-ctrl-wait-timeout");
361 return sd_event_loop(uctrl
->event
);