Add callback for wireless netlink events
[connman] / src / iface.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  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 <string.h>
27 #include <arpa/inet.h>
28
29 #include <glib.h>
30 #include <gdbus.h>
31
32 #include <hal/libhal.h>
33
34 #include "connman.h"
35
36 static GSList *drivers = NULL;
37
38 int connman_iface_register(struct connman_iface_driver *driver)
39 {
40         DBG("driver %p", driver);
41
42         drivers = g_slist_append(drivers, driver);
43
44         return 0;
45 }
46
47 void connman_iface_unregister(struct connman_iface_driver *driver)
48 {
49         DBG("driver %p", driver);
50
51         drivers = g_slist_remove(drivers, driver);
52 }
53
54 static GSList *interfaces = NULL;
55
56 struct connman_iface *__connman_iface_find(int index)
57 {
58         GSList *list;
59
60         for (list = interfaces; list; list = list->next) {
61                 struct connman_iface *iface = list->data;
62
63                 if (iface->index == index)
64                         return iface;
65         }
66
67         return NULL;
68 }
69
70 void __connman_iface_list(DBusMessageIter *iter)
71 {
72         GSList *list;
73
74         DBG("");
75
76         for (list = interfaces; list; list = list->next) {
77                 struct connman_iface *iface = list->data;
78
79                 dbus_message_iter_append_basic(iter,
80                                 DBUS_TYPE_OBJECT_PATH, &iface->path);
81         }
82 }
83
84 int connman_iface_update(struct connman_iface *iface,
85                                         enum connman_iface_state state)
86 {
87         switch (state) {
88         case CONNMAN_IFACE_STATE_ACTIVE:
89                 if (iface->type == CONNMAN_IFACE_TYPE_80211) {
90                         if (iface->driver->scan)
91                                 iface->driver->scan(iface);
92
93                         if (iface->driver->connect)
94                                 iface->driver->connect(iface, NULL);
95                 }
96                 break;
97
98         case CONNMAN_IFACE_STATE_CONNECTED:
99                 __connman_dhcp_request(iface);
100                 break;
101
102         default:
103                 break;
104         }
105
106         iface->state = state;
107
108         return 0;
109 }
110
111 static DBusMessage *enable_iface(DBusConnection *conn,
112                                         DBusMessage *msg, void *data)
113 {
114         struct connman_iface *iface = data;
115         struct connman_iface_driver *driver = iface->driver;
116         DBusMessage *reply;
117
118         DBG("conn %p", conn);
119
120         reply = dbus_message_new_method_return(msg);
121         if (reply == NULL)
122                 return NULL;
123
124         if (driver->activate)
125                 driver->activate(iface);
126
127         dbus_message_append_args(reply, DBUS_TYPE_INVALID);
128
129         return reply;
130 }
131
132 static GDBusMethodTable iface_methods[] = {
133         { "Enable", "", "", enable_iface },
134         { },
135 };
136
137 static dbus_bool_t get_type(DBusConnection *conn,
138                                         DBusMessageIter *iter, void *data)
139 {
140         struct connman_iface *iface = data;
141         const char *type;
142
143         DBG("iface %p", iface);
144
145         switch (iface->type) {
146         case CONNMAN_IFACE_TYPE_80203:
147                 type = "80203";
148                 break;
149         case CONNMAN_IFACE_TYPE_80211:
150                 type = "80211";
151                 break;
152         case CONNMAN_IFACE_TYPE_WIMAX:
153                 type = "wimax";
154                 break;
155         case CONNMAN_IFACE_TYPE_BLUETOOTH:
156                 type = "bluetooth";
157                 break;
158         default:
159                 type = "unknown";
160                 break;
161         }
162
163         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
164
165         return TRUE;
166 }
167
168 static GDBusPropertyTable iface_properties[] = {
169         { "Type", "s", get_type },
170         { },
171 };
172
173 static void device_free(void *data)
174 {
175         struct connman_iface *iface = data;
176
177         DBG("iface %p", iface);
178
179         if (iface->driver && iface->driver->remove)
180                 iface->driver->remove(iface);
181
182         g_free(iface->path);
183         g_free(iface->udi);
184         g_free(iface->sysfs);
185         g_free(iface);
186 }
187
188 static int probe_device(LibHalContext *ctx,
189                         struct connman_iface_driver *driver, const char *udi)
190 {
191         DBusConnection *conn;
192         struct connman_iface *iface;
193         char *temp, *sysfs;
194         int err;
195
196         DBG("ctx %p driver %p udi %s", ctx, driver, udi);
197
198         if (!driver->probe)
199                 return -1;
200
201         iface = g_try_new0(struct connman_iface, 1);
202         if (iface == NULL)
203                 return -1;
204
205         temp = g_path_get_basename(udi);
206         iface->path = g_strdup_printf("%s/%s", CONNMAN_IFACE_BASEPATH, temp);
207         g_free(temp);
208
209         iface->udi = g_strdup(udi);
210
211         DBG("path %s", iface->path);
212
213         sysfs = libhal_device_get_property_string(ctx, udi,
214                                                 "linux.sysfs_path", NULL);
215         if (sysfs != NULL)
216                 iface->sysfs = g_strdup(sysfs);
217
218         iface->index = -1;
219
220         if (g_str_has_prefix(driver->capability, "net") == TRUE)
221                 iface->index = libhal_device_get_property_int(ctx, udi,
222                                                 "net.linux.ifindex", NULL);
223
224         iface->type = CONNMAN_IFACE_TYPE_UNKNOWN;
225         iface->flags = 0;
226         iface->state = CONNMAN_IFACE_STATE_UNKNOWN;
227
228         DBG("iface %p", iface);
229
230         err = driver->probe(iface);
231         if (err < 0) {
232                 device_free(iface);
233                 return -1;
234         }
235
236         iface->driver = driver;
237
238         conn = libhal_ctx_get_dbus_connection(ctx);
239
240         g_dbus_register_object(conn, iface->path, iface, device_free);
241
242         interfaces = g_slist_append(interfaces, iface);
243
244         if ((iface->flags & CONNMAN_IFACE_FLAG_IPV4) &&
245                                                 driver->get_ipv4) {
246                 driver->get_ipv4(iface, &iface->ipv4);
247
248                 DBG("address %s", inet_ntoa(iface->ipv4.address));
249         }
250
251         g_dbus_register_interface(conn, iface->path,
252                                         CONNMAN_IFACE_INTERFACE,
253                                         iface_methods, NULL, iface_properties);
254
255         g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
256                                         CONNMAN_MANAGER_INTERFACE,
257                                         "InterfaceAdded",
258                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
259                                         DBUS_TYPE_INVALID);
260
261         return 0;
262 }
263
264 static void device_added(LibHalContext *ctx, const char *udi)
265 {
266         GSList *list;
267
268         DBG("ctx %p udi %s", ctx, udi);
269
270         for (list = drivers; list; list = list->next) {
271                 struct connman_iface_driver *driver = list->data;
272
273                 if (driver->capability == NULL)
274                         continue;
275
276                 if (libhal_device_query_capability(ctx, udi,
277                                         driver->capability, NULL) == TRUE) {
278                         if (probe_device(ctx, driver, udi) == 0)
279                                 break;
280                 }
281         }
282 }
283
284 static void device_removed(LibHalContext *ctx, const char *udi)
285 {
286         DBusConnection *conn;
287         GSList *list;
288
289         DBG("ctx %p udi %s", ctx, udi);
290
291         conn = libhal_ctx_get_dbus_connection(ctx);
292
293         for (list = interfaces; list; list = list->next) {
294                 struct connman_iface *iface = list->data;
295
296                 if (strcmp(udi, iface->udi) == 0) {
297                         g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
298                                         CONNMAN_MANAGER_INTERFACE,
299                                         "InterfaceRemoved",
300                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
301                                         DBUS_TYPE_INVALID);
302                         interfaces = g_slist_remove(interfaces, iface);
303                         g_dbus_unregister_interface(conn, iface->path,
304                                                 CONNMAN_IFACE_INTERFACE);
305                         g_dbus_unregister_object(conn, iface->path);
306                         break;
307                 }
308         }
309 }
310
311 static void probe_driver(LibHalContext *ctx,
312                                 struct connman_iface_driver *driver)
313 {
314         char **list;
315         int num;
316
317         DBG("ctx %p driver %p", ctx, driver);
318
319         list = libhal_find_device_by_capability(ctx,
320                                         driver->capability, &num, NULL);
321         if (list) {
322                 char **tmp = list;
323
324                 while (*tmp) {
325                         probe_device(ctx, driver, *tmp);
326                         tmp++;
327                 }
328
329                 libhal_free_string_array(list);
330         }
331 }
332
333 static void find_devices(LibHalContext *ctx)
334 {
335         GSList *list;
336
337         DBG("ctx %p", ctx);
338
339         for (list = drivers; list; list = list->next) {
340                 struct connman_iface_driver *driver = list->data;
341
342                 DBG("driver %p", driver);
343
344                 if (driver->capability == NULL)
345                         continue;
346
347                 probe_driver(ctx, driver);
348         }
349 }
350
351 static LibHalContext *hal_ctx = NULL;
352
353 static void hal_init(void *data)
354 {
355         DBusConnection *conn = data;
356
357         DBG("conn %p", conn);
358
359         if (hal_ctx != NULL)
360                 return;
361
362         hal_ctx = libhal_ctx_new();
363         if (hal_ctx == NULL)
364                 return;
365
366         if (libhal_ctx_set_dbus_connection(hal_ctx, conn) == FALSE) {
367                 libhal_ctx_free(hal_ctx);
368                 return;
369         }
370
371         if (libhal_ctx_init(hal_ctx, NULL) == FALSE) {
372                 libhal_ctx_free(hal_ctx);
373                 return ;
374         }
375
376         libhal_ctx_set_device_added(hal_ctx, device_added);
377         libhal_ctx_set_device_removed(hal_ctx, device_removed);
378
379         //libhal_ctx_set_device_new_capability(hal_ctx, new_capability);
380         //libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability);
381
382         find_devices(hal_ctx);
383 }
384
385 static void hal_cleanup(void *data)
386 {
387         DBusConnection *conn = data;
388         GSList *list;
389
390         DBG("conn %p", conn);
391
392         if (hal_ctx == NULL)
393                 return;
394
395         for (list = interfaces; list; list = list->next) {
396                 struct connman_iface *iface = list->data;
397
398                 DBG("path %s", iface->path);
399
400                 g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
401                                         CONNMAN_MANAGER_INTERFACE,
402                                         "InterfaceRemoved",
403                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
404                                         DBUS_TYPE_INVALID);
405
406                 g_dbus_unregister_interface(conn, iface->path,
407                                                 CONNMAN_IFACE_INTERFACE);
408
409                 g_dbus_unregister_object(conn, iface->path);
410         }
411
412         g_slist_free(interfaces);
413
414         interfaces = NULL;
415
416         libhal_ctx_shutdown(hal_ctx, NULL);
417
418         libhal_ctx_free(hal_ctx);
419
420         hal_ctx = NULL;
421 }
422
423 static DBusConnection *connection = NULL;
424 static guint hal_watch = 0;
425
426 int __connman_iface_init(DBusConnection *conn)
427 {
428         DBG("conn %p", conn);
429
430         connection = dbus_connection_ref(conn);
431         if (connection == NULL)
432                 return -1;
433
434         hal_init(connection);
435
436         hal_watch = g_dbus_add_watch(connection, "org.freedesktop.Hal",
437                                 hal_init, hal_cleanup, connection, NULL);
438
439         return 0;
440 }
441
442 void __connman_iface_cleanup(void)
443 {
444         DBG("conn %p", connection);
445
446         g_dbus_remove_watch(connection, hal_watch);
447
448         hal_cleanup(connection);
449
450         dbus_connection_unref(connection);
451 }