Imported Upstream version 1.37
[platform/upstream/connman.git] / plugins / iwd.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2016  BMW Car IT GmbH.
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 <errno.h>
27 #include <string.h>
28 #include <stdbool.h>
29 #include <linux/if_ether.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/dbus.h>
34 #include <connman/network.h>
35 #include <connman/technology.h>
36 #include <connman/inet.h>
37 #include <gdbus.h>
38
39 static DBusConnection *connection;
40 static GDBusClient *client;
41 static GDBusProxy *agent_proxy;
42 static GHashTable *adapters;
43 static GHashTable *devices;
44 static GHashTable *networks;
45 static bool agent_registered;
46
47 #define IWD_SERVICE                     "net.connman.iwd"
48 #define IWD_PATH                        "/"
49 #define IWD_AGENT_MANAGER_INTERFACE     "net.connman.iwd.AgentManager"
50 #define IWD_ADAPTER_INTERFACE           "net.connman.iwd.Adapter"
51 #define IWD_DEVICE_INTERFACE            "net.connman.iwd.Device"
52 #define IWD_NETWORK_INTERFACE           "net.connman.iwd.Network"
53
54 #define IWD_AGENT_INTERFACE             "net.connman.iwd.Agent"
55 #define IWD_AGENT_ERROR_INTERFACE       "net.connman.iwd.Agent.Error"
56 #define AGENT_PATH                      "/net/connman/iwd_agent"
57
58 struct iwd_adapter {
59         GDBusProxy *proxy;
60         char *path;
61         char *vendor;
62         char *model;
63         bool powered;
64 };
65
66 struct iwd_device {
67         GDBusProxy *proxy;
68         char *path;
69         char *adapter;
70         char *name;
71         char *address;
72         bool powered;
73         bool scanning;
74
75         struct connman_device *device;
76 };
77
78 struct iwd_network {
79         GDBusProxy *proxy;
80         char *path;
81         char *device;
82         char *name;
83         char *type;
84         bool connected;
85
86         struct iwd_device *iwdd;
87         struct connman_network *network;
88 };
89
90 static const char *proxy_get_string(GDBusProxy *proxy, const char *property)
91 {
92         DBusMessageIter iter;
93         const char *str;
94
95         if (!g_dbus_proxy_get_property(proxy, property, &iter))
96                 return NULL;
97
98         dbus_message_iter_get_basic(&iter, &str);
99
100         return str;
101 }
102
103 static bool proxy_get_bool(GDBusProxy *proxy, const char *property)
104 {
105         DBusMessageIter iter;
106         dbus_bool_t value;
107
108         if (!g_dbus_proxy_get_property(proxy, property, &iter))
109                 return false;
110
111         dbus_message_iter_get_basic(&iter, &value);
112
113         return value;
114 }
115
116 static void address2ident(const char *address, char *ident)
117 {
118         int i;
119
120         for (i = 0; i < ETH_ALEN; i++) {
121                 ident[i * 2] = address[i * 3];
122                 ident[i * 2 + 1] = address[i * 3 + 1];
123         }
124         ident[ETH_ALEN * 2] = '\0';
125 }
126
127 static int cm_network_probe(struct connman_network *network)
128 {
129         GHashTableIter iter;
130         gpointer key, value;
131
132         g_hash_table_iter_init(&iter, networks);
133
134         while (g_hash_table_iter_next(&iter, &key, &value)) {
135                 struct iwd_network *iwdn = value;
136
137                 if (network == iwdn->network)
138                         return 0;
139         }
140
141         return -EOPNOTSUPP;
142 }
143
144 static void update_network_connected(struct iwd_network *iwdn)
145 {
146         struct iwd_device *iwdd;
147         int index;
148
149         iwdd = g_hash_table_lookup(devices, iwdn->device);
150         if (!iwdd)
151                 return;
152
153         index = connman_inet_ifindex(iwdd->name);
154         if (index < 0)
155                 return;
156
157         DBG("interface name %s index %d", iwdd->name, index);
158         connman_network_set_index(iwdn->network, index);
159         connman_network_set_connected(iwdn->network, true);
160 }
161
162 static void update_network_disconnected(struct iwd_network *iwdn)
163 {
164         DBG("interface name %s", iwdn->name);
165         connman_network_set_connected(iwdn->network, false);
166 }
167
168 static void cm_network_connect_cb(DBusMessage *message, void *user_data)
169 {
170         const char *path = user_data;
171         struct iwd_network *iwdn;
172
173         iwdn = g_hash_table_lookup(networks, path);
174         if (!iwdn)
175                 return;
176
177         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
178                 const char *dbus_error = dbus_message_get_error_name(message);
179
180                 if (!strcmp(dbus_error, "net.connman.iwd.InProgress"))
181                         return;
182
183                 DBG("%s connect failed: %s", path, dbus_error);
184                 connman_network_set_error(iwdn->network,
185                                         CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
186                 return;
187         }
188
189         update_network_connected(iwdn);
190 }
191
192 static int cm_network_connect(struct connman_network *network)
193 {
194         struct iwd_network *iwdn = connman_network_get_data(network);
195
196         if (!iwdn)
197                 return -EINVAL;
198
199         if (!g_dbus_proxy_method_call(iwdn->proxy, "Connect",
200                         NULL, cm_network_connect_cb,
201                         g_strdup(iwdn->path), g_free))
202                 return -EIO;
203
204         connman_network_set_associating(iwdn->network, true);
205
206         return -EINPROGRESS;
207 }
208
209 static void cm_network_disconnect_cb(DBusMessage *message, void *user_data)
210 {
211         const char *path = user_data;
212         struct iwd_network *iwdn;
213
214         iwdn = g_hash_table_lookup(networks, path);
215         if (!iwdn)
216                 return;
217
218         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
219                 const char *dbus_error = dbus_message_get_error_name(message);
220
221                 if (!strcmp(dbus_error, "net.connman.iwd.NotConnected")) {
222                         /* fall through */
223                 } else {
224                         DBG("%s disconnect failed: %s", path, dbus_error);
225                         return;
226                 }
227         }
228
229         /*
230          * We end up in a tight loop in the error case. That is
231          * when we can't connect, bail out in cm_network_connect_cb() with
232          * an error.
233          */
234         if (connman_network_get_connected(iwdn->network))
235                 update_network_disconnected(iwdn);
236 }
237
238 static int cm_network_disconnect(struct connman_network *network)
239 {
240         struct iwd_network *iwdn = connman_network_get_data(network);
241         struct iwd_device *iwdd;
242
243         if (!iwdn)
244                 return -EINVAL;
245
246         iwdd = g_hash_table_lookup(devices, iwdn->device);
247         if (!iwdd)
248                 return -EIO;
249
250         if (!g_dbus_proxy_method_call(iwdd->proxy, "Disconnect",
251                         NULL, cm_network_disconnect_cb, g_strdup(iwdn->path), g_free))
252                 return -EIO;
253
254         return -EINPROGRESS;
255 }
256
257 static struct connman_network_driver network_driver = {
258         .name           = "iwd",
259         .type           = CONNMAN_NETWORK_TYPE_WIFI,
260         .probe          = cm_network_probe,
261         .connect        = cm_network_connect,
262         .disconnect     = cm_network_disconnect,
263 };
264
265 static int cm_device_probe(struct connman_device *device)
266 {
267         GHashTableIter iter;
268         gpointer key, value;
269
270         g_hash_table_iter_init(&iter, devices);
271
272         while (g_hash_table_iter_next(&iter, &key, &value)) {
273                 struct iwd_device *iwdd = value;
274
275                 if (device == iwdd->device)
276                         return 0;
277         }
278
279         return -EOPNOTSUPP;
280 }
281
282 static void cm_device_remove(struct connman_device *device)
283 {
284 }
285
286 struct dev_cb_data {
287         char *path;
288         bool powered;
289 };
290
291 static void device_powered_cb(const DBusError *error, void *user_data)
292 {
293         struct dev_cb_data *cbd = user_data;
294         struct iwd_device *iwdd;
295
296         iwdd = g_hash_table_lookup(devices, cbd->path);
297         if (!iwdd)
298                 goto out;
299
300         if (dbus_error_is_set(error)) {
301                 connman_warn("WiFi device %s not enabled %s",
302                                 cbd->path, error->message);
303                 goto out;
304         }
305
306         connman_device_set_powered(iwdd->device, cbd->powered);
307 out:
308         g_free(cbd->path);
309         g_free(cbd);
310 }
311
312 static int set_device_powered(struct connman_device *device, bool powered)
313 {
314         struct iwd_device *iwdd = connman_device_get_data(device);
315         dbus_bool_t device_powered = powered;
316         struct dev_cb_data *cbd;
317
318         if (proxy_get_bool(iwdd->proxy, "Powered"))
319                 return -EALREADY;
320
321         cbd = g_new(struct dev_cb_data, 1);
322         cbd->path = g_strdup(iwdd->path);
323         cbd->powered = powered;
324
325         g_dbus_proxy_set_property_basic(iwdd->proxy, "Powered",
326                         DBUS_TYPE_BOOLEAN, &device_powered,
327                         device_powered_cb, cbd, NULL);
328
329         return -EINPROGRESS;
330 }
331
332 static int cm_device_enable(struct connman_device *device)
333 {
334         return set_device_powered(device, true);
335 }
336
337 static int cm_device_disable(struct connman_device *device)
338 {
339         return set_device_powered(device, false);
340 }
341
342 static struct connman_device_driver device_driver = {
343         .name           = "iwd",
344         .type           = CONNMAN_DEVICE_TYPE_WIFI,
345         .probe          = cm_device_probe,
346         .remove         = cm_device_remove,
347         .enable         = cm_device_enable,
348         .disable        = cm_device_disable,
349 };
350
351 static int cm_tech_probe(struct connman_technology *technology)
352 {
353         return 0;
354 }
355
356 static void cm_tech_remove(struct connman_technology *technology)
357 {
358 }
359
360 static struct connman_technology_driver tech_driver = {
361         .name           = "iwd",
362         .type           = CONNMAN_SERVICE_TYPE_WIFI,
363         .probe          = cm_tech_probe,
364         .remove         = cm_tech_remove,
365 };
366
367 static unsigned char calculate_strength(int strength)
368 {
369         unsigned char res;
370
371         /*
372          * Network's maximum signal strength expressed in 100 * dBm.
373          * The value is the range of 0 (strongest signal) to -10000
374          * (weakest signal)
375          *
376          * ConnMan expects it in the range from 100 (strongest) to 0
377          * (weakest).
378          */
379         res = (unsigned char)((strength * -10000) / 100);
380
381         return res;
382 }
383
384 static void _update_signal_strength(const char *path, int16_t signal_strength)
385 {
386         struct iwd_network *iwdn;
387
388         iwdn = g_hash_table_lookup(networks, path);
389         if (!iwdn)
390                 return;
391
392         if (!iwdn->network)
393                 return;
394
395         connman_network_set_strength(iwdn->network,
396                                         calculate_strength(signal_strength));
397 }
398
399 static void ordered_networks_cb(DBusMessage *message, void *user_data)
400 {
401         DBusMessageIter array, entry;
402
403         DBG("");
404
405         if (!dbus_message_iter_init(message, &array))
406                 return;
407
408         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
409                 return;
410
411         dbus_message_iter_recurse(&array, &entry);
412         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
413                 DBusMessageIter value;
414                 const char *path, *name, *type;
415                 int16_t signal_strength;
416
417
418                 dbus_message_iter_recurse(&entry, &value);
419
420                 dbus_message_iter_get_basic(&value, &path);
421
422                 dbus_message_iter_next(&value);
423                 dbus_message_iter_get_basic(&value, &name);
424
425                 dbus_message_iter_next(&value);
426                 dbus_message_iter_get_basic(&value, &signal_strength);
427
428                 dbus_message_iter_next(&value);
429                 dbus_message_iter_get_basic(&value, &type);
430
431                 _update_signal_strength(path, signal_strength);
432
433                 dbus_message_iter_next(&entry);
434         }
435 }
436
437 static void update_signal_strength(struct iwd_device *iwdd)
438 {
439         if (!g_dbus_proxy_method_call(iwdd->proxy,
440                                         "GetOrderedNetworks",
441                                         NULL, ordered_networks_cb,
442                                         NULL, NULL))
443                 DBG("GetOrderedNetworks() failed");
444 }
445
446 static const char *security_remap(const char *security)
447 {
448         if (!g_strcmp0(security, "open"))
449                 return "none";
450         else if (!g_strcmp0(security, "psk"))
451                 return "psk";
452         else if (!g_strcmp0(security, "8021x"))
453                 return "ieee8021x";
454
455         return "unknown";
456 }
457
458 static char *create_identifier(const char *path, const char *security)
459 {
460         char *start, *end, *identifier;
461         char *_path = g_strdup(path);
462
463         /*
464          * _path is something like
465          *     /0/4/5363686970686f6c5f427573696e6573735f454150_8021x
466          */
467         start = strrchr(_path, '/');
468         start++;
469         end = strchr(start, '_');
470         *end = '\0';
471
472         /*
473          * Create an ident which is identical to the corresponding
474          * wpa_supplicant identifier.
475          */
476         identifier = g_strdup_printf("%s_managed_%s", start,
477                                 security_remap(security));
478         g_free(_path);
479
480         return identifier;
481 }
482
483 static void add_network(const char *path, struct iwd_network *iwdn)
484 {
485         struct iwd_device *iwdd;
486         char *identifier;
487
488         iwdd = g_hash_table_lookup(devices, iwdn->device);
489         if (!iwdd)
490                 return;
491
492         identifier = create_identifier(path, iwdn->type);
493         iwdn->network = connman_network_create(identifier,
494                                         CONNMAN_NETWORK_TYPE_WIFI);
495         connman_network_set_data(iwdn->network, iwdn);
496
497         connman_network_set_name(iwdn->network, iwdn->name);
498         connman_network_set_blob(iwdn->network, "WiFi.SSID", iwdn->name,
499                                         strlen(iwdn->name));
500         connman_network_set_string(iwdn->network, "WiFi.Security",
501                                         iwdn->type);
502         connman_network_set_string(iwdn->network, "WiFi.Mode", "managed");
503
504         if (connman_device_add_network(iwdd->device, iwdn->network) < 0) {
505                 connman_network_unref(iwdn->network);
506                 iwdn->network = NULL;
507                 return;
508         }
509         iwdn->iwdd = iwdd;
510
511         connman_network_set_available(iwdn->network, true);
512         connman_network_set_group(iwdn->network, identifier);
513
514         g_free(identifier);
515 }
516
517 static void remove_network(struct iwd_network *iwdn)
518 {
519         if (!iwdn->network)
520                 return;
521
522         if (iwdn->iwdd)
523                 connman_device_remove_network(iwdn->iwdd->device,
524                                                 iwdn->network);
525
526         connman_network_unref(iwdn->network);
527         iwdn->network = NULL;
528 }
529
530 static void add_device(const char *path, struct iwd_device *iwdd)
531 {
532         char ident[ETH_ALEN * 2 + 1];
533
534         iwdd->device = connman_device_create("wifi", CONNMAN_DEVICE_TYPE_WIFI);
535         if (!iwdd->device)
536                 return;
537
538         connman_device_set_data(iwdd->device, iwdd);
539
540         address2ident(iwdd->address, ident);
541         connman_device_set_ident(iwdd->device, ident);
542
543         if (connman_device_register(iwdd->device) < 0) {
544                 g_hash_table_remove(devices, path);
545                 return;
546         }
547
548         connman_device_set_powered(iwdd->device, iwdd->powered);
549 }
550
551 static void remove_device_networks(struct iwd_device *iwdd)
552 {
553         GHashTableIter iter;
554         gpointer key, value;
555         struct iwd_network *iwdn;
556         GSList *list, *nets = NULL;
557
558         g_hash_table_iter_init(&iter, networks);
559
560         while (g_hash_table_iter_next(&iter, &key, &value)) {
561                 iwdn = value;
562
563                 if (!strcmp(iwdd->path, iwdn->device))
564                         nets = g_slist_prepend(nets, iwdn);
565         }
566
567         for (list = nets; list; list = list->next) {
568                 iwdn = list->data;
569                 g_hash_table_remove(networks, iwdn->path);
570         }
571
572         g_slist_free(nets);
573 }
574
575 static void remove_device(struct iwd_device *iwdd)
576 {
577         if (!iwdd->device)
578                 return;
579
580         remove_device_networks(iwdd);
581         connman_device_unregister(iwdd->device);
582         connman_device_unref(iwdd->device);
583         iwdd->device = NULL;
584 }
585
586 static void adapter_property_change(GDBusProxy *proxy, const char *name,
587                 DBusMessageIter *iter, void *user_data)
588 {
589         struct iwd_adapter *adapter;
590         const char *path;
591
592         path = g_dbus_proxy_get_path(proxy);
593         adapter = g_hash_table_lookup(adapters, path);
594         if (!adapter)
595                 return;
596
597         if (!strcmp(name, "Powered")) {
598                 dbus_bool_t powered;
599
600                 dbus_message_iter_get_basic(iter, &powered);
601                 adapter->powered = powered;
602
603                 DBG("%p powered %d", path, adapter->powered);
604         }
605 }
606
607 static void device_property_change(GDBusProxy *proxy, const char *name,
608                 DBusMessageIter *iter, void *user_data)
609 {
610         struct iwd_device *iwdd;
611         const char *path;
612
613         path = g_dbus_proxy_get_path(proxy);
614         iwdd = g_hash_table_lookup(devices, path);
615         if (!iwdd)
616                 return;
617
618         if (!strcmp(name, "Name")) {
619                 const char *name;
620
621                 dbus_message_iter_get_basic(iter, &name);
622                 g_free(iwdd->name);
623                 iwdd->name = g_strdup(name);
624
625                 DBG("%p name %s", path, iwdd->name);
626         } else if (!strcmp(name, "Powered")) {
627                 dbus_bool_t powered;
628
629                 dbus_message_iter_get_basic(iter, &powered);
630                 iwdd->powered = powered;
631
632                 DBG("%s powered %d", path, iwdd->powered);
633         } else if (!strcmp(name, "Scanning")) {
634                 dbus_bool_t scanning;
635
636                 dbus_message_iter_get_basic(iter, &scanning);
637                 iwdd->scanning = scanning;
638
639                 DBG("%s scanning %d", path, iwdd->scanning);
640
641                 if (!iwdd->scanning)
642                         update_signal_strength(iwdd);
643
644         }
645 }
646
647 static void network_property_change(GDBusProxy *proxy, const char *name,
648                 DBusMessageIter *iter, void *user_data)
649 {
650         struct iwd_network *iwdn;
651         const char *path;
652
653         path = g_dbus_proxy_get_path(proxy);
654         iwdn = g_hash_table_lookup(networks, path);
655         if (!iwdn)
656                 return;
657
658         if (!strcmp(name, "Connected")) {
659                 dbus_bool_t connected;
660
661                 dbus_message_iter_get_basic(iter, &connected);
662                 iwdn->connected = connected;
663
664                 DBG("%s connected %d", path, iwdn->connected);
665
666                 if (iwdn->connected)
667                         update_network_connected(iwdn);
668                 else
669                         update_network_disconnected(iwdn);
670         }
671 }
672
673 static void adapter_free(gpointer data)
674 {
675         struct iwd_adapter *iwda = data;
676
677         if (iwda->proxy) {
678                 g_dbus_proxy_unref(iwda->proxy);
679                 iwda->proxy = NULL;
680         }
681
682         g_free(iwda->path);
683         g_free(iwda->vendor);
684         g_free(iwda->model);
685         g_free(iwda);
686 }
687
688 static void device_free(gpointer data)
689 {
690         struct iwd_device *iwdd = data;
691
692         if (iwdd->proxy) {
693                 g_dbus_proxy_unref(iwdd->proxy);
694                 iwdd->proxy = NULL;
695         }
696
697         remove_device(iwdd);
698
699         g_free(iwdd->path);
700         g_free(iwdd->adapter);
701         g_free(iwdd->name);
702         g_free(iwdd->address);
703         g_free(iwdd);
704 }
705
706 static void network_free(gpointer data)
707 {
708         struct iwd_network *iwdn = data;
709
710         if (iwdn->proxy) {
711                 g_dbus_proxy_unref(iwdn->proxy);
712                 iwdn->proxy = NULL;
713         }
714
715         remove_network(iwdn);
716
717         g_free(iwdn->path);
718         g_free(iwdn->device);
719         g_free(iwdn->name);
720         g_free(iwdn->type);
721         g_free(iwdn);
722 }
723
724 static void create_adapter(GDBusProxy *proxy)
725 {
726         const char *path = g_dbus_proxy_get_path(proxy);
727         struct iwd_adapter *iwda;
728
729         iwda = g_try_new0(struct iwd_adapter, 1);
730
731         if (!iwda) {
732                 connman_error("Out of memory creating IWD adapter");
733                 return;
734         }
735
736         iwda->path = g_strdup(path);
737         g_hash_table_replace(adapters, iwda->path, iwda);
738
739         iwda->proxy = g_dbus_proxy_ref(proxy);
740
741         if (!iwda->proxy) {
742                 connman_error("Cannot create IWD adapter watcher %s", path);
743                 g_hash_table_remove(adapters, path);
744                 return;
745         }
746
747         iwda->vendor = g_strdup(proxy_get_string(proxy, "Vendor"));
748         iwda->model = g_strdup(proxy_get_string(proxy, "Model"));
749         iwda->powered = proxy_get_bool(proxy, "Powered");
750
751         DBG("%s vendor '%s' model '%s' powered %d", path, iwda->vendor,
752                 iwda->model, iwda->powered);
753
754         g_dbus_proxy_set_property_watch(iwda->proxy,
755                         adapter_property_change, NULL);
756 }
757
758 static void create_device(GDBusProxy *proxy)
759 {
760         const char *path = g_dbus_proxy_get_path(proxy);
761         struct iwd_device *iwdd;
762
763         iwdd = g_try_new0(struct iwd_device, 1);
764
765         if (!iwdd) {
766                 connman_error("Out of memory creating IWD device");
767                 return;
768         }
769
770         iwdd->path = g_strdup(path);
771         g_hash_table_replace(devices, iwdd->path, iwdd);
772
773         iwdd->proxy = g_dbus_proxy_ref(proxy);
774
775         if (!iwdd->proxy) {
776                 connman_error("Cannot create IWD device watcher %s", path);
777                 g_hash_table_remove(devices, path);
778                 return;
779         }
780
781         iwdd->adapter = g_strdup(proxy_get_string(proxy, "Adapter"));
782         iwdd->name = g_strdup(proxy_get_string(proxy, "Name"));
783         iwdd->address = g_strdup(proxy_get_string(proxy, "Address"));
784         iwdd->powered = proxy_get_bool(proxy, "Powered");
785         iwdd->scanning = proxy_get_bool(proxy, "Scanning");
786
787         DBG("adapter %s name %s address %s powered %d scanning %d",
788                 iwdd->adapter, iwdd->name, iwdd->address,
789                 iwdd->powered, iwdd->scanning);
790
791         g_dbus_proxy_set_property_watch(iwdd->proxy,
792                         device_property_change, NULL);
793
794         add_device(path, iwdd);
795 }
796
797 static void unregister_agent();
798
799 static DBusMessage *agent_release_method(DBusConnection *dbus_conn,
800                                         DBusMessage *message, void *user_data)
801 {
802         unregister_agent();
803         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
804 }
805
806 static DBusMessage *get_reply_on_error(DBusMessage *message, int error)
807 {
808         return g_dbus_create_error(message,
809                 IWD_AGENT_ERROR_INTERFACE ".Failed", "Invalid parameters");
810 }
811
812 static DBusMessage *agent_request_passphrase(DBusConnection *dbus_conn,
813                                                 DBusMessage *message,
814                                                 void *user_data)
815 {
816         struct iwd_network *iwdn;
817         DBusMessageIter iter;
818         const char *path, *passwd;
819
820         DBG("");
821
822         dbus_message_iter_init(message, &iter);
823
824         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
825                 return get_reply_on_error(message, EINVAL);
826
827         dbus_message_iter_get_basic(&iter, &path);
828
829         iwdn = g_hash_table_lookup(networks, path);
830         if (!iwdn)
831                 return get_reply_on_error(message, EINVAL);
832
833         passwd = connman_network_get_string(iwdn->network, "WiFi.Passphrase");
834
835         return g_dbus_create_reply(message, DBUS_TYPE_STRING, &passwd,
836                                         DBUS_TYPE_INVALID);
837 }
838
839 static DBusMessage *agent_cancel(DBusConnection *dbus_conn,
840                                         DBusMessage *message, void *user_data)
841 {
842         DBusMessageIter iter;
843         const char *reason;
844
845         dbus_message_iter_init(message, &iter);
846
847         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
848                 return get_reply_on_error(message, EINVAL);
849
850         dbus_message_iter_get_basic(&iter, &reason);
851
852         DBG("cancel: %s", reason);
853
854         /*
855          * We don't have to do anything here, because we asked the
856          * user upfront for the passphrase. So
857          * agent_request_passphrase() will always send a passphrase
858          * immediately.
859          */
860
861         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
862 }
863
864 static const GDBusMethodTable agent_methods[] = {
865         { GDBUS_METHOD("Release", NULL, NULL, agent_release_method) },
866         { GDBUS_METHOD("RequestPassphrase",
867                         GDBUS_ARGS({ "path", "o" }),
868                         GDBUS_ARGS({ "passphrase", "s" }),
869                         agent_request_passphrase)},
870         { GDBUS_METHOD("Cancel",
871                         GDBUS_ARGS({ "reason", "s" }),
872                         NULL, agent_cancel) },
873         { },
874 };
875
876 static void agent_register_builder(DBusMessageIter *iter, void *user_data)
877 {
878         const char *path = AGENT_PATH;
879
880         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
881                                 &path);
882 }
883
884 static void register_agent(GDBusProxy *proxy)
885 {
886         if (!g_dbus_proxy_method_call(proxy,
887                                         "RegisterAgent",
888                                         agent_register_builder,
889                                         NULL, NULL, NULL))
890                 return;
891
892         agent_proxy = g_dbus_proxy_ref(proxy);
893 }
894
895 static void unregister_agent()
896 {
897         if (!agent_proxy)
898                 return;
899
900         g_dbus_proxy_method_call(agent_proxy,
901                                         "UnregisterAgent",
902                                         agent_register_builder,
903                                         NULL, NULL, NULL);
904
905         g_dbus_proxy_unref(agent_proxy);
906         agent_proxy = NULL;
907 }
908
909 static void iwd_is_present(DBusConnection *conn, void *user_data)
910 {
911         if (agent_registered)
912                 return;
913
914         if (!g_dbus_register_interface(connection, AGENT_PATH,
915                                         IWD_AGENT_INTERFACE, agent_methods,
916                                         NULL, NULL, NULL, NULL))
917                 return;
918
919         agent_registered = true;
920 }
921
922 static void iwd_is_out(DBusConnection *conn, void *user_data)
923 {
924         if (agent_registered) {
925                 g_dbus_unregister_interface(connection,
926                                         AGENT_PATH, IWD_AGENT_INTERFACE);
927                 agent_registered = false;
928         }
929 }
930
931 static void create_network(GDBusProxy *proxy)
932 {
933         const char *path = g_dbus_proxy_get_path(proxy);
934         struct iwd_network *iwdn;
935
936         iwdn = g_try_new0(struct iwd_network, 1);
937
938         if (!iwdn) {
939                 connman_error("Out of memory creating IWD network");
940                 return;
941         }
942
943         iwdn->path = g_strdup(path);
944         g_hash_table_replace(networks, iwdn->path, iwdn);
945
946         iwdn->proxy = g_dbus_proxy_ref(proxy);
947
948         if (!iwdn->proxy) {
949                 connman_error("Cannot create IWD network watcher %s", path);
950                 g_hash_table_remove(networks, path);
951                 return;
952         }
953
954         iwdn->device = g_strdup(proxy_get_string(proxy, "Device"));
955         iwdn->name = g_strdup(proxy_get_string(proxy, "Name"));
956         iwdn->type = g_strdup(proxy_get_string(proxy, "Type"));
957         iwdn->connected = proxy_get_bool(proxy, "Connected");
958
959         DBG("device %s name '%s' type %s connected %d",
960                 iwdn->device,
961                 iwdn->name,
962                 iwdn->type,
963                 iwdn->connected);
964
965         g_dbus_proxy_set_property_watch(iwdn->proxy,
966                         network_property_change, NULL);
967
968         add_network(path, iwdn);
969 }
970
971 static void object_added(GDBusProxy *proxy, void *user_data)
972 {
973         const char *interface;
974
975         interface = g_dbus_proxy_get_interface(proxy);
976         if (!interface) {
977                 connman_warn("Interface or proxy missing when adding "
978                                                         "iwd object");
979                 return;
980         }
981
982         DBG("%s %s", interface, g_dbus_proxy_get_path(proxy));
983
984         if (!strcmp(interface, IWD_AGENT_MANAGER_INTERFACE))
985                 register_agent(proxy);
986         else if (!strcmp(interface, IWD_ADAPTER_INTERFACE))
987                 create_adapter(proxy);
988         else if (!strcmp(interface, IWD_DEVICE_INTERFACE))
989                 create_device(proxy);
990         else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
991                 create_network(proxy);
992 }
993
994 static void object_removed(GDBusProxy *proxy, void *user_data)
995 {
996         const char *interface, *path;
997
998         interface = g_dbus_proxy_get_interface(proxy);
999         if (!interface) {
1000                 connman_warn("Interface or proxy missing when removing "
1001                                                         "iwd object");
1002                 return;
1003         }
1004
1005         path = g_dbus_proxy_get_path(proxy);
1006         DBG("%s %s", interface, path);
1007
1008         if (!strcmp(interface, IWD_AGENT_MANAGER_INTERFACE))
1009                 unregister_agent();
1010         if (!strcmp(interface, IWD_ADAPTER_INTERFACE))
1011                 g_hash_table_remove(adapters, path);
1012         else if (!strcmp(interface, IWD_DEVICE_INTERFACE))
1013                 g_hash_table_remove(devices, path);
1014         else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
1015                 g_hash_table_remove(networks, path);
1016 }
1017
1018 static int iwd_init(void)
1019 {
1020         connection = connman_dbus_get_connection();
1021         if (!connection)
1022                 goto out;
1023
1024         adapters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1025                         adapter_free);
1026
1027         devices = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1028                         device_free);
1029
1030         networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1031                         network_free);
1032
1033         if (connman_technology_driver_register(&tech_driver) < 0) {
1034                 connman_warn("Failed to initialize technology for IWD");
1035                 goto out;
1036         }
1037
1038         if (connman_device_driver_register(&device_driver) < 0) {
1039                 connman_warn("Failed to initialize device driver for "
1040                                 IWD_SERVICE);
1041                 connman_technology_driver_unregister(&tech_driver);
1042                 goto out;
1043         }
1044
1045         if (connman_network_driver_register(&network_driver) < 0) {
1046                 connman_technology_driver_unregister(&tech_driver);
1047                 connman_device_driver_unregister(&device_driver);
1048                 goto out;
1049         }
1050
1051         client = g_dbus_client_new(connection, IWD_SERVICE, IWD_PATH);
1052         if (!client) {
1053                 connman_warn("Failed to initialize D-Bus client for "
1054                                 IWD_SERVICE);
1055                 goto out;
1056         }
1057
1058         g_dbus_client_set_connect_watch(client, iwd_is_present, NULL);
1059         g_dbus_client_set_disconnect_watch(client, iwd_is_out, NULL);
1060         g_dbus_client_set_proxy_handlers(client, object_added, object_removed,
1061                         NULL, NULL);
1062
1063         return 0;
1064
1065 out:
1066         if (devices)
1067                 g_hash_table_destroy(devices);
1068
1069         if (networks)
1070                 g_hash_table_destroy(networks);
1071
1072         if (adapters)
1073                 g_hash_table_destroy(adapters);
1074
1075         if (connection)
1076                 dbus_connection_unref(connection);
1077
1078         return -EIO;
1079 }
1080
1081 static void iwd_exit(void)
1082 {
1083         connman_network_driver_unregister(&network_driver);
1084         connman_device_driver_unregister(&device_driver);
1085         connman_technology_driver_unregister(&tech_driver);
1086
1087         g_dbus_client_unref(client);
1088
1089         g_hash_table_destroy(networks);
1090         g_hash_table_destroy(devices);
1091         g_hash_table_destroy(adapters);
1092
1093         dbus_connection_unref(connection);
1094 }
1095
1096 CONNMAN_PLUGIN_DEFINE(iwd, "IWD plugin", VERSION,
1097                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, iwd_init, iwd_exit)