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