2 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
25 #include <sys/ioctl.h>
33 #include "secure_socket.h"
36 #include "com-core_internal.h"
40 #define EVENT_READY 'a'
41 #define EVENT_TERM 'e'
44 struct dlist *tcb_list;
45 struct dlist *server_list;
52 * \brief Representing the Server Object
55 int (*service_cb)(int fd, void *data);
63 * \brief This is used to holds a packet
74 * \brief Thread Control Block
79 struct dlist *chunk_list;
80 int evt_pipe[PIPE_MAX];
81 int ctrl_pipe[PIPE_MAX];
82 pthread_mutex_t chunk_lock;
83 guint id; /*!< g_io_watch */
87 int (*service_cb)(int fd, void *data);
91 static ssize_t write_safe(int fd, const void *data, size_t bufsz)
98 ret = write(fd, data, bufsz);
105 ErrPrint("Interrupted[%d] Again[%d]\n", fd, -ret);
108 ErrPrint("Failed to write: %s (%d)\n", strerror(-ret), -ret);
119 * Running thread: Main
121 static inline void server_destroy(struct server *server)
123 dlist_remove_data(s_info.server_list, server);
125 if (server->id > 0) {
126 g_source_remove(server->id);
129 if (server->handle > 0) {
130 secure_socket_destroy_handle(server->handle);
138 * Running thread: Main
140 static inline struct server *server_create(int handle, int (*service_cb)(int fd, void *data), void *data)
142 struct server *server;
144 server = malloc(sizeof(*server));
146 ErrPrint("Heap: %s\n", strerror(errno));
150 server->handle = handle;
151 server->service_cb = service_cb;
154 s_info.server_list = dlist_append(s_info.server_list, server);
160 * Running thread: Main
162 static inline void destroy_chunk(struct chunk *chunk)
170 * Running thread: Main
172 static inline void terminate_thread(struct tcb *tcb)
180 if (write_safe(tcb->ctrl_pipe[PIPE_WRITE], &tcb, sizeof(tcb)) != sizeof(tcb)) {
181 ErrPrint("Unable to write CTRL pipe (%d)\n", sizeof(tcb));
184 secure_socket_destroy_handle(tcb->handle);
186 status = pthread_join(tcb->thid, &res);
188 ErrPrint("Join: %s\n", strerror(status));
190 ErrPrint("Thread returns: %d\n", (int)res);
193 dlist_foreach_safe(tcb->chunk_list, l, n, chunk) {
195 * Discarding all packets
197 DbgPrint("Discarding chunks\n");
198 tcb->chunk_list = dlist_remove(tcb->chunk_list, l);
199 destroy_chunk(chunk);
205 * Running thread: Main
207 static inline void chunk_remove(struct tcb *tcb, struct chunk *chunk)
211 /* Consuming the event */
212 if (read(tcb->evt_pipe[PIPE_READ], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) {
213 ErrPrint("Failed to get readsize\n");
217 CRITICAL_SECTION_BEGIN(&tcb->chunk_lock);
219 dlist_remove_data(tcb->chunk_list, chunk);
221 CRITICAL_SECTION_END(&tcb->chunk_lock);
223 destroy_chunk(chunk);
228 * Running thread: Other
230 static inline int chunk_append(struct tcb *tcb, struct chunk *chunk)
232 char event_ch = EVENT_READY;
235 CRITICAL_SECTION_BEGIN(&tcb->chunk_lock);
237 tcb->chunk_list = dlist_append(tcb->chunk_list, chunk);
239 CRITICAL_SECTION_END(&tcb->chunk_lock);
241 ret = write_safe(tcb->evt_pipe[PIPE_WRITE], &event_ch, sizeof(event_ch));
243 CRITICAL_SECTION_BEGIN(&tcb->chunk_lock);
245 dlist_remove_data(tcb->chunk_list, chunk);
247 CRITICAL_SECTION_END(&tcb->chunk_lock);
251 if (ret != sizeof(event_ch)) {
252 ErrPrint("Failed to trigger reader\n");
262 * Running thread: Main
264 static inline int wait_event(struct tcb *tcb, double timeout)
270 FD_SET(tcb->evt_pipe[PIPE_READ], &set);
272 if (timeout > 0.0f) {
274 tv.tv_sec = (unsigned long)timeout;
275 tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u;
276 ret = select(tcb->evt_pipe[PIPE_READ] + 1, &set, NULL, NULL, &tv);
277 } else if (timeout == 0.0f) {
278 ret = select(tcb->evt_pipe[PIPE_READ] + 1, &set, NULL, NULL, NULL);
280 ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout);
286 if (errno == EINTR) {
287 DbgPrint("Select receives INTR\n");
291 ErrPrint("Error: %s\n", strerror(errno));
293 } else if (ret == 0) {
294 ErrPrint("Timeout expired\n");
298 if (!FD_ISSET(tcb->evt_pipe[PIPE_READ], &set)) {
299 ErrPrint("Unexpected handle is toggled\n");
308 * Running thread: Main
310 static inline struct chunk *create_chunk(int size)
314 chunk = malloc(sizeof(*chunk));
316 ErrPrint("Heap: %s\n", strerror(errno));
320 chunk->data = malloc(size);
322 ErrPrint("Heap: %s\n", strerror(errno));
327 chunk->pid = (pid_t)-1;
336 * Running thread: Other
338 static void *client_cb(void *data)
340 struct tcb *tcb = data;
348 DbgPrint("Thread is created for %d (server: %d)\n", tcb->handle, tcb->server_handle);
351 * Read all data from the socket as possible as it can do
355 FD_SET(tcb->handle, &set);
356 FD_SET(tcb->ctrl_pipe[PIPE_READ], &set);
358 fd = tcb->handle > tcb->ctrl_pipe[PIPE_READ] ? tcb->handle : tcb->ctrl_pipe[PIPE_READ];
360 ret = select(fd + 1, &set, NULL, NULL, NULL);
362 if (errno == EINTR) {
363 DbgPrint("Select receives INTR\n");
368 ErrPrint("Error: %s\n", strerror(errno));
370 } else if (ret == 0) {
371 ErrPrint("What happens? [%d]\n", tcb->handle);
375 if (FD_ISSET(tcb->ctrl_pipe[PIPE_READ], &set)) {
376 DbgPrint("Thread is canceled\n");
381 if (!FD_ISSET(tcb->handle, &set)) {
382 ErrPrint("Unexpected handle is toggled\n");
388 ret = ioctl(tcb->handle, FIONREAD, &readsize);
390 ErrPrint("ioctl: %s\n", strerror(errno));
395 ErrPrint("Available data: %d\n", readsize);
400 chunk = create_chunk(readsize);
402 ErrPrint("Failed to create a new chunk: %d\n", readsize);
407 ret = secure_socket_recv_with_fd(tcb->handle, chunk->data, chunk->size, &chunk->pid, &chunk->fd);
409 destroy_chunk(chunk);
410 if (ret == -EAGAIN) {
411 DbgPrint("Retry to get data\n");
415 DbgPrint("Recv returns: %d\n", ret);
419 /* Update chunk size */
423 * Count of chunk elements are same with PIPE'd data
425 if (chunk_append(tcb, chunk) < 0) {
426 destroy_chunk(chunk);
431 DbgPrint("Client CB is terminated (%d)\n", tcb->handle);
432 /* Wake up main thread to get disconnected event */
433 event_ch = EVENT_TERM;
435 if (write_safe(tcb->evt_pipe[PIPE_WRITE], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) {
436 ErrPrint("%d byte is not written\n", sizeof(event_ch));
439 return (void *)(unsigned long)ret;
444 * Running thread: Main
446 static inline void tcb_destroy(struct tcb *tcb)
450 dlist_remove_data(s_info.tcb_list, tcb);
453 g_source_remove(tcb->id);
456 CLOSE_PIPE(tcb->evt_pipe);
457 CLOSE_PIPE(tcb->ctrl_pipe);
459 status = pthread_mutex_destroy(&tcb->chunk_lock);
461 ErrPrint("Failed to destroy mutex: %s\n", strerror(status));
469 * Running thread: Main
471 static gboolean evt_pipe_cb(GIOChannel *src, GIOCondition cond, gpointer data)
474 struct tcb *tcb = data;
477 pipe_read = g_io_channel_unix_get_fd(src);
479 if (tcb->evt_pipe[PIPE_READ] != pipe_read) {
480 ErrPrint("Closed handle (%d <> %d)\n", tcb->evt_pipe[PIPE_READ], pipe_read);
484 if (!(cond & G_IO_IN)) {
485 ErrPrint("PIPE is not valid\n");
489 if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
490 ErrPrint("PIPE is not valid\n");
494 ret = tcb->service_cb(tcb->handle, tcb->data);
496 DbgPrint("Service callback returns %d < 0\n", ret);
503 DbgPrint("Disconnecting\n");
504 invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
505 terminate_thread(tcb);
512 * Running thread: Main
514 static inline struct tcb *tcb_create(int client_fd, int is_sync, int (*service_cb)(int fd, void *data), void *data)
519 tcb = malloc(sizeof(*tcb));
521 ErrPrint("Error: %s\n", strerror(errno));
525 tcb->handle = client_fd;
526 tcb->chunk_list = NULL;
527 tcb->service_cb = service_cb;
531 status = pthread_mutex_init(&tcb->chunk_lock, NULL);
533 ErrPrint("Error: %s\n", strerror(status));
538 if (pipe2(tcb->evt_pipe, O_CLOEXEC) < 0) {
539 ErrPrint("Error: %s\n", strerror(errno));
540 status = pthread_mutex_destroy(&tcb->chunk_lock);
542 ErrPrint("Error: %s\n", strerror(status));
548 if (pipe2(tcb->ctrl_pipe, O_CLOEXEC) < 0) {
549 ErrPrint("Error: %s\n", strerror(errno));
551 CLOSE_PIPE(tcb->evt_pipe);
553 status = pthread_mutex_destroy(&tcb->chunk_lock);
555 ErrPrint("Error: %s\n", strerror(status));
562 DbgPrint("[%d] New TCB created: R(%d), W(%d)\n", client_fd, tcb->evt_pipe[PIPE_READ], tcb->evt_pipe[PIPE_WRITE]);
568 * Running thread: Main
570 static gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer data)
577 struct server *server = data;
579 pthread_attr_t *pattr = NULL;
581 socket_fd = g_io_channel_unix_get_fd(src);
582 if (!(cond & G_IO_IN)) {
583 ErrPrint("Accept socket closed\n");
584 server_destroy(server);
588 if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
589 DbgPrint("Socket connection is lost\n");
590 server_destroy(server);
594 fd = secure_socket_get_connection_handle(socket_fd);
596 ErrPrint("Failed to get client fd from socket\n");
597 server_destroy(server);
601 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
602 ErrPrint("Error: %s\n", strerror(errno));
605 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
606 ErrPrint("Error: %s\n", strerror(errno));
609 tcb = tcb_create(fd, 0, server->service_cb, server->data);
611 ErrPrint("Failed to create a TCB\n");
612 secure_socket_destroy_handle(fd);
613 server_destroy(server);
617 tcb->server_handle = socket_fd;
619 s_info.tcb_list = dlist_append(s_info.tcb_list, tcb);
621 gio = g_io_channel_unix_new(tcb->evt_pipe[PIPE_READ]);
623 ErrPrint("Failed to get gio\n");
624 secure_socket_destroy_handle(tcb->handle);
626 server_destroy(server);
630 g_io_channel_set_close_on_unref(gio, FALSE);
632 tcb->id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_pipe_cb, tcb);
635 ErrPrint("Failed to add IO Watch\n");
636 g_io_channel_shutdown(gio, TRUE, &err);
638 ErrPrint("Shutdown: %s\n", err->message);
641 g_io_channel_unref(gio);
642 secure_socket_destroy_handle(tcb->handle);
644 server_destroy(server);
647 g_io_channel_unref(gio);
649 invoke_con_cb_list(tcb->handle, tcb->handle, 0, NULL, 0);
651 ret = pthread_attr_init(&attr);
655 ret = pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
657 ErrPrint("setscope: %s\n", strerror(ret));
660 ret = pthread_attr_setinheritsched(pattr, PTHREAD_EXPLICIT_SCHED);
662 ErrPrint("setinheritsched: %s\n", strerror(ret));
665 ErrPrint("attr_init: %s\n", strerror(ret));
667 ret = pthread_create(&tcb->thid, pattr, client_cb, tcb);
669 pthread_attr_destroy(pattr);
672 ErrPrint("Thread creation failed: %s\n", strerror(ret));
673 invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
674 secure_socket_destroy_handle(tcb->handle);
676 server_destroy(server);
685 * Running thread: Main
687 EAPI int com_core_thread_client_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data)
694 pthread_attr_t *pattr = NULL;
696 client_fd = secure_socket_create_client(addr);
701 if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0) {
702 ErrPrint("Error: %s\n", strerror(errno));
705 if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0) {
706 ErrPrint("Error: %s\n", strerror(errno));
709 tcb = tcb_create(client_fd, is_sync, service_cb, data);
711 ErrPrint("Failed to create a new TCB\n");
712 secure_socket_destroy_handle(client_fd);
716 tcb->server_handle = -1;
718 s_info.tcb_list = dlist_append(s_info.tcb_list, tcb);
720 gio = g_io_channel_unix_new(tcb->evt_pipe[PIPE_READ]);
722 ErrPrint("Failed to get gio\n");
723 secure_socket_destroy_handle(tcb->handle);
728 g_io_channel_set_close_on_unref(gio, FALSE);
730 tcb->id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_pipe_cb, tcb);
733 ErrPrint("Failed to add IO Watch\n");
734 g_io_channel_shutdown(gio, TRUE, &err);
736 ErrPrint("Shutdown: %s\n", err->message);
739 g_io_channel_unref(gio);
740 secure_socket_destroy_handle(tcb->handle);
745 g_io_channel_unref(gio);
747 invoke_con_cb_list(tcb->handle, tcb->handle, 0, NULL, 0);
749 ret = pthread_attr_init(&attr);
753 ret = pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
755 ErrPrint("setscope: %s\n", strerror(ret));
758 ret = pthread_attr_setinheritsched(pattr, PTHREAD_EXPLICIT_SCHED);
760 ErrPrint("setinheritsched: %s\n", strerror(ret));
763 ErrPrint("attr_init: %s\n", strerror(ret));
765 ret = pthread_create(&tcb->thid, pattr, client_cb, tcb);
767 pthread_attr_destroy(pattr);
770 ErrPrint("Thread creation failed: %s\n", strerror(ret));
771 invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
772 secure_socket_destroy_handle(tcb->handle);
782 * Running thread: Main
784 EAPI int com_core_thread_server_create(const char *addr, int is_sync, const char *label, int (*service_cb)(int fd, void *data), void *data)
788 struct server *server;
790 fd = secure_socket_create_server_with_permission(addr, label);
795 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
796 ErrPrint("fcntl: %s\n", strerror(errno));
799 if (!is_sync && fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
800 ErrPrint("fcntl: %s\n", strerror(errno));
803 server = server_create(fd, service_cb, data);
805 secure_socket_destroy_handle(fd);
809 DbgPrint("Create new IO channel for socket FD: %d\n", fd);
810 gio = g_io_channel_unix_new(server->handle);
812 ErrPrint("Failed to create new io channel\n");
813 server_destroy(server);
817 g_io_channel_set_close_on_unref(gio, FALSE);
819 server->id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc)accept_cb, server);
820 if (server->id == 0) {
822 ErrPrint("Failed to add IO watch\n");
823 g_io_channel_shutdown(gio, TRUE, &err);
825 ErrPrint("Shutdown: %s\n", err->message);
828 g_io_channel_unref(gio);
829 server_destroy(server);
833 g_io_channel_unref(gio);
834 return server->handle;
839 * Running thread: Main
841 static inline struct tcb *find_tcb_by_handle(int handle)
846 dlist_foreach(s_info.tcb_list, l, tcb) {
847 if (tcb->handle == handle) {
855 EAPI int com_core_thread_send_with_fd(int handle, const char *buffer, int size, double timeout, int fd)
863 tcb = find_tcb_by_handle(handle);
865 ErrPrint("TCB is not found\n");
872 FD_SET(tcb->handle, &set);
874 if (timeout > 0.0f) {
877 tv.tv_sec = (unsigned long)timeout;
878 tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u;
880 ret = select(tcb->handle + 1, NULL, &set, NULL, &tv);
881 } else if (timeout == 0.0f) {
882 ret = select(tcb->handle + 1, NULL, &set, NULL, NULL);
884 ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout);
890 if (errno == EINTR) {
891 DbgPrint("Select receives INTR\n");
895 ErrPrint("Error: %s\n", strerror(errno));
897 } else if (ret == 0) {
898 ErrPrint("Timeout expired\n");
902 if (!FD_ISSET(tcb->handle, &set)) {
903 ErrPrint("Unexpected handle is toggled\n");
907 ret = secure_socket_send_with_fd(tcb->handle, buffer + writesize, size, fd);
909 if (ret == -EAGAIN) {
910 DbgPrint("Retry to send data (%d:%d)\n", writesize, size);
913 DbgPrint("Failed to send: %d\n", ret);
915 } else if (ret == 0) {
916 DbgPrint("Disconnected? : Send bytes: 0\n");
920 fd = -1; /* Send only once if it is fd */
930 * Running thread: Main
932 EAPI int com_core_thread_send(int handle, const char *buffer, int size, double timeout)
934 return com_core_thread_send_with_fd(handle, buffer, size, timeout, -1);
937 EAPI int com_core_thread_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, double timeout, int *fd)
947 tcb = find_tcb_by_handle(handle);
949 ErrPrint("TCB is not exists\n");
954 sender_pid = &_sender_pid;
963 while (readsize < size) {
964 l = dlist_nth(tcb->chunk_list, 0);
965 chunk = dlist_data(l);
968 * Pumping up the pipe data
969 * This is the first time to use a chunk
972 ret = wait_event(tcb, timeout);
973 if (ret == -EAGAIN) {
974 /* Log is printed from wait_event */
976 } else if (ret == -ECONNRESET) {
977 DbgPrint("Connection is lost\n");
979 } else if (ret < 0) {
980 /* Log is printed from wait_event */
984 l = dlist_nth(tcb->chunk_list, 0);
985 chunk = dlist_data(l);
989 /* Consuming the event */
990 if (read(tcb->evt_pipe[PIPE_READ], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) {
991 ErrPrint("Failed to get readsize: %s\n", strerror(errno));
992 } else if (event_ch == EVENT_READY) {
993 ErrPrint("Failed to get a new chunk\n");
994 } else if (event_ch == EVENT_TERM) {
995 DbgPrint("Disconnected\n");
1002 ret = chunk->size - chunk->offset;
1003 ret = ret > (size - readsize) ? (size - readsize) : ret;
1004 memcpy(buffer + readsize, chunk->data + chunk->offset, ret);
1006 chunk->offset += ret;
1008 *sender_pid = chunk->pid;
1009 if (chunk->fd >= 0) {
1013 if (chunk->offset == chunk->size) {
1014 chunk_remove(tcb, chunk);
1023 * Running thread: Main
1025 EAPI int com_core_thread_recv(int handle, char *buffer, int size, int *sender_pid, double timeout)
1027 return com_core_thread_recv_with_fd(handle, buffer, size, sender_pid, timeout, NULL);
1032 * Running thread: Main
1034 EAPI int com_core_thread_server_destroy(int handle)
1039 struct server *server;
1041 dlist_foreach_safe(s_info.tcb_list, l, n, tcb) {
1042 if (tcb->server_handle != handle) {
1046 invoke_disconn_cb_list(handle, 0, 0, 0);
1047 terminate_thread(tcb);
1052 dlist_foreach_safe(s_info.server_list, l, n, server) {
1053 if (server->handle != handle) {
1057 invoke_disconn_cb_list(handle, 0, 0, 0);
1058 server_destroy(server);
1067 * Running thread: Main
1069 EAPI int com_core_thread_client_destroy(int handle)
1073 tcb = find_tcb_by_handle(handle);
1078 invoke_disconn_cb_list(handle, 0, 0, 0);
1079 terminate_thread(tcb);