]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dbus-loop.c
bus: support entering containers without specifying the type
[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) {
7fd1b19b 47 _cleanup_free_ EpollData *e = NULL;
2f9f5aa0 48 struct epoll_event ev = {};
20263082
LP
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
20263082 60 ev.events = bus_flags_to_events(watch);
2f9f5aa0 61 ev.data.ptr = e;
20263082
LP
62
63 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
64
e62d8c39 65 if (errno != EEXIST)
20263082 66 return FALSE;
20263082
LP
67
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(). */
72
73 e->fd = dup(e->fd);
e62d8c39 74 if (e->fd < 0)
20263082 75 return FALSE;
20263082
LP
76
77 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
78 close_nointr_nofail(e->fd);
20263082
LP
79 return FALSE;
80 }
81
82 e->fd_is_dupped = true;
83 }
84
85 dbus_watch_set_data(watch, e, NULL);
e62d8c39 86 e = NULL; /* prevent freeing */
20263082
LP
87
88 return TRUE;
89}
90
91static void remove_watch(DBusWatch *watch, void *data) {
7fd1b19b 92 _cleanup_free_ EpollData *e = NULL;
20263082
LP
93
94 assert(watch);
95
96 e = dbus_watch_get_data(watch);
97 if (!e)
98 return;
99
100 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
101
102 if (e->fd_is_dupped)
103 close_nointr_nofail(e->fd);
20263082
LP
104}
105
106static void toggle_watch(DBusWatch *watch, void *data) {
107 EpollData *e;
b92bea5d 108 struct epoll_event ev = {};
20263082
LP
109
110 assert(watch);
111
112 e = dbus_watch_get_data(watch);
113 if (!e)
114 return;
115
20263082 116 ev.data.ptr = e;
b92bea5d 117 ev.events = bus_flags_to_events(watch);
20263082
LP
118
119 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
120}
121
122static int timeout_arm(EpollData *e) {
b92bea5d 123 struct itimerspec its = {};
20263082
LP
124
125 assert(e);
126 assert(e->is_timeout);
127
20263082
LP
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;
131 }
132
133 if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
134 return -errno;
135
136 return 0;
137}
138
139static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
140 EpollData *e;
b92bea5d 141 struct epoll_event ev = {};
20263082
LP
142
143 assert(timeout);
144
145 e = new0(EpollData, 1);
146 if (!e)
147 return FALSE;
148
149 e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
150 if (e->fd < 0)
151 goto fail;
152
153 e->object = timeout;
154 e->is_timeout = true;
155
156 if (timeout_arm(e) < 0)
157 goto fail;
158
20263082
LP
159 ev.events = EPOLLIN;
160 ev.data.ptr = e;
161
162 if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
163 goto fail;
164
165 dbus_timeout_set_data(timeout, e, NULL);
166
167 return TRUE;
168
169fail:
170 if (e->fd >= 0)
171 close_nointr_nofail(e->fd);
172
173 free(e);
174 return FALSE;
175}
176
177static void remove_timeout(DBusTimeout *timeout, void *data) {
7fd1b19b 178 _cleanup_free_ EpollData *e = NULL;
20263082
LP
179
180 assert(timeout);
181
182 e = dbus_timeout_get_data(timeout);
183 if (!e)
184 return;
185
186 assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
187 close_nointr_nofail(e->fd);
20263082
LP
188}
189
190static void toggle_timeout(DBusTimeout *timeout, void *data) {
191 EpollData *e;
192 int r;
193
194 assert(timeout);
195
196 e = dbus_timeout_get_data(timeout);
197 if (!e)
198 return;
199
200 r = timeout_arm(e);
201 if (r < 0)
202 log_error("Failed to rearm timer: %s", strerror(-r));
203}
204
205int bus_loop_open(DBusConnection *c) {
206 int fd;
207
208 assert(c);
209
210 fd = epoll_create1(EPOLL_CLOEXEC);
211 if (fd < 0)
212 return -errno;
213
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);
217 return -ENOMEM;
218 }
219
220 return fd;
221}
222
223int bus_loop_dispatch(int fd) {
224 int n;
b92bea5d 225 struct epoll_event event = {};
20263082
LP
226 EpollData *d;
227
228 assert(fd >= 0);
229
20263082
LP
230 n = epoll_wait(fd, &event, 1, 0);
231 if (n < 0)
232 return errno == EAGAIN || errno == EINTR ? 0 : -errno;
233
234 assert_se(d = event.data.ptr);
235
236 if (d->is_timeout) {
237 DBusTimeout *t = d->object;
238
239 if (dbus_timeout_get_enabled(t))
240 dbus_timeout_handle(t);
241 } else {
242 DBusWatch *w = d->object;
243
244 if (dbus_watch_get_enabled(w))
245 dbus_watch_handle(w, bus_events_to_flags(event.events));
246 }
247
248 return 0;
249}