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