Line data Source code
1 : /*
2 : * wpa_supplicant D-Bus control interface - common functionality
3 : * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
4 : * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
5 : * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
6 : *
7 : * This software may be distributed under the terms of the BSD license.
8 : * See README for more details.
9 : */
10 :
11 : #include "utils/includes.h"
12 : #include <dbus/dbus.h>
13 :
14 : #include "utils/common.h"
15 : #include "utils/eloop.h"
16 : #include "dbus_common.h"
17 : #include "dbus_common_i.h"
18 : #include "dbus_new.h"
19 : #include "dbus_old.h"
20 : #include "../wpa_supplicant_i.h"
21 :
22 :
23 : #ifndef SIGPOLL
24 : #ifdef SIGIO
25 : /*
26 : * If we do not have SIGPOLL, try to use SIGIO instead. This is needed for
27 : * FreeBSD.
28 : */
29 : #define SIGPOLL SIGIO
30 : #endif
31 : #endif
32 :
33 :
34 1284 : static void dispatch_data(DBusConnection *con)
35 : {
36 3869 : while (dbus_connection_get_dispatch_status(con) ==
37 : DBUS_DISPATCH_DATA_REMAINS)
38 1301 : dbus_connection_dispatch(con);
39 1284 : }
40 :
41 :
42 : /**
43 : * dispatch_initial_dbus_messages - Dispatch initial dbus messages after
44 : * claiming bus name
45 : * @eloop_ctx: the DBusConnection to dispatch on
46 : * @timeout_ctx: unused
47 : *
48 : * If clients are quick to notice that service claimed its bus name,
49 : * there may have been messages that came in before initialization was
50 : * all finished. Dispatch those here.
51 : */
52 5 : static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx)
53 : {
54 5 : DBusConnection *con = eloop_ctx;
55 5 : dispatch_data(con);
56 5 : }
57 :
58 :
59 1279 : static void process_watch(struct wpas_dbus_priv *priv,
60 : DBusWatch *watch, eloop_event_type type)
61 : {
62 1279 : dbus_connection_ref(priv->con);
63 :
64 1279 : priv->should_dispatch = 0;
65 :
66 1279 : if (type == EVENT_TYPE_READ)
67 1279 : dbus_watch_handle(watch, DBUS_WATCH_READABLE);
68 0 : else if (type == EVENT_TYPE_WRITE)
69 0 : dbus_watch_handle(watch, DBUS_WATCH_WRITABLE);
70 0 : else if (type == EVENT_TYPE_EXCEPTION)
71 0 : dbus_watch_handle(watch, DBUS_WATCH_ERROR);
72 :
73 1279 : if (priv->should_dispatch) {
74 1279 : dispatch_data(priv->con);
75 1279 : priv->should_dispatch = 0;
76 : }
77 :
78 1279 : dbus_connection_unref(priv->con);
79 1279 : }
80 :
81 :
82 0 : static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx)
83 : {
84 0 : process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION);
85 0 : }
86 :
87 :
88 1279 : static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx)
89 : {
90 1279 : process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ);
91 1279 : }
92 :
93 :
94 0 : static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx)
95 : {
96 0 : process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE);
97 0 : }
98 :
99 :
100 10 : static dbus_bool_t add_watch(DBusWatch *watch, void *data)
101 : {
102 10 : struct wpas_dbus_priv *priv = data;
103 : unsigned int flags;
104 : int fd;
105 :
106 10 : if (!dbus_watch_get_enabled(watch))
107 5 : return TRUE;
108 :
109 5 : flags = dbus_watch_get_flags(watch);
110 5 : fd = dbus_watch_get_unix_fd(watch);
111 :
112 5 : eloop_register_sock(fd, EVENT_TYPE_EXCEPTION, process_watch_exception,
113 : priv, watch);
114 :
115 5 : if (flags & DBUS_WATCH_READABLE) {
116 5 : eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read,
117 : priv, watch);
118 : }
119 5 : if (flags & DBUS_WATCH_WRITABLE) {
120 0 : eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write,
121 : priv, watch);
122 : }
123 :
124 5 : dbus_watch_set_data(watch, priv, NULL);
125 :
126 5 : return TRUE;
127 : }
128 :
129 :
130 10 : static void remove_watch(DBusWatch *watch, void *data)
131 : {
132 : unsigned int flags;
133 : int fd;
134 :
135 10 : flags = dbus_watch_get_flags(watch);
136 10 : fd = dbus_watch_get_unix_fd(watch);
137 :
138 10 : eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION);
139 :
140 10 : if (flags & DBUS_WATCH_READABLE)
141 5 : eloop_unregister_sock(fd, EVENT_TYPE_READ);
142 10 : if (flags & DBUS_WATCH_WRITABLE)
143 5 : eloop_unregister_sock(fd, EVENT_TYPE_WRITE);
144 :
145 10 : dbus_watch_set_data(watch, NULL, NULL);
146 10 : }
147 :
148 :
149 0 : static void watch_toggled(DBusWatch *watch, void *data)
150 : {
151 0 : if (dbus_watch_get_enabled(watch))
152 0 : add_watch(watch, data);
153 : else
154 0 : remove_watch(watch, data);
155 0 : }
156 :
157 :
158 0 : static void process_timeout(void *eloop_ctx, void *sock_ctx)
159 : {
160 0 : DBusTimeout *timeout = sock_ctx;
161 0 : dbus_timeout_handle(timeout);
162 0 : }
163 :
164 :
165 3 : static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
166 : {
167 3 : struct wpas_dbus_priv *priv = data;
168 :
169 3 : if (!dbus_timeout_get_enabled(timeout))
170 0 : return TRUE;
171 :
172 3 : eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000,
173 : process_timeout, priv, timeout);
174 :
175 3 : dbus_timeout_set_data(timeout, priv, NULL);
176 :
177 3 : return TRUE;
178 : }
179 :
180 :
181 3 : static void remove_timeout(DBusTimeout *timeout, void *data)
182 : {
183 3 : struct wpas_dbus_priv *priv = data;
184 :
185 3 : eloop_cancel_timeout(process_timeout, priv, timeout);
186 3 : dbus_timeout_set_data(timeout, NULL, NULL);
187 3 : }
188 :
189 :
190 0 : static void timeout_toggled(DBusTimeout *timeout, void *data)
191 : {
192 0 : if (dbus_timeout_get_enabled(timeout))
193 0 : add_timeout(timeout, data);
194 : else
195 0 : remove_timeout(timeout, data);
196 0 : }
197 :
198 :
199 1279 : static void process_wakeup_main(int sig, void *signal_ctx)
200 : {
201 1279 : struct wpas_dbus_priv *priv = signal_ctx;
202 :
203 1279 : if (sig != SIGPOLL || !priv->con)
204 0 : return;
205 :
206 1279 : if (dbus_connection_get_dispatch_status(priv->con) !=
207 : DBUS_DISPATCH_DATA_REMAINS)
208 1279 : return;
209 :
210 : /* Only dispatch once - we do not want to starve other events */
211 0 : dbus_connection_ref(priv->con);
212 0 : dbus_connection_dispatch(priv->con);
213 0 : dbus_connection_unref(priv->con);
214 : }
215 :
216 :
217 : /**
218 : * wakeup_main - Attempt to wake our mainloop up
219 : * @data: dbus control interface private data
220 : *
221 : * Try to wake up the main eloop so it will process
222 : * dbus events that may have happened.
223 : */
224 1289 : static void wakeup_main(void *data)
225 : {
226 1289 : struct wpas_dbus_priv *priv = data;
227 :
228 : /* Use SIGPOLL to break out of the eloop select() */
229 1289 : raise(SIGPOLL);
230 1289 : priv->should_dispatch = 1;
231 1289 : }
232 :
233 :
234 : /**
235 : * integrate_with_eloop - Register our mainloop integration with dbus
236 : * @connection: connection to the system message bus
237 : * @priv: a dbus control interface data structure
238 : * Returns: 0 on success, -1 on failure
239 : */
240 5 : static int integrate_with_eloop(struct wpas_dbus_priv *priv)
241 : {
242 5 : if (!dbus_connection_set_watch_functions(priv->con, add_watch,
243 : remove_watch, watch_toggled,
244 5 : priv, NULL) ||
245 5 : !dbus_connection_set_timeout_functions(priv->con, add_timeout,
246 : remove_timeout,
247 : timeout_toggled, priv,
248 : NULL)) {
249 0 : wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions");
250 0 : return -1;
251 : }
252 :
253 5 : if (eloop_register_signal(SIGPOLL, process_wakeup_main, priv))
254 0 : return -1;
255 5 : dbus_connection_set_wakeup_main_function(priv->con, wakeup_main,
256 : priv, NULL);
257 :
258 5 : return 0;
259 : }
260 :
261 :
262 1301 : static DBusHandlerResult disconnect_filter(DBusConnection *conn,
263 : DBusMessage *message, void *data)
264 : {
265 1301 : struct wpas_dbus_priv *priv = data;
266 :
267 1301 : if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
268 : "Disconnected")) {
269 0 : wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
270 0 : dbus_connection_set_exit_on_disconnect(conn, FALSE);
271 0 : wpa_supplicant_terminate_proc(priv->global);
272 0 : return DBUS_HANDLER_RESULT_HANDLED;
273 : } else
274 1301 : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
275 : }
276 :
277 :
278 5 : static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
279 : {
280 : DBusError error;
281 5 : int ret = 0;
282 :
283 : /* Get a reference to the system bus */
284 5 : dbus_error_init(&error);
285 5 : priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
286 5 : if (priv->con) {
287 5 : dbus_connection_add_filter(priv->con, disconnect_filter, priv,
288 : NULL);
289 : } else {
290 0 : wpa_printf(MSG_ERROR,
291 : "dbus: Could not acquire the system bus: %s - %s",
292 : error.name, error.message);
293 0 : ret = -1;
294 : }
295 5 : dbus_error_free(&error);
296 :
297 5 : return ret;
298 : }
299 :
300 :
301 5 : static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv)
302 : {
303 : /* Tell dbus about our mainloop integration functions */
304 5 : integrate_with_eloop(priv);
305 :
306 : /*
307 : * Dispatch initial DBus messages that may have come in since the bus
308 : * name was claimed above. Happens when clients are quick to notice the
309 : * service.
310 : *
311 : * FIXME: is there a better solution to this problem?
312 : */
313 5 : eloop_register_timeout(0, 50, dispatch_initial_dbus_messages,
314 5 : priv->con, NULL);
315 :
316 5 : return 0;
317 : }
318 :
319 :
320 5 : static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv)
321 : {
322 5 : if (priv->con) {
323 5 : eloop_cancel_timeout(dispatch_initial_dbus_messages,
324 5 : priv->con, NULL);
325 5 : eloop_cancel_timeout(process_timeout, priv, ELOOP_ALL_CTX);
326 :
327 5 : dbus_connection_set_watch_functions(priv->con, NULL, NULL,
328 : NULL, NULL, NULL);
329 5 : dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
330 : NULL, NULL, NULL);
331 5 : dbus_connection_remove_filter(priv->con, disconnect_filter,
332 : priv);
333 :
334 5 : dbus_connection_unref(priv->con);
335 : }
336 :
337 5 : os_free(priv);
338 5 : }
339 :
340 :
341 5 : struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global)
342 : {
343 : struct wpas_dbus_priv *priv;
344 :
345 5 : priv = os_zalloc(sizeof(*priv));
346 5 : if (priv == NULL)
347 0 : return NULL;
348 5 : priv->global = global;
349 :
350 10 : if (wpas_dbus_init_common(priv) < 0 ||
351 : #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
352 10 : wpas_dbus_ctrl_iface_init(priv) < 0 ||
353 : #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
354 : #ifdef CONFIG_CTRL_IFACE_DBUS
355 10 : wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 ||
356 : #endif /* CONFIG_CTRL_IFACE_DBUS */
357 5 : wpas_dbus_init_common_finish(priv) < 0) {
358 0 : wpas_dbus_deinit(priv);
359 0 : return NULL;
360 : }
361 :
362 5 : return priv;
363 : }
364 :
365 :
366 5 : void wpas_dbus_deinit(struct wpas_dbus_priv *priv)
367 : {
368 5 : if (priv == NULL)
369 5 : return;
370 :
371 : #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
372 5 : wpas_dbus_ctrl_iface_deinit(priv);
373 : #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
374 :
375 : #ifdef CONFIG_CTRL_IFACE_DBUS
376 : /* TODO: is any deinit needed? */
377 : #endif /* CONFIG_CTRL_IFACE_DBUS */
378 :
379 5 : wpas_dbus_deinit_common(priv);
380 : }
|