4967b768769b9fbee12869832104cffc36cac351
[platform/core/multimedia/mmsvc-core.git] / server / src / muse_server_ipc.c
1 /*
2  * muse-server
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: YoungHun Kim <yh8004.kim@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include "muse_core_internal.h"
23 #include "muse_server_private.h"
24
25 #define MSG_THREAD_NAME                                         "msg"
26 #define DATA_THREAD_NAME                                        "data"
27
28 #define DATA_WORKER_QDATA_MAX_SIZE                      (3840 * 2160 * 4) /* UHD BGRA8888 */
29 #define UNLIMITED_INSTANCE                                      -1
30
31 static void _ms_ipc_data_ch_init(muse_module_h m);
32 static void _ms_ipc_data_ch_deinit(muse_module_h m);
33 static void _ms_ipc_module_data_ch_cleanup(muse_module_h m);
34 static void _ms_ipc_module_msg_ch_cleanup(muse_module_h m);
35 static void _ms_ipc_module_cleanup(muse_module_h m);
36 static gboolean _ms_ipc_module_instance_creation_is_allowed(int module_idx);
37 static gboolean _ms_ipc_get_module_idx(muse_module_h m, void *jobj);
38 static gboolean _ms_ipc_dispatch_create(muse_module_h m, void *jobj);
39 static gboolean _ms_ipc_dispatch_destroy(muse_module_h m);
40 static gboolean _ms_ipc_dispatch_no_instance(muse_module_h m, void *jobj);
41 static gpointer _ms_ipc_dispatch_worker(gpointer data);
42 static gboolean _ms_ipc_data_processing(int fd, muse_recv_data_head_t *header, muse_channel_info_t *ch);
43 static gpointer _ms_ipc_data_worker(gpointer data);
44
45 static void _ms_ipc_data_ch_init(muse_module_h m)
46 {
47         muse_return_if_fail(m);
48
49         m->ch[MUSE_CHANNEL_DATA].data_queue = g_queue_new();
50         g_mutex_init(&m->ch[MUSE_CHANNEL_DATA].data_mutex);
51         g_cond_init(&m->ch[MUSE_CHANNEL_DATA].data_cond);
52 }
53
54 static void _ms_ipc_data_ch_deinit(muse_module_h m)
55 {
56         muse_return_if_fail(m);
57
58         g_mutex_lock(&m->ch[MUSE_CHANNEL_DATA].data_mutex);
59         g_queue_free(m->ch[MUSE_CHANNEL_DATA].data_queue);
60         m->ch[MUSE_CHANNEL_DATA].data_queue = NULL;
61         g_cond_broadcast(&m->ch[MUSE_CHANNEL_DATA].data_cond);
62         g_mutex_unlock(&m->ch[MUSE_CHANNEL_DATA].data_mutex);
63
64         g_mutex_clear(&m->ch[MUSE_CHANNEL_DATA].data_mutex);
65         g_cond_clear(&m->ch[MUSE_CHANNEL_DATA].data_cond);
66 }
67
68 static void _ms_ipc_module_data_ch_cleanup(muse_module_h m)
69 {
70         muse_return_if_fail(m);
71
72         if (!m->ch[MUSE_CHANNEL_DATA].thread)
73                 return;
74
75         g_thread_join(m->ch[MUSE_CHANNEL_DATA].thread);
76         m->ch[MUSE_CHANNEL_DATA].thread = NULL;
77
78         _ms_ipc_data_ch_deinit(m);
79
80         LOGD("[close] [%d] MUSE_CHANNEL_DATA", m->ch[MUSE_CHANNEL_DATA].sock_fd);
81         muse_core_connection_close(m->ch[MUSE_CHANNEL_DATA].sock_fd);
82
83 }
84
85 static void _ms_ipc_module_msg_ch_cleanup(muse_module_h m)
86 {
87         muse_return_if_fail(m);
88         muse_return_if_fail(m->ch[MUSE_CHANNEL_MSG].thread);
89
90         SECURE_LOGD("[close] [%d] MUSE_CHANNEL_MSG %p", m->ch[MUSE_CHANNEL_MSG].sock_fd, m);
91         muse_core_connection_close(m->ch[MUSE_CHANNEL_MSG].sock_fd);
92
93         SECURE_LOGD("msg thread (%p) exit", m->ch[MUSE_CHANNEL_MSG].thread);
94         g_thread_unref(m->ch[MUSE_CHANNEL_MSG].thread);
95         m->ch[MUSE_CHANNEL_MSG].thread = NULL;
96 }
97
98 static void _ms_ipc_module_cleanup(muse_module_h m)
99 {
100         muse_return_if_fail(m);
101
102         _ms_ipc_module_data_ch_cleanup(m);
103
104         _ms_ipc_module_msg_ch_cleanup(m);
105
106         ms_connection_unregister(m);
107
108         g_mutex_clear(&m->dispatch_lock);
109
110         memset(m, 0, sizeof(muse_module_t));
111
112         LOGI("[module %p] EXIT pid %d handle %zd created %d", m, m->pid, m->handle, m->is_created);
113         g_free(m);
114 }
115
116 static gboolean _ms_ipc_module_instance_creation_is_allowed(int module_idx)
117 {
118         int max_instance, created_module_instance_count;
119
120         muse_return_val_if_fail(ms_check_module_idx(module_idx), FALSE);
121
122         max_instance = ms_config_get_max_instance(module_idx);
123         created_module_instance_count = muse_server_get_module_instance_count(module_idx);
124
125         if (max_instance == UNLIMITED_INSTANCE || created_module_instance_count < max_instance) {
126                 return TRUE;
127         } else {
128                 LOGW("The number (%d) of created module instance is over the value of max instance (%d)",
129                                 created_module_instance_count, max_instance);
130                 return FALSE;
131         }
132 }
133
134 static gboolean _ms_ipc_get_module_idx(muse_module_h m, void *jobj)
135 {
136         muse_return_val_if_fail(m, FALSE);
137         muse_return_val_if_fail(jobj, FALSE);
138
139         if (!muse_core_msg_object_get_value(MSG_KEY_MODULE_INDEX, jobj, MUSE_TYPE_INT, &m->idx)) {
140                 LOGE("Failed to get the value of module index");
141                 return FALSE;
142         }
143
144         return TRUE;
145 }
146
147 static gboolean _ms_ipc_dispatch_create(muse_module_h m, void *jobj)
148 {
149         int pid, dispatch_ret = MM_ERROR_NONE;
150         g_autoptr(GMutexLocker) locker = NULL;
151
152         muse_return_val_if_fail(m, FALSE);
153         muse_return_val_if_fail(jobj, FALSE);
154
155         locker = g_mutex_locker_new(&m->dispatch_lock);
156
157         if (!_ms_ipc_get_module_idx(m, jobj)) {
158                 ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_DEBUG_INFO_DUMP);
159                 return FALSE;
160         }
161
162         m->ch[MUSE_CHANNEL_MSG].dll_handle = ms_module_open(m->idx);
163
164         if (!_ms_ipc_module_instance_creation_is_allowed(m->idx)) {
165                 ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_RESOURCE_NOT_AVAILABLE);
166                 return FALSE;
167         }
168
169         if (muse_core_msg_object_get_value(MSG_KEY_PID, jobj, MUSE_TYPE_INT, &pid) && m->pid != pid)
170                 LOGW("connected pid [%d] msg [%d] is different", m->pid, pid);
171
172         ms_connection_register(m);
173
174         if (muse_server_is_ready())
175                 ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_CREATE_SERVER_ACK);
176         else
177                 LOGW("Do not send server acknowledgement because muse server is actually not ready");
178
179         _ms_ipc_data_ch_init(m);
180
181         LOGD("module fd: %d dll_handle: %p", m->ch[MUSE_CHANNEL_MSG].sock_fd, m->ch[MUSE_CHANNEL_MSG].dll_handle);
182         dispatch_ret = ms_module_dispatch(m);
183
184         if (dispatch_ret != MM_ERROR_NONE) {
185                 LOGE("create dispatch failed 0x%x, clean up module", dispatch_ret);
186                 return FALSE;
187         }
188
189         m->is_created = TRUE;
190
191         LOGD("Leave");
192
193         return TRUE;
194 }
195
196 static gboolean _ms_ipc_dispatch_destroy(muse_module_h m)
197 {
198         int dispatch_ret = MM_ERROR_NONE;
199         g_autoptr(GMutexLocker) locker = NULL;
200
201         muse_return_val_if_fail(m, FALSE);
202
203         locker = g_mutex_locker_new(&m->dispatch_lock);
204
205         dispatch_ret = ms_module_dispatch(m);
206         if (dispatch_ret != MM_ERROR_NONE) {
207                 LOGE("destroy dispatch failed 0x%x", dispatch_ret);
208                 return TRUE;
209         }
210
211         return FALSE;
212 }
213
214 static gboolean _ms_ipc_dispatch_no_instance(muse_module_h m, void *jobj)
215 {
216         muse_return_val_if_fail(m, FALSE);
217         muse_return_val_if_fail(jobj, FALSE);
218
219         g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&m->dispatch_lock);
220
221         if (!_ms_ipc_get_module_idx(m, jobj)) {
222                 ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_DEBUG_INFO_DUMP);
223                 return FALSE;
224         }
225
226         m->ch[MUSE_CHANNEL_MSG].dll_handle = ms_module_open(m->idx);
227
228         if (!_ms_ipc_module_instance_creation_is_allowed(m->idx)) {
229                 ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_RESOURCE_NOT_AVAILABLE);
230                 return FALSE;
231         }
232
233         ms_connection_register(m);
234
235         _ms_ipc_data_ch_init(m);
236
237         ms_module_dispatch(m);
238
239         SECURE_LOGW("[module %p] [loaded value %d]", m, ms_module_get_loaded_dllsym(m->idx));
240
241         return FALSE;
242 }
243
244 static gpointer _ms_ipc_dispatch_worker(gpointer data)
245 {
246         int len, fd, i;
247         int parse_len = 0;
248         muse_module_h m = NULL;
249         gboolean attempt_to_dispatch = TRUE;
250         void *jobj = NULL;
251         char err_msg[MUSE_MSG_LEN_MAX] = {'\0',};
252
253         muse_return_val_if_fail(data, NULL);
254
255         m = (muse_module_h)data;
256
257         fd = m->ch[MUSE_CHANNEL_MSG].sock_fd;
258         m->ch[MUSE_CHANNEL_MSG].thread = g_thread_self();
259
260         LOGI("Enter %d module %p thread %p", fd, m, m->ch[MUSE_CHANNEL_MSG].thread);
261
262         while (attempt_to_dispatch) {
263                 memset(m->recv_msg, 0x00, sizeof(m->recv_msg));
264
265                 for (i = 0; i < MUSE_NUM_FD; i++)
266                         m->ch[MUSE_CHANNEL_MSG].tbm_fd[i] = -1;
267
268                 len = muse_core_msg_recv_fd(fd, m->recv_msg, MUSE_MSG_MAX_LENGTH, m->ch[MUSE_CHANNEL_MSG].tbm_fd);
269                 if (len <= 0) {
270                         strerror_r(errno, err_msg, MUSE_MSG_LEN_MAX);
271                         LOGE("[%s] [%d] recv : %s (%d)", ms_config_get_host_name(m->idx), fd, err_msg, errno);
272                         ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_SHUTDOWN);
273                         attempt_to_dispatch = FALSE;
274                 }
275
276                 m->msg_offset = 0;
277
278                 while (attempt_to_dispatch && m->msg_offset < len && ms_is_server_ready()) {
279                         jobj = muse_core_msg_object_new(m->recv_msg + m->msg_offset, &parse_len, NULL);
280                         if (!jobj) {
281                                 LOGE("jobj is null");
282                                 attempt_to_dispatch = FALSE;
283                                 break;
284                         }
285
286                         if (muse_core_msg_object_get_value(MSG_KEY_API, jobj, MUSE_TYPE_INT, &m->api)) {
287                                 switch (m->api) {
288                                 case API_CREATE:
289                                         SECURE_LOGI("CREATE module %p %d", m, fd);
290                                         attempt_to_dispatch = _ms_ipc_dispatch_create(m, jobj);
291                                         break;
292                                 case API_DESTROY:
293                                         SECURE_LOGI("DESTROY module %p %d", m, fd);
294                                         attempt_to_dispatch = _ms_ipc_dispatch_destroy(m);
295                                         break;
296                                 default:
297                                         if (m->is_created) /* handle based */
298                                                 ms_module_dispatch(m);
299                                         else
300                                                 attempt_to_dispatch = _ms_ipc_dispatch_no_instance(m, jobj);
301                                         break;
302                                 }
303                         }
304                         muse_core_msg_object_free(jobj);
305                         jobj = NULL;
306                         m->msg_offset += parse_len;
307                         parse_len = len - parse_len;
308                 }
309         }
310
311         _ms_ipc_module_cleanup(m);
312
313         LOGD("worker exit");
314
315 #ifdef MUSE_GCOV_TEST
316         muse_core_gcov_flush();
317 #endif
318
319         return NULL;
320 }
321
322 static gboolean _ms_ipc_data_processing(int fd, muse_recv_data_head_t *header, muse_channel_info_t *ch)
323 {
324         char *raw_data = NULL;
325         muse_server_h ms = ms_get_instance();
326
327         muse_return_val_if_fail(ms, FALSE);
328
329         if (!(fd > 0 && header && ch)) {
330                 LOGE("invalid param %d %p %p", fd, header, ch);
331                 goto _PROCESSING_FAILED;
332         }
333
334         /* check marker */
335         if (header->marker != MUSE_DATA_HEAD) {
336                 LOGE("invalid marker 0x%x", header->marker);
337                 goto _PROCESSING_FAILED;
338         }
339
340         /* check data size */
341         if (header->size > DATA_WORKER_QDATA_MAX_SIZE) {
342                 LOGE("invalid data size %d", header->size);
343                 goto _PROCESSING_FAILED;
344         }
345
346         /* allocation data */
347         raw_data = (char *)g_try_new0(char, header->size + sizeof(muse_recv_data_head_t));
348         if (!raw_data) {
349                 LOGE("failed to alloc data %d + %zu", header->size, sizeof(muse_recv_data_head_t));
350                 goto _PROCESSING_FAILED;
351         }
352
353         /* copy header */
354         memcpy(raw_data, header, sizeof(muse_recv_data_head_t));
355
356         /* receive data */
357         if (!muse_core_msg_recv_len(fd, raw_data + sizeof(muse_recv_data_head_t), header->size)) {
358                 LOGE("receive data failed - length %d", header->size);
359                 goto _PROCESSING_FAILED;
360         }
361
362         /* push data */
363         g_mutex_lock(&ch->data_mutex);
364         g_queue_push_tail(ch->data_queue, (gpointer)raw_data);
365         g_cond_signal(&ch->data_cond);
366         g_mutex_unlock(&ch->data_mutex);
367
368         return TRUE;
369
370 _PROCESSING_FAILED:
371
372         MUSE_G_FREE(raw_data);
373
374         ms_log_process_info(ms->pid);
375
376         return FALSE;
377 }
378
379 static gpointer _ms_ipc_data_worker(gpointer data)
380 {
381         char recv_buf[MUSE_MSG_LEN_MAX] = {'\0',};
382         int fd;
383         muse_module_h m = NULL;
384         muse_channel_info_t *ch = NULL;
385
386         muse_return_val_if_fail(data, NULL);
387
388         m = (muse_module_h)data;
389         SECURE_LOGW("module : %p pid %d handle %zd created %d fd %d",
390                 m, m->pid, m->handle, m->is_created, m->ch[MUSE_CHANNEL_MSG].sock_fd);
391
392         muse_return_val_if_fail(muse_server_module_is_valid(m), NULL);
393
394         fd = m->ch[MUSE_CHANNEL_DATA].sock_fd;
395         ch = &m->ch[MUSE_CHANNEL_DATA];
396
397         /* get data */
398         while (1) {
399                 if (!muse_core_msg_recv_len(fd, recv_buf, sizeof(muse_recv_data_head_t)))
400                         break;
401
402                 if (!_ms_ipc_data_processing(fd, (muse_recv_data_head_t *)recv_buf, ch)) {
403                         LOGE("ipc data processing failed");
404                         break;
405                 }
406         }
407
408         LOGW("Leave");
409
410         return NULL;
411 }
412
413 gboolean ms_ipc_create_msg_dispatch_worker(muse_module_h m)
414 {
415         GThread *thread = NULL;
416         GError *error = NULL;
417         muse_server_h ms = ms_get_instance();
418         ms_connection_t *connection = NULL;
419
420         LOGD("Enter %p", m);
421
422         muse_return_val_if_fail(ms, FALSE);
423         muse_return_val_if_fail(ms_is_server_ready(), FALSE);
424         muse_return_val_if_fail(muse_server_module_is_valid(m), FALSE);
425
426         connection = ms->connection;
427         muse_return_val_if_fail(connection, FALSE);
428
429         ms_connection_lock(connection);
430
431         SECURE_LOGD("[PID %d module %p] module's msg channel fd : %d", m->pid, m, m->ch[MUSE_CHANNEL_MSG].sock_fd);
432
433         thread = g_thread_try_new(MSG_THREAD_NAME, _ms_ipc_dispatch_worker, (gpointer)m, &error);
434         if (!thread) {
435                 LOGE("[module %p] thread creation failed : %s", m, error->message);
436                 g_error_free(error);
437                 ms_log_process_info(ms->pid);
438                 ms_connection_unlock(connection);
439                 return FALSE;
440         }
441
442         LOGD("Leave module %p thread %p", m, thread);
443
444         ms_connection_unlock(connection);
445
446         return TRUE;
447 }
448
449 gboolean ms_ipc_create_data_dispatch_worker(muse_module_h m)
450 {
451         GError *error = NULL;
452         muse_server_h ms = ms_get_instance();
453         ms_connection_t *connection = NULL;
454
455         LOGD("Enter %p", m);
456
457         muse_return_val_if_fail(ms, FALSE);
458         muse_return_val_if_fail(ms_is_server_ready(), FALSE);
459         muse_return_val_if_fail(muse_server_module_is_valid(m), TRUE);
460
461         connection = ms->connection;
462         muse_return_val_if_fail(connection, FALSE);
463
464         ms_connection_lock(connection);
465
466         m->ch[MUSE_CHANNEL_DATA].thread = g_thread_try_new(DATA_THREAD_NAME, _ms_ipc_data_worker, (gpointer)m, &error);
467         if (!m->ch[MUSE_CHANNEL_DATA].thread) {
468                 LOGE("thread creation failed : %s", error->message);
469                 g_error_free(error);
470                 ms_log_process_info(ms->pid);
471                 ms_cmd_dispatch(m, MUSE_MODULE_COMMAND_RESOURCE_NOT_AVAILABLE);
472                 muse_core_connection_close(m->ch[MUSE_CHANNEL_MSG].sock_fd);
473                 ms_connection_unlock(connection);
474                 return FALSE;
475         }
476
477         LOGD("Leave %p", m);
478
479         ms_connection_unlock(connection);
480
481         return TRUE;
482 }