process as warning if cynara session is NULL
[platform/core/pim/pims-ipc.git] / src / pims-ipc-worker.c
1 /*
2  * PIMS IPC
3  *
4  * Copyright (c) 2012 - 2016 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 #include <pthread.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <poll.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/eventfd.h>
26 #include <fcntl.h>
27 #include <glib.h>
28
29 #include "pims-internal.h"
30 #include "pims-ipc-data-internal.h"
31 #include "pims-ipc-data.h"
32 #include "pims-socket.h"
33 #include "pims-ipc-utils.h"
34 #include "pims-ipc-worker.h"
35
36 #define PIMS_IPC_WORKER_THREAD_WAIT_TIME 100 /* milliseconds */
37
38 typedef struct {
39         pims_ipc_svc_client_disconnected_cb callback;
40         void * user_data;
41 } pims_ipc_svc_client_disconnected_cb_t;
42
43 /* idle_worker_pool SHOULD handle on main thread */
44 static GList *idle_worker_pool;
45 static GHashTable *worker_cb_table; /* call_id, cb_data */
46 static __thread pims_ipc_svc_client_disconnected_cb_t _client_disconnected_cb = {NULL, NULL};
47
48 static int unique_sequence_number;
49 static GHashTable *worker_client_info_map; /* key : worker_id, data : pims_ipc_client_info_s* */
50 static int client_register_info(pims_ipc_worker_data_s *worker_data, int client_pid);
51 static pthread_mutex_t _worker_client_mutex = PTHREAD_MUTEX_INITIALIZER; /* for worker_client_info_map */
52
53 int worker_wait_idle_worker_ready(pims_ipc_worker_data_s *worker_data)
54 {
55         struct timespec timeout = {0};
56
57         clock_gettime(CLOCK_REALTIME, &timeout);
58         timeout.tv_nsec += PIMS_IPC_WORKER_THREAD_WAIT_TIME * 1000000;
59         timeout.tv_sec += timeout.tv_nsec / 1000000000L;
60         timeout.tv_nsec = timeout.tv_nsec % 1000000000L;
61
62         pthread_mutex_lock(&worker_data->ready_mutex);
63
64         if (!worker_data->fd) {
65                 WARN("worker fd is null, wait until worker thread create done.");
66                 if (pthread_cond_timedwait(&worker_data->ready, &worker_data->ready_mutex, &timeout)) {
67                         ERR("Get idle worker timeout Fail!");
68                         pthread_mutex_unlock(&worker_data->ready_mutex);
69                         return -1;
70                 }
71         }
72
73         pthread_mutex_unlock(&worker_data->ready_mutex);
74         return 0;
75 }
76
77 pims_ipc_worker_data_s* worker_get_idle_worker(pims_ipc_svc_s *ipc_svc,
78                 const char *client_id)
79 {
80         pims_ipc_worker_data_s *worker_data;
81
82         RETV_IF(NULL == client_id, NULL);
83         RETVM_IF(NULL == idle_worker_pool, NULL, "There is no idle worker");
84
85         worker_data = g_hash_table_lookup(ipc_svc->client_worker_map, client_id);
86         if (worker_data)
87                 return worker_data;
88
89         worker_data = idle_worker_pool->data;
90
91         idle_worker_pool = g_list_delete_link(idle_worker_pool, idle_worker_pool);
92
93         if (worker_data)
94                 g_hash_table_insert(ipc_svc->client_worker_map, g_strdup(client_id), worker_data);
95
96         return worker_data;
97 }
98
99 pims_ipc_worker_data_s* worker_find(pims_ipc_svc_s *ipc_svc, const char *client_id)
100 {
101         pims_ipc_worker_data_s *worker_data;
102
103         RETV_IF(NULL == client_id, NULL);
104
105         if (FALSE == g_hash_table_lookup_extended(ipc_svc->client_worker_map, client_id,
106                                 NULL, (gpointer*)&worker_data)) {
107                 ERR("g_hash_table_lookup_extended(%s) Fail", client_id);
108                 return NULL;
109         }
110
111         return worker_data;
112 }
113
114 void worker_stop_client_worker(pims_ipc_svc_s *ipc_svc, const char *client_id)
115 {
116         pims_ipc_worker_data_s *worker_data;
117
118         worker_data = worker_find(ipc_svc, client_id);
119
120         /* remove client_fd */
121         g_hash_table_remove(ipc_svc->client_worker_map, client_id);
122
123         /* stop worker thread */
124         if (worker_data) {
125                 worker_data->stop_thread = TRUE;
126                 worker_data->client_fd = -1;
127                 write_command(worker_data->fd, 1);
128                 DBG("write command to worker terminate(worker_fd:%d)", worker_data->fd);
129         }
130 }
131
132
133 void worker_free_raw_data(void *data)
134 {
135         pims_ipc_raw_data_s *raw_data = data;
136
137         if (NULL == raw_data)
138                 return;
139
140         free(raw_data->client_id);
141         free(raw_data->call_id);
142         free(raw_data->data);
143         free(raw_data);
144 }
145
146 void worker_free_data(gpointer data)
147 {
148         pims_ipc_worker_data_s *worker_data = data;
149
150         pthread_mutex_lock(&worker_data->queue_mutex);
151         if (worker_data->list)
152                 g_list_free_full(worker_data->list, worker_free_raw_data);
153         pthread_mutex_unlock(&worker_data->queue_mutex);
154
155         pthread_cond_destroy(&worker_data->ready);
156         pthread_mutex_destroy(&worker_data->ready_mutex);
157
158         free(worker_data);
159 }
160
161
162 int worker_push_raw_data(pims_ipc_worker_data_s *worker_data, int client_fd,
163                 pims_ipc_raw_data_s *data)
164 {
165         pthread_mutex_lock(&worker_data->queue_mutex);
166         worker_data->list = g_list_append(worker_data->list, data);
167         worker_data->client_fd = client_fd;
168         pthread_mutex_unlock(&worker_data->queue_mutex);
169
170         return TRUE;
171 }
172
173 static gboolean worker_pop_raw_data(pims_ipc_worker_data_s *worker,
174                 pims_ipc_raw_data_s **data)
175 {
176         if (!worker)
177                 return FALSE;
178
179         pthread_mutex_lock(&worker->queue_mutex);
180         if (!worker->list) {
181                 pthread_mutex_unlock(&worker->queue_mutex);
182                 *data = NULL;
183                 return FALSE;
184         }
185
186         GList *cursor = g_list_first(worker->list);
187         if (NULL == cursor) {
188                 pthread_mutex_unlock(&worker->queue_mutex);
189                 *data = NULL;
190                 return FALSE;
191         }
192         *data = cursor->data;
193         worker->list = g_list_delete_link(worker->list, g_list_first(worker->list));
194         pthread_mutex_unlock(&worker->queue_mutex);
195
196         return TRUE;
197 }
198
199 int worker_set_callback(char *call_id, pims_ipc_svc_cb_s *cb_data)
200 {
201         return g_hash_table_insert(worker_cb_table, call_id, cb_data);
202 }
203
204 static void __run_callback(int client_pid, char *call_id, pims_ipc_data_h dhandle_in,
205                 pims_ipc_data_h *dhandle_out)
206 {
207         pims_ipc_svc_cb_s *cb_data = NULL;
208
209         VERBOSE("Call id [%s]", call_id);
210
211         cb_data = g_hash_table_lookup(worker_cb_table, call_id);
212         if (cb_data == NULL) {
213                 VERBOSE("No Data for %s", call_id);
214                 return;
215         }
216
217         /* TODO: client_pid is not valide pims_ipc_h */
218         cb_data->callback((pims_ipc_h)client_pid, dhandle_in, dhandle_out, cb_data->user_data);
219 }
220
221 static void __make_raw_data(const char *call_id, int seq_no, pims_ipc_data_h data,
222                 pims_ipc_raw_data_s **out)
223 {
224         pims_ipc_data_s *data_in = data;
225         pims_ipc_raw_data_s *raw_data = NULL;
226
227         RET_IF(NULL == out);
228         RET_IF(NULL == call_id);
229
230         raw_data = calloc(1, sizeof(pims_ipc_raw_data_s));
231         if (NULL == raw_data) {
232                 ERR("calloc() Fail(%d)", errno);
233                 return;
234         }
235
236         raw_data->call_id = g_strdup(call_id);
237         raw_data->call_id_len = strlen(raw_data->call_id);
238         raw_data->seq_no = seq_no;
239
240         if (data_in && 0 < data_in->buf_size) {
241                 raw_data->has_data = TRUE;
242                 raw_data->data = calloc(1, data_in->buf_size+1);
243                 if (NULL == raw_data->data) {
244                         ERR("calloc() Fail");
245                         free(raw_data->call_id);
246                         free(raw_data);
247                         return;
248                 }
249                 memcpy(raw_data->data, data_in->buf, data_in->buf_size);
250                 raw_data->data_len = data_in->buf_size;
251         } else {
252                 raw_data->has_data = FALSE;
253                 raw_data->data_len = 0;
254                 raw_data->data = NULL;
255         }
256         *out = raw_data;
257         return;
258 }
259
260 static int __send_raw_data(int fd, const char *client_id, pims_ipc_raw_data_s *data)
261 {
262         int ret = 0;
263         unsigned int len, total_len, client_id_len;
264
265         RETV_IF(NULL == data, -1);
266         RETV_IF(NULL == client_id, -1);
267
268         client_id_len = strlen(client_id);
269
270         len = sizeof(total_len) + sizeof(client_id_len) + client_id_len + sizeof(data->seq_no)
271                 + data->call_id_len + sizeof(data->call_id) + sizeof(data->has_data);
272         total_len = len;
273
274         if (data->has_data) {
275                 len += sizeof(data->data_len);
276                 total_len = len + data->data_len;
277         }
278
279         INFO("client_id: %s, call_id : %s, seq no :%d, len:%d, total len :%d", client_id,
280                         data->call_id, data->seq_no, len, total_len);
281
282         int length = 0;
283         char buf[len+1];
284         memset(buf, 0x0, len+1);
285
286         memcpy(buf, &total_len, sizeof(total_len));
287         length += sizeof(total_len);
288
289         memcpy(buf+length, &client_id_len, sizeof(client_id_len));
290         length += sizeof(client_id_len);
291         memcpy(buf+length, client_id, client_id_len);
292         length += client_id_len;
293
294         memcpy(buf+length, &(data->seq_no), sizeof(data->seq_no));
295         length += sizeof(data->seq_no);
296
297         memcpy(buf+length, &(data->call_id_len), sizeof(data->call_id_len));
298         length += sizeof(data->call_id_len);
299         memcpy(buf+length, data->call_id, data->call_id_len);
300         length += data->call_id_len;
301
302         memcpy(buf+length, &(data->has_data), sizeof(data->has_data));
303         length += sizeof(data->has_data);
304
305         if (data->has_data) {
306                 memcpy(buf+length, &(data->data_len), sizeof(data->data_len));
307                 length += sizeof(data->data_len);
308                 ret = socket_send(fd, buf, length);
309
310                 /* send data */
311                 if (ret > 0)
312                         ret += socket_send_data(fd, data->data, data->data_len);
313         } else {
314                 ret = socket_send(fd, buf, length);
315         }
316
317         return ret;
318 }
319
320 static int _get_pid_from_fd(int fd)
321 {
322         struct ucred uc;
323         socklen_t uc_len = sizeof(uc);
324
325         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &uc_len) < 0)
326                 ERR("getsockopt() Failed(%d)", errno);
327
328         DBG("Client PID(%d)", uc.pid);
329         return uc.pid;
330 }
331
332 static int __worker_loop_handle_raw_data(pims_ipc_worker_data_s *worker_data)
333 {
334         int disconnected = FALSE;
335         pims_ipc_data_h data_in = NULL;
336         pims_ipc_data_h data_out = NULL;
337         pims_ipc_raw_data_s *result = NULL;
338         pims_ipc_raw_data_s *raw_data = NULL;
339
340         if (FALSE == worker_pop_raw_data(worker_data, &raw_data))
341                 return disconnected;
342
343         int client_pid = _get_pid_from_fd(worker_data->client_fd);
344
345         if (UTILS_STR_EQUAL == strcmp(PIMS_IPC_CALL_ID_CREATE, raw_data->call_id)) {
346                 client_register_info(worker_data, client_pid);
347
348         } else if (UTILS_STR_EQUAL == strcmp(PIMS_IPC_CALL_ID_DESTROY, raw_data->call_id)) {
349                 disconnected = TRUE;
350         } else {
351                 data_in = pims_ipc_data_steal_unmarshal(raw_data->data, raw_data->data_len);
352
353                 __run_callback(client_pid, raw_data->call_id, data_in, &data_out);
354                 pims_ipc_data_destroy(data_in);
355         }
356
357         if (data_out) {
358                 __make_raw_data(raw_data->call_id, raw_data->seq_no, data_out, &result);
359                 pims_ipc_data_destroy(data_out);
360         } else
361                 __make_raw_data(raw_data->call_id, raw_data->seq_no, NULL, &result);
362
363         if (worker_data->client_fd != -1)
364                 __send_raw_data(worker_data->client_fd, raw_data->client_id, result);
365         worker_free_raw_data(raw_data);
366         worker_free_raw_data(result);
367
368         return disconnected;
369 }
370
371 static void* __worker_loop(void *data)
372 {
373         int ret;
374         pthread_t pid;
375         int worker_fd;
376         int disconnected = FALSE;
377         pims_ipc_worker_data_s *worker_data = data;
378
379         RETV_IF(NULL == data, NULL);
380
381         worker_fd = eventfd(0, 0);
382         if (worker_fd == -1)
383                 return NULL;
384
385         INFO("worker Created ********** worker_fd = %d ***********", worker_fd);
386
387         pid = pthread_self();
388         worker_data->client_fd = -1;
389         worker_data->stop_thread = FALSE;
390         pthread_mutex_lock(&worker_data->ready_mutex);
391         worker_data->fd = worker_fd;
392         pthread_cond_signal(&worker_data->ready);
393         pthread_mutex_unlock(&worker_data->ready_mutex);
394
395         struct pollfd pollfds[1];
396         pollfds[0].fd = worker_fd;
397         pollfds[0].events = POLLIN;
398         pollfds[0].revents = 0;
399
400         while (!worker_data->stop_thread) {
401                 ret = poll(pollfds, 1, 3000); /* waiting command from router */
402                 if (-1 == ret) {
403                         if (errno != EINTR)
404                                 ERR("poll() Fail(%d)", errno);
405                         continue;
406                 }
407                 if (worker_data->stop_thread)
408                         break;
409
410                 if (0 == ret)
411                         continue;
412
413                 if (pollfds[0].revents & POLLIN) {
414                         uint64_t dummy;
415                         read_command(pollfds[0].fd, &dummy);
416
417                         disconnected = __worker_loop_handle_raw_data(worker_data);
418                 }
419         }
420
421         if (!disconnected)
422                 ERR("client fd closed, worker_fd : %d", worker_fd);
423         INFO("task thread terminated --------------------------- (worker_fd : %d)", worker_fd);
424
425         int flag = fcntl(worker_data->client_fd, F_GETFL, 0);
426         if (0 == (FD_CLOEXEC & flag)) {
427                 int client_pid = _get_pid_from_fd(worker_data->client_fd);
428                 pthread_mutex_lock(&_worker_client_mutex);
429                 g_hash_table_remove(worker_client_info_map, GINT_TO_POINTER(client_pid));
430                 DBG("client pid(%u) is removed", client_pid);
431                 pthread_mutex_unlock(&_worker_client_mutex);
432         } else {
433                 DBG("fd(%d) is already closed", worker_data->client_fd);
434         }
435
436         worker_free_data(worker_data);
437         close(worker_fd);
438
439         if (_client_disconnected_cb.callback)
440                 _client_disconnected_cb.callback((pims_ipc_h)pid, _client_disconnected_cb.user_data);
441
442         return NULL;
443 }
444
445 void worker_start_idle_worker(pims_ipc_svc_s *ipc_data)
446 {
447         int i;
448         pims_ipc_worker_data_s *worker_data;
449
450         for (i = g_list_length(idle_worker_pool); i < ipc_data->workers_max_count; i++) {
451                 worker_data = calloc(1, sizeof(pims_ipc_worker_data_s));
452                 if (NULL == worker_data) {
453                         ERR("calloc() Fail(%d)", errno);
454                         continue;
455                 }
456                 pthread_mutex_init(&worker_data->queue_mutex, 0);
457                 pthread_mutex_init(&worker_data->ready_mutex, NULL);
458                 pthread_cond_init(&worker_data->ready, NULL);
459
460                 utils_launch_thread(__worker_loop, worker_data);
461                 idle_worker_pool = g_list_append(idle_worker_pool, worker_data);
462         }
463 }
464
465 void worker_stop_idle_worker()
466 {
467         GList *cursor;
468         pims_ipc_worker_data_s *worker_data;
469
470         cursor = idle_worker_pool;
471         while (cursor) {
472                 worker_data = cursor->data;
473                 worker_data->stop_thread = TRUE;
474                 write_command(worker_data->fd, 1);
475                 cursor = cursor->next;
476         }
477 }
478
479 void worker_init()
480 {
481         worker_cb_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
482         WARN_IF(NULL == worker_cb_table, "worker cb table is NULL");
483 }
484
485 void worker_deinit()
486 {
487         g_list_free(idle_worker_pool);
488         idle_worker_pool = NULL;
489
490         g_hash_table_destroy(worker_cb_table);
491         worker_cb_table = NULL;
492 }
493
494 API void pims_ipc_svc_set_client_disconnected_cb(
495                 pims_ipc_svc_client_disconnected_cb callback, void *user_data)
496 {
497         if (_client_disconnected_cb.callback) {
498                 ERR("already registered");
499                 return;
500         }
501         _client_disconnected_cb.callback = callback;
502         _client_disconnected_cb.user_data = user_data;
503 }
504
505 void client_destroy_info(gpointer p)
506 {
507         pims_ipc_client_info_s *client_info = p;
508
509         if (NULL == client_info)
510                 return;
511         free(client_info->smack);
512         free(client_info->uid);
513         free(client_info->client_session);
514         free(client_info);
515 }
516
517 void client_init(void)
518 {
519         pthread_mutex_init(&_worker_client_mutex, 0);
520
521         pthread_mutex_lock(&_worker_client_mutex);
522         unique_sequence_number = 0;
523         if (worker_client_info_map) {
524                 ERR("worker_client_info_map already exists");
525                 pthread_mutex_unlock(&_worker_client_mutex);
526                 return;
527         }
528
529         worker_client_info_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
530                         client_destroy_info);
531         pthread_mutex_unlock(&_worker_client_mutex);
532 }
533
534 void client_deinit(void)
535 {
536         DBG("----------destroied");
537         pthread_mutex_lock(&_worker_client_mutex);
538         g_hash_table_destroy(worker_client_info_map);
539         worker_client_info_map = NULL;
540         pthread_mutex_unlock(&_worker_client_mutex);
541         pthread_mutex_destroy(&_worker_client_mutex);
542 }
543
544 static int _create_client_info(int fd, pims_ipc_client_info_s **p_client_info)
545 {
546         int ret;
547         pid_t pid;
548         char errmsg[1024] = {0};
549
550         pims_ipc_client_info_s *client_info = calloc(1, sizeof(pims_ipc_client_info_s));
551         if (NULL == client_info) {
552                 ERR("calloc() return NULL");
553                 return -1;
554         }
555
556         ret = cynara_creds_socket_get_client(fd, CLIENT_METHOD_SMACK, &(client_info->smack));
557         if (CYNARA_API_SUCCESS != ret) {
558                 cynara_strerror(ret, errmsg, sizeof(errmsg));
559                 ERR("cynara_creds_socket_get_client() Fail(%d,%s)", ret, errmsg);
560                 client_destroy_info(client_info);
561                 return -1;
562         }
563
564         ret = cynara_creds_socket_get_user(fd, USER_METHOD_UID, &(client_info->uid));
565         if (CYNARA_API_SUCCESS != ret) {
566                 cynara_strerror(ret, errmsg, sizeof(errmsg));
567                 ERR("cynara_creds_socket_get_user() Fail(%d,%s)", ret, errmsg);
568                 client_destroy_info(client_info);
569                 return -1;
570         }
571
572         ret = cynara_creds_socket_get_pid(fd, &pid);
573         if (CYNARA_API_SUCCESS != ret) {
574                 cynara_strerror(ret, errmsg, sizeof(errmsg));
575                 ERR("cynara_creds_socket_get_pid() Fail(%d,%s)", ret, errmsg);
576                 client_destroy_info(client_info);
577                 return -1;
578         }
579
580         client_info->client_session = cynara_session_from_pid(pid);
581         if (NULL == client_info->client_session) {
582                 WARN("cynara_session_from_pid() return NULL");
583                 client_info->client_session = strdup("");
584         }
585         *p_client_info = client_info;
586
587         return 0;
588 }
589
590 static int client_register_info(pims_ipc_worker_data_s *worker_data, int client_pid)
591 {
592         pims_ipc_client_info_s *client_info = NULL;
593         int ret = 0;
594
595         ret = _create_client_info(worker_data->client_fd, &client_info);
596         if (ret < 0) {
597                 ERR("_create_client_info() Fail(%d)", ret);
598                 return -1;
599         }
600         pthread_mutex_lock(&_worker_client_mutex);
601         g_hash_table_insert(worker_client_info_map, GINT_TO_POINTER(client_pid), client_info);
602         DBG("-------inserted:pid(%d), info(%p)", client_pid, client_info);
603         pthread_mutex_unlock(&_worker_client_mutex);
604
605         return 0;
606 }
607
608 int client_get_unique_sequence_number(void)
609 {
610         return unique_sequence_number++;
611 }
612
613 pims_ipc_client_info_s* client_clone_info(pims_ipc_client_info_s *client_info)
614 {
615         if (NULL == client_info) {
616                 ERR("client_info is NULL");
617                 return NULL;
618         }
619
620         pims_ipc_client_info_s *clone = calloc(1, sizeof(pims_ipc_client_info_s));
621         if (NULL == clone) {
622                 ERR("calloc() Fail");
623                 return NULL;
624         }
625
626         if (client_info->smack) {
627                 clone->smack = strdup(client_info->smack);
628                 if (NULL == clone->smack) {
629                         ERR("strdup() Fail");
630                         client_destroy_info(clone);
631                         return NULL;
632                 }
633         }
634
635         if (client_info->uid) {
636                 clone->uid = strdup(client_info->uid);
637                 if (NULL == clone->uid) {
638                         ERR("strdup() Fail");
639                         client_destroy_info(clone);
640                         return NULL;
641                 }
642         }
643
644         if (client_info->client_session) {
645                 clone->client_session = strdup(client_info->client_session);
646                 if (NULL == clone->client_session) {
647                         ERR("strdup() Fail");
648                         client_destroy_info(clone);
649                         return NULL;
650                 }
651         }
652
653         return clone;
654 }
655
656 pims_ipc_client_info_s* client_get_info(int client_pid)
657 {
658         pims_ipc_client_info_s *client_info = NULL;
659         pims_ipc_client_info_s *client_info_clone = NULL;
660
661         pthread_mutex_lock(&_worker_client_mutex);
662         client_info = g_hash_table_lookup(worker_client_info_map, GINT_TO_POINTER(client_pid));
663         client_info_clone = client_clone_info(client_info);
664         pthread_mutex_unlock(&_worker_client_mutex);
665         DBG("Get client_info(%p) from pid(%d)", client_info, client_pid);
666         return client_info_clone;
667 }
668