Use async creation and removal for supplicant interfaces
[connman] / plugins / wifi.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <linux/if_arp.h>
32 #include <linux/wireless.h>
33
34 #include <dbus/dbus.h>
35
36 #define CONNMAN_API_SUBJECT_TO_CHANGE
37 #include <connman/plugin.h>
38 #include <connman/device.h>
39 #include <connman/driver.h>
40 #include <connman/log.h>
41
42 #include "inet.h"
43 #include "supplicant.h"
44
45 #define CLEANUP_TIMEOUT   8     /* in seconds */
46 #define INACTIVE_TIMEOUT  12    /* in seconds */
47
48 struct wifi_data {
49         GSList *current;
50         GSList *pending;
51         guint cleanup_timer;
52         guint inactive_timer;
53         gchar *identifier;
54         gboolean connected;
55 };
56
57 static int network_probe(struct connman_element *element)
58 {
59         DBG("element %p name %s", element, element->name);
60
61         return 0;
62 }
63
64 static void network_remove(struct connman_element *element)
65 {
66         DBG("element %p name %s", element, element->name);
67 }
68
69 static int network_enable(struct connman_element *element)
70 {
71         struct connman_device *device = (struct connman_device *) element->parent;
72         char *name, *security = NULL, *passphrase = NULL;
73         unsigned char *ssid;
74         int ssid_len;
75
76         DBG("element %p name %s", element, element->name);
77
78         if (connman_element_get_static_property(element,
79                                                 "Name", &name) == FALSE)
80                 return -EIO;
81
82         if (connman_element_get_static_array_property(element,
83                                 "WiFi.SSID", &ssid, &ssid_len) == FALSE)
84                 return -EIO;
85
86         if (device != NULL) {
87                 struct wifi_data *data = connman_device_get_data(device);
88
89                 if (data != NULL) {
90                         if (data->connected == TRUE)
91                                 return -EBUSY;
92
93                         g_free(data->identifier);
94                         data->identifier = g_strdup(name);
95                 }
96         }
97
98         connman_element_get_value(element,
99                         CONNMAN_PROPERTY_ID_WIFI_SECURITY, &security);
100
101         connman_element_get_value(element,
102                         CONNMAN_PROPERTY_ID_WIFI_PASSPHRASE, &passphrase);
103
104         DBG("name %s security %s passhprase %s",
105                                         name, security, passphrase);
106
107         if (__supplicant_connect(element, ssid, ssid_len,
108                                                 security, passphrase) < 0)
109                 connman_error("Failed to initiate connect");
110
111         return 0;
112 }
113
114 static int network_disable(struct connman_element *element)
115 {
116         DBG("element %p name %s", element, element->name);
117
118         connman_element_unregister_children(element);
119
120         __supplicant_disconnect(element);
121
122         return 0;
123 }
124
125 static struct connman_driver network_driver = {
126         .name           = "wifi-network",
127         .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
128         .subtype        = CONNMAN_ELEMENT_SUBTYPE_WIFI,
129         .probe          = network_probe,
130         .remove         = network_remove,
131         .enable         = network_enable,
132         .disable        = network_disable,
133 };
134
135 static struct connman_element *find_current_element(struct wifi_data *data,
136                                                         const char *identifier)
137 {
138         GSList *list;
139
140         for (list = data->current; list; list = list->next) {
141                 struct connman_element *element = list->data;
142
143                 if (connman_element_match_static_property(element,
144                                                 "Name", &identifier) == TRUE)
145                         return element;
146         }
147
148         return NULL;
149 }
150
151 static struct connman_element *find_pending_element(struct wifi_data *data,
152                                                         const char *identifier)
153 {
154         GSList *list;
155
156         for (list = data->pending; list; list = list->next) {
157                 struct connman_element *element = list->data;
158
159                 if (connman_element_match_static_property(element,
160                                                 "Name", &identifier) == TRUE)
161                         return element;
162         }
163
164         return NULL;
165 }
166
167 static gboolean inactive_scan(gpointer user_data)
168 {
169         struct connman_device *device = user_data;
170         struct wifi_data *data = connman_device_get_data(device);
171
172         DBG("device %p", device);
173
174         supplicant_scan(device);
175
176         data->inactive_timer = 0;
177
178         return FALSE;
179 }
180
181 static void connect_known_networks(struct connman_device *device)
182 {
183         struct wifi_data *data = connman_device_get_data(device);
184         GSList *list;
185
186         DBG("device %p", device);
187
188         if (data->inactive_timer > 0) {
189                 g_source_remove(data->inactive_timer);
190                 data->inactive_timer = 0;
191         }
192
193         for (list = data->current; list; list = list->next) {
194                 struct connman_element *element = list->data;
195
196                 if (element->policy == CONNMAN_ELEMENT_POLICY_AUTO &&
197                                                 element->remember == TRUE &&
198                                                 element->available == TRUE) {
199                         if (network_enable(element) == 0)
200                                 return;
201                 }
202         }
203
204         data->inactive_timer = g_timeout_add_seconds(INACTIVE_TIMEOUT,
205                                                         inactive_scan, device);
206 }
207
208 static void state_change(struct connman_device *device,
209                                                 enum supplicant_state state)
210 {
211         struct wifi_data *data = connman_device_get_data(device);
212         struct connman_element *element;
213
214         DBG("device %p state %d", device, state);
215
216         if (state == STATE_SCANNING)
217                 connman_device_set_scanning(device, TRUE);
218         else
219                 connman_device_set_scanning(device, FALSE);
220
221         if (data == NULL)
222                 return;
223
224         DBG("identifier %s", data->identifier);
225
226         if (data->identifier == NULL)
227                 goto reconnect;
228
229         element = find_current_element(data, data->identifier);
230         if (element == NULL)
231                 goto reconnect;
232
233         if (state == STATE_COMPLETED && data->connected == FALSE) {
234                 struct connman_element *dhcp;
235
236                 data->connected = TRUE;
237                 connman_element_set_enabled(element, TRUE);
238
239                 dhcp = connman_element_create(NULL);
240
241                 dhcp->type = CONNMAN_ELEMENT_TYPE_DHCP;
242                 dhcp->index = element->index;
243
244                 if (connman_element_register(dhcp, element) < 0)
245                         connman_element_unref(dhcp);
246         } else if (state == STATE_INACTIVE || state == STATE_DISCONNECTED) {
247                 data->connected = FALSE;
248                 connman_element_set_enabled(element, FALSE);
249
250                 connman_element_unregister_children(element);
251         }
252
253 reconnect:
254         if (state == STATE_INACTIVE) {
255                 data->connected = FALSE;
256                 connect_known_networks(device);
257         }
258 }
259
260 static gboolean cleanup_pending(gpointer user_data)
261 {
262         struct wifi_data *data = user_data;
263         GSList *list;
264
265         DBG("");
266
267         for (list = data->pending; list; list = list->next) {
268                 struct connman_element *element = list->data;
269
270                 DBG("element %p name %s", element, element->name);
271
272                 connman_element_unregister(element);
273                 connman_element_unref(element);
274         }
275
276         g_slist_free(data->pending);
277         data->pending = NULL;
278
279         data->cleanup_timer = 0;
280
281         return FALSE;
282 }
283
284 static void clear_results(struct connman_device *device)
285 {
286         struct wifi_data *data = connman_device_get_data(device);
287
288         DBG("pending %d", g_slist_length(data->pending));
289         DBG("current %d", g_slist_length(data->current));
290
291         if (data->cleanup_timer > 0) {
292                 g_source_remove(data->cleanup_timer);
293                 cleanup_pending(data);
294         }
295
296         data->pending = data->current;
297         data->current = NULL;
298
299         data->cleanup_timer = g_timeout_add_seconds(CLEANUP_TIMEOUT,
300                                                         cleanup_pending, data);
301 }
302
303 static void scan_result(struct connman_device *device,
304                                         struct supplicant_network *network)
305 {
306         struct wifi_data *data = connman_device_get_data(device);
307         struct connman_element *element;
308         gchar *temp;
309         unsigned int i;
310
311         DBG("device %p identifier %s", device, network->identifier);
312
313         if (data == NULL)
314                 return;
315
316         if (network->identifier == NULL)
317                 return;
318
319         if (network->identifier[0] == '\0')
320                 return;
321
322         temp = g_strdup(network->identifier);
323
324         for (i = 0; i < strlen(temp); i++) {
325                 char tmp = temp[i];
326                 if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') &&
327                                                 (tmp < 'a' || tmp > 'z'))
328                         temp[i] = '_';
329         }
330
331         element = find_pending_element(data, network->identifier);
332         if (element == NULL) {
333                 const char *mode;
334
335                 element = connman_element_create(temp);
336
337                 element->type    = CONNMAN_ELEMENT_TYPE_NETWORK;
338                 element->subtype = CONNMAN_ELEMENT_SUBTYPE_WIFI;
339                 element->index   = connman_device_get_index(device);
340
341                 connman_element_add_static_property(element, "Name",
342                                 DBUS_TYPE_STRING, &network->identifier);
343
344                 connman_element_add_static_array_property(element, "WiFi.SSID",
345                         DBUS_TYPE_BYTE, &network->ssid, network->ssid_len);
346
347                 mode = (network->adhoc == TRUE) ? "adhoc" : "managed";
348                 connman_element_add_static_property(element, "WiFi.Mode",
349                                                 DBUS_TYPE_STRING, &mode);
350
351                 if (element->wifi.security == NULL) {
352                         const char *security;
353
354                         if (network->has_rsn == TRUE)
355                                 security = "wpa2";
356                         else if (network->has_wpa == TRUE)
357                                 security = "wpa";
358                         else if (network->has_wep == TRUE)
359                                 security = "wep";
360                         else
361                                 security = "none";
362
363                         element->wifi.security = g_strdup(security);
364                 }
365
366                 element->strength = network->quality;
367
368                 connman_element_add_static_property(element, "Strength",
369                                         DBUS_TYPE_BYTE, &element->strength);
370
371                 DBG("%s (%s %s) strength %d", network->identifier, mode,
372                                 element->wifi.security, element->strength);
373
374                 if (connman_element_register(element,
375                                 (struct connman_element *) device) < 0) {
376                         connman_element_unref(element);
377                         goto done;
378                 }
379         } else {
380                 data->pending = g_slist_remove(data->pending, element);
381
382                 if (element->strength != network->quality) {
383                         element->strength = network->quality;
384
385                         connman_element_set_static_property(element, "Strength",
386                                         DBUS_TYPE_BYTE, &element->strength);
387
388                         connman_element_update(element);
389                 }
390         }
391
392         data->current = g_slist_append(data->current, element);
393
394         element->available = TRUE;
395
396 done:
397         g_free(temp);
398 }
399
400 static int wifi_probe(struct connman_device *device)
401 {
402         struct wifi_data *data;
403
404         DBG("device %p", device);
405
406         data = g_try_new0(struct wifi_data, 1);
407         if (data == NULL)
408                 return -ENOMEM;
409
410         data->connected = FALSE;
411
412         connman_device_set_data(device, data);
413
414         return 0;
415 }
416
417 static void wifi_remove(struct connman_device *device)
418 {
419         struct wifi_data *data = connman_device_get_data(device);
420
421         DBG("device %p", device);
422
423         connman_device_set_data(device, NULL);
424
425         g_free(data->identifier);
426         g_free(data);
427 }
428
429 static int wifi_enable(struct connman_device *device)
430 {
431         DBG("device %p", device);
432
433         return supplicant_start(device);
434 }
435
436 static int wifi_disable(struct connman_device *device)
437 {
438         struct wifi_data *data = connman_device_get_data(device);
439         GSList *list;
440
441         DBG("device %p", device);
442
443         if (data->cleanup_timer > 0) {
444                 g_source_remove(data->cleanup_timer);
445                 cleanup_pending(data);
446         }
447
448         if (data->inactive_timer > 0) {
449                 g_source_remove(data->inactive_timer);
450                 data->inactive_timer = 0;
451         }
452
453         for (list = data->current; list; list = list->next) {
454                 struct connman_element *network = list->data;
455
456                 if (network->enabled == TRUE)
457                         __supplicant_disconnect(network);
458
459                 connman_element_unref(network);
460         }
461
462         g_slist_free(data->current);
463         data->current = NULL;
464
465         connman_element_unregister_children((struct connman_element *) device);
466
467         return supplicant_stop(device);
468 }
469
470 static int wifi_scan(struct connman_device *device)
471 {
472         DBG("device %p", device);
473
474         return supplicant_scan(device);
475 }
476
477 static struct connman_device_driver wifi_driver = {
478         .name           = "wifi",
479         .type           = CONNMAN_DEVICE_TYPE_WIFI,
480         .probe          = wifi_probe,
481         .remove         = wifi_remove,
482         .enable         = wifi_enable,
483         .disable        = wifi_disable,
484         .scan           = wifi_scan,
485 };
486
487 static void wifi_register(void)
488 {
489         DBG("");
490
491         if (connman_device_driver_register(&wifi_driver) < 0)
492                 connman_error("Failed to register WiFi driver");
493 }
494
495 static void wifi_unregister(void)
496 {
497         DBG("");
498
499         connman_device_driver_unregister(&wifi_driver);
500 }
501
502 static struct supplicant_driver supplicant = {
503         .name           = "wifi",
504         .probe          = wifi_register,
505         .remove         = wifi_unregister,
506
507         .state_change   = state_change,
508         .clear_results  = clear_results,
509         .scan_result    = scan_result,
510 };
511
512 static int wifi_init(void)
513 {
514         int err;
515
516         err = connman_driver_register(&network_driver);
517         if (err < 0)
518                 return err;
519
520         err = supplicant_register(&supplicant);
521         if (err < 0) {
522                 connman_driver_unregister(&network_driver);
523                 return err;
524         }
525
526         return 0;
527 }
528
529 static void wifi_exit(void)
530 {
531         supplicant_unregister(&supplicant);
532
533         connman_driver_unregister(&network_driver);
534 }
535
536 CONNMAN_PLUGIN_DEFINE(wifi, "WiFi interface plugin", VERSION,
537                                                         wifi_init, wifi_exit)