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