Make sure to sort connected services first always first
[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         char *passphrase;
48         char *profile;
49         struct connman_device *device;
50         struct connman_network *network;
51         DBusMessage *pending;
52         guint timeout;
53 };
54
55 static void append_path(gpointer value, gpointer user_data)
56 {
57         struct connman_service *service = value;
58         DBusMessageIter *iter = user_data;
59
60         if (service->path == NULL)
61                 return;
62
63         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
64                                                         &service->path);
65 }
66
67 void __connman_service_list(DBusMessageIter *iter)
68 {
69         DBG("");
70
71         g_sequence_foreach(service_list, append_path, iter);
72 }
73
74 static const char *type2string(enum connman_service_type type)
75 {
76         switch (type) {
77         case CONNMAN_SERVICE_TYPE_UNKNOWN:
78                 break;
79         case CONNMAN_SERVICE_TYPE_ETHERNET:
80                 return "ethernet";
81         case CONNMAN_SERVICE_TYPE_WIFI:
82                 return "wifi";
83         case CONNMAN_SERVICE_TYPE_WIMAX:
84                 return "wimax";
85         }
86
87         return NULL;
88 }
89
90 static const char *mode2string(enum connman_service_mode mode)
91 {
92         switch (mode) {
93         case CONNMAN_SERVICE_MODE_UNKNOWN:
94                 break;
95         case CONNMAN_SERVICE_MODE_MANAGED:
96                 return "managed";
97         case CONNMAN_SERVICE_MODE_ADHOC:
98                 return "adhoc";
99         }
100
101         return NULL;
102 }
103
104 static const char *security2string(enum connman_service_security security)
105 {
106         switch (security) {
107         case CONNMAN_SERVICE_SECURITY_UNKNOWN:
108                 break;
109         case CONNMAN_SERVICE_SECURITY_NONE:
110                 return "none";
111         case CONNMAN_SERVICE_SECURITY_WEP:
112                 return "wep";
113         case CONNMAN_SERVICE_SECURITY_WPA:
114                 return "wpa";
115         case CONNMAN_SERVICE_SECURITY_WPA2:
116                 return "wpa2";
117         }
118
119         return NULL;
120 }
121
122 static const char *state2string(enum connman_service_state state)
123 {
124         switch (state) {
125         case CONNMAN_SERVICE_STATE_UNKNOWN:
126                 break;
127         case CONNMAN_SERVICE_STATE_IDLE:
128                 return "idle";
129         case CONNMAN_SERVICE_STATE_CARRIER:
130                 return "carrier";
131         case CONNMAN_SERVICE_STATE_ASSOCIATION:
132                 return "association";
133         case CONNMAN_SERVICE_STATE_CONFIGURATION:
134                 return "configuration";
135         case CONNMAN_SERVICE_STATE_READY:
136                 return "ready";
137         case CONNMAN_SERVICE_STATE_DISCONNECT:
138                 return "disconnect";
139         case CONNMAN_SERVICE_STATE_FAILURE:
140                 return "failure";
141         }
142
143         return NULL;
144 }
145
146 static void state_changed(struct connman_service *service)
147 {
148         DBusMessage *signal;
149         DBusMessageIter entry, value;
150         const char *str, *key = "State";
151
152         if (service->path == NULL)
153                 return;
154
155         str = state2string(service->state);
156         if (str == NULL)
157                 return;
158
159         signal = dbus_message_new_signal(service->path,
160                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
161         if (signal == NULL)
162                 return;
163
164         dbus_message_iter_init_append(signal, &entry);
165
166         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
167
168         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
169                                         DBUS_TYPE_STRING_AS_STRING, &value);
170         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
171         dbus_message_iter_close_container(&entry, &value);
172
173         g_dbus_send_message(connection, signal);
174 }
175
176 static DBusMessage *get_properties(DBusConnection *conn,
177                                         DBusMessage *msg, void *user_data)
178 {
179         struct connman_service *service = user_data;
180         DBusMessage *reply;
181         DBusMessageIter array, dict;
182         const char *str;
183
184         DBG("service %p", service);
185
186         reply = dbus_message_new_method_return(msg);
187         if (reply == NULL)
188                 return NULL;
189
190         dbus_message_iter_init_append(reply, &array);
191
192         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
193                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
194                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
195                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
196
197         str = type2string(service->type);
198         if (str != NULL)
199                 connman_dbus_dict_append_variant(&dict, "Type",
200                                                 DBUS_TYPE_STRING, &str);
201
202         str = mode2string(service->mode);
203         if (str != NULL)
204                 connman_dbus_dict_append_variant(&dict, "Mode",
205                                                 DBUS_TYPE_STRING, &str);
206
207         str = security2string(service->security);
208         if (str != NULL)
209                 connman_dbus_dict_append_variant(&dict, "Security",
210                                                 DBUS_TYPE_STRING, &str);
211
212         str = state2string(service->state);
213         if (str != NULL)
214                 connman_dbus_dict_append_variant(&dict, "State",
215                                                 DBUS_TYPE_STRING, &str);
216
217         if (service->strength > 0)
218                 connman_dbus_dict_append_variant(&dict, "Strength",
219                                         DBUS_TYPE_BYTE, &service->strength);
220
221         connman_dbus_dict_append_variant(&dict, "Favorite",
222                                         DBUS_TYPE_BOOLEAN, &service->favorite);
223
224         if (service->name != NULL)
225                 connman_dbus_dict_append_variant(&dict, "Name",
226                                         DBUS_TYPE_STRING, &service->name);
227
228         if (service->passphrase != NULL &&
229                         __connman_security_check_privilege(msg,
230                                 CONNMAN_SECURITY_PRIVILEGE_SECRET) == 0)
231                 connman_dbus_dict_append_variant(&dict, "Passphrase",
232                                 DBUS_TYPE_STRING, &service->passphrase);
233
234         dbus_message_iter_close_container(&array, &dict);
235
236         return reply;
237 }
238
239 static DBusMessage *set_property(DBusConnection *conn,
240                                         DBusMessage *msg, void *user_data)
241 {
242         struct connman_service *service = user_data;
243         DBusMessageIter iter, value;
244         const char *name;
245         int type;
246
247         DBG("service %p", service);
248
249         if (dbus_message_iter_init(msg, &iter) == FALSE)
250                 return __connman_error_invalid_arguments(msg);
251
252         dbus_message_iter_get_basic(&iter, &name);
253         dbus_message_iter_next(&iter);
254         dbus_message_iter_recurse(&iter, &value);
255
256         if (__connman_security_check_privilege(msg,
257                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
258                 return __connman_error_permission_denied(msg);
259
260         type = dbus_message_iter_get_arg_type(&value);
261
262         if (g_str_equal(name, "Passphrase") == TRUE) {
263                 const char *passphrase;
264
265                 if (type != DBUS_TYPE_STRING)
266                         return __connman_error_invalid_arguments(msg);
267
268                 if (__connman_security_check_privilege(msg,
269                                         CONNMAN_SECURITY_PRIVILEGE_SECRET) < 0)
270                         return __connman_error_permission_denied(msg);
271
272                 dbus_message_iter_get_basic(&value, &passphrase);
273
274                 g_free(service->passphrase);
275                 service->passphrase = g_strdup(passphrase);
276
277                 if (service->network != NULL)
278                         connman_network_set_string(service->network,
279                                 "WiFi.Passphrase", service->passphrase);
280
281                 __connman_storage_save_service(service);
282         }
283
284         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
285 }
286
287 static gboolean connect_timeout(gpointer user_data)
288 {
289         struct connman_service *service = user_data;
290
291         DBG("service %p", service);
292
293         service->timeout = 0;
294
295         if (service->pending != NULL) {
296                 DBusMessage *reply;
297
298                 reply = __connman_error_operation_timeout(service->pending);
299                 if (reply != NULL)
300                         g_dbus_send_message(connection, reply);
301
302                 dbus_message_unref(service->pending);
303                 service->pending = NULL;
304
305                 __connman_service_indicate_state(service,
306                                         CONNMAN_SERVICE_STATE_FAILURE);
307         }
308
309         return FALSE;
310 }
311
312 static DBusMessage *connect_service(DBusConnection *conn,
313                                         DBusMessage *msg, void *user_data)
314 {
315         struct connman_service *service = user_data;
316
317         DBG("service %p", service);
318
319         if (service->pending != NULL)
320                 return __connman_error_in_progress(msg);
321
322         if (service->state == CONNMAN_SERVICE_STATE_READY)
323                 return __connman_error_already_connected(msg);
324
325         if (service->network != NULL) {
326                 int err;
327
328                 connman_network_set_string(service->network,
329                                 "WiFi.Passphrase", service->passphrase);
330
331                 err = connman_network_connect(service->network);
332                 if (err < 0 && err != -EINPROGRESS)
333                         return __connman_error_failed(msg, -err);
334
335                 service->pending = dbus_message_ref(msg);
336
337                 service->timeout = g_timeout_add_seconds(45,
338                                                 connect_timeout, service);
339
340                 return NULL;
341         } else if (service->device != NULL) {
342                 if (service->favorite == FALSE)
343                         return __connman_error_no_carrier(msg);
344
345                 if (__connman_device_connect(service->device) < 0)
346                         return __connman_error_failed(msg, EINVAL);
347
348                 service->pending = dbus_message_ref(msg);
349                 service->timeout = g_timeout_add_seconds(15,
350                                                 connect_timeout, service);
351
352                 return NULL;
353         }
354
355         return __connman_error_not_supported(msg);
356 }
357
358 static DBusMessage *disconnect_service(DBusConnection *conn,
359                                         DBusMessage *msg, void *user_data)
360 {
361         struct connman_service *service = user_data;
362
363         DBG("service %p", service);
364
365         if (service->pending != NULL) {
366                 DBusMessage *reply;
367
368                 reply = __connman_error_operation_aborted(service->pending);
369                 if (reply != NULL)
370                         g_dbus_send_message(conn, reply);
371
372                 dbus_message_unref(service->pending);
373                 service->pending = NULL;
374
375                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
376         }
377
378         if (service->network != NULL) {
379                 int err;
380
381                 err = __connman_network_disconnect(service->network);
382                 if (err < 0 && err != -EINPROGRESS)
383                         return __connman_error_failed(msg, -err);
384
385                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
386         } else if (service->device != NULL) {
387                 int err;
388
389                 if (service->favorite == FALSE)
390                         return __connman_error_no_carrier(msg);
391
392                 err = __connman_device_disconnect(service->device);
393                 if (err < 0)
394                         return __connman_error_failed(msg, -err);
395
396                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
397         }
398
399         return __connman_error_not_supported(msg);
400 }
401
402 static DBusMessage *remove_service(DBusConnection *conn,
403                                         DBusMessage *msg, void *user_data)
404 {
405         struct connman_service *service = user_data;
406
407         DBG("service %p", service);
408
409         if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
410                 return __connman_error_not_supported(msg);
411
412         if (service->network != NULL) {
413                 int err;
414
415                 err = __connman_network_disconnect(service->network);
416                 if (err < 0 && err != -EINPROGRESS)
417                         return __connman_error_failed(msg, -err);
418         }
419
420         connman_service_set_favorite(service, FALSE);
421         __connman_storage_save_service(service);
422
423         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
424 }
425
426 static DBusMessage *move_before(DBusConnection *conn,
427                                         DBusMessage *msg, void *user_data)
428 {
429         struct connman_service *service = user_data;
430
431         DBG("service %p", service);
432
433         if (service->favorite == FALSE)
434                 return __connman_error_not_supported(msg);
435
436         return __connman_error_not_implemented(msg);
437 }
438
439 static DBusMessage *move_after(DBusConnection *conn,
440                                         DBusMessage *msg, void *user_data)
441 {
442         struct connman_service *service = user_data;
443
444         DBG("service %p", service);
445
446         if (service->favorite == FALSE)
447                 return __connman_error_not_supported(msg);
448
449         return __connman_error_not_implemented(msg);
450 }
451
452 static GDBusMethodTable service_methods[] = {
453         { "GetProperties", "",   "a{sv}", get_properties     },
454         { "SetProperty",   "sv", "",      set_property       },
455         { "Connect",       "",   "",      connect_service,
456                                                 G_DBUS_METHOD_FLAG_ASYNC },
457         { "Disconnect",    "",   "",      disconnect_service },
458         { "Remove",        "",   "",      remove_service     },
459         { "MoveBefore",    "o",  "",      move_before        },
460         { "MoveAfter",     "o",  "",      move_after         },
461         { },
462 };
463
464 static GDBusSignalTable service_signals[] = {
465         { "PropertyChanged", "sv" },
466         { },
467 };
468
469 static void service_free(gpointer user_data)
470 {
471         struct connman_service *service = user_data;
472         char *path = service->path;
473
474         DBG("service %p", service);
475
476         g_hash_table_remove(service_hash, service->identifier);
477
478         if (service->timeout > 0)
479                 g_source_remove(service->timeout);
480
481         if (service->pending != NULL) {
482                 dbus_message_unref(service->pending);
483                 service->pending = NULL;
484         }
485
486         service->path = NULL;
487
488         if (path != NULL) {
489                 __connman_profile_changed();
490
491                 g_dbus_unregister_interface(connection, path,
492                                                 CONNMAN_SERVICE_INTERFACE);
493                 g_free(path);
494         }
495
496         if (service->network != NULL)
497                 connman_network_unref(service->network);
498
499         g_free(service->profile);
500         g_free(service->name);
501         g_free(service->passphrase);
502         g_free(service->identifier);
503         g_free(service);
504 }
505
506 /**
507  * __connman_service_put:
508  * @service: service structure
509  *
510  * Release service if no longer needed
511  */
512 void __connman_service_put(struct connman_service *service)
513 {
514         DBG("service %p", service);
515
516         if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
517                 GSequenceIter *iter;
518
519                 iter = g_hash_table_lookup(service_hash, service->identifier);
520                 if (iter != NULL)
521                         g_sequence_remove(iter);
522                 else
523                         service_free(service);
524         }
525 }
526
527 static void __connman_service_initialize(struct connman_service *service)
528 {
529         DBG("service %p", service);
530
531         service->refcount = 1;
532
533         service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
534         service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
535         service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
536         service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
537
538         service->favorite = FALSE;
539
540         service->order = 0;
541 }
542
543 /**
544  * connman_service_create:
545  *
546  * Allocate a new service.
547  *
548  * Returns: a newly-allocated #connman_service structure
549  */
550 struct connman_service *connman_service_create(void)
551 {
552         struct connman_service *service;
553
554         service = g_try_new0(struct connman_service, 1);
555         if (service == NULL)
556                 return NULL;
557
558         DBG("service %p", service);
559
560         __connman_service_initialize(service);
561
562         return service;
563 }
564
565 /**
566  * connman_service_ref:
567  * @service: service structure
568  *
569  * Increase reference counter of service
570  */
571 struct connman_service *connman_service_ref(struct connman_service *service)
572 {
573         g_atomic_int_inc(&service->refcount);
574
575         return service;
576 }
577
578 /**
579  * connman_service_unref:
580  * @service: service structure
581  *
582  * Decrease reference counter of service
583  */
584 void connman_service_unref(struct connman_service *service)
585 {
586         __connman_service_put(service);
587 }
588
589 static gint service_compare(gconstpointer a, gconstpointer b,
590                                                         gpointer user_data)
591 {
592         struct connman_service *service_a = (void *) a;
593         struct connman_service *service_b = (void *) b;
594
595         if (service_a->state != service_b->state) {
596                 if (service_a->state == CONNMAN_SERVICE_STATE_READY)
597                         return -1;
598                 if (service_b->state == CONNMAN_SERVICE_STATE_READY)
599                         return 1;
600         }
601
602         if (service_a->order > service_b->order)
603                 return -1;
604
605         if (service_a->order < service_b->order)
606                 return 1;
607
608         if (service_a->favorite == TRUE && service_b->favorite == FALSE)
609                 return -1;
610
611         if (service_a->favorite == FALSE && service_b->favorite == TRUE)
612                 return 1;
613
614         return (gint) service_b->strength - (gint) service_a->strength;
615 }
616
617 /**
618  * connman_service_set_favorite:
619  * @service: service structure
620  * @favorite: favorite value
621  *
622  * Change the favorite setting of service
623  */
624 int connman_service_set_favorite(struct connman_service *service,
625                                                 connman_bool_t favorite)
626 {
627         GSequenceIter *iter;
628
629         iter = g_hash_table_lookup(service_hash, service->identifier);
630         if (iter == NULL)
631                 return -ENOENT;
632
633         if (service->favorite == favorite)
634                 return -EALREADY;
635
636         service->favorite = favorite;
637
638         g_sequence_sort_changed(iter, service_compare, NULL);
639
640         __connman_profile_changed();
641
642         return 0;
643 }
644
645 int __connman_service_set_carrier(struct connman_service *service,
646                                                 connman_bool_t carrier)
647 {
648         DBG("service %p carrier %d", service, carrier);
649
650         if (service == NULL)
651                 return -EINVAL;
652
653         switch (service->type) {
654         case CONNMAN_SERVICE_TYPE_UNKNOWN:
655         case CONNMAN_SERVICE_TYPE_WIFI:
656         case CONNMAN_SERVICE_TYPE_WIMAX:
657                 return -EINVAL;
658         case CONNMAN_SERVICE_TYPE_ETHERNET:
659                 break;
660         }
661
662         if (carrier == FALSE) {
663                 service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
664                 state_changed(service);
665
666                 service->state = CONNMAN_SERVICE_STATE_IDLE;
667                 state_changed(service);
668         } else {
669                 service->state = CONNMAN_SERVICE_STATE_CARRIER;
670                 state_changed(service);
671         }
672
673         return connman_service_set_favorite(service, carrier);
674 }
675
676 int __connman_service_indicate_state(struct connman_service *service,
677                                         enum connman_service_state state)
678 {
679         GSequenceIter *iter;
680
681         DBG("service %p state %d", service, state);
682
683         if (service == NULL)
684                 return -EINVAL;
685
686         if (state == CONNMAN_SERVICE_STATE_CARRIER)
687                 return __connman_service_set_carrier(service, TRUE);
688
689         if (service->state == state)
690                 return -EALREADY;
691
692         if (service->state == CONNMAN_SERVICE_STATE_IDLE &&
693                                 state == CONNMAN_SERVICE_STATE_DISCONNECT)
694                 return -EINVAL;
695
696         if (state == CONNMAN_SERVICE_STATE_IDLE &&
697                         service->state != CONNMAN_SERVICE_STATE_DISCONNECT) {
698                 service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
699                 state_changed(service);
700         }
701
702         service->state = state;
703         state_changed(service);
704
705         if (state == CONNMAN_SERVICE_STATE_READY) {
706                 connman_service_set_favorite(service, TRUE);
707                 __connman_storage_save_service(service);
708
709                 if (service->timeout > 0)
710                         g_source_remove(service->timeout);
711
712                 if (service->pending != NULL) {
713                         g_dbus_send_reply(connection, service->pending,
714                                                         DBUS_TYPE_INVALID);
715
716                         dbus_message_unref(service->pending);
717                         service->pending = NULL;
718                 }
719         }
720
721         if (state == CONNMAN_SERVICE_STATE_FAILURE) {
722                 if (service->timeout > 0)
723                         g_source_remove(service->timeout);
724
725                 if (service->pending != NULL) {
726                         DBusMessage *reply;
727
728                         reply = __connman_error_failed(service->pending, EIO);
729                         if (reply != NULL)
730                                 g_dbus_send_message(connection, reply);
731
732                         dbus_message_unref(service->pending);
733                         service->pending = NULL;
734                 }
735
736                 service->state = CONNMAN_SERVICE_STATE_IDLE;
737                 state_changed(service);
738         }
739
740         iter = g_hash_table_lookup(service_hash, service->identifier);
741         if (iter != NULL)
742                 g_sequence_sort_changed(iter, service_compare, NULL);
743
744         __connman_profile_changed();
745
746         return 0;
747 }
748
749 /**
750  * __connman_service_lookup:
751  * @identifier: service identifier
752  *
753  * Look up a service by identifier (reference count will not be increased)
754  */
755 static struct connman_service *__connman_service_lookup(const char *identifier)
756 {
757         GSequenceIter *iter;
758
759         iter = g_hash_table_lookup(service_hash, identifier);
760         if (iter != NULL)
761                 return g_sequence_get(iter);
762
763         return NULL;
764 }
765
766 /**
767  * __connman_service_get:
768  * @identifier: service identifier
769  *
770  * Look up a service by identifier or create a new one if not found
771  */
772 static struct connman_service *__connman_service_get(const char *identifier)
773 {
774         struct connman_service *service;
775         GSequenceIter *iter;
776
777         iter = g_hash_table_lookup(service_hash, identifier);
778         if (iter != NULL) {
779                 service = g_sequence_get(iter);
780                 if (service != NULL)
781                         g_atomic_int_inc(&service->refcount);
782                 return service;
783         }
784
785         service = g_try_new0(struct connman_service, 1);
786         if (service == NULL)
787                 return NULL;
788
789         DBG("service %p", service);
790
791         __connman_service_initialize(service);
792
793         service->identifier = g_strdup(identifier);
794
795         service->profile = g_strdup(__connman_profile_active_ident());
796
797         __connman_storage_load_service(service);
798
799         iter = g_sequence_insert_sorted(service_list, service,
800                                                 service_compare, NULL);
801
802         g_hash_table_insert(service_hash, service->identifier, iter);
803
804         return service;
805 }
806
807 static int service_register(struct connman_service *service)
808 {
809         const char *path = __connman_profile_active_path();
810         GSequenceIter *iter;
811
812         DBG("service %p", service);
813
814         if (service->path != NULL)
815                 return -EALREADY;
816
817         service->path = g_strdup_printf("%s/%s", path, service->identifier);
818
819         DBG("path %s", service->path);
820
821         g_dbus_register_interface(connection, service->path,
822                                         CONNMAN_SERVICE_INTERFACE,
823                                         service_methods, service_signals,
824                                                         NULL, service, NULL);
825
826         __connman_storage_load_service(service);
827
828         iter = g_hash_table_lookup(service_hash, service->identifier);
829         if (iter != NULL)
830                 g_sequence_sort_changed(iter, service_compare, NULL);
831
832         __connman_profile_changed();
833
834         return 0;
835 }
836
837 /**
838  * connman_service_lookup_from_device:
839  * @device: device structure
840  *
841  * Look up a service by device (reference count will not be increased)
842  */
843 struct connman_service *__connman_service_lookup_from_device(struct connman_device *device)
844 {
845         struct connman_service *service;
846         const char *ident;
847         char *name;
848
849         ident = __connman_device_get_ident(device);
850         if (ident == NULL)
851                 return NULL;
852
853         name = g_strdup_printf("%s_%s",
854                                 __connman_device_get_type(device), ident);
855
856         service = __connman_service_lookup(name);
857
858         g_free(name);
859
860         return service;
861 }
862
863 static enum connman_service_type convert_device_type(struct connman_device *device)
864 {
865         enum connman_device_type type = connman_device_get_type(device);
866
867         switch (type) {
868         case CONNMAN_DEVICE_TYPE_UNKNOWN:
869         case CONNMAN_DEVICE_TYPE_VENDOR:
870         case CONNMAN_DEVICE_TYPE_WIFI:
871         case CONNMAN_DEVICE_TYPE_WIMAX:
872         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
873         case CONNMAN_DEVICE_TYPE_GPS:
874         case CONNMAN_DEVICE_TYPE_HSO:
875         case CONNMAN_DEVICE_TYPE_NOZOMI:
876         case CONNMAN_DEVICE_TYPE_HUAWEI:
877         case CONNMAN_DEVICE_TYPE_NOVATEL:
878                 break;
879         case CONNMAN_DEVICE_TYPE_ETHERNET:
880                 return CONNMAN_SERVICE_TYPE_ETHERNET;
881         }
882
883         return CONNMAN_SERVICE_TYPE_UNKNOWN;
884 }
885
886 /**
887  * connman_service_create_from_device:
888  * @device: device structure
889  *
890  * Look up service by device and if not found, create one
891  */
892 struct connman_service *__connman_service_create_from_device(struct connman_device *device)
893 {
894         struct connman_service *service;
895         const char *ident;
896         char *name;
897
898         ident = __connman_device_get_ident(device);
899         if (ident == NULL)
900                 return NULL;
901
902         name = g_strdup_printf("%s_%s",
903                                 __connman_device_get_type(device), ident);
904
905         service = __connman_service_get(name);
906         if (service == NULL)
907                 goto done;
908
909         if (service->path != NULL) {
910                 __connman_service_put(service);
911                 service = NULL;
912                 goto done;
913         }
914
915         service->type = convert_device_type(device);
916
917         service->device = device;
918
919         service_register(service);
920
921 done:
922         g_free(name);
923
924         return service;
925 }
926
927 /**
928  * connman_service_lookup_from_network:
929  * @network: network structure
930  *
931  * Look up a service by network (reference count will not be increased)
932  */
933 struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
934 {
935         struct connman_service *service;
936         const char *ident, *group;
937         char *name;
938
939         ident = __connman_network_get_ident(network);
940         if (ident == NULL)
941                 return NULL;
942
943         group = __connman_network_get_group(network);
944         if (group == NULL)
945                 return NULL;
946
947         name = g_strdup_printf("%s_%s_%s",
948                         __connman_network_get_type(network), ident, group);
949
950         service = __connman_service_lookup(name);
951
952         g_free(name);
953
954         return service;
955 }
956
957 static enum connman_service_type convert_network_type(struct connman_network *network)
958 {
959         enum connman_network_type type = connman_network_get_type(network);
960
961         switch (type) {
962         case CONNMAN_NETWORK_TYPE_UNKNOWN:
963         case CONNMAN_NETWORK_TYPE_VENDOR:
964         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
965         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
966         case CONNMAN_NETWORK_TYPE_HSO:
967                 break;
968         case CONNMAN_NETWORK_TYPE_WIFI:
969                 return CONNMAN_SERVICE_TYPE_WIFI;
970         case CONNMAN_NETWORK_TYPE_WIMAX:
971                 return CONNMAN_SERVICE_TYPE_WIMAX;
972         }
973
974         return CONNMAN_SERVICE_TYPE_UNKNOWN;
975 }
976
977 static enum connman_service_mode convert_wifi_mode(const char *mode)
978 {
979         if (mode == NULL)
980                 return CONNMAN_SERVICE_MODE_UNKNOWN;
981         else if (g_str_equal(mode, "managed") == TRUE)
982                 return CONNMAN_SERVICE_MODE_MANAGED;
983         else if (g_str_equal(mode, "adhoc") == TRUE)
984                 return CONNMAN_SERVICE_MODE_ADHOC;
985         else
986                 return CONNMAN_SERVICE_MODE_UNKNOWN;
987 }
988
989 static enum connman_service_mode convert_wifi_security(const char *security)
990 {
991         if (security == NULL)
992                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
993         else if (g_str_equal(security, "none") == TRUE)
994                 return CONNMAN_SERVICE_SECURITY_NONE;
995         else if (g_str_equal(security, "wep") == TRUE)
996                 return CONNMAN_SERVICE_SECURITY_WEP;
997         else if (g_str_equal(security, "wpa") == TRUE)
998                 return CONNMAN_SERVICE_SECURITY_WPA;
999         else if (g_str_equal(security, "wpa2") == TRUE)
1000                 return CONNMAN_SERVICE_SECURITY_WPA2;
1001         else
1002                 return CONNMAN_SERVICE_SECURITY_UNKNOWN;
1003 }
1004
1005 static void update_from_network(struct connman_service *service,
1006                                         struct connman_network *network)
1007 {
1008         connman_uint8_t strength = service->strength;
1009         GSequenceIter *iter;
1010         const char *str;
1011
1012         str = connman_network_get_string(network, "Name");
1013         if (str != NULL) {
1014                 g_free(service->name);
1015                 service->name = g_strdup(str);
1016         }
1017
1018         service->strength = connman_network_get_uint8(network, "Strength");
1019
1020         str = connman_network_get_string(network, "WiFi.Mode");
1021         service->mode = convert_wifi_mode(str);
1022
1023         str = connman_network_get_string(network, "WiFi.Security");
1024         service->security = convert_wifi_security(str);
1025
1026         if (service->strength > strength && service->network != NULL) {
1027                 connman_network_unref(service->network);
1028                 service->network = NULL;
1029         }
1030
1031         if (service->network == NULL) {
1032                 service->network = connman_network_ref(network);
1033
1034                 str = connman_network_get_string(network, "WiFi.Passphrase");
1035                 if (str != NULL) {
1036                         g_free(service->passphrase);
1037                         service->passphrase = g_strdup(str);
1038                 }
1039         }
1040
1041         iter = g_hash_table_lookup(service_hash, service->identifier);
1042         if (iter != NULL)
1043                 g_sequence_sort_changed(iter, service_compare, NULL);
1044 }
1045
1046 /**
1047  * connman_service_create_from_network:
1048  * @network: network structure
1049  *
1050  * Look up service by network and if not found, create one
1051  */
1052 struct connman_service *__connman_service_create_from_network(struct connman_network *network)
1053 {
1054         struct connman_service *service;
1055         const char *ident, *group;
1056         char *name;
1057
1058         ident = __connman_network_get_ident(network);
1059         if (ident == NULL)
1060                 return NULL;
1061
1062         group = __connman_network_get_group(network);
1063         if (group == NULL)
1064                 return NULL;
1065
1066         name = g_strdup_printf("%s_%s_%s",
1067                         __connman_network_get_type(network), ident, group);
1068
1069         service = __connman_service_get(name);
1070         if (service == NULL)
1071                 goto done;
1072
1073         if (service->path != NULL) {
1074                 update_from_network(service, network);
1075
1076                 __connman_profile_changed();
1077
1078                 __connman_service_put(service);
1079                 service = NULL;
1080                 goto done;
1081         }
1082
1083         service->type = convert_network_type(network);
1084
1085         service->state = CONNMAN_SERVICE_STATE_IDLE;
1086
1087         update_from_network(service, network);
1088
1089         service_register(service);
1090
1091 done:
1092         g_free(name);
1093
1094         return service;
1095 }
1096
1097 static int service_load(struct connman_service *service)
1098 {
1099         GKeyFile *keyfile;
1100         gchar *pathname, *data = NULL;
1101         gsize length;
1102         char *str;
1103
1104         DBG("service %p", service);
1105
1106         if (service->profile == NULL)
1107                 return -EINVAL;
1108
1109         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
1110         if (pathname == NULL)
1111                 return -ENOMEM;
1112
1113         keyfile = g_key_file_new();
1114
1115         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
1116                 g_free(pathname);
1117                 return -ENOENT;
1118         }
1119
1120         g_free(pathname);
1121
1122         if (g_key_file_load_from_data(keyfile, data, length,
1123                                                         0, NULL) == FALSE) {
1124                 g_free(data);
1125                 return -EILSEQ;
1126         }
1127
1128         g_free(data);
1129
1130         switch (service->type) {
1131         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1132         case CONNMAN_SERVICE_TYPE_ETHERNET:
1133                 break;
1134         case CONNMAN_SERVICE_TYPE_WIFI:
1135         case CONNMAN_SERVICE_TYPE_WIMAX:
1136                 service->favorite = g_key_file_get_boolean(keyfile,
1137                                 service->identifier, "Favorite", NULL);
1138                 break;
1139         }
1140
1141         str = g_key_file_get_string(keyfile,
1142                                 service->identifier, "Passphrase", NULL);
1143         if (str != NULL) {
1144                 g_free(service->passphrase);
1145                 service->passphrase = str;
1146         }
1147
1148         g_key_file_free(keyfile);
1149
1150         return 0;
1151 }
1152
1153 static int service_save(struct connman_service *service)
1154 {
1155         GKeyFile *keyfile;
1156         gchar *pathname, *data = NULL;
1157         gsize length;
1158
1159         DBG("service %p", service);
1160
1161         if (service->profile == NULL)
1162                 return -EINVAL;
1163
1164         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
1165         if (pathname == NULL)
1166                 return -ENOMEM;
1167
1168         keyfile = g_key_file_new();
1169
1170         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
1171                 goto update;
1172
1173         if (length > 0) {
1174                 if (g_key_file_load_from_data(keyfile, data, length,
1175                                                         0, NULL) == FALSE)
1176                         goto done;
1177         }
1178
1179         g_free(data);
1180
1181 update:
1182         if (service->name != NULL)
1183                 g_key_file_set_string(keyfile, service->identifier,
1184                                                 "Name", service->name);
1185
1186         switch (service->type) {
1187         case CONNMAN_SERVICE_TYPE_UNKNOWN:
1188         case CONNMAN_SERVICE_TYPE_ETHERNET:
1189                 break;
1190         case CONNMAN_SERVICE_TYPE_WIFI:
1191         case CONNMAN_SERVICE_TYPE_WIMAX:
1192                 g_key_file_set_boolean(keyfile, service->identifier,
1193                                         "Favorite", service->favorite);
1194                 break;
1195         }
1196
1197         if (service->passphrase != NULL)
1198                 g_key_file_set_string(keyfile, service->identifier,
1199                                         "Passphrase", service->passphrase);
1200
1201         data = g_key_file_to_data(keyfile, &length, NULL);
1202
1203         if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
1204                 connman_error("Failed to store service information");
1205
1206 done:
1207         g_free(data);
1208
1209         g_key_file_free(keyfile);
1210
1211         g_free(pathname);
1212
1213         return 0;
1214 }
1215
1216 static struct connman_storage service_storage = {
1217         .name           = "service",
1218         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1219         .service_load   = service_load,
1220         .service_save   = service_save,
1221 };
1222
1223 int __connman_service_init(void)
1224 {
1225         DBG("");
1226
1227         connection = connman_dbus_get_connection();
1228
1229         if (connman_storage_register(&service_storage) < 0)
1230                 connman_error("Failed to register service storage");
1231
1232         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1233                                                                 NULL, NULL);
1234
1235         service_list = g_sequence_new(service_free);
1236
1237         return 0;
1238 }
1239
1240 void __connman_service_cleanup(void)
1241 {
1242         DBG("");
1243
1244         g_sequence_free(service_list);
1245         service_list = NULL;
1246
1247         g_hash_table_destroy(service_hash);
1248         service_hash = NULL;
1249
1250         connman_storage_unregister(&service_storage);
1251
1252         dbus_connection_unref(connection);
1253 }