Line data Source code
1 : /*
2 : Authors:
3 : Simo Sorce <ssorce@redhat.com>
4 : Stephen Gallagher <sgallagh@redhat.com>
5 :
6 : Copyright (C) 2009 Red Hat
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <sys/time.h>
23 : #include <tevent.h>
24 : #include <dbus/dbus.h>
25 :
26 : #include "util/util.h"
27 : #include "sbus/sssd_dbus.h"
28 : #include "sbus/sssd_dbus_private.h"
29 :
30 : /* =Watches=============================================================== */
31 :
32 : /* DBUS may ask us to add a watch to a file descriptor that already had a watch
33 : * associated. Need to check if that's the case */
34 0 : static struct sbus_watch_ctx *fd_to_watch(struct sbus_watch_ctx *list, int fd)
35 : {
36 : struct sbus_watch_ctx *watch_iter;
37 :
38 0 : watch_iter = list;
39 0 : while (watch_iter != NULL) {
40 0 : if (watch_iter->fd == fd) {
41 0 : return watch_iter;
42 : }
43 :
44 0 : watch_iter = watch_iter->next;
45 : }
46 :
47 0 : return NULL;
48 : }
49 :
50 0 : static int watch_destructor(void *mem)
51 : {
52 : struct sbus_watch_ctx *watch;
53 :
54 0 : watch = talloc_get_type(mem, struct sbus_watch_ctx);
55 0 : DLIST_REMOVE(watch->conn->watch_list, watch);
56 :
57 0 : return 0;
58 : }
59 :
60 : /*
61 : * watch_handler
62 : * Callback for D-BUS to handle messages on a file-descriptor
63 : */
64 0 : static void sbus_watch_handler(struct tevent_context *ev,
65 : struct tevent_fd *fde,
66 : uint16_t flags, void *data)
67 : {
68 0 : struct sbus_watch_ctx *watch = talloc_get_type(data,
69 : struct sbus_watch_ctx);
70 : enum dbus_conn_type type;
71 : union dbus_conn_pointer dbus_p;
72 :
73 : /* conn may get freed inside a handle, save the data we need for later */
74 0 : type = watch->conn->type;
75 0 : dbus_p = watch->conn->dbus;
76 :
77 : /* Take a reference while handling watch */
78 0 : if (type == SBUS_SERVER) {
79 0 : dbus_server_ref(dbus_p.server);
80 : } else {
81 0 : dbus_connection_ref(dbus_p.conn);
82 : }
83 :
84 : /* Fire if readable */
85 0 : if (flags & TEVENT_FD_READ) {
86 0 : if (watch->dbus_read_watch) {
87 0 : dbus_watch_handle(watch->dbus_read_watch, DBUS_WATCH_READABLE);
88 : }
89 : }
90 :
91 : /* Fire if writeable */
92 0 : if (flags & TEVENT_FD_WRITE) {
93 0 : if (watch->dbus_write_watch) {
94 0 : dbus_watch_handle(watch->dbus_write_watch, DBUS_WATCH_WRITABLE);
95 : }
96 : }
97 :
98 : /* Release reference once done */
99 0 : if (type == SBUS_SERVER) {
100 0 : dbus_server_unref(dbus_p.server);
101 : } else {
102 0 : dbus_connection_unref(dbus_p.conn);
103 : }
104 0 : }
105 :
106 : /*
107 : * add_watch
108 : * Set up hooks into the libevents mainloop for
109 : * D-BUS to add file descriptor-based events
110 : */
111 0 : dbus_bool_t sbus_add_watch(DBusWatch *dbus_watch, void *data)
112 : {
113 : unsigned int flags;
114 : uint16_t event_flags;
115 : struct sbus_connection *conn;
116 : struct sbus_watch_ctx *watch;
117 : dbus_bool_t enabled;
118 : int fd;
119 :
120 0 : conn = talloc_get_type(data, struct sbus_connection);
121 :
122 : #ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
123 0 : fd = dbus_watch_get_unix_fd(dbus_watch);
124 : #else
125 : fd = dbus_watch_get_fd(dbus_watch);
126 : #endif
127 :
128 0 : watch = fd_to_watch(conn->watch_list, fd);
129 0 : if (!watch) {
130 : /* does not exist, allocate new one */
131 0 : watch = talloc_zero(conn, struct sbus_watch_ctx);
132 0 : if (!watch) {
133 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Out of Memory!\n");
134 0 : return FALSE;
135 : }
136 0 : watch->conn = conn;
137 0 : watch->fd = fd;
138 : }
139 :
140 0 : enabled = dbus_watch_get_enabled(dbus_watch);
141 0 : flags = dbus_watch_get_flags(dbus_watch);
142 :
143 : /* Save the event to the watch object so it can be found later */
144 0 : if (flags & DBUS_WATCH_READABLE) {
145 0 : watch->dbus_read_watch = dbus_watch;
146 : }
147 0 : if (flags & DBUS_WATCH_WRITABLE) {
148 0 : watch->dbus_write_watch = dbus_watch;
149 : }
150 0 : dbus_watch_set_data(dbus_watch, watch, NULL);
151 :
152 0 : if (watch->fde) {
153 : /* pre-existing event, just toggle flags */
154 0 : sbus_toggle_watch(dbus_watch, data);
155 0 : return TRUE;
156 : }
157 :
158 0 : event_flags = 0;
159 0 : if (enabled) {
160 0 : if (flags & DBUS_WATCH_READABLE) {
161 0 : event_flags |= TEVENT_FD_READ;
162 : }
163 0 : if (flags & DBUS_WATCH_WRITABLE) {
164 0 : event_flags |= TEVENT_FD_WRITE;
165 : }
166 : }
167 :
168 : /* Add the file descriptor to the event loop */
169 0 : watch->fde = tevent_add_fd(conn->ev,
170 : watch, fd, event_flags,
171 : sbus_watch_handler, watch);
172 0 : if (!watch->fde) {
173 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set up fd event!\n");
174 0 : talloc_zfree(watch);
175 0 : return FALSE;
176 : }
177 :
178 0 : DLIST_ADD(conn->watch_list, watch);
179 0 : talloc_set_destructor((TALLOC_CTX *)watch, watch_destructor);
180 :
181 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "%p/%p (%d), %s/%s (%s)\n",
182 : watch, dbus_watch, fd,
183 : ((flags & DBUS_WATCH_READABLE)?"R":"-"),
184 : ((flags & DBUS_WATCH_WRITABLE)?"W":"-"),
185 : enabled?"enabled":"disabled");
186 :
187 0 : return TRUE;
188 : }
189 :
190 : /*
191 : * toggle_watch
192 : * Hook for D-BUS to toggle the enabled/disabled state of
193 : * an event in the mainloop
194 : */
195 0 : void sbus_toggle_watch(DBusWatch *dbus_watch, void *data)
196 : {
197 : struct sbus_watch_ctx *watch;
198 : unsigned int flags;
199 : dbus_bool_t enabled;
200 : void *watch_data;
201 0 : int fd = -1;
202 :
203 0 : enabled = dbus_watch_get_enabled(dbus_watch);
204 0 : flags = dbus_watch_get_flags(dbus_watch);
205 :
206 0 : watch_data = dbus_watch_get_data(dbus_watch);
207 0 : watch = talloc_get_type(watch_data, struct sbus_watch_ctx);
208 0 : if (!watch) {
209 0 : DEBUG(SSSDBG_OP_FAILURE,
210 : "[%p] does not carry watch context?!\n", dbus_watch);
211 : /* abort ? */
212 0 : return;
213 : }
214 :
215 0 : if (enabled) {
216 0 : if (flags & DBUS_WATCH_READABLE) {
217 0 : TEVENT_FD_READABLE(watch->fde);
218 : }
219 0 : if (flags & DBUS_WATCH_WRITABLE) {
220 0 : TEVENT_FD_WRITEABLE(watch->fde);
221 : }
222 : } else {
223 0 : if (flags & DBUS_WATCH_READABLE) {
224 0 : TEVENT_FD_NOT_READABLE(watch->fde);
225 : }
226 0 : if (flags & DBUS_WATCH_WRITABLE) {
227 0 : TEVENT_FD_NOT_WRITEABLE(watch->fde);
228 : }
229 : }
230 :
231 0 : if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
232 : #ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
233 0 : fd = dbus_watch_get_unix_fd(dbus_watch);
234 : #else
235 : fd = dbus_watch_get_fd(dbus_watch);
236 : #endif
237 : }
238 0 : DEBUG(SSSDBG_TRACE_ALL,
239 : "%p/%p (%d), %s/%s (%s)\n",
240 : watch, dbus_watch, fd,
241 : ((flags & DBUS_WATCH_READABLE)?"R":"-"),
242 : ((flags & DBUS_WATCH_WRITABLE)?"W":"-"),
243 : enabled?"enabled":"disabled");
244 : }
245 :
246 : /*
247 : * sbus_remove_watch
248 : * Hook for D-BUS to remove file descriptor-based events
249 : * from the libevents mainloop
250 : */
251 0 : void sbus_remove_watch(DBusWatch *dbus_watch, void *data)
252 : {
253 : struct sbus_watch_ctx *watch;
254 : void *watch_data;
255 :
256 0 : watch_data = dbus_watch_get_data(dbus_watch);
257 0 : watch = talloc_get_type(watch_data, struct sbus_watch_ctx);
258 :
259 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "%p/%p\n", watch, dbus_watch);
260 :
261 0 : if (!watch) {
262 0 : DEBUG(SSSDBG_OP_FAILURE, "DBUS trying to remove unknown watch!\n");
263 0 : return;
264 : }
265 :
266 : /* remove dbus watch data */
267 0 : dbus_watch_set_data(dbus_watch, NULL, NULL);
268 :
269 : /* check which watch to remove, or free if none left */
270 0 : if (watch->dbus_read_watch == dbus_watch) {
271 0 : watch->dbus_read_watch = NULL;
272 : }
273 0 : if (watch->dbus_write_watch == dbus_watch) {
274 0 : watch->dbus_write_watch = NULL;
275 : }
276 0 : if (!watch->dbus_read_watch && !watch->dbus_write_watch) {
277 0 : talloc_free(watch);
278 : }
279 : }
280 :
281 : /* =Timeouts============================================================== */
282 :
283 0 : static struct timeval _get_interval_tv(int interval) {
284 : struct timeval tv;
285 : struct timeval rightnow;
286 :
287 0 : gettimeofday(&rightnow,NULL);
288 :
289 0 : tv.tv_sec = interval / 1000 + rightnow.tv_sec;
290 0 : tv.tv_usec = (interval % 1000) * 1000 + rightnow.tv_usec;
291 0 : return tv;
292 : }
293 :
294 : /*
295 : * timeout_handler
296 : * Callback for D-BUS to handle timed events
297 : */
298 0 : static void sbus_timeout_handler(struct tevent_context *ev,
299 : struct tevent_timer *te,
300 : struct timeval t, void *data)
301 : {
302 : struct sbus_timeout_ctx *timeout;
303 0 : timeout = talloc_get_type(data, struct sbus_timeout_ctx);
304 :
305 0 : dbus_timeout_handle(timeout->dbus_timeout);
306 0 : }
307 :
308 : /*
309 : * add_timeout
310 : * Hook for D-BUS to add time-based events to the mainloop
311 : */
312 0 : dbus_bool_t sbus_add_timeout(DBusTimeout *dbus_timeout, void *data)
313 : {
314 : struct sbus_connection *conn;
315 : struct sbus_timeout_ctx *timeout;
316 : struct timeval tv;
317 :
318 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "%p\n", dbus_timeout);
319 :
320 0 : if (!dbus_timeout_get_enabled(dbus_timeout)) {
321 0 : return TRUE;
322 : }
323 :
324 0 : conn = talloc_get_type(data, struct sbus_connection);
325 :
326 0 : timeout = talloc_zero(conn, struct sbus_timeout_ctx);
327 0 : if (!timeout) {
328 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Out of Memory!\n");
329 0 : return FALSE;
330 : }
331 0 : timeout->dbus_timeout = dbus_timeout;
332 :
333 0 : tv = _get_interval_tv(dbus_timeout_get_interval(dbus_timeout));
334 0 : timeout->te = tevent_add_timer(conn->ev, timeout, tv,
335 : sbus_timeout_handler, timeout);
336 0 : if (!timeout->te) {
337 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set up timeout event!\n");
338 0 : return FALSE;
339 : }
340 :
341 : /* Save the event to the watch object so it can be removed later */
342 0 : dbus_timeout_set_data(timeout->dbus_timeout, timeout, NULL);
343 :
344 0 : return TRUE;
345 : }
346 :
347 : /*
348 : * sbus_toggle_timeout
349 : * Hook for D-BUS to toggle the enabled/disabled state of a mainloop
350 : * event
351 : */
352 0 : void sbus_toggle_timeout(DBusTimeout *dbus_timeout, void *data)
353 : {
354 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "%p\n", dbus_timeout);
355 :
356 0 : if (dbus_timeout_get_enabled(dbus_timeout)) {
357 0 : sbus_add_timeout(dbus_timeout, data);
358 : } else {
359 0 : sbus_remove_timeout(dbus_timeout, data);
360 : }
361 0 : }
362 :
363 : /*
364 : * sbus_remove_timeout
365 : * Hook for D-BUS to remove time-based events from the mainloop
366 : */
367 0 : void sbus_remove_timeout(DBusTimeout *dbus_timeout, void *data)
368 : {
369 : void *timeout;
370 :
371 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "%p\n", dbus_timeout);
372 :
373 0 : timeout = dbus_timeout_get_data(dbus_timeout);
374 :
375 : /* remove dbus timeout data */
376 0 : dbus_timeout_set_data(dbus_timeout, NULL, NULL);
377 :
378 : /* Freeing the event object will remove it from the event loop */
379 0 : talloc_free(timeout);
380 :
381 0 : }
|