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