manager: Add session mode
[platform/upstream/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <string.h>
27
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 static DBusConnection *connection;
33 static GHashTable *session_hash;
34 static GHashTable *bearer_hash;
35 static connman_bool_t sessionmode;
36
37 struct connman_bearer {
38         gint refcount;
39         char *name;
40 };
41
42 struct connman_session {
43         gint refcount;
44         char *owner;
45         guint watch;
46         struct connman_bearer *bearer;
47         struct connman_service *service;
48 };
49
50 static enum connman_service_type bearer2service(const char *bearer)
51 {
52         if (bearer == NULL)
53                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
54
55         DBG("%s", bearer);
56
57         if (g_strcmp0(bearer, "ethernet") == 0)
58                 return CONNMAN_SERVICE_TYPE_ETHERNET;
59         else if (g_strcmp0(bearer, "wifi") == 0)
60                 return CONNMAN_SERVICE_TYPE_WIFI;
61         else if (g_strcmp0(bearer, "wimax") == 0)
62                 return CONNMAN_SERVICE_TYPE_WIMAX;
63         else if (g_strcmp0(bearer, "bluetooth") == 0)
64                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
65         else if (g_strcmp0(bearer, "3g") == 0)
66                 return CONNMAN_SERVICE_TYPE_CELLULAR;
67         else
68                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
69 }
70
71 static char *service2bearer(enum connman_service_type type)
72 {
73         DBG("%d", type);
74
75         switch (type) {
76         case CONNMAN_SERVICE_TYPE_ETHERNET:
77                 return "ethernet";
78         case CONNMAN_SERVICE_TYPE_WIFI:
79                 return "wifi";
80         case CONNMAN_SERVICE_TYPE_WIMAX:
81                 return "wimax";
82         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
83                 return "bluetooth";
84         case CONNMAN_SERVICE_TYPE_CELLULAR:
85                 return "3g";
86         case CONNMAN_SERVICE_TYPE_UNKNOWN:
87         case CONNMAN_SERVICE_TYPE_SYSTEM:
88         case CONNMAN_SERVICE_TYPE_GPS:
89         case CONNMAN_SERVICE_TYPE_VPN:
90         case CONNMAN_SERVICE_TYPE_GADGET:
91                 return NULL;
92         }
93
94         return NULL;
95 }
96
97 static void remove_bearer(gpointer user_data)
98 {
99         struct connman_bearer *bearer = user_data;
100
101         g_free(bearer->name);
102         g_free(bearer);
103 }
104
105 static void remove_session(gpointer user_data)
106 {
107         struct connman_session *session = user_data;
108
109         session->bearer = NULL;
110         if (session->service)
111                 connman_service_unref(session->service);
112         g_free(session->owner);
113         g_free(session);
114 }
115
116 static int session_disconnect(struct connman_session *session)
117 {
118         struct connman_bearer *bearer = session->bearer;
119
120         DBG("%s", session->owner);
121
122         if (session == NULL)
123                 return -EINVAL;
124
125         /*
126          * Once a bearer is no longer referenced we actually disconnect
127          * the corresponding service.
128          */
129         if (bearer == NULL || g_atomic_int_dec_and_test(&bearer->refcount)) {
130                 struct connman_network *network;
131                 struct connman_device *device;
132
133                 /*
134                  * We toggle the reconnect flag to false when releasing a
135                  * session. This way a previously connected service will
136                  * not autoconnect once we've completely release a session.
137                  */
138                 network = __connman_service_get_network(session->service);
139                 if (network == NULL)
140                         return -EINVAL;
141
142                 device = connman_network_get_device(network);
143                 if (device == NULL)
144                         return -EINVAL;
145
146                 __connman_device_set_reconnect(device, FALSE);
147
148                 __connman_service_disconnect(session->service);
149                 connman_service_unref(session->service);
150
151                 g_hash_table_remove(bearer_hash, bearer);
152         }
153
154         if (session->watch > 0)
155                 g_dbus_remove_watch(connection, session->watch);
156
157         g_hash_table_remove(session_hash, session);
158
159         return 0;
160 }
161
162 static void owner_disconnect(DBusConnection *connection, void *user_data)
163 {
164         struct connman_session *session = user_data;
165
166         DBG("%s died", session->owner);
167
168         session_disconnect(session);
169 }
170
171 int __connman_session_release(const char *owner)
172 {
173         struct connman_session *session;
174
175         DBG("owner %s", owner);
176
177         session = g_hash_table_lookup(session_hash, owner);
178         if (session == NULL)
179                 return -EINVAL;
180
181         if (g_atomic_int_dec_and_test(&session->refcount))
182                 return session_disconnect(session);
183
184         return 0;
185 }
186
187 struct connman_service *__connman_session_request(const char *bearer_name,
188                                                         const char *owner)
189 {
190         struct connman_session *session;
191         struct connman_bearer *bearer;
192         enum connman_service_type service_type;
193         const char *bearer_name_new;
194         size_t bearer_name_len;
195
196         if (bearer_name == NULL)
197                 return NULL;
198
199         DBG("owner %s bearer %s", owner, bearer_name);
200
201         bearer_name_len = strlen(bearer_name);
202
203         session = g_hash_table_lookup(session_hash, owner);
204         if (session) {
205                 /* we only support one bearer per process */
206                 if (bearer_name_len &&
207                         g_strcmp0(session->bearer->name, bearer_name))
208                                 return NULL;
209
210                 g_atomic_int_inc(&session->refcount);
211
212                 return session->service;
213         }
214
215         session = g_try_new0(struct connman_session, 1);
216         if (session == NULL)
217                 return NULL;
218
219         session->refcount = 1;
220         session->owner = g_strdup(owner);
221         session->service = NULL;
222         g_hash_table_replace(session_hash, session->owner, session);
223
224         /* Find and connect service */
225         service_type = bearer2service(bearer_name);
226
227         session->service = __connman_service_connect_type(service_type);
228         if (session->service == NULL)
229                 goto failed_connect;
230
231         connman_service_ref(session->service);
232
233         service_type = connman_service_get_type(session->service);
234
235         /* We might get a different bearer from the one we requested */
236         bearer_name_new = service2bearer(service_type);
237
238         /* Refcount the exisiting bearer, or create one */
239         bearer = g_hash_table_lookup(bearer_hash, bearer_name_new);
240         if (bearer == NULL) {
241                 bearer = g_try_new0(struct connman_bearer, 1);
242                 if (bearer == NULL)
243                         goto failed_bearer;
244
245                 bearer->refcount = 0;
246                 bearer->name = g_strdup(bearer_name_new);
247                 g_hash_table_replace(bearer_hash, bearer->name, bearer);
248         }
249
250         g_atomic_int_inc(&bearer->refcount);
251         session->bearer = bearer;
252
253         session->watch = g_dbus_add_disconnect_watch(connection, session->owner,
254                                         owner_disconnect, session, NULL);
255         return session->service;
256
257 failed_bearer:
258         session_disconnect(session);
259
260 failed_connect:
261         g_hash_table_remove(session_hash, session);
262
263         return NULL;
264 }
265
266 connman_bool_t __connman_session_mode()
267 {
268         return sessionmode;
269 }
270
271 void __connman_session_set_mode(connman_bool_t enable)
272 {
273         DBG("enable %d", enable);
274
275         if (sessionmode == enable)
276                 return;
277
278         sessionmode = enable;
279
280         if (sessionmode == TRUE)
281                 __connman_service_disconnect_all();
282 }
283
284 int __connman_session_init(void)
285 {
286         DBG("");
287
288         connection = connman_dbus_get_connection();
289         if (connection == NULL)
290                 return -1;
291
292         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
293                                                 NULL, remove_session);
294
295         bearer_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
296                                                 NULL, remove_bearer);
297
298         sessionmode = FALSE;
299         return 0;
300 }
301
302 void __connman_session_cleanup(void)
303 {
304         DBG("");
305
306         if (connection == NULL)
307                 return;
308
309         g_hash_table_destroy(bearer_hash);
310         g_hash_table_destroy(session_hash);
311         dbus_connection_unref(connection);
312 }