Use read/write lock for driver list instead of a mutex
[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
28 #include <glib.h>
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 static DBusConnection *connection;
34
35 static GStaticRWLock driver_lock = G_STATIC_RW_LOCK_INIT;
36 static GSList *driver_list = NULL;
37 static GThreadPool *driver_thread;
38
39 static GStaticMutex element_mutex = G_STATIC_MUTEX_INIT;
40 static GNode *element_root = NULL;
41 static GThreadPool *element_thread;
42
43 static const char *type2string(enum connman_element_type type)
44 {
45         switch (type) {
46         case CONNMAN_ELEMENT_TYPE_UNKNOWN:
47                 return "unknown";
48         case CONNMAN_ELEMENT_TYPE_ROOT:
49                 return "root";
50         case CONNMAN_ELEMENT_TYPE_DEVICE:
51                 return "device";
52         case CONNMAN_ELEMENT_TYPE_NETWORK:
53                 return "network";
54         case CONNMAN_ELEMENT_TYPE_IPV4:
55                 return "ipv4";
56         case CONNMAN_ELEMENT_TYPE_IPV6:
57                 return "ipv6";
58         case CONNMAN_ELEMENT_TYPE_DHCP:
59                 return "dhcp";
60         case CONNMAN_ELEMENT_TYPE_BOOTP:
61                 return "bootp";
62         case CONNMAN_ELEMENT_TYPE_ZEROCONF:
63                 return "zeroconf";
64         case CONNMAN_ELEMENT_TYPE_CONNECTION:
65                 return "42";
66         }
67
68         return NULL;
69 }
70
71 static const char *subtype2string(enum connman_element_subtype type)
72 {
73         switch (type) {
74         case CONNMAN_ELEMENT_SUBTYPE_UNKNOWN:
75                 return "unknown";
76         case CONNMAN_ELEMENT_SUBTYPE_ETHERNET:
77                 return "ethernet";
78         case CONNMAN_ELEMENT_SUBTYPE_WIFI:
79                 return "wifi";
80         case CONNMAN_ELEMENT_SUBTYPE_WIMAX:
81                 return "wimax";
82         case CONNMAN_ELEMENT_SUBTYPE_MODEM:
83                 return "modem";
84         case CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH:
85                 return "bluetooth";
86         }
87
88         return NULL;
89 }
90
91 static void append_entry(DBusMessageIter *dict,
92                                 const char *key, int type, void *val)
93 {
94         DBusMessageIter entry, value;
95         const char *signature;
96
97         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
98                                                                 NULL, &entry);
99
100         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
101
102         switch (type) {
103         case DBUS_TYPE_STRING:
104                 signature = DBUS_TYPE_STRING_AS_STRING;
105                 break;
106         case DBUS_TYPE_UINT16:
107                 signature = DBUS_TYPE_UINT16_AS_STRING;
108                 break;
109         case DBUS_TYPE_OBJECT_PATH:
110                 signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
111                 break;
112         default:
113                 signature = DBUS_TYPE_VARIANT_AS_STRING;
114                 break;
115         }
116
117         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
118                                                         signature, &value);
119         dbus_message_iter_append_basic(&value, type, val);
120         dbus_message_iter_close_container(&entry, &value);
121
122         dbus_message_iter_close_container(dict, &entry);
123 }
124
125 static void append_property(DBusMessageIter *dict,
126                                 struct connman_property *property)
127 {
128         if (property->flags & CONNMAN_PROPERTY_FLAG_STATIC) {
129                 append_entry(dict, property->name, property->type,
130                                                         &property->value);
131                 return;
132         }
133 }
134
135 static DBusMessage *get_properties(DBusConnection *conn,
136                                         DBusMessage *msg, void *data)
137 {
138         struct connman_element *element = data;
139         GSList *list;
140         DBusMessage *reply;
141         DBusMessageIter array, dict;
142         const char *str;
143
144         DBG("conn %p", conn);
145
146         reply = dbus_message_new_method_return(msg);
147         if (reply == NULL)
148                 return NULL;
149
150         dbus_message_iter_init_append(reply, &array);
151
152         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
153                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
154                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
155                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
156
157         if (element->parent != NULL)
158                 append_entry(&dict, "Parent",
159                                 DBUS_TYPE_OBJECT_PATH, &element->parent->path);
160
161         str = type2string(element->type);
162         if (str != NULL)
163                 append_entry(&dict, "Type", DBUS_TYPE_STRING, &str);
164         str = subtype2string(element->subtype);
165         if (str != NULL)
166                 append_entry(&dict, "Subtype", DBUS_TYPE_STRING, &str);
167
168         if (element->ipv4.address != NULL)
169                 append_entry(&dict, "IPv4.Address",
170                                 DBUS_TYPE_STRING, &element->ipv4.address);
171         if (element->ipv4.netmask != NULL)
172                 append_entry(&dict, "IPv4.Netmask",
173                                 DBUS_TYPE_STRING, &element->ipv4.netmask);
174         if (element->ipv4.gateway != NULL)
175                 append_entry(&dict, "IPv4.Gateway",
176                                 DBUS_TYPE_STRING, &element->ipv4.gateway);
177
178         for (list = element->properties; list; list = list->next) {
179                 struct connman_property *property = list->data;
180
181                 append_property(&dict, property);
182         }
183
184         dbus_message_iter_close_container(&array, &dict);
185
186         return reply;
187 }
188
189 static GDBusMethodTable element_methods[] = {
190         { "GetProperties", "", "a{sv}", get_properties },
191         { },
192 };
193
194 struct append_filter {
195         enum connman_element_type type;
196         DBusMessageIter *iter;
197 };
198
199 static gboolean append_path(GNode *node, gpointer data)
200 {
201         struct connman_element *element = node->data;
202         struct append_filter *filter = data;
203
204         DBG("element %p name %s", element, element->name);
205
206         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
207                 return FALSE;
208
209         if (filter->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
210                                         filter->type != element->type)
211                 return FALSE;
212
213         dbus_message_iter_append_basic(filter->iter,
214                                 DBUS_TYPE_OBJECT_PATH, &element->path);
215
216         return FALSE;
217 }
218
219 void __connman_element_list(enum connman_element_type type,
220                                                 DBusMessageIter *iter)
221 {
222         struct append_filter filter = { type, iter };
223
224         DBG("");
225
226         g_static_mutex_lock(&element_mutex);
227         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
228                                                         append_path, &filter);
229         g_static_mutex_unlock(&element_mutex);
230 }
231
232 static gint compare_priority(gconstpointer a, gconstpointer b)
233 {
234         const struct connman_driver *driver1 = a;
235         const struct connman_driver *driver2 = b;
236
237         return driver2->priority - driver1->priority;
238 }
239
240 int connman_driver_register(struct connman_driver *driver)
241 {
242         DBG("driver %p name %s", driver, driver->name);
243
244         if (driver->type == CONNMAN_ELEMENT_TYPE_ROOT)
245                 return -1;
246
247         if (!driver->probe)
248                 return -EINVAL;
249
250         g_static_rw_lock_writer_lock(&driver_lock);
251         driver_list = g_slist_insert_sorted(driver_list, driver,
252                                                         compare_priority);
253         g_static_rw_lock_writer_unlock(&driver_lock);
254
255         g_thread_pool_push(driver_thread, driver, NULL);
256
257         return 0;
258 }
259
260 static gboolean remove_driver(GNode *node, gpointer data)
261 {
262         struct connman_element *element = node->data;
263         struct connman_driver *driver = data;
264
265         DBG("element %p name %s", element, element->name);
266
267         if (element->driver == driver) {
268                 if (driver->remove)
269                         driver->remove(element);
270                 element->driver = NULL;
271         }
272
273         return FALSE;
274 }
275
276 void connman_driver_unregister(struct connman_driver *driver)
277 {
278         DBG("driver %p name %s", driver, driver->name);
279
280         g_static_mutex_lock(&element_mutex);
281         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
282                                                         remove_driver, driver);
283         g_static_mutex_unlock(&element_mutex);
284
285         g_static_rw_lock_writer_lock(&driver_lock);
286         driver_list = g_slist_remove(driver_list, driver);
287         g_static_rw_lock_writer_unlock(&driver_lock);
288 }
289
290 struct connman_element *connman_element_create(void)
291 {
292         struct connman_element *element;
293
294         element = g_new0(struct connman_element, 1);
295
296         DBG("element %p", element);
297
298         element->refcount = 1;
299
300         element->type    = CONNMAN_ELEMENT_TYPE_UNKNOWN;
301         element->subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN;
302         element->state   = CONNMAN_ELEMENT_STATE_CLOSED;
303
304         element->netdev.index = -1;
305
306         return element;
307 }
308
309 struct connman_element *connman_element_ref(struct connman_element *element)
310 {
311         DBG("element %p name %s refcount %d", element, element->name,
312                                 g_atomic_int_get(&element->refcount) + 1);
313
314         g_atomic_int_inc(&element->refcount);
315
316         return element;
317 }
318
319 void connman_element_unref(struct connman_element *element)
320 {
321         DBG("element %p name %s refcount %d", element, element->name,
322                                 g_atomic_int_get(&element->refcount) - 1);
323
324         if (g_atomic_int_dec_and_test(&element->refcount) == TRUE) {
325                 GSList *list;
326
327                 for (list = element->properties; list; list = list->next) {
328                         struct connman_property *property = list->data;
329                         if ((property->flags & CONNMAN_PROPERTY_FLAG_STATIC) &&
330                                         property->type == DBUS_TYPE_STRING)
331                                 g_free(property->value);
332                         g_free(property);
333                         list->data = NULL;
334                 }
335                 g_slist_free(element->properties);
336
337                 g_free(element->ipv4.address);
338                 g_free(element->ipv4.netmask);
339                 g_free(element->ipv4.gateway);
340                 g_free(element->ipv4.network);
341                 g_free(element->ipv4.broadcast);
342                 g_free(element->ipv4.nameserver);
343                 g_free(element->netdev.name);
344                 g_free(element->path);
345                 g_free(element->name);
346                 g_free(element);
347         }
348 }
349
350 int connman_element_add_static_property(struct connman_element *element,
351                                 const char *name, int type, const void *value)
352 {
353         struct connman_property *property;
354
355         DBG("element %p name %s", element, element->name);
356
357         if (type != DBUS_TYPE_STRING)
358                 return -EINVAL;
359
360         property = g_try_new0(struct connman_property, 1);
361         if (property == NULL)
362                 return -ENOMEM;
363
364         property->flags = CONNMAN_PROPERTY_FLAG_STATIC;
365
366         property->name = g_strdup(name);
367         property->type = type;
368
369         DBG("name %s type %d value %p", name, type, value);
370
371         switch (type) {
372         case DBUS_TYPE_STRING:
373                 property->value = g_strdup(*((const char **) value));
374                 break;
375         }
376
377         element->properties = g_slist_append(element->properties, property);
378
379         return 0;
380 }
381
382 int connman_element_set_property(struct connman_element *element,
383                         enum connman_property_type type, const void *value)
384 {
385         switch (type) {
386         case CONNMAN_PROPERTY_TYPE_INVALID:
387                 return -EINVAL;
388         case CONNMAN_PROPERTY_TYPE_IPV4_ADDRESS:
389                 g_free(element->ipv4.address);
390                 element->ipv4.address = g_strdup(*((const char **) value));
391                 break;
392         case CONNMAN_PROPERTY_TYPE_IPV4_NETMASK:
393                 g_free(element->ipv4.netmask);
394                 element->ipv4.netmask = g_strdup(*((const char **) value));
395                 break;
396         case CONNMAN_PROPERTY_TYPE_IPV4_GATEWAY:
397                 g_free(element->ipv4.gateway);
398                 element->ipv4.gateway = g_strdup(*((const char **) value));
399                 break;
400         }
401
402         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
403                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
404                                 DBUS_TYPE_OBJECT_PATH, &element->path,
405                                                         DBUS_TYPE_INVALID);
406
407         return 0;
408 }
409
410 int connman_element_get_value(struct connman_element *element,
411                                 enum connman_property_type type, void *value)
412 {
413         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
414                 return -EINVAL;
415
416         switch (type) {
417         case CONNMAN_PROPERTY_TYPE_INVALID:
418                 return -EINVAL;
419         case CONNMAN_PROPERTY_TYPE_IPV4_ADDRESS:
420                 if (element->ipv4.address == NULL)
421                         return connman_element_get_value(element->parent,
422                                                                 type, value);
423                 *((char **) value) = element->ipv4.address;
424                 break;
425         case CONNMAN_PROPERTY_TYPE_IPV4_NETMASK:
426                 if (element->ipv4.netmask == NULL)
427                         return connman_element_get_value(element->parent,
428                                                                 type, value);
429                 *((char **) value) = element->ipv4.netmask;
430                 break;
431         case CONNMAN_PROPERTY_TYPE_IPV4_GATEWAY:
432                 if (element->ipv4.gateway == NULL)
433                         return connman_element_get_value(element->parent,
434                                                                 type, value);
435                 *((char **) value) = element->ipv4.gateway;
436                 break;
437         }
438
439         return 0;
440 }
441
442 int connman_element_register(struct connman_element *element,
443                                         struct connman_element *parent)
444 {
445         GNode *node;
446         const gchar *basepath;
447
448         DBG("element %p name %s parent %p", element, element->name, parent);
449
450         if (connman_element_ref(element) == NULL)
451                 return -1;
452
453         g_static_mutex_lock(&element_mutex);
454
455         if (parent) {
456                 node = g_node_find(element_root, G_PRE_ORDER,
457                                                 G_TRAVERSE_ALL, parent);
458                 basepath = parent->path;
459
460                 if (element->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
461                         element->subtype = parent->subtype;
462         } else {
463                 node = element_root;
464                 basepath = "";
465         }
466
467         if (element->name == NULL) {
468                 switch (element->type) {
469                 case CONNMAN_ELEMENT_TYPE_IPV4:
470                         element->name = g_strdup("ipv4");
471                         break;
472                 case CONNMAN_ELEMENT_TYPE_IPV6:
473                         element->name = g_strdup("ipv6");
474                         break;
475                 case CONNMAN_ELEMENT_TYPE_DHCP:
476                         element->name = g_strdup("dhcp");
477                         break;
478                 case CONNMAN_ELEMENT_TYPE_BOOTP:
479                         element->name = g_strdup("bootp");
480                         break;
481                 case CONNMAN_ELEMENT_TYPE_ZEROCONF:
482                         element->name = g_strdup("zeroconf");
483                         break;
484                 default:
485                         break;
486                 }
487         }
488
489         element->path = g_strdup_printf("%s/%s", basepath, element->name);
490         element->parent = parent;
491
492         DBG("element %p path %s", element, element->path);
493
494         g_node_append_data(node, element);
495
496         g_static_mutex_unlock(&element_mutex);
497
498         g_dbus_register_interface(connection, element->path,
499                                         CONNMAN_ELEMENT_INTERFACE,
500                                         element_methods, NULL, NULL,
501                                                         element, NULL);
502
503         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
504                                 CONNMAN_MANAGER_INTERFACE, "ElementAdded",
505                                 DBUS_TYPE_OBJECT_PATH, &element->path,
506                                                         DBUS_TYPE_INVALID);
507
508         if (element->type == CONNMAN_ELEMENT_TYPE_DEVICE)
509                 g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
510                                 CONNMAN_MANAGER_INTERFACE, "DeviceAdded",
511                                 DBUS_TYPE_OBJECT_PATH, &element->path,
512                                                         DBUS_TYPE_INVALID);
513
514         g_thread_pool_push(element_thread, element, NULL);
515
516         return 0;
517 }
518
519 void connman_element_unregister(struct connman_element *element)
520 {
521         GNode *node;
522
523         DBG("element %p name %s", element, element->name);
524
525         if (element->type == CONNMAN_ELEMENT_TYPE_DEVICE)
526                 g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
527                                 CONNMAN_MANAGER_INTERFACE, "DeviceRemoved",
528                                 DBUS_TYPE_OBJECT_PATH, &element->path,
529                                                         DBUS_TYPE_INVALID);
530
531         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
532                                 CONNMAN_MANAGER_INTERFACE, "ElementRemoved",
533                                 DBUS_TYPE_OBJECT_PATH, &element->path,
534                                                         DBUS_TYPE_INVALID);
535
536         g_dbus_unregister_interface(connection, element->path,
537                                                 CONNMAN_ELEMENT_INTERFACE);
538
539         g_static_mutex_lock(&element_mutex);
540
541         if (element->driver) {
542                 if (element->driver->remove)
543                         element->driver->remove(element);
544                 element->driver = NULL;
545         }
546
547         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
548         if (node != NULL) {
549                 g_node_unlink(node);
550                 g_node_destroy(node);
551         }
552
553         g_static_mutex_unlock(&element_mutex);
554
555         connman_element_unref(element);
556 }
557
558 void connman_element_update(struct connman_element *element)
559 {
560         DBG("element %p name %s", element, element->name);
561
562         g_static_mutex_lock(&element_mutex);
563
564         if (element->driver && element->driver->update)
565                 element->driver->update(element);
566
567         g_static_mutex_unlock(&element_mutex);
568
569         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
570                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
571                                 DBUS_TYPE_OBJECT_PATH, &element->path,
572                                                         DBUS_TYPE_INVALID);
573 }
574
575 static inline void set_driver(struct connman_element *element,
576                                                 struct connman_driver *driver)
577 {
578         g_static_mutex_lock(&element_mutex);
579         element->driver = driver;
580         g_static_mutex_unlock(&element_mutex);
581 }
582
583 static gboolean match_driver(struct connman_element *element,
584                                         struct connman_driver *driver)
585 {
586         if (element->type != driver->type &&
587                         driver->type != CONNMAN_ELEMENT_TYPE_UNKNOWN)
588                 return FALSE;
589
590         if (element->subtype == driver->subtype ||
591                         driver->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
592                 return TRUE;
593
594         return FALSE;
595 }
596
597 static gboolean probe_driver(GNode *node, gpointer data)
598 {
599         struct connman_element *element = node->data;
600         struct connman_driver *driver = data;
601
602         DBG("element %p name %s", element, element->name);
603
604         if (!element->driver && match_driver(element, driver) == TRUE) {
605                 element->driver = driver;
606
607                 if (driver->probe(element) < 0)
608                         element->driver = NULL;
609         }
610
611         return FALSE;
612 }
613
614 static void driver_probe(gpointer data, gpointer user_data)
615 {
616         struct connman_driver *driver = data;
617
618         DBG("driver %p name %s", driver, driver->name);
619
620         g_static_mutex_lock(&element_mutex);
621         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
622                                                         probe_driver, driver);
623         g_static_mutex_unlock(&element_mutex);
624 }
625
626 static void element_probe(gpointer data, gpointer user_data)
627 {
628         struct connman_element *element = data;
629         GSList *list;
630
631         DBG("element %p name %s", element, element->name);
632
633         if (connman_element_ref(element) == NULL)
634                 return;
635
636         g_static_rw_lock_reader_lock(&driver_lock);
637
638         for (list = driver_list; list; list = list->next) {
639                 struct connman_driver *driver = list->data;
640
641                 DBG("driver %p name %s", driver, driver->name);
642
643                 set_driver(element, driver);
644
645                 if (match_driver(element, driver) == TRUE &&
646                                                 driver->probe(element) == 0)
647                         break;
648
649                 set_driver(element, NULL);
650         }
651
652         g_static_rw_lock_reader_unlock(&driver_lock);
653
654         connman_element_unref(element);
655 }
656
657 int __connman_element_init(DBusConnection *conn)
658 {
659         struct connman_element *element;
660
661         DBG("conn %p", conn);
662
663         connection = dbus_connection_ref(conn);
664         if (connection == NULL)
665                 return -EIO;
666
667         g_static_mutex_lock(&element_mutex);
668
669         element = connman_element_create();
670
671         element->name = g_strdup("root");
672         element->path = g_strdup("/");
673         element->type = CONNMAN_ELEMENT_TYPE_ROOT;
674
675         element_root = g_node_new(element);
676
677         g_static_mutex_unlock(&element_mutex);
678
679         element_thread = g_thread_pool_new(element_probe, NULL, 1, FALSE, NULL);
680
681         driver_thread = g_thread_pool_new(driver_probe, NULL, 1, FALSE, NULL);
682
683         return 0;
684 }
685
686 static gboolean free_node(GNode *node, gpointer data)
687 {
688         struct connman_element *element = node->data;
689
690         DBG("element %p name %s", element, element->name);
691
692         g_dbus_unregister_interface(connection, element->path,
693                                                 CONNMAN_ELEMENT_INTERFACE);
694
695         if (element->driver) {
696                 if (element->driver->remove)
697                         element->driver->remove(element);
698                 element->driver = NULL;
699         }
700
701         connman_element_unref(element);
702
703         node->data = NULL;
704
705         return FALSE;
706 }
707
708 void __connman_element_cleanup(void)
709 {
710         DBG("");
711
712         g_thread_pool_free(driver_thread, TRUE, TRUE);
713
714         g_thread_pool_free(element_thread, TRUE, TRUE);
715
716         g_static_mutex_lock(&element_mutex);
717
718         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
719                                                         free_node, NULL);
720
721         g_node_destroy(element_root);
722         element_root = NULL;
723
724         g_static_mutex_unlock(&element_mutex);
725
726         dbus_connection_unref(connection);
727 }