1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/epoll.h>
27 #include <sys/timerfd.h>
30 #include "dbus-loop.h"
31 #include "dbus-common.h"
34 /* Minimal implementation of the dbus loop which integrates all dbus
35 * events into a single epoll fd which we can triviall integrate with
36 * other loops. Note that this is not used in the main systemd daemon
37 * since we run a more elaborate mainloop there. */
39 typedef struct EpollData
{
46 static dbus_bool_t
add_watch(DBusWatch
*watch
, void *data
) {
48 struct epoll_event ev
;
52 e
= new0(EpollData
, 1);
56 e
->fd
= dbus_watch_get_unix_fd(watch
);
58 e
->is_timeout
= false;
61 ev
.events
= bus_flags_to_events(watch
);
64 if (epoll_ctl(PTR_TO_INT(data
), EPOLL_CTL_ADD
, e
->fd
, &ev
) < 0) {
66 if (errno
!= EEXIST
) {
71 /* Hmm, bloody D-Bus creates multiple watches on the
72 * same fd. epoll() does not like that. As a dirty
73 * hack we simply dup() the fd and hence get a second
74 * one we can safely add to the epoll(). */
82 if (epoll_ctl(PTR_TO_INT(data
), EPOLL_CTL_ADD
, e
->fd
, &ev
) < 0) {
83 close_nointr_nofail(e
->fd
);
88 e
->fd_is_dupped
= true;
91 dbus_watch_set_data(watch
, e
, NULL
);
96 static void remove_watch(DBusWatch
*watch
, void *data
) {
101 e
= dbus_watch_get_data(watch
);
105 assert_se(epoll_ctl(PTR_TO_INT(data
), EPOLL_CTL_DEL
, e
->fd
, NULL
) >= 0);
108 close_nointr_nofail(e
->fd
);
113 static void toggle_watch(DBusWatch
*watch
, void *data
) {
115 struct epoll_event ev
;
119 e
= dbus_watch_get_data(watch
);
124 ev
.events
= bus_flags_to_events(watch
);
127 assert_se(epoll_ctl(PTR_TO_INT(data
), EPOLL_CTL_MOD
, e
->fd
, &ev
) == 0);
130 static int timeout_arm(EpollData
*e
) {
131 struct itimerspec its
;
134 assert(e
->is_timeout
);
138 if (dbus_timeout_get_enabled(e
->object
)) {
139 timespec_store(&its
.it_value
, dbus_timeout_get_interval(e
->object
) * USEC_PER_MSEC
);
140 its
.it_interval
= its
.it_value
;
143 if (timerfd_settime(e
->fd
, 0, &its
, NULL
) < 0)
149 static dbus_bool_t
add_timeout(DBusTimeout
*timeout
, void *data
) {
151 struct epoll_event ev
;
155 e
= new0(EpollData
, 1);
159 e
->fd
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
|TFD_CLOEXEC
);
164 e
->is_timeout
= true;
166 if (timeout_arm(e
) < 0)
173 if (epoll_ctl(PTR_TO_INT(data
), EPOLL_CTL_ADD
, e
->fd
, &ev
) < 0)
176 dbus_timeout_set_data(timeout
, e
, NULL
);
182 close_nointr_nofail(e
->fd
);
188 static void remove_timeout(DBusTimeout
*timeout
, void *data
) {
193 e
= dbus_timeout_get_data(timeout
);
197 assert_se(epoll_ctl(PTR_TO_INT(data
), EPOLL_CTL_DEL
, e
->fd
, NULL
) >= 0);
198 close_nointr_nofail(e
->fd
);
202 static void toggle_timeout(DBusTimeout
*timeout
, void *data
) {
208 e
= dbus_timeout_get_data(timeout
);
214 log_error("Failed to rearm timer: %s", strerror(-r
));
217 int bus_loop_open(DBusConnection
*c
) {
222 fd
= epoll_create1(EPOLL_CLOEXEC
);
226 if (!dbus_connection_set_watch_functions(c
, add_watch
, remove_watch
, toggle_watch
, INT_TO_PTR(fd
), NULL
) ||
227 !dbus_connection_set_timeout_functions(c
, add_timeout
, remove_timeout
, toggle_timeout
, INT_TO_PTR(fd
), NULL
)) {
228 close_nointr_nofail(fd
);
235 int bus_loop_dispatch(int fd
) {
237 struct epoll_event event
;
244 n
= epoll_wait(fd
, &event
, 1, 0);
246 return errno
== EAGAIN
|| errno
== EINTR
? 0 : -errno
;
248 assert_se(d
= event
.data
.ptr
);
251 DBusTimeout
*t
= d
->object
;
253 if (dbus_timeout_get_enabled(t
))
254 dbus_timeout_handle(t
);
256 DBusWatch
*w
= d
->object
;
258 if (dbus_watch_get_enabled(w
))
259 dbus_watch_handle(w
, bus_events_to_flags(event
.events
));