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 _cleanup_free_ EpollData *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;
60 ev.events = bus_flags_to_events(watch);
63 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
68 /* Hmm, bloody D-Bus creates multiple watches on the
69 * same fd. epoll() does not like that. As a dirty
70 * hack we simply dup() the fd and hence get a second
71 * one we can safely add to the epoll(). */
77 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
78 close_nointr_nofail(e->fd);
82 e->fd_is_dupped = true;
85 dbus_watch_set_data(watch, e, NULL);
86 e = NULL; /* prevent freeing */
91 static void remove_watch(DBusWatch *watch, void *data) {
92 _cleanup_free_ EpollData *e = NULL;
96 e = dbus_watch_get_data(watch);
100 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
103 close_nointr_nofail(e->fd);
106 static void toggle_watch(DBusWatch *watch, void *data) {
108 struct epoll_event ev = {};
112 e = dbus_watch_get_data(watch);
117 ev.events = bus_flags_to_events(watch);
119 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
122 static int timeout_arm(EpollData *e) {
123 struct itimerspec its = {};
126 assert(e->is_timeout);
128 if (dbus_timeout_get_enabled(e->object)) {
129 timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC);
130 its.it_interval = its.it_value;
133 if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
139 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
141 struct epoll_event ev = {};
145 e = new0(EpollData, 1);
149 e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
154 e->is_timeout = true;
156 if (timeout_arm(e) < 0)
162 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
165 dbus_timeout_set_data(timeout, e, NULL);
171 close_nointr_nofail(e->fd);
177 static void remove_timeout(DBusTimeout *timeout, void *data) {
178 _cleanup_free_ EpollData *e = NULL;
182 e = dbus_timeout_get_data(timeout);
186 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
187 close_nointr_nofail(e->fd);
190 static void toggle_timeout(DBusTimeout *timeout, void *data) {
196 e = dbus_timeout_get_data(timeout);
202 log_error("Failed to rearm timer: %s", strerror(-r));
205 int bus_loop_open(DBusConnection *c) {
210 fd = epoll_create1(EPOLL_CLOEXEC);
214 if (!dbus_connection_set_watch_functions(c, add_watch, remove_watch, toggle_watch, INT_TO_PTR(fd), NULL) ||
215 !dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, toggle_timeout, INT_TO_PTR(fd), NULL)) {
216 close_nointr_nofail(fd);
223 int bus_loop_dispatch(int fd) {
225 struct epoll_event event = {};
230 n = epoll_wait(fd, &event, 1, 0);
232 return errno == EAGAIN || errno == EINTR ? 0 : -errno;
234 assert_se(d = event.data.ptr);
237 DBusTimeout *t = d->object;
239 if (dbus_timeout_get_enabled(t))
240 dbus_timeout_handle(t);
242 DBusWatch *w = d->object;
244 if (dbus_watch_get_enabled(w))
245 dbus_watch_handle(w, bus_events_to_flags(event.events));