428a3e130b242ddf5c78431545ba5ca2f7da26f8
[connman] / src / element.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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 <errno.h>
27 #include <stdarg.h>
28
29 #include <glib.h>
30 #include <gdbus.h>
31
32 #include "connman.h"
33
34 static DBusConnection *connection;
35
36 static GStaticRWLock element_lock = G_STATIC_RW_LOCK_INIT;
37 static GNode *element_root = NULL;
38
39 static GSList *driver_list = NULL;
40
41 static GThreadPool *thread_register = NULL;
42 static GThreadPool *thread_unregister = NULL;
43 static GThreadPool *thread_unregister_children = NULL;
44
45 static gchar *device_filter = NULL;
46
47 static struct {
48         enum connman_property_id id;
49         int type;
50         const char *name;
51         const void *value;
52 } propid_table[] = {
53         { CONNMAN_PROPERTY_ID_IPV4_METHOD,
54                 DBUS_TYPE_STRING, "IPv4.Method", "dhcp" },
55         { CONNMAN_PROPERTY_ID_IPV4_ADDRESS,
56                 DBUS_TYPE_STRING, "IPv4.Address" },
57         { CONNMAN_PROPERTY_ID_IPV4_NETMASK,
58                 DBUS_TYPE_STRING, "IPv4.Netmask" },
59         { CONNMAN_PROPERTY_ID_IPV4_GATEWAY,
60                 DBUS_TYPE_STRING, "IPv4.Gateway" },
61         { CONNMAN_PROPERTY_ID_IPV4_NAMESERVER,
62                 DBUS_TYPE_STRING, "IPv4.Nameserver" },
63         { }
64 };
65
66 static int propid2type(enum connman_property_id id)
67 {
68         int i;
69
70         for (i = 0; propid_table[i].name; i++) {
71                 if (propid_table[i].id == id)
72                         return propid_table[i].type;
73         }
74
75         return DBUS_TYPE_INVALID;
76 }
77
78 static const char *propid2name(enum connman_property_id id)
79 {
80         int i;
81
82         for (i = 0; propid_table[i].name; i++) {
83                 if (propid_table[i].id == id)
84                         return propid_table[i].name;
85         }
86
87         return NULL;
88 }
89
90 static const char *type2string(enum connman_element_type type)
91 {
92         switch (type) {
93         case CONNMAN_ELEMENT_TYPE_UNKNOWN:
94                 return "unknown";
95         case CONNMAN_ELEMENT_TYPE_ROOT:
96                 return "root";
97         case CONNMAN_ELEMENT_TYPE_PROFILE:
98                 return "profile";
99         case CONNMAN_ELEMENT_TYPE_DEVICE:
100                 return "device";
101         case CONNMAN_ELEMENT_TYPE_NETWORK:
102                 return "network";
103         case CONNMAN_ELEMENT_TYPE_IPV4:
104                 return "ipv4";
105         case CONNMAN_ELEMENT_TYPE_IPV6:
106                 return "ipv6";
107         case CONNMAN_ELEMENT_TYPE_DHCP:
108                 return "dhcp";
109         case CONNMAN_ELEMENT_TYPE_BOOTP:
110                 return "bootp";
111         case CONNMAN_ELEMENT_TYPE_ZEROCONF:
112                 return "zeroconf";
113         case CONNMAN_ELEMENT_TYPE_RESOLVER:
114                 return "resolver";
115         case CONNMAN_ELEMENT_TYPE_INTERNET:
116                 return "internet";
117         }
118
119         return NULL;
120 }
121
122 static const char *subtype2string(enum connman_element_subtype type)
123 {
124         switch (type) {
125         case CONNMAN_ELEMENT_SUBTYPE_UNKNOWN:
126                 return "unknown";
127         case CONNMAN_ELEMENT_SUBTYPE_FAKE:
128                 return "fake";
129         case CONNMAN_ELEMENT_SUBTYPE_NETWORK:
130                 return "network";
131         case CONNMAN_ELEMENT_SUBTYPE_ETHERNET:
132                 return "ethernet";
133         case CONNMAN_ELEMENT_SUBTYPE_WIFI:
134                 return "wifi";
135         case CONNMAN_ELEMENT_SUBTYPE_WIMAX:
136                 return "wimax";
137         case CONNMAN_ELEMENT_SUBTYPE_MODEM:
138                 return "modem";
139         case CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH:
140                 return "bluetooth";
141         }
142
143         return NULL;
144 }
145
146 static void append_entry(DBusMessageIter *dict,
147                                 const char *key, int type, void *val)
148 {
149         DBusMessageIter entry, value;
150         const char *signature;
151
152         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
153                                                                 NULL, &entry);
154
155         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
156
157         switch (type) {
158         case DBUS_TYPE_BOOLEAN:
159                 signature = DBUS_TYPE_BOOLEAN_AS_STRING;
160                 break;
161         case DBUS_TYPE_STRING:
162                 signature = DBUS_TYPE_STRING_AS_STRING;
163                 break;
164         case DBUS_TYPE_UINT16:
165                 signature = DBUS_TYPE_UINT16_AS_STRING;
166                 break;
167         case DBUS_TYPE_UINT32:
168                 signature = DBUS_TYPE_UINT32_AS_STRING;
169                 break;
170         case DBUS_TYPE_OBJECT_PATH:
171                 signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
172                 break;
173         default:
174                 signature = DBUS_TYPE_VARIANT_AS_STRING;
175                 break;
176         }
177
178         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
179                                                         signature, &value);
180         dbus_message_iter_append_basic(&value, type, val);
181         dbus_message_iter_close_container(&entry, &value);
182
183         dbus_message_iter_close_container(dict, &entry);
184 }
185
186 static void append_property(DBusMessageIter *dict,
187                                 struct connman_property *property)
188 {
189         if (property->value == NULL)
190                 return;
191
192         append_entry(dict, property->name, property->type, &property->value);
193 }
194
195 static DBusMessage *get_properties(DBusConnection *conn,
196                                         DBusMessage *msg, void *data)
197 {
198         struct connman_element *element = data;
199         GSList *list;
200         DBusMessage *reply;
201         DBusMessageIter array, dict;
202         const char *str;
203
204         DBG("conn %p", conn);
205
206         reply = dbus_message_new_method_return(msg);
207         if (reply == NULL)
208                 return NULL;
209
210         dbus_message_iter_init_append(reply, &array);
211
212         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
213                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
214                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
215                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
216
217         if (element->parent != NULL &&
218                         element->parent->type != CONNMAN_ELEMENT_TYPE_ROOT) {
219                 append_entry(&dict, "Parent",
220                                 DBUS_TYPE_OBJECT_PATH, &element->parent->path);
221         }
222
223         str = type2string(element->type);
224         if (str != NULL)
225                 append_entry(&dict, "Type", DBUS_TYPE_STRING, &str);
226         str = subtype2string(element->subtype);
227         if (str != NULL)
228                 append_entry(&dict, "Subtype", DBUS_TYPE_STRING, &str);
229
230         append_entry(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &element->enabled);
231
232         if (element->priority > 0)
233                 append_entry(&dict, "Priority",
234                                 DBUS_TYPE_UINT16, &element->priority);
235
236 #if 0
237         if (element->network.identifier != NULL)
238                 append_entry(&dict, "Identifier",
239                                 DBUS_TYPE_STRING, &element->network.identifier);
240 #endif
241
242         if (element->ipv4.address != NULL)
243                 append_entry(&dict, "IPv4.Address",
244                                 DBUS_TYPE_STRING, &element->ipv4.address);
245         if (element->ipv4.netmask != NULL)
246                 append_entry(&dict, "IPv4.Netmask",
247                                 DBUS_TYPE_STRING, &element->ipv4.netmask);
248         if (element->ipv4.gateway != NULL)
249                 append_entry(&dict, "IPv4.Gateway",
250                                 DBUS_TYPE_STRING, &element->ipv4.gateway);
251
252         connman_element_lock(element);
253
254         for (list = element->properties; list; list = list->next) {
255                 struct connman_property *property = list->data;
256
257                 append_property(&dict, property);
258         }
259
260         connman_element_unlock(element);
261
262         dbus_message_iter_close_container(&array, &dict);
263
264         return reply;
265 }
266
267 static DBusMessage *set_property(DBusConnection *conn,
268                                         DBusMessage *msg, void *data)
269 {
270         struct connman_element *element = data;
271         DBusMessageIter iter;
272         DBusMessageIter value;
273         const char *name;
274         GSList *list;
275
276         DBG("conn %p", conn);
277
278         if (dbus_message_iter_init(msg, &iter) == FALSE)
279                 return __connman_error_invalid_arguments(msg);
280
281         dbus_message_iter_get_basic(&iter, &name);
282         dbus_message_iter_next(&iter);
283         dbus_message_iter_recurse(&iter, &value);
284
285         if (__connman_security_check_privileges(msg) < 0)
286                 return __connman_error_permission_denied(msg);
287
288         connman_element_lock(element);
289
290         for (list = element->properties; list; list = list->next) {
291                 struct connman_property *property = list->data;
292                 const char *str;
293
294                 if (g_str_equal(property->name, name) == FALSE)
295                         continue;
296
297                 if (property->flags & CONNMAN_PROPERTY_FLAG_STATIC)
298                         continue;
299
300                 property->flags &= ~CONNMAN_PROPERTY_FLAG_REFERENCE;
301
302                 if (property->type == DBUS_TYPE_STRING) {
303                         dbus_message_iter_get_basic(&value, &str);
304                         g_free(property->value);
305                         property->value = g_strdup(str);
306                 } else
307                         property->value = NULL;
308         }
309
310         connman_element_unlock(element);
311
312         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
313 }
314
315 static DBusMessage *clear_property(DBusConnection *conn,
316                                         DBusMessage *msg, void *data)
317 {
318         struct connman_element *element = data;
319         const char *name;
320         GSList *list;
321
322         DBG("conn %p", conn);
323
324         if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name,
325                                                 DBUS_TYPE_INVALID) == FALSE)
326                 return __connman_error_invalid_arguments(msg);
327
328         if (__connman_security_check_privileges(msg) < 0)
329                 return __connman_error_permission_denied(msg);
330
331         connman_element_lock(element);
332
333         for (list = element->properties; list; list = list->next) {
334                 struct connman_property *property = list->data;
335
336                 if (g_str_equal(property->name, name) == FALSE)
337                         continue;
338
339                 if (property->flags & CONNMAN_PROPERTY_FLAG_STATIC)
340                         continue;
341
342                 if (property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE)
343                         continue;
344
345                 property->flags |= CONNMAN_PROPERTY_FLAG_REFERENCE;
346
347                 if (property->type == DBUS_TYPE_STRING)
348                         g_free(property->value);
349
350                 property->value = NULL;
351         }
352
353         connman_element_unlock(element);
354
355         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
356 }
357
358 static DBusMessage *do_update(DBusConnection *conn,
359                                         DBusMessage *msg, void *data)
360 {
361         struct connman_element *element = data;
362
363         DBG("conn %p", conn);
364
365         if (element->enabled == FALSE)
366                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
367
368         if (element->driver && element->driver->update) {
369                 DBG("Calling update callback");
370                 element->driver->update(element);
371         }
372
373         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
374 }
375
376 static DBusMessage *do_enable(DBusConnection *conn,
377                                         DBusMessage *msg, void *data)
378 {
379         struct connman_element *element = data;
380
381         DBG("conn %p", conn);
382
383         if (element->enabled == TRUE)
384                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
385
386         if (element->driver && element->driver->enable) {
387                 DBG("Calling enable callback");
388                 if (element->driver->enable(element) < 0)
389                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
390         }
391
392         element->enabled = TRUE;
393
394         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
395                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
396                                 DBUS_TYPE_OBJECT_PATH, &element->path,
397                                                         DBUS_TYPE_INVALID);
398
399         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
400 }
401
402 static DBusMessage *do_disable(DBusConnection *conn,
403                                         DBusMessage *msg, void *data)
404 {
405         struct connman_element *element = data;
406
407         DBG("conn %p", conn);
408
409         if (element->enabled == FALSE)
410                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
411
412         if (element->driver && element->driver->disable) {
413                 DBG("Calling disable callback");
414                 if (element->driver->disable(element) < 0)
415                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
416         }
417
418         element->enabled = FALSE;
419
420         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
421                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
422                                 DBUS_TYPE_OBJECT_PATH, &element->path,
423                                                         DBUS_TYPE_INVALID);
424
425         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
426 }
427
428 static GDBusMethodTable element_methods[] = {
429         { "GetProperties", "",   "a{sv}", get_properties },
430         { "SetProperty",   "sv", "",      set_property   },
431         { "ClearProperty", "s",  "",      clear_property },
432         { "Update",        "",   "",      do_update      },
433         { "Enable",        "",   "",      do_enable      },
434         { "Disable",       "",   "",      do_disable     },
435         { },
436 };
437
438 static GDBusSignalTable element_signals[] = {
439         { "PropertyChanged", "sv" },
440         { },
441 };
442
443 struct append_filter {
444         enum connman_element_type type;
445         DBusMessageIter *iter;
446 };
447
448 static gboolean append_path(GNode *node, gpointer data)
449 {
450         struct connman_element *element = node->data;
451         struct append_filter *filter = data;
452
453         DBG("element %p name %s", element, element->name);
454
455         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
456                 return FALSE;
457
458         if (filter->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
459                                         filter->type != element->type)
460                 return FALSE;
461
462         dbus_message_iter_append_basic(filter->iter,
463                                 DBUS_TYPE_OBJECT_PATH, &element->path);
464
465         return FALSE;
466 }
467
468 void __connman_element_list(enum connman_element_type type,
469                                                 DBusMessageIter *iter)
470 {
471         struct append_filter filter = { type, iter };
472
473         DBG("");
474
475         g_static_rw_lock_reader_lock(&element_lock);
476         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
477                                                         append_path, &filter);
478         g_static_rw_lock_reader_unlock(&element_lock);
479 }
480
481 static gint compare_priority(gconstpointer a, gconstpointer b)
482 {
483         const struct connman_driver *driver1 = a;
484         const struct connman_driver *driver2 = b;
485
486         return driver2->priority - driver1->priority;
487 }
488
489 static gboolean match_driver(struct connman_element *element,
490                                         struct connman_driver *driver)
491 {
492         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
493                 return FALSE;
494
495         if (element->type != driver->type &&
496                         driver->type != CONNMAN_ELEMENT_TYPE_UNKNOWN)
497                 return FALSE;
498
499         if (element->subtype == driver->subtype ||
500                         driver->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
501                 return TRUE;
502
503         return FALSE;
504 }
505
506 static gboolean probe_driver(GNode *node, gpointer data)
507 {
508         struct connman_element *element = node->data;
509         struct connman_driver *driver = data;
510
511         DBG("element %p name %s", element, element->name);
512
513         if (!element->driver && match_driver(element, driver) == TRUE) {
514                 if (driver->probe(element) < 0)
515                         return FALSE;
516
517                 connman_element_lock(element);
518                 element->driver = driver;
519                 connman_element_unlock(element);
520         }
521
522         return FALSE;
523 }
524
525 void __connman_driver_rescan(struct connman_driver *driver)
526 {
527         DBG("driver %p name %s", driver, driver->name);
528
529         if (!driver->probe)
530                 return;
531
532         g_static_rw_lock_writer_lock(&element_lock);
533
534         if (element_root != NULL)
535                 g_node_traverse(element_root, G_PRE_ORDER,
536                                 G_TRAVERSE_ALL, -1, probe_driver, driver);
537
538         g_static_rw_lock_writer_unlock(&element_lock);
539 }
540
541 /**
542  * connman_driver_register:
543  * @driver: driver definition
544  *
545  * Register a new driver
546  *
547  * Returns: %0 on success
548  */
549 int connman_driver_register(struct connman_driver *driver)
550 {
551         DBG("driver %p name %s", driver, driver->name);
552
553         if (driver->type == CONNMAN_ELEMENT_TYPE_ROOT)
554                 return -EINVAL;
555
556         if (!driver->probe)
557                 return -EINVAL;
558
559         g_static_rw_lock_writer_lock(&element_lock);
560
561         driver_list = g_slist_insert_sorted(driver_list, driver,
562                                                         compare_priority);
563
564         if (element_root != NULL)
565                 g_node_traverse(element_root, G_PRE_ORDER,
566                                 G_TRAVERSE_ALL, -1, probe_driver, driver);
567
568         g_static_rw_lock_writer_unlock(&element_lock);
569
570         return 0;
571 }
572
573 static gboolean remove_driver(GNode *node, gpointer data)
574 {
575         struct connman_element *element = node->data;
576         struct connman_driver *driver = data;
577
578         DBG("element %p name %s", element, element->name);
579
580         if (element->driver == driver) {
581                 if (driver->remove)
582                         driver->remove(element);
583
584                 connman_element_lock(element);
585                 element->driver = NULL;
586                 connman_element_unlock(element);
587         }
588
589         return FALSE;
590 }
591
592 /**
593  * connman_driver_unregister:
594  * @driver: driver definition
595  *
596  * Remove a previously registered driver
597  */
598 void connman_driver_unregister(struct connman_driver *driver)
599 {
600         DBG("driver %p name %s", driver, driver->name);
601
602         g_static_rw_lock_writer_lock(&element_lock);
603
604         driver_list = g_slist_remove(driver_list, driver);
605
606         if (element_root != NULL)
607                 g_node_traverse(element_root, G_POST_ORDER,
608                                 G_TRAVERSE_ALL, -1, remove_driver, driver);
609
610         g_static_rw_lock_writer_unlock(&element_lock);
611 }
612
613 /**
614  * connman_element_create:
615  * @name: element name
616  *
617  * Allocate a new element and assign the given #name to it. If the name
618  * is #NULL, it will be later on created based on the element type.
619  *
620  * Returns: a newly-allocated #connman_element structure
621  */
622 struct connman_element *connman_element_create(const char *name)
623 {
624         struct connman_element *element;
625
626         element = g_try_new0(struct connman_element, 1);
627         if (element == NULL)
628                 return NULL;
629
630         DBG("element %p", element);
631
632         element->refcount = 1;
633
634         g_static_mutex_init(&element->mutex);
635
636         element->name    = g_strdup(name);
637         element->type    = CONNMAN_ELEMENT_TYPE_UNKNOWN;
638         element->subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN;
639         element->state   = CONNMAN_ELEMENT_STATE_CLOSED;
640         element->index   = -1;
641         element->enabled = FALSE;
642
643         return element;
644 }
645
646 struct connman_element *connman_element_ref(struct connman_element *element)
647 {
648         DBG("element %p name %s refcount %d", element, element->name,
649                                 g_atomic_int_get(&element->refcount) + 1);
650
651         g_atomic_int_inc(&element->refcount);
652
653         return element;
654 }
655
656 static void free_properties(struct connman_element *element)
657 {
658         GSList *list;
659
660         DBG("element %p name %s", element, element->name);
661
662         connman_element_lock(element);
663
664         for (list = element->properties; list; list = list->next) {
665                 struct connman_property *property = list->data;
666
667                 if (!(property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE)) {
668                         if (property->type == DBUS_TYPE_STRING)
669                                 g_free(property->value);
670                 }
671
672                 g_free(property);
673         }
674
675         g_slist_free(element->properties);
676
677         element->properties = NULL;
678
679         connman_element_unlock(element);
680 }
681
682 void connman_element_unref(struct connman_element *element)
683 {
684         DBG("element %p name %s refcount %d", element, element->name,
685                                 g_atomic_int_get(&element->refcount) - 1);
686
687         if (g_atomic_int_dec_and_test(&element->refcount) == TRUE) {
688                 free_properties(element);
689                 g_free(element->ipv4.address);
690                 g_free(element->ipv4.netmask);
691                 g_free(element->ipv4.gateway);
692                 g_free(element->ipv4.network);
693                 g_free(element->ipv4.broadcast);
694                 g_free(element->ipv4.nameserver);
695                 g_free(element->network.identifier);
696                 g_free(element->path);
697                 g_free(element->name);
698                 g_free(element);
699         }
700 }
701
702 int connman_element_add_static_property(struct connman_element *element,
703                                 const char *name, int type, const void *value)
704 {
705         struct connman_property *property;
706
707         DBG("element %p name %s", element, element->name);
708
709         if (type != DBUS_TYPE_STRING)
710                 return -EINVAL;
711
712         property = g_try_new0(struct connman_property, 1);
713         if (property == NULL)
714                 return -ENOMEM;
715
716         property->flags = CONNMAN_PROPERTY_FLAG_STATIC;
717         property->id    = CONNMAN_PROPERTY_ID_INVALID;
718         property->name  = g_strdup(name);
719         property->type  = type;
720
721         DBG("name %s type %d value %p", name, type, value);
722
723         switch (type) {
724         case DBUS_TYPE_STRING:
725                 property->value = g_strdup(*((const char **) value));
726                 break;
727         }
728
729         connman_element_lock(element);
730         element->properties = g_slist_append(element->properties, property);
731         connman_element_unlock(element);
732
733         return 0;
734 }
735
736 static void *get_reference_value(struct connman_element *element,
737                                                 enum connman_property_id id)
738 {
739         GSList *list;
740
741         DBG("element %p name %s", element, element->name);
742
743         for (list = element->properties; list; list = list->next) {
744                 struct connman_property *property = list->data;
745
746                 if (property->id != id)
747                         continue;
748
749                 if (!(property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE))
750                         return property->value;
751         }
752
753         if (element->parent == NULL)
754                 return NULL;
755
756         return get_reference_value(element->parent, id);
757 }
758
759 static void set_reference_properties(struct connman_element *element)
760 {
761         GSList *list;
762
763         DBG("element %p name %s", element, element->name);
764
765         for (list = element->properties; list; list = list->next) {
766                 struct connman_property *property = list->data;
767
768                 if (!(property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE))
769                         continue;
770
771                 property->value = get_reference_value(element->parent,
772                                                                 property->id);
773         }
774 }
775
776 static struct connman_property *create_property(struct connman_element *element,
777                                                 enum connman_property_id id)
778 {
779         struct connman_property *property;
780         GSList *list;
781
782         DBG("element %p name %s", element, element->name);
783
784         connman_element_lock(element);
785
786         for (list = element->properties; list; list = list->next) {
787                 property = list->data;
788
789                 if (property->id == id)
790                         goto unlock;
791         }
792
793         property = g_try_new0(struct connman_property, 1);
794         if (property == NULL)
795                 goto unlock;
796
797         property->flags = CONNMAN_PROPERTY_FLAG_REFERENCE;
798         property->id    = id;
799         property->name  = g_strdup(propid2name(id));
800         property->type  = propid2type(id);
801
802         if (property->name == NULL) {
803                 g_free(property);
804                 property = NULL;
805                 goto unlock;
806         }
807
808         element->properties = g_slist_append(element->properties, property);
809
810 unlock:
811         connman_element_unlock(element);
812
813         return property;
814 }
815
816 static void create_default_properties(struct connman_element *element)
817 {
818         struct connman_property *property;
819         int i;
820
821         DBG("element %p name %s", element, element->name);
822
823         for (i = 0; propid_table[i].name; i++) {
824                 DBG("property %s", propid_table[i].name);
825
826                 property = create_property(element, propid_table[i].id);
827
828                 property->flags &= ~CONNMAN_PROPERTY_FLAG_REFERENCE;
829
830                 if (propid_table[i].type != DBUS_TYPE_STRING)
831                         continue;
832
833                 if (propid_table[i].value)
834                         property->value = g_strdup(propid_table[i].value);
835                 else
836                         property->value = g_strdup("");
837         }
838 }
839
840 static int define_properties_valist(struct connman_element *element,
841                                                                 va_list args)
842 {
843         enum connman_property_id id;
844
845         DBG("element %p name %s", element, element->name);
846
847         id = va_arg(args, enum connman_property_id);
848
849         while (id != CONNMAN_PROPERTY_ID_INVALID) {
850
851                 DBG("property %d", id);
852
853                 create_property(element, id);
854
855                 id = va_arg(args, enum connman_property_id);
856         }
857
858         return 0;
859 }
860
861 /**
862  * connman_element_define_properties:
863  * @element: an element
864  * @varargs: list of property identifiers
865  *
866  * Define the valid properties for an element.
867  *
868  * Returns: %0 on success
869  */
870 int connman_element_define_properties(struct connman_element *element, ...)
871 {
872         va_list args;
873         int err;
874
875         DBG("element %p name %s", element, element->name);
876
877         va_start(args, element);
878
879         err = define_properties_valist(element, args);
880
881         va_end(args);
882
883         return err;
884 }
885
886 int connman_element_create_property(struct connman_element *element,
887                                                 const char *name, int type)
888 {
889         return -EIO;
890 }
891
892 int connman_element_set_property(struct connman_element *element,
893                                 enum connman_property_id id, const void *value)
894 {
895         switch (id) {
896         case CONNMAN_PROPERTY_ID_IPV4_ADDRESS:
897                 connman_element_lock(element);
898                 g_free(element->ipv4.address);
899                 element->ipv4.address = g_strdup(*((const char **) value));
900                 connman_element_unlock(element);
901                 break;
902         case CONNMAN_PROPERTY_ID_IPV4_NETMASK:
903                 connman_element_lock(element);
904                 g_free(element->ipv4.netmask);
905                 element->ipv4.netmask = g_strdup(*((const char **) value));
906                 connman_element_unlock(element);
907                 break;
908         case CONNMAN_PROPERTY_ID_IPV4_GATEWAY:
909                 connman_element_lock(element);
910                 g_free(element->ipv4.gateway);
911                 element->ipv4.gateway = g_strdup(*((const char **) value));
912                 connman_element_unlock(element);
913                 break;
914         case CONNMAN_PROPERTY_ID_IPV4_NAMESERVER:
915                 connman_element_lock(element);
916                 g_free(element->ipv4.nameserver);
917                 element->ipv4.nameserver = g_strdup(*((const char **) value));
918                 connman_element_unlock(element);
919                 break;
920         default:
921                 return -EINVAL;
922         }
923
924         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
925                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
926                                 DBUS_TYPE_OBJECT_PATH, &element->path,
927                                                         DBUS_TYPE_INVALID);
928
929         return 0;
930 }
931
932 int connman_element_get_value(struct connman_element *element,
933                                 enum connman_property_id id, void *value)
934 {
935         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
936                 return -EINVAL;
937
938         switch (id) {
939         case CONNMAN_PROPERTY_ID_IPV4_ADDRESS:
940                 if (element->ipv4.address == NULL)
941                         return connman_element_get_value(element->parent,
942                                                                 id, value);
943                 connman_element_lock(element);
944                 *((char **) value) = element->ipv4.address;
945                 connman_element_unlock(element);
946                 break;
947         case CONNMAN_PROPERTY_ID_IPV4_NETMASK:
948                 if (element->ipv4.netmask == NULL)
949                         return connman_element_get_value(element->parent,
950                                                                 id, value);
951                 connman_element_lock(element);
952                 *((char **) value) = element->ipv4.netmask;
953                 connman_element_unlock(element);
954                 break;
955         case CONNMAN_PROPERTY_ID_IPV4_GATEWAY:
956                 if (element->ipv4.gateway == NULL)
957                         return connman_element_get_value(element->parent,
958                                                                 id, value);
959                 connman_element_lock(element);
960                 *((char **) value) = element->ipv4.gateway;
961                 connman_element_unlock(element);
962                 break;
963         case CONNMAN_PROPERTY_ID_IPV4_NAMESERVER:
964                 if (element->ipv4.nameserver == NULL)
965                         return connman_element_get_value(element->parent,
966                                                                 id, value);
967                 connman_element_lock(element);
968                 *((char **) value) = element->ipv4.nameserver;
969                 connman_element_unlock(element);
970                 break;
971         default:
972                 return -EINVAL;
973         }
974
975         return 0;
976 }
977
978 /**
979  * connman_element_register:
980  * @element: the element to register
981  * @parent: the parent to register the element with
982  *
983  * Register an element with the core. It will be register under the given
984  * parent of if %NULL is provided under the root element.
985  *
986  * Returns: %0 on success
987  */
988 int connman_element_register(struct connman_element *element,
989                                         struct connman_element *parent)
990 {
991         DBG("element %p name %s parent %p", element, element->name, parent);
992
993         if (device_filter && element->type == CONNMAN_ELEMENT_TYPE_DEVICE) {
994                 if (g_pattern_match_simple(device_filter,
995                                                 element->name) == FALSE) {
996                         DBG("ignoring %s device", element->name);
997                         return -EPERM;
998                 }
999         }
1000
1001         if (connman_element_ref(element) == NULL)
1002                 return -EINVAL;
1003
1004         connman_element_lock(element);
1005
1006         __connman_element_load(element);
1007
1008         if (element->name == NULL) {
1009                 element->name = g_strdup(type2string(element->type));
1010                 if (element->name == NULL) {
1011                         connman_element_unlock(element);
1012                         return -EINVAL;
1013                 }
1014         }
1015
1016         element->parent = parent;
1017
1018         connman_element_unlock(element);
1019
1020         if (thread_register != NULL)
1021                 g_thread_pool_push(thread_register, element, NULL);
1022
1023         return 0;
1024 }
1025
1026 void connman_element_unregister(struct connman_element *element)
1027 {
1028         DBG("element %p name %s", element, element->name);
1029
1030         if (thread_unregister != NULL)
1031                 g_thread_pool_push(thread_unregister, element, NULL);
1032 }
1033
1034 void connman_element_unregister_children(struct connman_element *element)
1035 {
1036         DBG("element %p name %s", element, element->name);
1037
1038         if (thread_unregister_children != NULL)
1039                 g_thread_pool_push(thread_unregister_children, element, NULL);
1040 }
1041
1042 static gboolean update_element(GNode *node, gpointer user_data)
1043 {
1044         struct connman_element *element = node->data;
1045
1046         DBG("element %p name %s", element, element->name);
1047
1048         if (element->driver && element->driver->update)
1049                 element->driver->update(element);
1050
1051         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
1052                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
1053                                 DBUS_TYPE_OBJECT_PATH, &element->path,
1054                                                         DBUS_TYPE_INVALID);
1055
1056         return FALSE;
1057 }
1058
1059 void connman_element_update(struct connman_element *element)
1060 {
1061         GNode *node;
1062
1063         DBG("element %p name %s", element, element->name);
1064
1065         g_static_rw_lock_reader_lock(&element_lock);
1066
1067         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
1068
1069         if (node != NULL)
1070                 g_node_traverse(node, G_PRE_ORDER,
1071                                 G_TRAVERSE_ALL, -1, update_element, NULL);
1072
1073         g_static_rw_lock_reader_unlock(&element_lock);
1074 }
1075
1076 int connman_element_set_enabled(struct connman_element *element,
1077                                                         gboolean enabled)
1078 {
1079         if (element->enabled == enabled)
1080                 return 0;
1081
1082         element->enabled = enabled;
1083
1084         connman_element_update(element);
1085
1086         return 0;
1087 }
1088
1089 static void register_element(gpointer data, gpointer user_data)
1090 {
1091         struct connman_element *element = data;
1092         const gchar *basepath;
1093         GSList *list;
1094         GNode *node;
1095
1096         g_static_rw_lock_writer_lock(&element_lock);
1097
1098         connman_element_lock(element);
1099
1100         if (element->parent) {
1101                 node = g_node_find(element_root, G_PRE_ORDER,
1102                                         G_TRAVERSE_ALL, element->parent);
1103                 basepath = element->parent->path;
1104
1105                 if (element->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
1106                         element->subtype = element->parent->subtype;
1107         } else {
1108                 element->parent = element_root->data;
1109
1110                 node = element_root;
1111                 basepath = "";
1112         }
1113
1114         element->path = g_strdup_printf("%s/%s", basepath, element->name);
1115
1116         set_reference_properties(element);
1117
1118         connman_element_unlock(element);
1119
1120         DBG("element %p path %s", element, element->path);
1121
1122         g_node_append_data(node, element);
1123
1124         if (g_dbus_register_interface(connection, element->path,
1125                                         CONNMAN_ELEMENT_INTERFACE,
1126                                         element_methods, element_signals,
1127                                         NULL, element, NULL) == FALSE)
1128                 connman_error("Failed to register %s", element->path);
1129
1130         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
1131                                 CONNMAN_MANAGER_INTERFACE, "ElementAdded",
1132                                 DBUS_TYPE_OBJECT_PATH, &element->path,
1133                                                         DBUS_TYPE_INVALID);
1134
1135         g_static_rw_lock_writer_unlock(&element_lock);
1136
1137         __connman_element_store(element);
1138
1139         g_static_rw_lock_writer_lock(&element_lock);
1140
1141         for (list = driver_list; list; list = list->next) {
1142                 struct connman_driver *driver = list->data;
1143
1144                 if (match_driver(element, driver) == FALSE)
1145                         continue;
1146
1147                 DBG("driver %p name %s", driver, driver->name);
1148
1149                 if (driver->probe(element) == 0) {
1150                         connman_element_lock(element);
1151                         element->driver = driver;
1152                         connman_element_unlock(element);
1153                         break;
1154                 }
1155         }
1156
1157         g_static_rw_lock_writer_unlock(&element_lock);
1158 }
1159
1160 static gboolean remove_element(GNode *node, gpointer user_data)
1161 {
1162         struct connman_element *element = node->data;
1163         struct connman_element *root = user_data;
1164
1165         DBG("element %p name %s", element, element->name);
1166
1167         if (element == root)
1168                 return FALSE;
1169
1170         if (element->driver) {
1171                 if (element->driver->remove)
1172                         element->driver->remove(element);
1173
1174                 connman_element_lock(element);
1175                 element->driver = NULL;
1176                 connman_element_unlock(element);
1177         }
1178
1179         if (node != NULL) {
1180                 g_node_unlink(node);
1181                 g_node_destroy(node);
1182         }
1183
1184         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
1185                                 CONNMAN_MANAGER_INTERFACE, "ElementRemoved",
1186                                 DBUS_TYPE_OBJECT_PATH, &element->path,
1187                                                         DBUS_TYPE_INVALID);
1188
1189         g_dbus_unregister_interface(connection, element->path,
1190                                                 CONNMAN_ELEMENT_INTERFACE);
1191
1192         connman_element_unref(element);
1193
1194         return FALSE;
1195 }
1196
1197 static void unregister_element(gpointer data, gpointer user_data)
1198 {
1199         struct connman_element *element = data;
1200         GNode *node;
1201
1202         DBG("element %p name %s", element, element->name);
1203
1204         g_static_rw_lock_writer_lock(&element_lock);
1205
1206         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
1207
1208         if (node != NULL)
1209                 g_node_traverse(node, G_POST_ORDER,
1210                                 G_TRAVERSE_ALL, -1, remove_element, NULL);
1211
1212         g_static_rw_lock_writer_unlock(&element_lock);
1213 }
1214
1215 static void unregister_children(gpointer data, gpointer user_data)
1216 {
1217         struct connman_element *element = data;
1218         GNode *node;
1219
1220         DBG("element %p name %s", element, element->name);
1221
1222         g_static_rw_lock_writer_lock(&element_lock);
1223
1224         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
1225
1226         if (node != NULL)
1227                 g_node_traverse(node, G_POST_ORDER,
1228                                 G_TRAVERSE_ALL, -1, remove_element, element);
1229
1230         g_static_rw_lock_writer_unlock(&element_lock);
1231 }
1232
1233 int __connman_element_init(DBusConnection *conn, const char *device)
1234 {
1235         struct connman_element *element;
1236
1237         DBG("conn %p", conn);
1238
1239         connection = dbus_connection_ref(conn);
1240         if (connection == NULL)
1241                 return -EIO;
1242
1243         device_filter = g_strdup(device);
1244
1245         g_static_rw_lock_writer_lock(&element_lock);
1246
1247         element = connman_element_create("root");
1248
1249         element->path = g_strdup("/");
1250         element->type = CONNMAN_ELEMENT_TYPE_ROOT;
1251
1252         create_default_properties(element);
1253
1254         element_root = g_node_new(element);
1255
1256         g_static_rw_lock_writer_unlock(&element_lock);
1257
1258         thread_register = g_thread_pool_new(register_element,
1259                                                         NULL, 1, FALSE, NULL);
1260         thread_unregister = g_thread_pool_new(unregister_element,
1261                                                         NULL, 1, FALSE, NULL);
1262         thread_unregister_children = g_thread_pool_new(unregister_children,
1263                                                         NULL, 1, FALSE, NULL);
1264
1265         __connman_device_init();
1266
1267         return 0;
1268 }
1269
1270 static gboolean free_driver(GNode *node, gpointer data)
1271 {
1272         struct connman_element *element = node->data;
1273
1274         DBG("element %p name %s", element, element->name);
1275
1276         if (element->driver) {
1277                 if (element->driver->remove)
1278                         element->driver->remove(element);
1279
1280                 connman_element_lock(element);
1281                 element->driver = NULL;
1282                 connman_element_unlock(element);
1283         }
1284
1285         return FALSE;
1286 }
1287
1288 static gboolean free_node(GNode *node, gpointer data)
1289 {
1290         struct connman_element *element = node->data;
1291
1292         DBG("element %p name %s", element, element->name);
1293
1294         if (g_node_depth(node) > 1)
1295                 g_thread_pool_push(thread_unregister, element, NULL);
1296
1297         return FALSE;
1298 }
1299
1300 void __connman_element_cleanup(void)
1301 {
1302         DBG("");
1303
1304         __connman_device_cleanup();
1305
1306         g_thread_pool_free(thread_register, TRUE, TRUE);
1307         thread_register = NULL;
1308
1309         g_static_rw_lock_writer_lock(&element_lock);
1310         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1311                                                         free_driver, NULL);
1312         g_static_rw_lock_writer_unlock(&element_lock);
1313
1314         g_static_rw_lock_writer_lock(&element_lock);
1315         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1316                                                         free_node, NULL);
1317         g_static_rw_lock_writer_unlock(&element_lock);
1318
1319         g_thread_pool_free(thread_unregister, FALSE, TRUE);
1320         thread_unregister = NULL;
1321
1322         g_thread_pool_free(thread_unregister_children, FALSE, TRUE);
1323         thread_unregister_children = NULL;
1324
1325         g_static_rw_lock_writer_lock(&element_lock);
1326         g_node_destroy(element_root);
1327         element_root = NULL;
1328         g_static_rw_lock_writer_unlock(&element_lock);
1329
1330         g_free(device_filter);
1331
1332         dbus_connection_unref(connection);
1333 }