eee45d00a214dda577feee13a024649632e640b4
[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 /* LCOV_EXCL_START */
73 bool ctsvc_ipc_is_busy()
74 {
75         bool ret = false;
76
77         pims_ipc_h ipc = _ctsvc_get_ipc_handle();
78         if (NULL == ipc) {
79                 ERR("_ctsvc_get_ipc_handle() return NULL");
80                 return false;
81         }
82
83         ret = pims_ipc_is_call_in_progress(ipc);
84         if (ret)
85                 ERR("global ipc channel is busy.");
86
87         return ret;
88 }
89 /* LCOV_EXCL_STOP */
90
91 static int _ctsvc_ipc_create(pims_ipc_h *p_ipc)
92 {
93         char sock_file[CTSVC_PATH_MAX_LEN] = {0};
94         uid_t uid = getuid();
95
96         if (ctsvc_client_is_in_system_session()) {
97                 if (CONTACTS_ERROR_NONE != ctsvc_client_get_active_uid(&uid))
98                         return CONTACTS_ERROR_SYSTEM;
99         }
100
101         snprintf(sock_file, sizeof(sock_file), CTSVC_SOCK_PATH"/.%s", uid,
102                         CTSVC_IPC_SERVICE);
103         pims_ipc_h ipc = pims_ipc_create(sock_file);
104         if (NULL == ipc) {
105                 /* LCOV_EXCL_START */
106                 if (errno == EACCES) {
107                         ERR("pims_ipc_create() Fail(%d)", CONTACTS_ERROR_PERMISSION_DENIED);
108                         return CONTACTS_ERROR_PERMISSION_DENIED;
109                 } else {
110                         ERR("pims_ipc_create() Fail(%d)", CONTACTS_ERROR_IPC_NOT_AVALIABLE);
111                         return CONTACTS_ERROR_IPC_NOT_AVALIABLE;
112                 }
113                 /* LCOV_EXCL_STOP */
114         }
115
116         *p_ipc = ipc;
117         return CONTACTS_ERROR_NONE;
118 }
119
120 static void _ctsvc_ipc_data_free(gpointer p)
121 {
122         struct ctsvc_ipc_s *ipc_data = p;
123         if (NULL == ipc_data)
124                 return;
125
126         if (ipc_data->ipc) {
127                 ctsvc_ipc_unset_disconnected_cb(ipc_data->ipc);
128                 pims_ipc_destroy(ipc_data->ipc);
129         }
130
131         g_list_free(ipc_data->list_handle);
132
133         free(ipc_data);
134 }
135
136 static int _ctsvc_ipc_connect(contacts_h contact, pims_ipc_h ipc)
137 {
138         int ret;
139         pims_ipc_data_h outdata = NULL;
140         pims_ipc_data_h indata = NULL;
141
142         /* Access control : put cookie to indata */
143         indata = pims_ipc_data_create(0);
144         if (indata == NULL) {
145                 /* LCOV_EXCL_START */
146                 ERR("pims_ipc_data_create() return NULL");
147                 return CONTACTS_ERROR_OUT_OF_MEMORY;
148                 /* LCOV_EXCL_STOP */
149         }
150
151         ret = ctsvc_ipc_marshal_handle(contact, indata);
152         if (CONTACTS_ERROR_NONE != ret) {
153                 /* LCOV_EXCL_START */
154                 ERR("ctsvc_ipc_marshal_handle() Fail(%d)", ret);
155                 pims_ipc_data_destroy(indata);
156                 return ret;
157                 /* LCOV_EXCL_STOP */
158         }
159
160         /* ipc call */
161         ret = pims_ipc_call(ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CONNECT, indata,
162                         &outdata);
163         if (0 != ret) {
164                 /* LCOV_EXCL_START */
165                 ERR("pims_ipc_call() Fail(%d)", ret);
166                 pims_ipc_data_destroy(indata);
167                 return CONTACTS_ERROR_IPC;
168                 /* LCOV_EXCL_STOP */
169         }
170         pims_ipc_data_destroy(indata);
171
172         if (outdata) {
173                 ctsvc_ipc_unmarshal_int(outdata, &ret);
174                 pims_ipc_data_destroy(outdata);
175                 if (CONTACTS_ERROR_NONE != ret)
176                         ERR("ctsvc_ipc_unmarshal_int() return(%d)", ret);
177         }
178         return ret;
179 }
180
181 /* LCOV_EXCL_START */
182 static void _ctsvc_ipc_disconnected_cb(void *user_data)
183 {
184         ctsvc_ipc_set_disconnected(true);
185 }
186 /* LCOV_EXCL_STOP */
187
188 int ctsvc_ipc_connect(contacts_h contact, unsigned int handle_id)
189 {
190         int ret = CONTACTS_ERROR_NONE;
191         struct ctsvc_ipc_s *ipc_data = NULL;
192         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
193
194         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
195
196         if (NULL == _ctsvc_ipc_table) {
197                 _ctsvc_ipc_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
198                                 _ctsvc_ipc_data_free);
199         } else {
200                 ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
201         }
202
203         if (NULL == ipc_data) {
204                 ipc_data = calloc(1, sizeof(struct ctsvc_ipc_s));
205                 if (NULL == ipc_data) {
206                         /* LCOV_EXCL_START */
207                         ERR("calloc() Fail");
208                         return CONTACTS_ERROR_OUT_OF_MEMORY;
209                         /* LCOV_EXCL_STOP */
210                 }
211                 ret = _ctsvc_ipc_create(&(ipc_data->ipc));
212                 if (CONTACTS_ERROR_NONE != ret) {
213                         _ctsvc_ipc_data_free(ipc_data);
214                         return ret;
215                 }
216                 g_hash_table_insert(_ctsvc_ipc_table, strdup(ipc_key), ipc_data);
217                 ctsvc_ipc_set_disconnected_cb(ipc_data->ipc, _ctsvc_ipc_disconnected_cb, NULL);
218         }
219         _ctsvc_ipc_connect(contact, ipc_data->ipc);
220         ipc_data->list_handle = g_list_append(ipc_data->list_handle, contact);
221
222         return CONTACTS_ERROR_NONE;
223 }
224
225
226 int ctsvc_ipc_disconnect(contacts_h contact, unsigned int handle_id,
227                 int connection_count)
228 {
229         int ret = CONTACTS_ERROR_NONE;
230         struct ctsvc_ipc_s *ipc_data = NULL;
231         pims_ipc_data_h outdata = NULL;
232         pims_ipc_data_h indata = NULL;
233         char ipc_key[CTSVC_STR_SHORT_LEN] = {0};
234
235         RETVM_IF(NULL == _ctsvc_ipc_table, CONTACTS_ERROR_IPC, "contacts not connected");
236         snprintf(ipc_key, sizeof(ipc_key), "%u", handle_id);
237
238         ipc_data = g_hash_table_lookup(_ctsvc_ipc_table, ipc_key);
239         RETVM_IF(ipc_data == NULL, CONTACTS_ERROR_IPC, "contacts not connected");
240
241         indata = pims_ipc_data_create(0);
242         if (indata == NULL) {
243                 /* LCOV_EXCL_START */
244                 ERR("pims_ipc_data_create() Fail");
245                 return CONTACTS_ERROR_OUT_OF_MEMORY;
246                 /* LCOV_EXCL_STOP */
247         }
248
249         ret = ctsvc_ipc_marshal_handle(contact, indata);
250         if (CONTACTS_ERROR_NONE != ret) {
251                 /* LCOV_EXCL_START */
252                 ERR("ctsvc_ipc_marshal_handle() Fail(%d)", ret);
253                 pims_ipc_data_destroy(indata);
254                 return ret;
255                 /* LCOV_EXCL_STOP */
256         }
257
258         ret = pims_ipc_call(ipc_data->ipc, CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_DISCONNECT,
259                         indata, &outdata);
260         if (0 != ret) {
261                 pims_ipc_data_destroy(indata);
262                 /* LCOV_EXCL_START */
263                 ERR("pims_ipc_call() Fail(%d)", ret);
264                 return CONTACTS_ERROR_IPC;
265                 /* LCOV_EXCL_STOP */
266         }
267         pims_ipc_data_destroy(indata);
268
269         if (outdata) {
270                 ctsvc_ipc_unmarshal_int(outdata, &ret);
271                 pims_ipc_data_destroy(outdata);
272
273                 if (ret != CONTACTS_ERROR_NONE) {
274                         /* LCOV_EXCL_START */
275                         ERR("ctsvc_ipc_unmarshal_int() Fail(%d)", ret);
276                         return ret;
277                         /* LCOV_EXCL_STOP */
278                 }
279
280                 if (1 == connection_count)
281                         g_hash_table_remove(_ctsvc_ipc_table, ipc_key);
282         } else {
283                 /* LCOV_EXCL_START */
284                 ERR("pims_ipc_call out data is NULL");
285                 return CONTACTS_ERROR_IPC;
286                 /* LCOV_EXCL_STOP */
287         }
288
289         return ret;
290 }
291
292 static void __ctsvc_ipc_lock()
293 {
294         if (0 == ctsvc_client_get_thread_connection_count())
295                 ctsvc_mutex_lock(CTS_MUTEX_PIMS_IPC_CALL);
296 }
297
298 static void __ctsvc_ipc_unlock(void)
299 {
300         if (0 == ctsvc_client_get_thread_connection_count())
301                 ctsvc_mutex_unlock(CTS_MUTEX_PIMS_IPC_CALL);
302 }
303
304 int ctsvc_ipc_call(char *module, char *function, pims_ipc_h data_in,
305                 pims_ipc_data_h *data_out)
306 {
307         pims_ipc_h ipc_handle;
308
309         if (true == ctsvc_ipc_get_disconnected()) {
310                 ctsvc_ipc_set_disconnected(false);
311                 ctsvc_ipc_recovery();
312                 ctsvc_ipc_recover_for_change_subscription();
313         }
314
315         ipc_handle = _ctsvc_get_ipc_handle();
316
317         __ctsvc_ipc_lock();
318         int ret = pims_ipc_call(ipc_handle, module, function, data_in, data_out);
319         __ctsvc_ipc_unlock();
320
321         return ret;
322 }
323
324 void ctsvc_client_ipc_set_change_version(contacts_h contact, int version)
325 {
326         ctsvc_base_s *base = (ctsvc_base_s*)contact;
327
328         RET_IF(NULL == contact);
329
330         base->version = version;
331 }
332
333 int ctsvc_client_ipc_get_change_version(contacts_h contact)
334 {
335         ctsvc_base_s *base = (ctsvc_base_s*)contact;
336
337         RETV_IF(NULL == contact, -1);
338
339         return base->version;
340 }
341
342 int ctsvc_ipc_client_check_permission(int permission, bool *result)
343 {
344         pims_ipc_data_h indata = NULL;
345         pims_ipc_data_h outdata = NULL;
346         int ret;
347
348         if (result)
349                 *result = false;
350
351         indata = pims_ipc_data_create(0);
352         if (indata == NULL) {
353                 /* LCOV_EXCL_START */
354                 ERR("pims_ipc_data_create() Fail");
355                 return CONTACTS_ERROR_OUT_OF_MEMORY;
356                 /* LCOV_EXCL_STOP */
357         }
358
359         ret = ctsvc_ipc_marshal_int(permission, indata);
360         if (ret != CONTACTS_ERROR_NONE) {
361                 /* LCOV_EXCL_START */
362                 ERR("ctsvc_ipc_marshal_int() Fail(%d)", ret);
363                 pims_ipc_data_destroy(indata);
364                 return ret;
365                 /* LCOV_EXCL_STOP */
366         }
367
368         ret = ctsvc_ipc_call(CTSVC_IPC_MODULE, CTSVC_IPC_SERVER_CHECK_PERMISSION, indata,
369                         &outdata);
370         if (0 != ret) {
371                 /* LCOV_EXCL_START */
372                 ERR("ctsvc_ipc_call() Fail(%d)", ret);
373                 pims_ipc_data_destroy(indata);
374                 return CONTACTS_ERROR_IPC;
375                 /* LCOV_EXCL_STOP */
376         }
377
378         pims_ipc_data_destroy(indata);
379
380         if (outdata) {
381                 if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_int(outdata, &ret)) {
382                         /* LCOV_EXCL_START */
383                         ERR("ctsvc_ipc_unmarshal_int() Fail");
384                         pims_ipc_data_destroy(outdata);
385                         return CONTACTS_ERROR_IPC;
386                         /* LCOV_EXCL_STOP */
387                 }
388
389                 if (CONTACTS_ERROR_NONE == ret && result) {
390                         if (CONTACTS_ERROR_NONE != ctsvc_ipc_unmarshal_bool(outdata, result)) {
391                                 /* LCOV_EXCL_START */
392                                 ERR("ctsvc_ipc_unmarshal_bool() Fail");
393                                 pims_ipc_data_destroy(outdata);
394                                 return CONTACTS_ERROR_IPC;
395                                 /* LCOV_EXCL_STOP */
396                         }
397                 }
398                 pims_ipc_data_destroy(outdata);
399         }
400
401         return ret;
402 }
403
404 /* LCOV_EXCL_START */
405 int ctsvc_ipc_set_disconnected_cb(pims_ipc_h ipc, void (*cb)(void *), void *user_data)
406 {
407         return pims_ipc_add_server_disconnected_cb(ipc, cb, user_data);
408 }
409
410 int ctsvc_ipc_unset_disconnected_cb(pims_ipc_h ipc)
411 {
412         return pims_ipc_remove_server_disconnected_cb(ipc);
413 }
414
415 void ctsvc_ipc_set_disconnected(bool is_disconnected)
416 {
417         pthread_mutex_lock(&_ctsvc_mutex_disconnected);
418         _ctsvc_ipc_disconnected = is_disconnected;
419         pthread_mutex_unlock(&_ctsvc_mutex_disconnected);
420 }
421
422 int ctsvc_ipc_get_disconnected()
423 {
424         pthread_mutex_lock(&_ctsvc_mutex_disconnected);
425         DBG("_ctsvc_ipc_disconnected=%d", _ctsvc_ipc_disconnected);
426         pthread_mutex_unlock(&_ctsvc_mutex_disconnected);
427         return _ctsvc_ipc_disconnected;
428 }
429
430 static void _ctsvc_ipc_recovery_foreach_cb(gpointer key, gpointer value,
431                 gpointer user_data)
432 {
433         GList *c;
434         struct ctsvc_ipc_s *ipc_data = value;
435
436         ctsvc_ipc_unset_disconnected_cb(ipc_data->ipc);
437
438         int ret = _ctsvc_ipc_create(&(ipc_data->ipc));
439         RETM_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_create() Fail(%d)", ret);
440
441         ctsvc_ipc_set_disconnected_cb(ipc_data->ipc, _ctsvc_ipc_disconnected_cb, NULL);
442
443         for (c = ipc_data->list_handle; c; c = c->next) {
444                 contacts_h contact = c->data;
445                 ret = _ctsvc_ipc_connect(contact, ipc_data->ipc);
446                 WARN_IF(CONTACTS_ERROR_NONE != ret, "_ctsvc_ipc_connect() Fail(%d)", ret);
447         }
448 }
449
450 void ctsvc_ipc_recovery()
451 {
452         CTS_FN_CALL;
453         g_hash_table_foreach(_ctsvc_ipc_table, _ctsvc_ipc_recovery_foreach_cb, NULL);
454 }
455 /* LCOV_EXCL_STOP */
456