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