fixed bug:delete handle after using it
[platform/core/pim/contacts-service.git] / client / ctsvc_client_ipc.c
1 /*
2  * Contacts Service
3  *
4  * Copyright (c) 2010 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <glib.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <glib.h>
26 #include <pims-ipc-data.h>
27
28 #include "ctsvc_client_ipc.h"
29 #include "ctsvc_client_utils.h"
30 #include "ctsvc_client_service_helper.h"
31
32 #include "ctsvc_internal.h"
33 #include "ctsvc_list.h"
34 #include "ctsvc_record.h"
35 #include "ctsvc_inotify.h"
36
37 #include "ctsvc_ipc_define.h"
38 #include "ctsvc_ipc_marshal.h"
39 #include "ctsvc_view.h"
40 #include "ctsvc_mutex.h"
41 #include "ctsvc_handle.h"
42
43 struct ctsvc_ipc_s {
44         pims_ipc_h ipc;
45         GList *list_handle;
46 };
47
48 static GHashTable *_ctsvc_ipc_table = NULL;
49 static bool _ctsvc_ipc_disconnected = false;
50
51 static pims_ipc_h _ctsvc_get_ipc_handle()
52 {
53         struct ctsvc_ipc_s *ipc_data = NULL;
54         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
55         RETVM_IF(NULL == _ctsvc_ipc_table, NULL, "contacts not connected");
56
57         snprintf(ipc_key, sizeof(ipc_key), "%u", ctsvc_client_get_tid());
58         ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
59
60         if (NULL == ipc_data) {
61                 snprintf(ipc_key, sizeof(ipc_key), "%u", ctsvc_client_get_pid());
62                 ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
63         }
64
65         RETVM_IF(NULL == ipc_data, NULL, "g_hash_table_lookup(%s) Fail", ipc_key);
66
67         return ipc_data->ipc;
68 }
69
70 bool ctsvc_ipc_is_busy()
71 {
72         bool ret = false;
73
74         pims_ipc_h ipc = _ctsvc_get_ipc_handle();
75         if (NULL == ipc) {
76                 CTS_ERR("_ctsvc_get_ipc_handle() return NULL");
77                 return false;
78         }
79
80         ret = pims_ipc_is_call_in_progress(ipc);
81         if (ret)
82                 CTS_ERR("global ipc channel is busy.");
83
84         return ret;
85 }
86
87 static int _ctsvc_ipc_create(pims_ipc_h *p_ipc)
88 {
89         char sock_file[CTSVC_PATH_MAX_LEN] = {0};
90         snprintf(sock_file, sizeof(sock_file), CTSVC_SOCK_PATH"/.%s", getuid(), CTSVC_IPC_SERVICE);
91         pims_ipc_h ipc = pims_ipc_create(sock_file);
92         if (NULL == ipc) {
93                 if (errno == EACCES) {
94                         CTS_ERR("pims_ipc_create() Failed(%d)", CONTACTS_ERROR_PERMISSION_DENIED);
95                         return CONTACTS_ERROR_PERMISSION_DENIED;
96                 }
97                 else {
98                         CTS_ERR("pims_ipc_create() Failed(%d)", CONTACTS_ERROR_IPC_NOT_AVALIABLE);
99                         return CONTACTS_ERROR_IPC_NOT_AVALIABLE;
100                 }
101         }
102
103         *p_ipc = ipc;
104         return CONTACTS_ERROR_NONE;
105 }
106
107 static void _ctsvc_ipc_data_free(gpointer p)
108 {
109         struct ctsvc_ipc_s *ipc_data = p;
110         if (NULL == ipc_data)
111                 return;
112
113         if (ipc_data->ipc)
114                 pims_ipc_destroy(ipc_data->ipc);
115
116         g_list_free(ipc_data->list_handle);
117
118         free(ipc_data);
119 }
120
121 static int _ctsvc_ipc_connect(contacts_h contact, pims_ipc_h ipc)
122 {
123         int ret;
124         pims_ipc_data_h outdata = NULL;
125         pims_ipc_data_h indata = NULL;
126
127         /* Access control : put cookie to indata */
128         indata = pims_ipc_data_create(0);
129         if (indata == NULL) {
130                 CTS_ERR("pims_ipc_data_create() return NULL");
131                 return CONTACTS_ERROR_OUT_OF_MEMORY;
132         }
133
134         ret = ctsvc_ipc_marshal_handle(contact, indata);
135         if (CONTACTS_ERROR_NONE != ret) {
136                 CTS_ERR("ctsvc_ipc_marshal_handle Fail(%d)", ret);
137                 pims_ipc_data_destroy(indata);
138                 return ret;
139         }
140
141         /* ipc call */
142         if (pims_ipc_call(ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CONNECT, indata, &outdata) != 0) {
143                 CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc_call failed");
144                 pims_ipc_data_destroy(indata);
145                 return CONTACTS_ERROR_IPC;
146         }
147         pims_ipc_data_destroy(indata);
148
149         if (outdata) {
150                 ctsvc_ipc_unmarshal_int(outdata, &ret);
151                 pims_ipc_data_destroy(outdata);
152                 RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "ctsvc_ipc_server_connect return(%d)", ret);
153         }
154         return ret;
155 }
156
157 int ctsvc_ipc_connect(contacts_h contact, unsigned int handle_id)
158 {
159         int ret = CONTACTS_ERROR_NONE;
160         struct ctsvc_ipc_s *ipc_data = NULL;
161         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
162
163         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
164
165         if (NULL == _ctsvc_ipc_table)
166                 _ctsvc_ipc_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, _ctsvc_ipc_data_free);
167         else
168                 ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
169
170         if (NULL == ipc_data) {
171                 ipc_data = calloc(1, sizeof(struct ctsvc_ipc_s));
172                 if (NULL == ipc_data)
173                 {
174                         CTS_ERR("calloc() Fail");
175                         return CONTACTS_ERROR_OUT_OF_MEMORY;
176                 }
177                 ret = _ctsvc_ipc_create(&(ipc_data->ipc));
178                 if (CONTACTS_ERROR_NONE != ret) {
179                         _ctsvc_ipc_data_free(ipc_data);
180                         return ret;
181                 }
182                 g_hash_table_insert(_ctsvc_ipc_table, strdup(ipc_key), ipc_data);
183         }
184         _ctsvc_ipc_connect(contact, ipc_data->ipc);
185         ipc_data->list_handle = g_list_append(ipc_data->list_handle, contact);
186
187         return CONTACTS_ERROR_NONE;
188 }
189
190
191 int ctsvc_ipc_disconnect(contacts_h contact, unsigned int handle_id, int connection_count)
192 {
193         int ret = CONTACTS_ERROR_NONE;
194         struct ctsvc_ipc_s *ipc_data = NULL;
195         pims_ipc_data_h outdata = NULL;
196         pims_ipc_data_h indata = NULL;
197         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
198
199         RETVM_IF(NULL == _ctsvc_ipc_table, CONTACTS_ERROR_IPC, "contacts not connected");
200         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
201
202         ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
203         RETVM_IF(ipc_data == NULL, CONTACTS_ERROR_IPC, "contacts not connected");
204
205         indata = pims_ipc_data_create(0);
206         if (indata == NULL) {
207                 CTS_ERR("ipc data created fail!");
208                 return CONTACTS_ERROR_OUT_OF_MEMORY;
209         }
210
211         ret = ctsvc_ipc_marshal_handle(contact, indata);
212         RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "ctsvc_ipc_marshal_handle() Fail(%d)", ret);
213
214         if (pims_ipc_call(ipc_data->ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_DISCONNECT, indata, &outdata) != 0) {
215                 pims_ipc_data_destroy(indata);
216                 CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc_call failed");
217                 return CONTACTS_ERROR_IPC;
218         }
219
220         if (outdata) {
221                 ctsvc_ipc_unmarshal_int(outdata, &ret);
222                 pims_ipc_data_destroy(outdata);
223
224                 if (ret != CONTACTS_ERROR_NONE) {
225                         CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc didn't destroyed!!!(%d)", ret);
226                         return ret;
227                 }
228
229                 if (1 == connection_count) {
230                         g_hash_table_remove(_ctsvc_ipc_table, ipc_key);
231                 }
232         }
233         else {
234                 CTS_ERR("pims_ipc_call out data is NULL");
235                 return CONTACTS_ERROR_IPC;
236         }
237
238         return ret;
239 }
240
241 static void __ctsvc_ipc_lock()
242 {
243         if (0 == ctsvc_client_get_thread_connection_count())
244                 ctsvc_mutex_lock(CTS_MUTEX_PIMS_IPC_CALL);
245 }
246
247 static void __ctsvc_ipc_unlock(void)
248 {
249         if (0 == ctsvc_client_get_thread_connection_count())
250                 ctsvc_mutex_unlock(CTS_MUTEX_PIMS_IPC_CALL);
251 }
252
253 int ctsvc_ipc_call(char *module, char *function, pims_ipc_h data_in, pims_ipc_data_h *data_out)
254 {
255         pims_ipc_h ipc_handle;
256
257         if (true == ctsvc_ipc_get_disconnected()) {
258                 ctsvc_ipc_set_disconnected(false);
259                 ctsvc_ipc_recovery();
260                 ctsvc_ipc_recover_for_change_subscription();
261         }
262
263         ipc_handle = _ctsvc_get_ipc_handle();
264
265         __ctsvc_ipc_lock();
266         int ret = pims_ipc_call(ipc_handle, module, function, data_in, data_out);
267         __ctsvc_ipc_unlock();
268
269         return ret;
270 }
271
272 void ctsvc_client_ipc_set_change_version(contacts_h contact, int version)
273 {
274         RETM_IF(NULL == contact, "contact is NULL");
275         ctsvc_base_s *base = (ctsvc_base_s *)contact;
276         base->version = version;
277 }
278
279 int ctsvc_client_ipc_get_change_version(contacts_h contact)
280 {
281         RETVM_IF(NULL == contact, -1, "contact is NULL");
282         ctsvc_base_s *base = (ctsvc_base_s *)contact;
283         return base->version;
284 }
285
286 int ctsvc_ipc_client_check_permission(int permission, bool *result)
287 {
288         pims_ipc_data_h indata = NULL;
289         pims_ipc_data_h outdata = NULL;
290         int ret;
291
292         if (result)
293                 *result = false;
294
295         indata = pims_ipc_data_create(0);
296         if (indata == NULL) {
297                 CTS_ERR("ipc data created fail !");
298                 return CONTACTS_ERROR_OUT_OF_MEMORY;
299         }
300
301         ret = ctsvc_ipc_marshal_int(permission, indata);
302         if (ret != CONTACTS_ERROR_NONE) {
303                 CTS_ERR("marshal fail");
304                 pims_ipc_data_destroy(indata);
305                 return ret;
306         }
307
308         if (ctsvc_ipc_call(CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CHECK_PERMISSION, indata, &outdata) != 0) {
309                 CTS_ERR("ctsvc_ipc_call Fail");
310                 pims_ipc_data_destroy(indata);
311                 return CONTACTS_ERROR_IPC;
312         }
313
314         pims_ipc_data_destroy(indata);
315
316         if (outdata) {
317                 if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_int(outdata, &ret)) {
318                         CTS_ERR("ctsvc_ipc_unmarshal_int() Fail");
319                         pims_ipc_data_destroy(outdata);
320                         return CONTACTS_ERROR_IPC;
321                 }
322
323                 if (CONTACTS_ERROR_NONE == ret && result) {
324                         if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_bool(outdata, result)) {
325                                 CTS_ERR("ctsvc_ipc_unmarshal_bool() Fail");
326                                 pims_ipc_data_destroy(outdata);
327                                 return CONTACTS_ERROR_IPC;
328                         }
329                 }
330                 pims_ipc_data_destroy(outdata);
331         }
332
333         return ret;
334 }
335
336 int ctsvc_ipc_set_disconnected_cb(pims_ipc_h ipc, void (*cb)(void *), void *user_data)
337 {
338         return pims_ipc_add_server_disconnected_cb(ipc, cb, user_data);
339 }
340
341 int ctsvc_ipc_unset_disconnected_cb(pims_ipc_h ipc)
342 {
343         return pims_ipc_remove_server_disconnected_cb(ipc);
344 }
345
346 void ctsvc_ipc_set_disconnected(bool is_disconnected)
347 {
348         _ctsvc_ipc_disconnected = is_disconnected;
349 }
350
351 int ctsvc_ipc_get_disconnected()
352 {
353         CTS_DBG("_ctsvc_ipc_disconnected=%d", _ctsvc_ipc_disconnected);
354         return _ctsvc_ipc_disconnected;
355 }
356
357 static void _ctsvc_ipc_recovery_foreach_cb(gpointer key, gpointer value, gpointer user_data)
358 {
359         GList *c;
360         struct ctsvc_ipc_s *ipc_data = value;
361
362         int ret = _ctsvc_ipc_create(&(ipc_data->ipc));
363         RETM_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_create() Fail(%d)", ret);
364
365         for (c=ipc_data->list_handle;c;c=c->next) {
366                 contacts_h contact = c->data;
367                 ret = _ctsvc_ipc_connect(contact, ipc_data->ipc);
368                 WARN_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_connect() Fail(%d)", ret);
369         }
370 }
371
372 void ctsvc_ipc_recovery()
373 {
374         CTS_FN_CALL;
375         g_hash_table_foreach(_ctsvc_ipc_table, _ctsvc_ipc_recovery_foreach_cb, NULL);
376 }
377
378