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 = { .data.ptr = e };
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);
62 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
67 /* Hmm, bloody D-Bus creates multiple watches on the
68 * same fd. epoll() does not like that. As a dirty
69 * hack we simply dup() the fd and hence get a second
70 * one we can safely add to the epoll(). */
76 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
77 close_nointr_nofail(e->fd);
81 e->fd_is_dupped = true;
84 dbus_watch_set_data(watch, e, NULL);
85 e = NULL; /* prevent freeing */
90 static void remove_watch(DBusWatch *watch, void *data) {
91 EpollData _cleanup_free_ *e = NULL;
95 e = dbus_watch_get_data(watch);
99 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
102 close_nointr_nofail(e->fd);
105 static void toggle_watch(DBusWatch *watch, void *data) {
107 struct epoll_event ev = {};
111 e = dbus_watch_get_data(watch);
116 ev.events = bus_flags_to_events(watch);
118 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
121 static int timeout_arm(EpollData *e) {
122 struct itimerspec its = {};
125 assert(e->is_timeout);
127 if (dbus_timeout_get_enabled(e->object)) {
128 timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC);
129 its.it_interval = its.it_value;
132 if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
138 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
140 struct epoll_event ev = {};
144 e = new0(EpollData, 1);
148 e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
153 e->is_timeout = true;
155 if (timeout_arm(e) < 0)
161 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
164 dbus_timeout_set_data(timeout, e, NULL);
170 close_nointr_nofail(e->fd);
176 static void remove_timeout(DBusTimeout *timeout, void *data) {
177 EpollData _cleanup_free_ *e = NULL;
181 e = dbus_timeout_get_data(timeout);
185 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
186 close_nointr_nofail(e->fd);
189 static void toggle_timeout(DBusTimeout *timeout, void *data) {
195 e = dbus_timeout_get_data(timeout);
201 log_error("Failed to rearm timer: %s", strerror(-r));
204 int bus_loop_open(DBusConnection *c) {
209 fd = epoll_create1(EPOLL_CLOEXEC);
213 if (!dbus_connection_set_watch_functions(c, add_watch, remove_watch, toggle_watch, INT_TO_PTR(fd), NULL) ||
214 !dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, toggle_timeout, INT_TO_PTR(fd), NULL)) {
215 close_nointr_nofail(fd);
222 int bus_loop_dispatch(int fd) {
224 struct epoll_event event = {};
229 n = epoll_wait(fd, &event, 1, 0);
231 return errno == EAGAIN || errno == EINTR ? 0 : -errno;
233 assert_se(d = event.data.ptr);
236 DBusTimeout *t = d->object;
238 if (dbus_timeout_get_enabled(t))
239 dbus_timeout_handle(t);
241 DBusWatch *w = d->object;
243 if (dbus_watch_get_enabled(w))
244 dbus_watch_handle(w, bus_events_to_flags(event.events));