Add service creation and reference counting functions
[connman] / src / profile.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  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 <glib.h>
27 #include <gdbus.h>
28
29 #include "connman.h"
30
31 #define PROFILE_DEFAULT  "/profile/default"
32
33 struct connman_group {
34         GSequenceIter *iter;
35         char *id;
36         char *path;
37         char *name;
38         char *mode;
39         char *security;
40         connman_uint8_t strength;
41         connman_bool_t favorite;
42         enum connman_service_type type;
43         enum connman_service_state state;
44         struct connman_network *network;
45 };
46
47 static GSequence *groups = NULL;
48
49 static DBusConnection *connection = NULL;
50
51 static const char *type2string(enum connman_service_type type)
52 {
53         switch (type) {
54         case CONNMAN_SERVICE_TYPE_UNKNOWN:
55                 break;
56         case CONNMAN_SERVICE_TYPE_ETHERNET:
57                 return "ethernet";
58         case CONNMAN_SERVICE_TYPE_WIFI:
59                 return "wifi";
60         case CONNMAN_SERVICE_TYPE_WIMAX:
61                 return "wimax";
62         }
63
64         return NULL;
65 }
66
67 static const char *state2string(enum connman_service_state state)
68 {
69         switch (state) {
70         case CONNMAN_SERVICE_STATE_UNKNOWN:
71                 break;
72         case CONNMAN_SERVICE_STATE_IDLE:
73                 return "idle";
74         case CONNMAN_SERVICE_STATE_ASSOCIATION:
75                 return "association";
76         case CONNMAN_SERVICE_STATE_CONFIGURATION:
77                 return "configuration";
78         case CONNMAN_SERVICE_STATE_READY:
79                 return "ready";
80         case CONNMAN_SERVICE_STATE_DISCONNECT:
81                 return "disconnect";
82         case CONNMAN_SERVICE_STATE_FAILURE:
83                 return "failure";
84         }
85
86         return NULL;
87 }
88
89 static DBusMessage *get_properties(DBusConnection *conn,
90                                         DBusMessage *msg, void *data)
91 {
92         struct connman_group *group = data;
93         DBusMessage *reply;
94         DBusMessageIter array, dict;
95         const char *str;
96
97         DBG("conn %p", conn);
98
99         reply = dbus_message_new_method_return(msg);
100         if (reply == NULL)
101                 return NULL;
102
103         dbus_message_iter_init_append(reply, &array);
104
105         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
106                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
107                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
108                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
109
110         str = type2string(group->type);
111         if (str != NULL)
112                 connman_dbus_dict_append_variant(&dict, "Type",
113                                                 DBUS_TYPE_STRING, &str);
114
115         str = state2string(group->state);
116         if (str != NULL)
117                 connman_dbus_dict_append_variant(&dict, "State",
118                                                 DBUS_TYPE_STRING, &str);
119
120         if (group->name != NULL)
121                 connman_dbus_dict_append_variant(&dict, "Name",
122                                         DBUS_TYPE_STRING, &group->name);
123
124         if (group->mode != NULL)
125                 connman_dbus_dict_append_variant(&dict, "Mode",
126                                         DBUS_TYPE_STRING, &group->mode);
127
128         if (group->security != NULL)
129                 connman_dbus_dict_append_variant(&dict, "Security",
130                                         DBUS_TYPE_STRING, &group->security);
131
132         if (group->strength > 0)
133                 connman_dbus_dict_append_variant(&dict, "Strength",
134                                         DBUS_TYPE_BYTE, &group->strength);
135
136         connman_dbus_dict_append_variant(&dict, "Favorite",
137                                         DBUS_TYPE_BOOLEAN, &group->favorite);
138
139         dbus_message_iter_close_container(&array, &dict);
140
141         return reply;
142 }
143
144 static DBusMessage *connect_service(DBusConnection *conn,
145                                         DBusMessage *msg, void *data)
146 {
147         struct connman_group *group = data;
148
149         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
150                 return __connman_error_not_supported(msg);
151
152         return __connman_error_not_implemented(msg);
153 }
154
155 static DBusMessage *disconnect_service(DBusConnection *conn,
156                                         DBusMessage *msg, void *data)
157 {
158         struct connman_group *group = data;
159
160         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
161                 return __connman_error_not_supported(msg);
162
163         return __connman_error_not_implemented(msg);
164 }
165
166 static DBusMessage *remove_service(DBusConnection *conn,
167                                         DBusMessage *msg, void *data)
168 {
169         struct connman_group *group = data;
170
171         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
172                 return __connman_error_not_supported(msg);
173
174         group->favorite = FALSE;
175
176         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
177 }
178
179 static DBusMessage *move_before(DBusConnection *conn,
180                                         DBusMessage *msg, void *data)
181 {
182         struct connman_group *group = data;
183
184         if (group->favorite == FALSE)
185                 return __connman_error_not_supported(msg);
186
187         return __connman_error_not_implemented(msg);
188 }
189
190 static DBusMessage *move_after(DBusConnection *conn,
191                                         DBusMessage *msg, void *data)
192 {
193         struct connman_group *group = data;
194
195         if (group->favorite == FALSE)
196                 return __connman_error_not_supported(msg);
197
198         return __connman_error_not_implemented(msg);
199 }
200
201 static GDBusMethodTable service_methods[] = {
202         { "GetProperties", "",  "a{sv}", get_properties     },
203         { "Connect",       "",  "",      connect_service    },
204         { "Disconnect",    "",  "",      disconnect_service },
205         { "Remove",        "",  "",      remove_service     },
206         { "MoveBefore",    "o", "",      move_before        },
207         { "MoveAfter",     "o", "",      move_after         },
208         { },
209 };
210
211 static GDBusSignalTable service_signals[] = {
212         { "PropertyChanged", "sv" },
213         { },
214 };
215
216 const char *__connman_profile_active(void)
217 {
218         DBG("");
219
220         return PROFILE_DEFAULT;
221 }
222
223 static void append_path(gpointer value, gpointer user_data)
224 {
225         struct connman_group *group = value;
226         DBusMessageIter *iter = user_data;
227
228         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
229                                                         &group->path);
230 }
231
232 void __connman_profile_list_services(DBusMessageIter *iter)
233 {
234         DBG("");
235
236         g_sequence_foreach(groups, append_path, iter);
237 }
238
239 static void append_services(DBusMessageIter *entry)
240 {
241         DBusMessageIter value, iter;
242         const char *key = "Services";
243
244         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
245
246         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
247                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
248                                                                 &value);
249
250         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
251                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
252         __connman_profile_list_services(&iter);
253         dbus_message_iter_close_container(&value, &iter);
254
255         dbus_message_iter_close_container(entry, &value);
256 }
257
258 static void emit_services_signal(void)
259 {
260         const char *path = __connman_profile_active();
261         DBusMessage *signal;
262         DBusMessageIter entry;
263
264         signal = dbus_message_new_signal(path,
265                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
266         if (signal == NULL)
267                 return;
268
269         dbus_message_iter_init_append(signal, &entry);
270         append_services(&entry);
271         g_dbus_send_message(connection, signal);
272
273         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
274                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
275         if (signal == NULL)
276                 return;
277
278         dbus_message_iter_init_append(signal, &entry);
279         append_services(&entry);
280         g_dbus_send_message(connection, signal);
281 }
282
283 static void free_group(gpointer data)
284 {
285         struct connman_group *group = data;
286
287         DBG("group %p", group);
288
289         g_dbus_unregister_interface(connection, group->path,
290                                                 CONNMAN_SERVICE_INTERFACE);
291
292         g_free(group->security);
293         g_free(group->mode);
294         g_free(group->name);
295         g_free(group->path);
296         g_free(group->id);
297         g_free(group);
298 }
299
300 static gint compare_group(gconstpointer a, gconstpointer b, gpointer user_data)
301 {
302         struct connman_group *group_a = (void *) a;
303         struct connman_group *group_b = (void *) b;
304
305         if (group_a->favorite == TRUE && group_b->favorite == FALSE)
306                 return -1;
307
308         if (group_a->favorite == FALSE && group_b->favorite == TRUE)
309                 return 1;
310
311         return (gint) group_b->strength - (gint) group_a->strength;
312 }
313
314 static struct connman_group *lookup_group(const char *name)
315 {
316         GSequenceIter *iter;
317         struct connman_group *group;
318
319         DBG("name %s", name);
320
321         if (name == NULL)
322                 return NULL;
323
324         iter = g_sequence_get_begin_iter(groups);
325         while (g_sequence_iter_is_end(iter) == FALSE) {
326                 group = g_sequence_get(iter);
327
328                 if (g_strcmp0(group->id, name) == 0)
329                         goto done;
330         }
331
332         group = g_try_new0(struct connman_group, 1);
333         if (group == NULL)
334                 return NULL;
335
336         group->id = g_strdup(name);
337
338         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
339         group->path = g_strdup_printf("%s/%s", PROFILE_DEFAULT, name);
340
341         group->favorite = FALSE;
342
343         group->state = CONNMAN_SERVICE_STATE_IDLE;
344
345         group->iter = g_sequence_insert_sorted(groups, group,
346                                                 compare_group, NULL);
347
348         g_dbus_register_interface(connection, group->path,
349                                         CONNMAN_SERVICE_INTERFACE,
350                                         service_methods, service_signals,
351                                                         NULL, group, NULL);
352
353 done:
354         DBG("group %p", group);
355
356         return group;
357 }
358
359 static enum connman_service_type convert_device_type(struct connman_device *device)
360 {
361         enum connman_device_type type = connman_device_get_type(device);
362
363         switch (type) {
364         case CONNMAN_DEVICE_TYPE_UNKNOWN:
365         case CONNMAN_DEVICE_TYPE_VENDOR:
366         case CONNMAN_DEVICE_TYPE_WIFI:
367         case CONNMAN_DEVICE_TYPE_WIMAX:
368         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
369         case CONNMAN_DEVICE_TYPE_GPS:
370         case CONNMAN_DEVICE_TYPE_HSO:
371         case CONNMAN_DEVICE_TYPE_NOZOMI:
372         case CONNMAN_DEVICE_TYPE_HUAWEI:
373         case CONNMAN_DEVICE_TYPE_NOVATEL:
374                 break;
375         case CONNMAN_DEVICE_TYPE_ETHERNET:
376                 return CONNMAN_SERVICE_TYPE_ETHERNET;
377         }
378
379         return CONNMAN_SERVICE_TYPE_UNKNOWN;
380 }
381
382 int __connman_profile_add_device(struct connman_device *device)
383 {
384         struct connman_group *group;
385         char *name;
386
387         DBG("device %p", device);
388
389         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
390                                         connman_device_get_index(device));
391         group = lookup_group(name);
392         g_free(name);
393
394         if (group == NULL)
395                 return -EINVAL;
396
397         group->type = convert_device_type(device);
398
399         g_sequence_sort_changed(group->iter, compare_group, NULL);
400         emit_services_signal();
401
402         return 0;
403 }
404
405 int __connman_profile_remove_device(struct connman_device *device)
406 {
407         struct connman_group *group;
408         char *name;
409
410         DBG("device %p", device);
411
412         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
413                                         connman_device_get_index(device));
414         group = lookup_group(name);
415         g_free(name);
416
417         if (group == NULL)
418                 return -EINVAL;
419
420         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
421
422         g_sequence_sort_changed(group->iter, compare_group, NULL);
423         emit_services_signal();
424
425         return 0;
426 }
427
428 int __connman_profile_set_carrier(struct connman_device *device,
429                                                 connman_bool_t carrier)
430 {
431         struct connman_group *group;
432         char *name;
433
434         DBG("device %p carrier %d", device, carrier);
435
436         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
437                                         connman_device_get_index(device));
438         group = lookup_group(name);
439         g_free(name);
440
441         if (group == NULL)
442                 return -EINVAL;
443
444         if (group->favorite == carrier)
445                 return -EALREADY;
446
447         group->favorite = carrier;
448
449         g_sequence_sort_changed(group->iter, compare_group, NULL);
450         emit_services_signal();
451
452         return 0;
453 }
454
455 static enum connman_service_type convert_network_type(struct connman_network *network)
456 {
457         enum connman_network_type type = connman_network_get_type(network);
458
459         switch (type) {
460         case CONNMAN_NETWORK_TYPE_UNKNOWN:
461         case CONNMAN_NETWORK_TYPE_VENDOR:
462         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
463         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
464         case CONNMAN_NETWORK_TYPE_HSO:
465                 break;
466         case CONNMAN_NETWORK_TYPE_WIFI:
467                 return CONNMAN_SERVICE_TYPE_WIFI;
468         case CONNMAN_NETWORK_TYPE_WIMAX:
469                 return CONNMAN_SERVICE_TYPE_WIMAX;
470         }
471
472         return CONNMAN_SERVICE_TYPE_UNKNOWN;
473 }
474
475 int __connman_profile_add_network(struct connman_network *network)
476 {
477         struct connman_group *group;
478         char *name;
479
480         DBG("network %p", network);
481
482         if (__connman_network_get_group(network) == NULL)
483                 return -EINVAL;
484
485         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
486                                         __connman_network_get_group(network));
487         group = lookup_group(name);
488         g_free(name);
489
490         if (group == NULL)
491                 return -EINVAL;
492
493         group->type = convert_network_type(network);
494
495         g_free(group->name);
496         group->name = g_strdup(connman_network_get_string(network, "Name"));
497
498         group->strength = connman_network_get_uint8(network, "Strength");
499
500         if (group->network == NULL) {
501                 group->network = network;
502
503                 group->mode = g_strdup(connman_network_get_string(network,
504                                                                 "WiFi.Mode"));
505                 group->security = g_strdup(connman_network_get_string(network,
506                                                         "WiFi.Security"));
507         }
508
509         g_sequence_sort_changed(group->iter, compare_group, NULL);
510         emit_services_signal();
511
512         return 0;
513 }
514
515 int __connman_profile_remove_network(struct connman_network *network)
516 {
517         struct connman_group *group;
518         char *name;
519
520         DBG("network %p", network);
521
522         if (__connman_network_get_group(network) == NULL)
523                 return -EINVAL;
524
525         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
526                                         __connman_network_get_group(network));
527         group = lookup_group(name);
528         g_free(name);
529
530         if (group == NULL)
531                 return -EINVAL;
532
533         if (group->network == network) {
534                 g_free(group->security);
535                 group->security = NULL;
536
537                 g_free(group->mode);
538                 group->mode = NULL;
539
540                 group->network = NULL;
541         }
542
543         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
544
545         g_sequence_sort_changed(group->iter, compare_group, NULL);
546         emit_services_signal();
547
548         return 0;
549 }
550
551 void __connman_profile_list(DBusMessageIter *iter)
552 {
553         const char *path = __connman_profile_active();
554
555         DBG("");
556
557         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
558 }
559
560 static DBusMessage *profile_properties(DBusConnection *conn,
561                                         DBusMessage *msg, void *data)
562 {
563         const char *name = "Default";
564         DBusMessage *reply;
565         DBusMessageIter array, dict, entry;
566
567         DBG("conn %p", conn);
568
569         reply = dbus_message_new_method_return(msg);
570         if (reply == NULL)
571                 return NULL;
572
573         dbus_message_iter_init_append(reply, &array);
574
575         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
576                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
577                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
578                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
579
580         connman_dbus_dict_append_variant(&dict, "Name",
581                                                 DBUS_TYPE_STRING, &name);
582
583         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
584                                                                 NULL, &entry);
585         append_services(&entry);
586         dbus_message_iter_close_container(&dict, &entry);
587
588         dbus_message_iter_close_container(&array, &dict);
589
590         return reply;
591 }
592
593 static GDBusMethodTable profile_methods[] = {
594         { "GetProperties", "", "a{sv}", profile_properties },
595         { },
596 };
597
598 static GDBusSignalTable profile_signals[] = {
599         { "PropertyChanged", "sv" },
600         { },
601 };
602
603 int __connman_profile_init(DBusConnection *conn)
604 {
605         DBG("conn %p", conn);
606
607         connection = dbus_connection_ref(conn);
608         if (connection == NULL)
609                 return -1;
610
611         groups = g_sequence_new(free_group);
612
613         g_dbus_register_interface(connection, PROFILE_DEFAULT,
614                                         CONNMAN_PROFILE_INTERFACE,
615                                         profile_methods, profile_signals,
616                                                         NULL, NULL, NULL);
617
618         return 0;
619 }
620
621 void __connman_profile_cleanup(void)
622 {
623         DBG("conn %p", connection);
624
625         g_dbus_unregister_interface(connection, PROFILE_DEFAULT,
626                                                 CONNMAN_PROFILE_INTERFACE);
627
628         g_sequence_free(groups);
629         groups = NULL;
630
631         if (connection == NULL)
632                 return;
633
634         dbus_connection_unref(connection);
635 }