]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dbus-loop.c
Modernization
[thirdparty/systemd.git] / src / shared / dbus-loop.c
CommitLineData
20263082
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
20263082
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
20263082 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
20263082
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <stdbool.h>
23#include <assert.h>
24#include <sys/epoll.h>
25#include <string.h>
26#include <errno.h>
27#include <sys/timerfd.h>
28#include <unistd.h>
29
30#include "dbus-loop.h"
31#include "dbus-common.h"
32#include "util.h"
33
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. */
38
39typedef struct EpollData {
40 int fd;
41 void *object;
42 bool is_timeout:1;
43 bool fd_is_dupped:1;
44} EpollData;
45
46static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
e62d8c39 47 EpollData _cleanup_free_ *e = NULL;
20263082
LP
48 struct epoll_event ev;
49
50 assert(watch);
51
52 e = new0(EpollData, 1);
53 if (!e)
54 return FALSE;
55
56 e->fd = dbus_watch_get_unix_fd(watch);
57 e->object = watch;
58 e->is_timeout = false;
59
60 zero(ev);
61 ev.events = bus_flags_to_events(watch);
62 ev.data.ptr = e;
63
64 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
65
e62d8c39 66 if (errno != EEXIST)
20263082 67 return FALSE;
20263082
LP
68
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(). */
73
74 e->fd = dup(e->fd);
e62d8c39 75 if (e->fd < 0)
20263082 76 return FALSE;
20263082
LP
77
78 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
79 close_nointr_nofail(e->fd);
20263082
LP
80 return FALSE;
81 }
82
83 e->fd_is_dupped = true;
84 }
85
86 dbus_watch_set_data(watch, e, NULL);
e62d8c39 87 e = NULL; /* prevent freeing */
20263082
LP
88
89 return TRUE;
90}
91
92static void remove_watch(DBusWatch *watch, void *data) {
e62d8c39 93 EpollData _cleanup_free_ *e = NULL;
20263082
LP
94
95 assert(watch);
96
97 e = dbus_watch_get_data(watch);
98 if (!e)
99 return;
100
101 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
102
103 if (e->fd_is_dupped)
104 close_nointr_nofail(e->fd);
20263082
LP
105}
106
107static void toggle_watch(DBusWatch *watch, void *data) {
108 EpollData *e;
109 struct epoll_event ev;
110
111 assert(watch);
112
113 e = dbus_watch_get_data(watch);
114 if (!e)
115 return;
116
117 zero(ev);
118 ev.events = bus_flags_to_events(watch);
119 ev.data.ptr = e;
120
121 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
122}
123
124static int timeout_arm(EpollData *e) {
125 struct itimerspec its;
126
127 assert(e);
128 assert(e->is_timeout);
129
130 zero(its);
131
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;
135 }
136
137 if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
138 return -errno;
139
140 return 0;
141}
142
143static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
144 EpollData *e;
145 struct epoll_event ev;
146
147 assert(timeout);
148
149 e = new0(EpollData, 1);
150 if (!e)
151 return FALSE;
152
153 e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
154 if (e->fd < 0)
155 goto fail;
156
157 e->object = timeout;
158 e->is_timeout = true;
159
160 if (timeout_arm(e) < 0)
161 goto fail;
162
163 zero(ev);
164 ev.events = EPOLLIN;
165 ev.data.ptr = e;
166
167 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
168 goto fail;
169
170 dbus_timeout_set_data(timeout, e, NULL);
171
172 return TRUE;
173
174fail:
175 if (e->fd >= 0)
176 close_nointr_nofail(e->fd);
177
178 free(e);
179 return FALSE;
180}
181
182static void remove_timeout(DBusTimeout *timeout, void *data) {
e62d8c39 183 EpollData _cleanup_free_ *e = NULL;
20263082
LP
184
185 assert(timeout);
186
187 e = dbus_timeout_get_data(timeout);
188 if (!e)
189 return;
190
191 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
192 close_nointr_nofail(e->fd);
20263082
LP
193}
194
195static void toggle_timeout(DBusTimeout *timeout, void *data) {
196 EpollData *e;
197 int r;
198
199 assert(timeout);
200
201 e = dbus_timeout_get_data(timeout);
202 if (!e)
203 return;
204
205 r = timeout_arm(e);
206 if (r < 0)
207 log_error("Failed to rearm timer: %s", strerror(-r));
208}
209
210int bus_loop_open(DBusConnection *c) {
211 int fd;
212
213 assert(c);
214
215 fd = epoll_create1(EPOLL_CLOEXEC);
216 if (fd < 0)
217 return -errno;
218
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);
222 return -ENOMEM;
223 }
224
225 return fd;
226}
227
228int bus_loop_dispatch(int fd) {
229 int n;
230 struct epoll_event event;
231 EpollData *d;
232
233 assert(fd >= 0);
234
235 zero(event);
236
237 n = epoll_wait(fd, &event, 1, 0);
238 if (n < 0)
239 return errno == EAGAIN || errno == EINTR ? 0 : -errno;
240
241 assert_se(d = event.data.ptr);
242
243 if (d->is_timeout) {
244 DBusTimeout *t = d->object;
245
246 if (dbus_timeout_get_enabled(t))
247 dbus_timeout_handle(t);
248 } else {
249 DBusWatch *w = d->object;
250
251 if (dbus_watch_get_enabled(w))
252 dbus_watch_handle(w, bus_events_to_flags(event.events));
253 }
254
255 return 0;
256}