Send signals for service state changes
[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 void state_changed(struct connman_service *service)
142 {
143         DBusMessage *signal;
144         DBusMessageIter entry;
145         const char *str;
146
147         str = state2string(service->state);
148         if (str == NULL)
149                 return;
150
151         signal = dbus_message_new_signal(service->path,
152                                 CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
153         if (signal == NULL)
154                 return;
155
156         dbus_message_iter_init_append(signal, &entry);
157         connman_dbus_dict_append_variant(&entry, "State",
158                                                 DBUS_TYPE_STRING, &str);
159         g_dbus_send_message(connection, signal);
160 }
161
162 static DBusMessage *get_properties(DBusConnection *conn,
163                                         DBusMessage *msg, void *data)
164 {
165         struct connman_service *service = data;
166         DBusMessage *reply;
167         DBusMessageIter array, dict;
168         const char *str;
169
170         DBG("conn %p", conn);
171
172         reply = dbus_message_new_method_return(msg);
173         if (reply == NULL)
174                 return NULL;
175
176         dbus_message_iter_init_append(reply, &array);
177
178         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
179                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
180                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
181                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
182
183         str = type2string(service->type);
184         if (str != NULL)
185                 connman_dbus_dict_append_variant(&dict, "Type",
186                                                 DBUS_TYPE_STRING, &str);
187
188         str = mode2string(service->mode);
189         if (str != NULL)
190                 connman_dbus_dict_append_variant(&dict, "Mode",
191                                                 DBUS_TYPE_STRING, &str);
192
193         str = security2string(service->security);
194         if (str != NULL)
195                 connman_dbus_dict_append_variant(&dict, "Security",
196                                                 DBUS_TYPE_STRING, &str);
197
198         str = state2string(service->state);
199         if (str != NULL)
200                 connman_dbus_dict_append_variant(&dict, "State",
201                                                 DBUS_TYPE_STRING, &str);
202
203         if (service->strength > 0)
204                 connman_dbus_dict_append_variant(&dict, "Strength",
205                                         DBUS_TYPE_BYTE, &service->strength);
206
207         connman_dbus_dict_append_variant(&dict, "Favorite",
208                                         DBUS_TYPE_BOOLEAN, &service->favorite);
209
210         if (service->name != NULL)
211                 connman_dbus_dict_append_variant(&dict, "Name",
212                                         DBUS_TYPE_STRING, &service->name);
213
214         dbus_message_iter_close_container(&array, &dict);
215
216         return reply;
217 }
218
219 static DBusMessage *connect_service(DBusConnection *conn,
220                                         DBusMessage *msg, void *data)
221 {
222         struct connman_service *service = data;
223
224         if (service->device != NULL) {
225                 if (__connman_device_connect(service->device) < 0)
226                         return __connman_error_failed(msg);
227
228                 service->state = CONNMAN_SERVICE_STATE_READY;
229
230                 state_changed(service);
231         }
232
233         return __connman_error_not_supported(msg);
234 }
235
236 static DBusMessage *disconnect_service(DBusConnection *conn,
237                                         DBusMessage *msg, void *data)
238 {
239         struct connman_service *service = data;
240
241         if (service->device != NULL) {
242                 if (__connman_device_connect(service->device) < 0)
243                         return __connman_error_failed(msg);
244
245                 service->state = CONNMAN_SERVICE_STATE_IDLE;
246
247                 state_changed(service);
248         }
249
250         return __connman_error_not_supported(msg);
251 }
252
253 static DBusMessage *remove_service(DBusConnection *conn,
254                                         DBusMessage *msg, void *data)
255 {
256         struct connman_service *service = data;
257
258         if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
259                 return __connman_error_not_supported(msg);
260
261         connman_service_set_favorite(service, FALSE);
262
263         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
264 }
265
266 static DBusMessage *move_before(DBusConnection *conn,
267                                         DBusMessage *msg, void *data)
268 {
269         struct connman_service *service = data;
270
271         if (service->favorite == FALSE)
272                 return __connman_error_not_supported(msg);
273
274         return __connman_error_not_implemented(msg);
275 }
276
277 static DBusMessage *move_after(DBusConnection *conn,
278                                         DBusMessage *msg, void *data)
279 {
280         struct connman_service *service = data;
281
282         if (service->favorite == FALSE)
283                 return __connman_error_not_supported(msg);
284
285         return __connman_error_not_implemented(msg);
286 }
287
288 static GDBusMethodTable service_methods[] = {
289         { "GetProperties", "",  "a{sv}", get_properties     },
290         { "Connect",       "",  "",      connect_service    },
291         { "Disconnect",    "",  "",      disconnect_service },
292         { "Remove",        "",  "",      remove_service     },
293         { "MoveBefore",    "o", "",      move_before        },
294         { "MoveAfter",     "o", "",      move_after         },
295         { },
296 };
297
298 static GDBusSignalTable service_signals[] = {
299         { "PropertyChanged", "sv" },
300         { },
301 };
302
303 static void service_free(gpointer data)
304 {
305         struct connman_service *service = data;
306         char *path = service->path;
307
308         DBG("service %p", service);
309
310         g_hash_table_remove(service_hash, service->identifier);
311
312         service->path = NULL;
313
314         if (path != NULL) {
315                 __connman_profile_changed();
316
317                 g_dbus_unregister_interface(connection, path,
318                                                 CONNMAN_SERVICE_INTERFACE);
319                 g_free(path);
320         }
321
322         g_free(service->name);
323         g_free(service->identifier);
324         g_free(service);
325 }
326
327 /**
328  * connman_service_put:
329  * @service: service structure
330  *
331  * Release service if no longer needed
332  */
333 void connman_service_put(struct connman_service *service)
334 {
335         DBG("service %p", service);
336
337         if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
338                 GSequenceIter *iter;
339
340                 iter = g_hash_table_lookup(service_hash, service->identifier);
341                 if (iter != NULL)
342                         g_sequence_remove(iter);
343                 else
344                         service_free(service);
345         }
346 }
347
348 static void __connman_service_initialize(struct connman_service *service)
349 {
350         DBG("service %p", service);
351
352         service->refcount = 1;
353
354         service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
355         service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
356         service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
357         service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
358
359         service->favorite = FALSE;
360
361         service->order = 0;
362 }
363
364 /**
365  * connman_service_create:
366  *
367  * Allocate a new service.
368  *
369  * Returns: a newly-allocated #connman_service structure
370  */
371 struct connman_service *connman_service_create(void)
372 {
373         struct connman_service *service;
374
375         service = g_try_new0(struct connman_service, 1);
376         if (service == NULL)
377                 return NULL;
378
379         DBG("service %p", service);
380
381         __connman_service_initialize(service);
382
383         return service;
384 }
385
386 /**
387  * connman_service_ref:
388  * @service: service structure
389  *
390  * Increase reference counter of service
391  */
392 struct connman_service *connman_service_ref(struct connman_service *service)
393 {
394         g_atomic_int_inc(&service->refcount);
395
396         return service;
397 }
398
399 /**
400  * connman_service_unref:
401  * @service: service structure
402  *
403  * Decrease reference counter of service
404  */
405 void connman_service_unref(struct connman_service *service)
406 {
407         connman_service_put(service);
408 }
409
410 static gint service_compare(gconstpointer a, gconstpointer b,
411                                                         gpointer user_data)
412 {
413         struct connman_service *service_a = (void *) a;
414         struct connman_service *service_b = (void *) b;
415
416         if (service_a->order > service_b->order)
417                 return -1;
418
419         if (service_a->order < service_b->order)
420                 return 1;
421
422         if (service_a->favorite == TRUE && service_b->favorite == FALSE)
423                 return -1;
424
425         if (service_a->favorite == FALSE && service_b->favorite == TRUE)
426                 return 1;
427
428         return (gint) service_b->strength - (gint) service_a->strength;
429 }
430
431 /**
432  * connman_service_set_favorite:
433  * @service: service structure
434  * @favorite: favorite value
435  *
436  * Change the favorite setting of service
437  */
438 int connman_service_set_favorite(struct connman_service *service,
439                                                 connman_bool_t favorite)
440 {
441         GSequenceIter *iter;
442
443         iter = g_hash_table_lookup(service_hash, service->identifier);
444         if (iter == NULL)
445                 return -ENOENT;
446
447         if (service->favorite)
448                 return -EALREADY;
449
450         service->favorite = favorite;
451
452         g_sequence_sort_changed(iter, service_compare, NULL);
453
454         __connman_profile_changed();
455
456         return 0;
457 }
458
459 int __connman_service_set_carrier(struct connman_service *service,
460                                                 connman_bool_t carrier)
461 {
462         if (service == NULL)
463                 return -EINVAL;
464
465         switch (service->type) {
466         case CONNMAN_SERVICE_TYPE_UNKNOWN:
467         case CONNMAN_SERVICE_TYPE_WIFI:
468         case CONNMAN_SERVICE_TYPE_WIMAX:
469                 return -EINVAL;
470         case CONNMAN_SERVICE_TYPE_ETHERNET:
471                 break;
472         }
473
474         if (carrier == TRUE)
475                 service->state = CONNMAN_SERVICE_STATE_CARRIER;
476         else
477                 service->state = CONNMAN_SERVICE_STATE_IDLE;
478
479         state_changed(service);
480
481         return connman_service_set_favorite(service, carrier);
482 }
483
484 int __connman_service_indicate_configuration(struct connman_service *service)
485 {
486         if (service == NULL)
487                 return -EINVAL;
488
489         service->state = CONNMAN_SERVICE_STATE_CONFIGURATION;
490
491         state_changed(service);
492
493         return 0;
494 }
495
496 /**
497  * connman_service_lookup:
498  * @identifier: service identifier
499  *
500  * Look up a service by identifier (reference count will not be increased)
501  */
502 struct connman_service *connman_service_lookup(const char *identifier)
503 {
504         GSequenceIter *iter;
505
506         iter = g_hash_table_lookup(service_hash, identifier);
507         if (iter != NULL)
508                 return g_sequence_get(iter);
509
510         return NULL;
511 }
512
513 /**
514  * connman_service_get:
515  * @identifier: service identifier
516  *
517  * Look up a service by identifier or create a new one if not found
518  */
519 struct connman_service *connman_service_get(const char *identifier)
520 {
521         struct connman_service *service;
522         GSequenceIter *iter;
523
524         iter = g_hash_table_lookup(service_hash, identifier);
525         if (iter != NULL) {
526                 service = g_sequence_get(iter);
527                 if (service != NULL)
528                         g_atomic_int_inc(&service->refcount);
529                 return service;
530         }
531
532         service = g_try_new0(struct connman_service, 1);
533         if (service == NULL)
534                 return NULL;
535
536         DBG("service %p", service);
537
538         __connman_service_initialize(service);
539
540         service->identifier = g_strdup(identifier);
541
542         iter = g_sequence_insert_sorted(service_list, service,
543                                                 service_compare, NULL);
544
545         g_hash_table_insert(service_hash, service->identifier, iter);
546
547         return service;
548 }
549
550 static int service_register(struct connman_service *service)
551 {
552         const char *path = __connman_profile_active();
553
554         if (service->path != NULL)
555                 return -EALREADY;
556
557         service->path = g_strdup_printf("%s/%s", path, service->identifier);
558
559         g_dbus_register_interface(connection, service->path,
560                                         CONNMAN_SERVICE_INTERFACE,
561                                         service_methods, service_signals,
562                                                         NULL, service, NULL);
563
564         __connman_profile_changed();
565
566         return 0;
567 }
568
569 /**
570  * connman_service_lookup_from_device:
571  * @device: device structure
572  *
573  * Look up a service by device (reference count will not be increased)
574  */
575 struct connman_service *__connman_service_lookup_from_device(struct connman_device *device)
576 {
577         struct connman_service *service;
578         char *name;
579
580         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
581                                         connman_device_get_index(device));
582
583         service = connman_service_lookup(name);
584
585         g_free(name);
586
587         return service;
588 }
589
590 static enum connman_service_type convert_device_type(struct connman_device *device)
591 {
592         enum connman_device_type type = connman_device_get_type(device);
593
594         switch (type) {
595         case CONNMAN_DEVICE_TYPE_UNKNOWN:
596         case CONNMAN_DEVICE_TYPE_VENDOR:
597         case CONNMAN_DEVICE_TYPE_WIFI:
598         case CONNMAN_DEVICE_TYPE_WIMAX:
599         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
600         case CONNMAN_DEVICE_TYPE_GPS:
601         case CONNMAN_DEVICE_TYPE_HSO:
602         case CONNMAN_DEVICE_TYPE_NOZOMI:
603         case CONNMAN_DEVICE_TYPE_HUAWEI:
604         case CONNMAN_DEVICE_TYPE_NOVATEL:
605                 break;
606         case CONNMAN_DEVICE_TYPE_ETHERNET:
607                 return CONNMAN_SERVICE_TYPE_ETHERNET;
608         }
609
610         return CONNMAN_SERVICE_TYPE_UNKNOWN;
611 }
612
613 /**
614  * connman_service_create_from_device:
615  * @device: device structure
616  *
617  * Look up service by device and if not found, create one
618  */
619 struct connman_service *__connman_service_create_from_device(struct connman_device *device)
620 {
621         struct connman_service *service;
622         char *name;
623
624         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
625                                         connman_device_get_index(device));
626
627         service = connman_service_get(name);
628         if (service == NULL)
629                 goto done;
630
631         if (service->path != NULL) {
632                 connman_service_put(service);
633                 service = NULL;
634                 goto done;
635         }
636
637         service->type = convert_device_type(device);
638
639         service->device = device;
640
641         service_register(service);
642
643 done:
644         g_free(name);
645
646         return service;
647 }
648
649 /**
650  * connman_service_lookup_from_network:
651  * @device: device structure
652  *
653  * Look up a service by network (reference count will not be increased)
654  */
655 struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
656 {
657         struct connman_service *service;
658         char *name;
659
660         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
661                                         __connman_network_get_group(network));
662
663         service = connman_service_lookup(name);
664
665         g_free(name);
666
667         return service;
668 }
669
670 static enum connman_service_type convert_network_type(struct connman_network *network)
671 {
672         enum connman_network_type type = connman_network_get_type(network);
673
674         switch (type) {
675         case CONNMAN_NETWORK_TYPE_UNKNOWN:
676         case CONNMAN_NETWORK_TYPE_VENDOR:
677         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
678         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
679         case CONNMAN_NETWORK_TYPE_HSO:
680                 break;
681         case CONNMAN_NETWORK_TYPE_WIFI:
682                 return CONNMAN_SERVICE_TYPE_WIFI;
683         case CONNMAN_NETWORK_TYPE_WIMAX:
684                 return CONNMAN_SERVICE_TYPE_WIMAX;
685         }
686
687         return CONNMAN_SERVICE_TYPE_UNKNOWN;
688 }
689
690 /**
691  * connman_service_create_from_network:
692  * @device: device structure
693  *
694  * Look up service by network and if not found, create one
695  */
696 struct connman_service *__connman_service_create_from_network(struct connman_network *network)
697 {
698         struct connman_service *service;
699         char *name;
700
701         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
702                                         __connman_network_get_group(network));
703
704         service = connman_service_get(name);
705         if (service == NULL)
706                 goto done;
707
708         if (service->path != NULL) {
709                 connman_service_put(service);
710                 service = NULL;
711                 goto done;
712         }
713
714         service->type = convert_network_type(network);
715
716         service_register(service);
717
718 done:
719         g_free(name);
720
721         return service;
722 }
723
724 int __connman_service_init(void)
725 {
726         DBG("");
727
728         connection = connman_dbus_get_connection();
729
730         service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
731                                                                 NULL, NULL);
732
733         service_list = g_sequence_new(service_free);
734
735         return 0;
736 }
737
738 void __connman_service_cleanup(void)
739 {
740         DBG("");
741
742         g_sequence_free(service_list);
743         service_list = NULL;
744
745         g_hash_table_destroy(service_hash);
746         service_hash = NULL;
747
748         dbus_connection_unref(connection);
749 }