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"
33 /* Minimal implementation of the dbus loop which integrates all dbus
34 * events into a single epoll fd which we can triviall integrate with
35 * other loops. Note that this is not used in the main systemd daemon
36 * since we run a more elaborate mainloop there. */
38 typedef struct EpollData {
45 static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
47 struct epoll_event ev;
51 e = new0(EpollData, 1);
55 e->fd = dbus_watch_get_unix_fd(watch);
57 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) {
65 if (errno != EEXIST) {
70 /* Hmm, bloody D-Bus creates multiple watches on the
71 * same fd. epoll() does not like that. As a dirty
72 * hack we simply dup() the fd and hence get a second
73 * one we can safely add to the epoll(). */
81 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
82 close_nointr_nofail(e->fd);
87 e->fd_is_dupped = true;
90 dbus_watch_set_data(watch, e, NULL);
95 static void remove_watch(DBusWatch *watch, void *data) {
100 e = dbus_watch_get_data(watch);
104 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
107 close_nointr_nofail(e->fd);
112 static void toggle_watch(DBusWatch *watch, void *data) {
114 struct epoll_event ev;
118 e = dbus_watch_get_data(watch);
123 ev.events = bus_flags_to_events(watch);
126 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
129 static int timeout_arm(EpollData *e) {
130 struct itimerspec its;
133 assert(e->is_timeout);
137 if (dbus_timeout_get_enabled(e->object)) {
138 timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC);
139 its.it_interval = its.it_value;
142 if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
148 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
150 struct epoll_event ev;
154 e = new0(EpollData, 1);
158 e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
163 e->is_timeout = true;
165 if (timeout_arm(e) < 0)
172 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
175 dbus_timeout_set_data(timeout, e, NULL);
181 close_nointr_nofail(e->fd);
187 static void remove_timeout(DBusTimeout *timeout, void *data) {
192 e = dbus_timeout_get_data(timeout);
196 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
197 close_nointr_nofail(e->fd);
201 static void toggle_timeout(DBusTimeout *timeout, void *data) {
207 e = dbus_timeout_get_data(timeout);
213 log_error("Failed to rearm timer: %s", strerror(-r));
216 int bus_loop_open(DBusConnection *c) {
221 fd = epoll_create1(EPOLL_CLOEXEC);
225 if (!dbus_connection_set_watch_functions(c, add_watch, remove_watch, toggle_watch, INT_TO_PTR(fd), NULL) ||
226 !dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, toggle_timeout, INT_TO_PTR(fd), NULL)) {
227 close_nointr_nofail(fd);
234 int bus_loop_dispatch(int fd) {
236 struct epoll_event event;
243 n = epoll_wait(fd, &event, 1, 0);
245 return errno == EAGAIN || errno == EINTR ? 0 : -errno;
247 assert_se(d = event.data.ptr);
250 DBusTimeout *t = d->object;
252 if (dbus_timeout_get_enabled(t))
253 dbus_timeout_handle(t);
255 DBusWatch *w = d->object;
257 if (dbus_watch_get_enabled(w))
258 dbus_watch_handle(w, bus_events_to_flags(event.events));