Add support for carrier service state
[connman] / src / service.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 <gdbus.h>
27
28 #include "connman.h"
29
30 static DBusConnection *connection = NULL;
31
32 static GSequence *service_list = NULL;
33 static GHashTable *service_hash = NULL;
34
35 struct connman_service {
36         gint refcount;
37         char *identifier;
38         char *path;
39         enum connman_service_type type;
40         enum connman_service_mode mode;
41         enum connman_service_security security;
42         enum connman_service_state state;
43         connman_uint8_t strength;
44         connman_bool_t favorite;
45         unsigned int order;
46         char *name;
47         struct connman_device *device;
48 };
49
50 static void append_path(gpointer value, gpointer user_data)
51 {
52         struct connman_service *service = value;
53         DBusMessageIter *iter = user_data;
54
55         if (service->path == NULL)
56                 return;
57
58         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
59                                                         &service->path);
60 }
61
62 void __connman_service_list(DBusMessageIter *iter)
63 {
64         DBG("");
65
66         g_sequence_foreach(service_list, append_path, iter);
67 }
68
69 static const char *type2string(enum connman_service_type type)
70 {
71         switch (type) {
72         case CONNMAN_SERVICE_TYPE_UNKNOWN:
73                 break;
74         case CONNMAN_SERVICE_TYPE_ETHERNET:
75                 return "ethernet";
76         case CONNMAN_SERVICE_TYPE_WIFI:
77                 return "wifi";
78         case CONNMAN_SERVICE_TYPE_WIMAX:
79                 return "wimax";
80         }
81
82         return NULL;
83 }
84
85 static const char *mode2string(enum connman_service_mode mode)
86 {
87         switch (mode) {
88         case CONNMAN_SERVICE_MODE_UNKNOWN:
89                 break;
90         case CONNMAN_SERVICE_MODE_MANAGED:
91                 return "managed";
92         case CONNMAN_SERVICE_MODE_ADHOC:
93                 return "adhoc";
94         }
95
96         return NULL;
97 }
98
99 static const char *security2string(enum connman_service_security security)
100 {
101         switch (security) {
102         case CONNMAN_SERVICE_SECURITY_UNKNOWN:
103                 break;
104         case CONNMAN_SERVICE_SECURITY_NONE:
105                 return "none";
106         case CONNMAN_SERVICE_SECURITY_WEP:
107                 return "wep";
108         case CONNMAN_SERVICE_SECURITY_WPA:
109                 return "wpa";
110         case CONNMAN_SERVICE_SECURITY_WPA2:
111                 return "wpa2";
112         }
113
114         return NULL;
115 }
116
117 static const char *state2string(enum connman_service_state state)
118 {
119         switch (state) {
120         case CONNMAN_SERVICE_STATE_UNKNOWN:
121                 break;
122         case CONNMAN_SERVICE_STATE_IDLE:
123                 return "idle";
124         case CONNMAN_SERVICE_STATE_CARRIER:
125                 return "carrier";
126         case CONNMAN_SERVICE_STATE_ASSOCIATION:
127                 return "association";
128         case CONNMAN_SERVICE_STATE_CONFIGURATION:
129                 return "configuration";
130         case CONNMAN_SERVICE_STATE_READY:
131                 return "ready";
132         case CONNMAN_SERVICE_STATE_DISCONNECT:
133                 return "disconnect";
134         case CONNMAN_SERVICE_STATE_FAILURE:
135                 return "failure";
136         }
137
138         return NULL;
139 }
140
141 static DBusMessage *get_properties(DBusConnection *conn,
142                                         DBusMessage *msg, void *data)
143 {
144         struct connman_service *service = data;
145         DBusMessage *reply;
146         DBusMessageIter array, dict;
147         const char *str;
148
149         DBG("conn %p", conn);
150
151         reply = dbus_message_new_method_return(msg);
152         if (reply == NULL)
153                 return NULL;
154
155         dbus_message_iter_init_append(reply, &array);
156
157         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
158                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
159                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
160                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
161
162         str = type2string(service->type);
163         if (str != NULL)
164                 connman_dbus_dict_append_variant(&dict, "Type",
165                                                 DBUS_TYPE_STRING, &str);
166
167         str = mode2string(service->mode);
168         if (str != NULL)
169                 connman_dbus_dict_append_variant(&dict, "Mode",
170                                                 DBUS_TYPE_STRING, &str);
171
172         str = security2string(service->security);
173         if (str != NULL)
174                 connman_dbus_dict_append_variant(&dict, "Security",
175                                                 DBUS_TYPE_STRING, &str);
176
177         str = state2string(service->state);
178         if (str != NULL)
179                 connman_dbus_dict_append_variant(&dict, "State",
180                                                 DBUS_TYPE_STRING, &str);
181
182         if (service->strength > 0)
183                 connman_dbus_dict_append_variant(&dict, "Strength",
184                                         DBUS_TYPE_BYTE, &service->strength);
185
186         connman_dbus_dict_append_variant(&dict, "Favorite",
187                                         DBUS_TYPE_BOOLEAN, &service->favorite);
188
189         if (service->name != NULL)
190                 connman_dbus_dict_append_variant(&dict, "Name",
191                                         DBUS_TYPE_STRING, &service->name);
192
193         dbus_message_iter_close_container(&array, &dict);
194
195         return reply;
196 }
197
198 static DBusMessage *connect_service(DBusConnection *conn,
199                                         DBusMessage *msg, void *data)
200 {
201         struct connman_service *service = data;
202
203         if (service->device != NULL) {
204                 if (__connman_device_connect(service->device) < 0)
205                         return __connman_error_failed(msg);
206
207                 service->state = CONNMAN_SERVICE_STATE_READY;
208         }
209
210         return __connman_error_not_supported(msg);
211 }
212
213 static DBusMessage *disconnect_service(DBusConnection *conn,
214                                         DBusMessage *msg, void *data)
215 {
216         struct connman_service *service = data;
217
218         if (service->device != NULL) {
219                 if (__connman_device_connect(service->device) < 0)
220                         return __connman_error_failed(msg);
221
222                 service->state = CONNMAN_SERVICE_STATE_IDLE;
223         }
224
225         return __connman_error_not_supported(msg);
226 }
227
228 static DBusMessage *remove_service(DBusConnection *conn,
229                                         DBusMessage *msg, void *data)
230 {
231         struct connman_service *service = data;
232
233         if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
234                 return __connman_error_not_supported(msg);
235
236         connman_service_set_favorite(service, FALSE);
237
238         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
239 }
240
241 static DBusMessage *move_before(DBusConnection *conn,
242                                         DBusMessage *msg, void *data)
243 {
244         struct connman_service *service = data;
245
246         if (service->favorite == FALSE)
247                 return __connman_error_not_supported(msg);
248
249         return __connman_error_not_implemented(msg);
250 }
251
252 static DBusMessage *move_after(DBusConnection *conn,
253                                         DBusMessage *msg, void *data)
254 {
255         struct connman_service *service = data;
256
257         if (service->favorite == FALSE)
258                 return __connman_error_not_supported(msg);
259
260         return __connman_error_not_implemented(msg);
261 }
262
263 static GDBusMethodTable service_methods[] = {
264         { "GetProperties", "",  "a{sv}", get_properties     },
265         { "Connect",       "",  "",      connect_service    },
266         { "Disconnect",    "",  "",      disconnect_service },
267         { "Remove",        "",  "",      remove_service     },
268         { "MoveBefore",    "o", "",      move_before        },
269         { "MoveAfter",     "o", "",      move_after         },
270         { },
271 };
272
273 static GDBusSignalTable service_signals[] = {
274         { "PropertyChanged", "sv" },
275         { },
276 };
277
278 static void service_free(gpointer data)
279 {
280         struct connman_service *service = data;
281         char *path = service->path;
282
283         DBG("service %p", service);
284
285         g_hash_table_remove(service_hash, service->identifier);
286
287         service->path = NULL;
288
289         if (path != NULL) {
290                 __connman_profile_changed();
291
292                 g_dbus_unregister_interface(connection, path,
293                                                 CONNMAN_SERVICE_INTERFACE);
294                 g_free(path);
295         }
296
297         g_free(service->name);
298         g_free(service->identifier);
299         g_free(service);
300 }
301
302 /**
303  * connman_service_put:
304  * @service: service structure
305  *
306  * Release service if no longer needed
307  */
308 void connman_service_put(struct connman_service *service)
309 {
310         DBG("service %p", service);
311
312         if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
313                 GSequenceIter *iter;
314
315                 iter = g_hash_table_lookup(service_hash, service->identifier);
316                 if (iter != NULL)
317                         g_sequence_remove(iter);
318                 else
319                         service_free(service);
320         }
321 }
322
323 static void __connman_service_initialize(struct connman_service *service)
324 {
325         DBG("service %p", service);
326
327         service->refcount = 1;
328
329         service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
330         service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
331         service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
332         service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
333
334         service->favorite = FALSE;
335
336         service->order = 0;
337 }
338
339 /**
340  * connman_service_create:
341  *
342  * Allocate a new service.
343  *
344  * Returns: a newly-allocated #connman_service structure
345  */
346 struct connman_service *connman_service_create(void)
347 {
348         struct connman_service *service;
349
350         service = g_try_new0(struct connman_service, 1);
351         if (service == NULL)
352                 return NULL;
353
354         DBG("service %p", service);
355
356         __connman_service_initialize(service);
357
358         return service;
359 }
360
361 /**
362  * connman_service_ref:
363  * @service: service structure
364  *
365  * Increase reference counter of service
366  */
367 struct connman_service *connman_service_ref(struct connman_service *service)
368 {
369         g_atomic_int_inc(&service->refcount);
370
371         return service;
372 }
373
374 /**
375  * connman_service_unref:
376  * @service: service structure
377  *
378  * Decrease reference counter of service
379  */
380 void connman_service_unref(struct connman_service *service)
381 {
382         connman_service_put(service);
383 }
384
385 static gint service_compare(gconstpointer a, gconstpointer b,
386                                                         gpointer user_data)
387 {
388         struct connman_service *service_a = (void *) a;
389         struct connman_service *service_b = (void *) b;
390
391         if (service_a->order > service_b->order)
392                 return -1;
393
394         if (service_a->order < service_b->order)
395                 return 1;
396
397         if (service_a->favorite == TRUE && service_b->favorite == FALSE)
398                 return -1;
399
400         if (service_a->favorite == FALSE && service_b->favorite == TRUE)
401                 return 1;
402
403         return (gint) service_b->strength - (gint) service_a->strength;
404 }
405
406 /**
407  * connman_service_set_favorite:
408  * @service: service structure
409  * @favorite: favorite value
410  *
411  * Change the favorite setting of service
412  */
413 int connman_service_set_favorite(struct connman_service *service,
414                                                 connman_bool_t favorite)
415 {
416         GSequenceIter *iter;
417
418         iter = g_hash_table_lookup(service_hash, service->identifier);
419         if (iter == NULL)
420                 return -ENOENT;
421
422         if (service->favorite)
423                 return -EALREADY;
424
425         service->favorite = favorite;
426
427         g_sequence_sort_changed(iter, service_compare, NULL);
428
429         __connman_profile_changed();
430
431         return 0;
432 }
433
434 int __connman_service_set_carrier(struct connman_service *service,
435                                                 connman_bool_t carrier)
436 {
437         switch (service->type) {
438         case CONNMAN_SERVICE_TYPE_UNKNOWN:
439         case CONNMAN_SERVICE_TYPE_WIFI:
440         case CONNMAN_SERVICE_TYPE_WIMAX:
441                 return -EINVAL;
442         case CONNMAN_SERVICE_TYPE_ETHERNET:
443                 break;
444         }
445
446         if (carrier == TRUE)
447                 service->state = CONNMAN_SERVICE_STATE_CARRIER;
448         else
449                 service->state = CONNMAN_SERVICE_STATE_IDLE;
450
451         return connman_service_set_favorite(service, carrier);
452 }
453
454 /**
455  * connman_service_lookup:
456  * @identifier: service identifier
457  *
458  * Look up a service by identifier (reference count will not be increased)
459  */
460 struct connman_service *connman_service_lookup(const char *identifier)
461 {
462         GSequenceIter *iter;
463
464         iter = g_hash_table_lookup(service_hash, identifier);
465         if (iter != NULL)
466                 return g_sequence_get(iter);
467
468         return NULL;
469 }
470
471 /**
472  * connman_service_get:
473  * @identifier: service identifier
474  *
475  * Look up a service by identifier or create a new one if not found
476  */
477 struct connman_service *connman_service_get(const char *identifier)
478 {
479         struct connman_service *service;
480         GSequenceIter *iter;
481
482         iter = g_hash_table_lookup(service_hash, identifier);
483         if (iter != NULL) {
484                 service = g_sequence_get(iter);
485                 if (service != NULL)
486                         g_atomic_int_inc(&service->refcount);
487                 return service;
488         }
489
490         service = g_try_new0(struct connman_service, 1);
491         if (service == NULL)
492                 return NULL;
493
494         DBG("service %p", service);
495
496         __connman_service_initialize(service);
497
498         service->identifier = g_strdup(identifier);
499
500         iter = g_sequence_insert_sorted(service_list, service,
501                                                 service_compare, NULL);
502
503         g_hash_table_insert(service_hash, service->identifier, iter);
504
505         return service;
506 }
507
508 static int service_register(struct connman_service *service)
509 {
510         const char *path = __connman_profile_active();
511
512         if (service->path != NULL)
513                 return -EALREADY;
514
515         service->path = g_strdup_printf("%s/%s", path, service->identifier);
516
517         g_dbus_register_interface(connection, service->path,
518                                         CONNMAN_SERVICE_INTERFACE,
519                                         service_methods, service_signals,
520                                                         NULL, service, NULL);
521
522         __connman_profile_changed();
523
524         return 0;
525 }
526
527 /**
528  * connman_service_lookup_from_device:
529  * @device: device structure
530  *
531  * Look up a service by device (reference count will not be increased)
532  */
533 struct connman_service *__connman_service_lookup_from_device(struct connman_device *device)
534 {
535         struct connman_service *service;
536         char *name;
537
538         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
539                                         connman_device_get_index(device));
540
541         service = connman_service_lookup(name);
542
543         g_free(name);
544
545         return service;
546 }
547
548 static enum connman_service_type convert_device_type(struct connman_device *device)
549 {
550         enum connman_device_type type = connman_device_get_type(device);
551
552         switch (type) {
553         case CONNMAN_DEVICE_TYPE_UNKNOWN:
554         case CONNMAN_DEVICE_TYPE_VENDOR:
555         case CONNMAN_DEVICE_TYPE_WIFI:
556         case CONNMAN_DEVICE_TYPE_WIMAX:
557         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
558         case CONNMAN_DEVICE_TYPE_GPS:
559         case CONNMAN_DEVICE_TYPE_HSO:
560         case CONNMAN_DEVICE_TYPE_NOZOMI:
561         case CONNMAN_DEVICE_TYPE_HUAWEI:
562         case CONNMAN_DEVICE_TYPE_NOVATEL:
563                 break;
564         case CONNMAN_DEVICE_TYPE_ETHERNET:
565                 return CONNMAN_SERVICE_TYPE_ETHERNET;
566         }
567
568         return CONNMAN_SERVICE_TYPE_UNKNOWN;
569 }
570
571 /**
572  * connman_service_create_from_device:
573  * @device: device structure
574  *
575  * Look up service by device and if not found, create one
576  */
577 struct connman_service *__connman_service_create_from_device(struct connman_device *device)
578 {
579         struct connman_service *service;
580         char *name;
581
582         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
583                                         connman_device_get_index(device));
584
585         service = connman_service_get(name);
586         if (service == NULL)
587                 goto done;
588
589         if (service->path != NULL) {
590                 connman_service_put(service);
591                 service = NULL;
592                 goto done;
593         }
594
595         service->type = convert_device_type(device);
596
597         service->device = device;
598
599         service_register(service);
600
601 done:
602         g_free(name);
603
604         return service;
605 }
606
607 /**
608  * connman_service_lookup_from_network:
609  * @device: device structure
610  *
611  * Look up a service by network (reference count will not be increased)
612  */
613 struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
614 {
615         struct connman_service *service;
616         char *name;
617
618         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
619                                         __connman_network_get_group(network));
620
621         service = connman_service_lookup(name);
622
623         g_free(name);
624
625         return service;
626 }
627
628 static enum connman_service_type convert_network_type(struct connman_network *network)
629 {
630         enum connman_network_type type = connman_network_get_type(network);
631
632         switch (type) {
633         case CONNMAN_NETWORK_TYPE_UNKNOWN:
634         case CONNMAN_NETWORK_TYPE_VENDOR:
635         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
636         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
637         case CONNMAN_NETWORK_TYPE_HSO:
638                 break;
639         case CONNMAN_NETWORK_TYPE_WIFI:
640                 return CONNMAN_SERVICE_TYPE_WIFI;
641         case CONNMAN_NETWORK_TYPE_WIMAX:
642                 return CONNMAN_SERVICE_TYPE_WIMAX;
643         }
644
645         return CONNMAN_SERVICE_TYPE_UNKNOWN;
646 }
647
648 /**
649  * connman_service_create_from_network:
650  * @device: device structure
651  *
652  * Look up service by network and if not found, create one
653  */
654 struct connman_service *__connman_service_create_from_network(struct connman_network *network)
655 {
656         struct connman_service *service;
657         char *name;
658
659         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
660                                         __connman_network_get_group(network));
661
662         service = connman_service_get(name);
663         if (service == NULL)
664                 goto done;
665
666         if (service->path != NULL) {
667                 connman_service_put(service);
668                 service = NULL;
669                 goto done;
670         }
671
672         service->type = convert_network_type(network);
673
674         service_register(service);
675
676 done:
677         g_free(name);
678
679         return service;
680 }
681
682 int __connman_service_init(void)
683 {
684         DBG("");
685
686         connection = connman_dbus_get_connection();
687
688         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
689                                                                 NULL, NULL);
690
691         service_list = g_sequence_new(service_free);
692
693         return 0;
694 }
695
696 void __connman_service_cleanup(void)
697 {
698         DBG("");
699
700         g_sequence_free(service_list);
701         service_list = NULL;
702
703         g_hash_table_destroy(service_hash);
704         service_hash = NULL;
705
706         dbus_connection_unref(connection);
707 }