fix svace issues
[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 #include <pthread.h>
28
29 #include "ctsvc_client_ipc.h"
30 #include "ctsvc_client_utils.h"
31 #include "ctsvc_client_service_helper.h"
32
33 #include "ctsvc_internal.h"
34 #include "ctsvc_list.h"
35 #include "ctsvc_record.h"
36 #include "ctsvc_inotify.h"
37
38 #include "ctsvc_ipc_define.h"
39 #include "ctsvc_ipc_marshal.h"
40 #include "ctsvc_view.h"
41 #include "ctsvc_mutex.h"
42 #include "ctsvc_handle.h"
43
44 struct ctsvc_ipc_s {
45         pims_ipc_h ipc;
46         GList *list_handle;
47 };
48
49 static pthread_mutex_t _ctsvc_mutex_disconnected = PTHREAD_MUTEX_INITIALIZER;
50 static GHashTable *_ctsvc_ipc_table = NULL;
51 static bool _ctsvc_ipc_disconnected = false;
52
53 static pims_ipc_h _ctsvc_get_ipc_handle()
54 {
55         struct ctsvc_ipc_s *ipc_data = NULL;
56         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
57         RETVM_IF(NULL == _ctsvc_ipc_table, NULL, "contacts not connected");
58
59         snprintf(ipc_key, sizeof(ipc_key), "%u", ctsvc_client_get_tid());
60         ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
61
62         if (NULL == ipc_data) {
63                 snprintf(ipc_key, sizeof(ipc_key), "%u", ctsvc_client_get_pid());
64                 ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
65         }
66
67         RETVM_IF(NULL == ipc_data, NULL, "g_hash_table_lookup(%s) Fail", ipc_key);
68
69         return ipc_data->ipc;
70 }
71
72 bool ctsvc_ipc_is_busy()
73 {
74         bool ret = false;
75
76         pims_ipc_h ipc = _ctsvc_get_ipc_handle();
77         if (NULL == ipc) {
78                 CTS_ERR("_ctsvc_get_ipc_handle() return NULL");
79                 return false;
80         }
81
82         ret = pims_ipc_is_call_in_progress(ipc);
83         if (ret)
84                 CTS_ERR("global ipc channel is busy.");
85
86         return ret;
87 }
88
89 static int _ctsvc_ipc_create(pims_ipc_h *p_ipc)
90 {
91         char sock_file[CTSVC_PATH_MAX_LEN] = {0};
92         snprintf(sock_file, sizeof(sock_file), CTSVC_SOCK_PATH"/.%s", getuid(), CTSVC_IPC_SERVICE);
93         pims_ipc_h ipc = pims_ipc_create(sock_file);
94         if (NULL == ipc) {
95                 if (errno == EACCES) {
96                         CTS_ERR("pims_ipc_create() Failed(%d)", CONTACTS_ERROR_PERMISSION_DENIED);
97                         return CONTACTS_ERROR_PERMISSION_DENIED;
98                 }
99                 else {
100                         CTS_ERR("pims_ipc_create() Failed(%d)", CONTACTS_ERROR_IPC_NOT_AVALIABLE);
101                         return CONTACTS_ERROR_IPC_NOT_AVALIABLE;
102                 }
103         }
104
105         *p_ipc = ipc;
106         return CONTACTS_ERROR_NONE;
107 }
108
109 static void _ctsvc_ipc_data_free(gpointer p)
110 {
111         struct ctsvc_ipc_s *ipc_data = p;
112         if (NULL == ipc_data)
113                 return;
114
115         if (ipc_data->ipc) {
116                 ctsvc_ipc_unset_disconnected_cb(ipc_data->ipc);
117                 pims_ipc_destroy(ipc_data->ipc);
118         }
119
120         g_list_free(ipc_data->list_handle);
121
122         free(ipc_data);
123 }
124
125 static int _ctsvc_ipc_connect(contacts_h contact, pims_ipc_h ipc)
126 {
127         int ret;
128         pims_ipc_data_h outdata = NULL;
129         pims_ipc_data_h indata = NULL;
130
131         /* Access control : put cookie to indata */
132         indata = pims_ipc_data_create(0);
133         if (indata == NULL) {
134                 CTS_ERR("pims_ipc_data_create() return NULL");
135                 return CONTACTS_ERROR_OUT_OF_MEMORY;
136         }
137
138         ret = ctsvc_ipc_marshal_handle(contact, indata);
139         if (CONTACTS_ERROR_NONE != ret) {
140                 CTS_ERR("ctsvc_ipc_marshal_handle Fail(%d)", ret);
141                 pims_ipc_data_destroy(indata);
142                 return ret;
143         }
144
145         /* ipc call */
146         if (pims_ipc_call(ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CONNECT, indata, &outdata) != 0) {
147                 CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc_call failed");
148                 pims_ipc_data_destroy(indata);
149                 return CONTACTS_ERROR_IPC;
150         }
151         pims_ipc_data_destroy(indata);
152
153         if (outdata) {
154                 ctsvc_ipc_unmarshal_int(outdata, &ret);
155                 pims_ipc_data_destroy(outdata);
156                 RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "ctsvc_ipc_server_connect return(%d)", ret);
157         }
158         return ret;
159 }
160
161 static void _ctsvc_ipc_disconnected_cb(void *user_data)
162 {
163         ctsvc_ipc_set_disconnected(true);
164 }
165
166 int ctsvc_ipc_connect(contacts_h contact, unsigned int handle_id)
167 {
168         int ret = CONTACTS_ERROR_NONE;
169         struct ctsvc_ipc_s *ipc_data = NULL;
170         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
171
172         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
173
174         if (NULL == _ctsvc_ipc_table)
175                 _ctsvc_ipc_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, _ctsvc_ipc_data_free);
176         else
177                 ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
178
179         if (NULL == ipc_data) {
180                 ipc_data = calloc(1, sizeof(struct ctsvc_ipc_s));
181                 if (NULL == ipc_data)
182                 {
183                         CTS_ERR("calloc() Fail");
184                         return CONTACTS_ERROR_OUT_OF_MEMORY;
185                 }
186                 ret = _ctsvc_ipc_create(&(ipc_data->ipc));
187                 if (CONTACTS_ERROR_NONE != ret) {
188                         _ctsvc_ipc_data_free(ipc_data);
189                         return ret;
190                 }
191                 g_hash_table_insert(_ctsvc_ipc_table, strdup(ipc_key), ipc_data);
192                 ctsvc_ipc_set_disconnected_cb(ipc_data->ipc, _ctsvc_ipc_disconnected_cb, NULL);
193         }
194         _ctsvc_ipc_connect(contact, ipc_data->ipc);
195         ipc_data->list_handle = g_list_append(ipc_data->list_handle, contact);
196
197         return CONTACTS_ERROR_NONE;
198 }
199
200
201 int ctsvc_ipc_disconnect(contacts_h contact, unsigned int handle_id, int connection_count)
202 {
203         int ret = CONTACTS_ERROR_NONE;
204         struct ctsvc_ipc_s *ipc_data = NULL;
205         pims_ipc_data_h outdata = NULL;
206         pims_ipc_data_h indata = NULL;
207         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
208
209         RETVM_IF(NULL == _ctsvc_ipc_table, CONTACTS_ERROR_IPC, "contacts not connected");
210         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
211
212         ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
213         RETVM_IF(ipc_data == NULL, CONTACTS_ERROR_IPC, "contacts not connected");
214
215         indata = pims_ipc_data_create(0);
216         if (indata == NULL) {
217                 CTS_ERR("ipc data created fail!");
218                 return CONTACTS_ERROR_OUT_OF_MEMORY;
219         }
220
221         ret = ctsvc_ipc_marshal_handle(contact, indata);
222         if (CONTACTS_ERROR_NONE != ret) {
223                 CTS_ERR("ctsvc_ipc_marshal_handle() Fail(%d)", ret);
224                 pims_ipc_data_destroy(indata);
225                 return ret;
226         }
227         if (pims_ipc_call(ipc_data->ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_DISCONNECT, indata, &outdata) != 0) {
228                 pims_ipc_data_destroy(indata);
229                 CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc_call failed");
230                 return CONTACTS_ERROR_IPC;
231         }
232
233         if (outdata) {
234                 ctsvc_ipc_unmarshal_int(outdata, &ret);
235                 pims_ipc_data_destroy(outdata);
236
237                 if (ret != CONTACTS_ERROR_NONE) {
238                         CTS_ERR("[GLOBAL_IPC_CHANNEL] pims_ipc didn't destroyed!!!(%d)", ret);
239                         return ret;
240                 }
241
242                 if (1 == connection_count) {
243                         g_hash_table_remove(_ctsvc_ipc_table, ipc_key);
244                 }
245         }
246         else {
247                 CTS_ERR("pims_ipc_call out data is NULL");
248                 return CONTACTS_ERROR_IPC;
249         }
250
251         return ret;
252 }
253
254 static void __ctsvc_ipc_lock()
255 {
256         if (0 == ctsvc_client_get_thread_connection_count())
257                 ctsvc_mutex_lock(CTS_MUTEX_PIMS_IPC_CALL);
258 }
259
260 static void __ctsvc_ipc_unlock(void)
261 {
262         if (0 == ctsvc_client_get_thread_connection_count())
263                 ctsvc_mutex_unlock(CTS_MUTEX_PIMS_IPC_CALL);
264 }
265
266 int ctsvc_ipc_call(char *module, char *function, pims_ipc_h data_in, pims_ipc_data_h *data_out)
267 {
268         pims_ipc_h ipc_handle;
269
270         if (true == ctsvc_ipc_get_disconnected()) {
271                 ctsvc_ipc_set_disconnected(false);
272                 ctsvc_ipc_recovery();
273                 ctsvc_ipc_recover_for_change_subscription();
274         }
275
276         ipc_handle = _ctsvc_get_ipc_handle();
277
278         __ctsvc_ipc_lock();
279         int ret = pims_ipc_call(ipc_handle, module, function, data_in, data_out);
280         __ctsvc_ipc_unlock();
281
282         return ret;
283 }
284
285 void ctsvc_client_ipc_set_change_version(contacts_h contact, int version)
286 {
287         RETM_IF(NULL == contact, "contact is NULL");
288         ctsvc_base_s *base = (ctsvc_base_s *)contact;
289         base->version = version;
290 }
291
292 int ctsvc_client_ipc_get_change_version(contacts_h contact)
293 {
294         RETVM_IF(NULL == contact, -1, "contact is NULL");
295         ctsvc_base_s *base = (ctsvc_base_s *)contact;
296         return base->version;
297 }
298
299 int ctsvc_ipc_client_check_permission(int permission, bool *result)
300 {
301         pims_ipc_data_h indata = NULL;
302         pims_ipc_data_h outdata = NULL;
303         int ret;
304
305         if (result)
306                 *result = false;
307
308         indata = pims_ipc_data_create(0);
309         if (indata == NULL) {
310                 CTS_ERR("ipc data created fail !");
311                 return CONTACTS_ERROR_OUT_OF_MEMORY;
312         }
313
314         ret = ctsvc_ipc_marshal_int(permission, indata);
315         if (ret != CONTACTS_ERROR_NONE) {
316                 CTS_ERR("marshal fail");
317                 pims_ipc_data_destroy(indata);
318                 return ret;
319         }
320
321         if (ctsvc_ipc_call(CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CHECK_PERMISSION, indata, &outdata) != 0) {
322                 CTS_ERR("ctsvc_ipc_call Fail");
323                 pims_ipc_data_destroy(indata);
324                 return CONTACTS_ERROR_IPC;
325         }
326
327         pims_ipc_data_destroy(indata);
328
329         if (outdata) {
330                 if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_int(outdata, &ret)) {
331                         CTS_ERR("ctsvc_ipc_unmarshal_int() Fail");
332                         pims_ipc_data_destroy(outdata);
333                         return CONTACTS_ERROR_IPC;
334                 }
335
336                 if (CONTACTS_ERROR_NONE == ret && result) {
337                         if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_bool(outdata, result)) {
338                                 CTS_ERR("ctsvc_ipc_unmarshal_bool() Fail");
339                                 pims_ipc_data_destroy(outdata);
340                                 return CONTACTS_ERROR_IPC;
341                         }
342                 }
343                 pims_ipc_data_destroy(outdata);
344         }
345
346         return ret;
347 }
348
349 int ctsvc_ipc_set_disconnected_cb(pims_ipc_h ipc, void (*cb)(void *), void *user_data)
350 {
351         return pims_ipc_add_server_disconnected_cb(ipc, cb, user_data);
352 }
353
354 int ctsvc_ipc_unset_disconnected_cb(pims_ipc_h ipc)
355 {
356         return pims_ipc_remove_server_disconnected_cb(ipc);
357 }
358
359 void ctsvc_ipc_set_disconnected(bool is_disconnected)
360 {
361         pthread_mutex_lock(&_ctsvc_mutex_disconnected);
362         _ctsvc_ipc_disconnected = is_disconnected;
363         pthread_mutex_unlock(&_ctsvc_mutex_disconnected);
364 }
365
366 int ctsvc_ipc_get_disconnected()
367 {
368         pthread_mutex_lock(&_ctsvc_mutex_disconnected);
369         CTS_DBG("_ctsvc_ipc_disconnected=%d", _ctsvc_ipc_disconnected);
370         pthread_mutex_unlock(&_ctsvc_mutex_disconnected);
371         return _ctsvc_ipc_disconnected;
372 }
373
374 static void _ctsvc_ipc_recovery_foreach_cb(gpointer key, gpointer value, gpointer user_data)
375 {
376         GList *c;
377         struct ctsvc_ipc_s *ipc_data = value;
378
379         ctsvc_ipc_unset_disconnected_cb(ipc_data->ipc);
380         int ret = _ctsvc_ipc_create(&(ipc_data->ipc));
381         RETM_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_create() Fail(%d)", ret);
382         ctsvc_ipc_set_disconnected_cb(ipc_data->ipc, _ctsvc_ipc_disconnected_cb, NULL);
383
384         for (c=ipc_data->list_handle;c;c=c->next) {
385                 contacts_h contact = c->data;
386                 ret = _ctsvc_ipc_connect(contact, ipc_data->ipc);
387                 WARN_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_connect() Fail(%d)", ret);
388         }
389 }
390
391 void ctsvc_ipc_recovery()
392 {
393         CTS_FN_CALL;
394         g_hash_table_foreach(_ctsvc_ipc_table, _ctsvc_ipc_recovery_foreach_cb, NULL);
395 }
396
397