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