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