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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser 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) {
47 EpollData _cleanup_free_ *e = NULL;
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) {
69 /* Hmm, bloody D-Bus creates multiple watches on the
70 * same fd. epoll() does not like that. As a dirty
71 * hack we simply dup() the fd and hence get a second
72 * one we can safely add to the epoll(). */
78 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
79 close_nointr_nofail(e->fd);
83 e->fd_is_dupped = true;
86 dbus_watch_set_data(watch, e, NULL);
87 e = NULL; /* prevent freeing */
92 static void remove_watch(DBusWatch *watch, void *data) {
93 EpollData _cleanup_free_ *e = NULL;
97 e = dbus_watch_get_data(watch);
101 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
104 close_nointr_nofail(e->fd);
107 static void toggle_watch(DBusWatch *watch, void *data) {
109 struct epoll_event ev;
113 e = dbus_watch_get_data(watch);
118 ev.events = bus_flags_to_events(watch);
121 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
124 static int timeout_arm(EpollData *e) {
125 struct itimerspec its;
128 assert(e->is_timeout);
132 if (dbus_timeout_get_enabled(e->object)) {
133 timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC);
134 its.it_interval = its.it_value;
137 if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
143 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
145 struct epoll_event ev;
149 e = new0(EpollData, 1);
153 e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
158 e->is_timeout = true;
160 if (timeout_arm(e) < 0)
167 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
170 dbus_timeout_set_data(timeout, e, NULL);
176 close_nointr_nofail(e->fd);
182 static void remove_timeout(DBusTimeout *timeout, void *data) {
183 EpollData _cleanup_free_ *e = NULL;
187 e = dbus_timeout_get_data(timeout);
191 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
192 close_nointr_nofail(e->fd);
195 static void toggle_timeout(DBusTimeout *timeout, void *data) {
201 e = dbus_timeout_get_data(timeout);
207 log_error("Failed to rearm timer: %s", strerror(-r));
210 int bus_loop_open(DBusConnection *c) {
215 fd = epoll_create1(EPOLL_CLOEXEC);
219 if (!dbus_connection_set_watch_functions(c, add_watch, remove_watch, toggle_watch, INT_TO_PTR(fd), NULL) ||
220 !dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, toggle_timeout, INT_TO_PTR(fd), NULL)) {
221 close_nointr_nofail(fd);
228 int bus_loop_dispatch(int fd) {
230 struct epoll_event event;
237 n = epoll_wait(fd, &event, 1, 0);
239 return errno == EAGAIN || errno == EINTR ? 0 : -errno;
241 assert_se(d = event.data.ptr);
244 DBusTimeout *t = d->object;
246 if (dbus_timeout_get_enabled(t))
247 dbus_timeout_handle(t);
249 DBusWatch *w = d->object;
251 if (dbus_watch_get_enabled(w))
252 dbus_watch_handle(w, bus_events_to_flags(event.events));