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) {
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));