ctsvc_client_handle_remove in CTS_MUTEX_CONNECTION
[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 static void _ctsvc_ipc_disconnected_cb(void *user_data)
158 {
159         ctsvc_ipc_set_disconnected(true);
160 }
161
162 int ctsvc_ipc_connect(contacts_h contact, unsigned int handle_id)
163 {
164         int ret = CONTACTS_ERROR_NONE;
165         struct ctsvc_ipc_s *ipc_data = NULL;
166         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
167
168         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
169
170         if (NULL == _ctsvc_ipc_table)
171                 _ctsvc_ipc_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, _ctsvc_ipc_data_free);
172         else
173                 ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
174
175         if (NULL == ipc_data) {
176                 ipc_data = calloc(1, sizeof(struct ctsvc_ipc_s));
177                 if (NULL == ipc_data)
178                 {
179                         CTS_ERR("calloc() Fail");
180                         return CONTACTS_ERROR_OUT_OF_MEMORY;
181                 }
182                 ret = _ctsvc_ipc_create(&(ipc_data->ipc));
183                 if (CONTACTS_ERROR_NONE != ret) {
184                         _ctsvc_ipc_data_free(ipc_data);
185                         return ret;
186                 }
187                 g_hash_table_insert(_ctsvc_ipc_table, strdup(ipc_key), ipc_data);
188                 ctsvc_ipc_set_disconnected_cb(ipc_data->ipc, _ctsvc_ipc_disconnected_cb, NULL);
189         }
190         _ctsvc_ipc_connect(contact, ipc_data->ipc);
191         ipc_data->list_handle = g_list_append(ipc_data->list_handle, contact);
192
193         return CONTACTS_ERROR_NONE;
194 }
195
196
197 int ctsvc_ipc_disconnect(contacts_h contact, unsigned int handle_id, int connection_count)
198 {
199         int ret = CONTACTS_ERROR_NONE;
200         struct ctsvc_ipc_s *ipc_data = NULL;
201         pims_ipc_data_h outdata = NULL;
202         pims_ipc_data_h indata = NULL;
203         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
204
205         RETVM_IF(NULL == _ctsvc_ipc_table, CONTACTS_ERROR_IPC, "contacts not connected");
206         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
207
208         ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
209         RETVM_IF(ipc_data == NULL, CONTACTS_ERROR_IPC, "contacts not connected");
210
211         indata = pims_ipc_data_create(0);
212         if (indata == NULL) {
213                 CTS_ERR("ipc data created fail!");
214                 return CONTACTS_ERROR_OUT_OF_MEMORY;
215         }
216
217         ret = ctsvc_ipc_marshal_handle(contact, indata);
218         RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "ctsvc_ipc_marshal_handle() Fail(%d)", ret);
219
220         if (pims_ipc_call(ipc_data->ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_DISCONNECT, indata, &outdata) != 0) {
221                 pims_ipc_data_destroy(indata);
222                 CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc_call failed");
223                 return CONTACTS_ERROR_IPC;
224         }
225
226         if (outdata) {
227                 ctsvc_ipc_unmarshal_int(outdata, &ret);
228                 pims_ipc_data_destroy(outdata);
229
230                 if (ret != CONTACTS_ERROR_NONE) {
231                         CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc didn't destroyed!!!(%d)", ret);
232                         return ret;
233                 }
234
235                 if (1 == connection_count) {
236                         g_hash_table_remove(_ctsvc_ipc_table, ipc_key);
237                 }
238         }
239         else {
240                 CTS_ERR("pims_ipc_call out data is NULL");
241                 return CONTACTS_ERROR_IPC;
242         }
243
244         return ret;
245 }
246
247 static void __ctsvc_ipc_lock()
248 {
249         if (0 == ctsvc_client_get_thread_connection_count())
250                 ctsvc_mutex_lock(CTS_MUTEX_PIMS_IPC_CALL);
251 }
252
253 static void __ctsvc_ipc_unlock(void)
254 {
255         if (0 == ctsvc_client_get_thread_connection_count())
256                 ctsvc_mutex_unlock(CTS_MUTEX_PIMS_IPC_CALL);
257 }
258
259 int ctsvc_ipc_call(char *module, char *function, pims_ipc_h data_in, pims_ipc_data_h *data_out)
260 {
261         pims_ipc_h ipc_handle;
262
263         if (true == ctsvc_ipc_get_disconnected()) {
264                 ctsvc_ipc_set_disconnected(false);
265                 ctsvc_ipc_recovery();
266                 ctsvc_ipc_recover_for_change_subscription();
267         }
268
269         ipc_handle = _ctsvc_get_ipc_handle();
270
271         __ctsvc_ipc_lock();
272         int ret = pims_ipc_call(ipc_handle, module, function, data_in, data_out);
273         __ctsvc_ipc_unlock();
274
275         return ret;
276 }
277
278 void ctsvc_client_ipc_set_change_version(contacts_h contact, int version)
279 {
280         RETM_IF(NULL == contact, "contact is NULL");
281         ctsvc_base_s *base = (ctsvc_base_s *)contact;
282         base->version = version;
283 }
284
285 int ctsvc_client_ipc_get_change_version(contacts_h contact)
286 {
287         RETVM_IF(NULL == contact, -1, "contact is NULL");
288         ctsvc_base_s *base = (ctsvc_base_s *)contact;
289         return base->version;
290 }
291
292 int ctsvc_ipc_client_check_permission(int permission, bool *result)
293 {
294         pims_ipc_data_h indata = NULL;
295         pims_ipc_data_h outdata = NULL;
296         int ret;
297
298         if (result)
299                 *result = false;
300
301         indata = pims_ipc_data_create(0);
302         if (indata == NULL) {
303                 CTS_ERR("ipc data created fail !");
304                 return CONTACTS_ERROR_OUT_OF_MEMORY;
305         }
306
307         ret = ctsvc_ipc_marshal_int(permission, indata);
308         if (ret != CONTACTS_ERROR_NONE) {
309                 CTS_ERR("marshal fail");
310                 pims_ipc_data_destroy(indata);
311                 return ret;
312         }
313
314         if (ctsvc_ipc_call(CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CHECK_PERMISSION, indata, &outdata) != 0) {
315                 CTS_ERR("ctsvc_ipc_call Fail");
316                 pims_ipc_data_destroy(indata);
317                 return CONTACTS_ERROR_IPC;
318         }
319
320         pims_ipc_data_destroy(indata);
321
322         if (outdata) {
323                 if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_int(outdata, &ret)) {
324                         CTS_ERR("ctsvc_ipc_unmarshal_int() Fail");
325                         pims_ipc_data_destroy(outdata);
326                         return CONTACTS_ERROR_IPC;
327                 }
328
329                 if (CONTACTS_ERROR_NONE == ret && result) {
330                         if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_bool(outdata, result)) {
331                                 CTS_ERR("ctsvc_ipc_unmarshal_bool() Fail");
332                                 pims_ipc_data_destroy(outdata);
333                                 return CONTACTS_ERROR_IPC;
334                         }
335                 }
336                 pims_ipc_data_destroy(outdata);
337         }
338
339         return ret;
340 }
341
342 int ctsvc_ipc_set_disconnected_cb(pims_ipc_h ipc, void (*cb)(void *), void *user_data)
343 {
344         return pims_ipc_add_server_disconnected_cb(ipc, cb, user_data);
345 }
346
347 int ctsvc_ipc_unset_disconnected_cb(pims_ipc_h ipc)
348 {
349         return pims_ipc_remove_server_disconnected_cb(ipc);
350 }
351
352 void ctsvc_ipc_set_disconnected(bool is_disconnected)
353 {
354         _ctsvc_ipc_disconnected = is_disconnected;
355 }
356
357 int ctsvc_ipc_get_disconnected()
358 {
359         CTS_DBG("_ctsvc_ipc_disconnected=%d", _ctsvc_ipc_disconnected);
360         return _ctsvc_ipc_disconnected;
361 }
362
363 static void _ctsvc_ipc_recovery_foreach_cb(gpointer key, gpointer value, gpointer user_data)
364 {
365         GList *c;
366         struct ctsvc_ipc_s *ipc_data = value;
367
368         int ret = _ctsvc_ipc_create(&(ipc_data->ipc));
369         RETM_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_create() Fail(%d)", ret);
370         ctsvc_ipc_set_disconnected_cb(ipc_data->ipc, _ctsvc_ipc_disconnected_cb, NULL);
371
372         for (c=ipc_data->list_handle;c;c=c->next) {
373                 contacts_h contact = c->data;
374                 ret = _ctsvc_ipc_connect(contact, ipc_data->ipc);
375                 WARN_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_connect() Fail(%d)", ret);
376         }
377 }
378
379 void ctsvc_ipc_recovery()
380 {
381         CTS_FN_CALL;
382         g_hash_table_foreach(_ctsvc_ipc_table, _ctsvc_ipc_recovery_foreach_cb, NULL);
383 }
384
385