From d0120e3388118d63261a672cd77d216089f0cb72 Mon Sep 17 00:00:00 2001 From: Sung-jae Park Date: Thu, 18 Apr 2013 20:19:19 +0900 Subject: [PATCH] Implement new service threads. Badge service Notification service Shortcut service Implement common service routine Change-Id: I622de087bfc3d75ae6ce7eaed769ddf240a77038 --- CMakeLists.txt | 4 + include/badge_service.h | 0 include/notification_service.h | 0 include/service_common.h | 14 + include/shortcut_service.h | 2 + src/badge_service.c | 0 src/notification_service.c | 0 src/service_common.c | 671 +++++++++++++++++++++++++++++++++++++++++ src/shortcut_service.c | 77 +++++ 9 files changed, 768 insertions(+) create mode 100644 include/badge_service.h create mode 100644 include/notification_service.h create mode 100644 include/service_common.h create mode 100644 include/shortcut_service.h create mode 100644 src/badge_service.c create mode 100644 src/notification_service.c create mode 100644 src/service_common.c create mode 100644 src/shortcut_service.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 507375c..660cb1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,10 @@ ADD_EXECUTABLE(${PROJECT_NAME} src/liveinfo.c src/pkgmgr.c src/event.c + src/shortcut_service.c + src/badge_service.c + src/notification_service.c + src/service_common.c ) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkg_LDFLAGS} "-ldl") diff --git a/include/badge_service.h b/include/badge_service.h new file mode 100644 index 0000000..e69de29 diff --git a/include/notification_service.h b/include/notification_service.h new file mode 100644 index 0000000..e69de29 diff --git a/include/service_common.h b/include/service_common.h new file mode 100644 index 0000000..037b94b --- /dev/null +++ b/include/service_common.h @@ -0,0 +1,14 @@ +struct tcb; +struct service_context; + +extern int tcb_fd(struct tcb *tcb); +extern struct service_context *tcb_svc_ctx(struct tcb *tcb); +extern int tcb_client_type(struct tcb *tcb); + +extern struct service_context *service_common_create(const char *addr, int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data), void *data); +extern int service_common_destroy(struct service_context *svc_ctx); + +extern int service_common_multicast_packet(struct tcb *tcb, struct packet *packet, int type); +extern int service_common_unicast_packet(struct tcb *tcb, struct packet *packet); + +/* End of a file */ diff --git a/include/shortcut_service.h b/include/shortcut_service.h new file mode 100644 index 0000000..cb657e7 --- /dev/null +++ b/include/shortcut_service.h @@ -0,0 +1,2 @@ + +/* End of a file */ diff --git a/src/badge_service.c b/src/badge_service.c new file mode 100644 index 0000000..e69de29 diff --git a/src/notification_service.c b/src/notification_service.c new file mode 100644 index 0000000..e69de29 diff --git a/src/service_common.c b/src/service_common.c new file mode 100644 index 0000000..86ccc98 --- /dev/null +++ b/src/service_common.c @@ -0,0 +1,671 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "service_common.h" +#include "util.h" +#include "debug.h" +#include "conf.h" + +#define EVT_CH 'e' +#define EVT_END_CH 'x' + +#define CRITICAL_SECTION_BEGIN(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_lock(handle); \ + if (ret != 0) \ + ErrPrint("Failed to lock: %s\n", strerror(ret)); \ +} while (0) + +#define CRITICAL_SECTION_END(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_unlock(handle); \ + if (ret != 0) \ + ErrPrint("Failed to unlock: %s\n", strerror(ret)); \ +} while (0) + + +int errno; + +/*! + * \note + * Server information and global (only in this file-scope) variables are defined + */ +#define EVT_READ 0 +#define EVT_WRITE 1 +#define EVT_MAX 2 + +struct service_context { + pthread_t thid; /*!< Server thread Id */ + int fd; /*!< Server socket handle */ + + pthread_t service_thid; + + Eina_List *tcb_list; /*!< TCB list, list of every thread for client connections */ + pthread_mutex_t tcb_list_lock; /*!< tcb_list has to be handled safely */ + + Eina_List *packet_list; + pthread_mutex_t packet_list_lock; + int evt_pipe[EVT_MAX]; + + int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data); + void *service_thread_data; +}; + +struct packet_info { + struct tcb *tcb; + struct packet *packet; +}; + +enum tcb_type { + TCB_CLIENT_TYPE_UNDEFINED = 0x00, + TCB_CLIENT_TYPE_APP = 0x01, + TCB_CLIENT_TYPE_SERVICE = 0x02, + TCB_CLIENT_TYPE_UNKNOWN = 0xff, +}; +/*! + * \note + * Thread Control Block + * - The main server will create a thread for every client connections. + * When a new client is comming to us, this TCB block will be allocated and initialized. + */ +struct tcb { /* Thread controll block */ + struct service_context *svc_ctx; + pthread_t thid; /*!< Thread Id */ + int fd; /*!< Connection handle */ + enum tcb_type type; +}; + +/*! + * \note + * Called from Client Thread + */ +static inline int tcb_destroy(struct service_context *svc_ctx, struct tcb *tcb) +{ + void *ret; + int status; + + CRITICAL_SECTION_BEGIN(&svc_ctx->tcb_list_lock); + svc_ctx->tcb_list = eina_list_remove(svc_ctx->tcb_list, tcb); + CRITICAL_SECTION_END(&svc_ctx->tcb_list_lock); + + /*! + * ASSERT(tcb->fd >= 0); + */ + secure_socket_destroy_handle(tcb->fd); + + status = pthread_join(tcb->thid, &ret); + if (status != 0) + ErrPrint("Unable to join a thread: %s\n", strerror(status)); + /*! + * \NOTE + * Waiting termination client thread + */ + free(tcb); + + return 0; +} + +/*! + * Do service for clients + * Routing packets to destination processes. + */ +static void *client_packet_pump_main(void *data) +{ + struct tcb *tcb = data; + struct service_context *svc_ctx = tcb->svc_ctx; + struct packet *packet; + char *ptr; + int size; + int packet_offset; + int recv_offset; + int pid; + int ret; + char evt_ch = EVT_CH; + enum { + RECV_INIT, + RECV_HEADER, + RECV_PAYLOAD, + RECV_DONE, + } recv_state; + struct packet_info *packet_info; + + ret = 0; + recv_state = RECV_INIT; + while (ret == 0) { + /*! + * \TODO + * Service!!! Receive packet & route packet + */ + switch (recv_state) { + case RECV_INIT: + size = packet_header_size(); + packet_offset = 0; + recv_offset = 0; + packet = NULL; + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + ret = -ENOMEM; + break; + } + break; + case RECV_HEADER: + ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid); + if (ret <= 0) { + free(ptr); + break; + } + + recv_offset += ret; + + if (recv_offset == size) { + packet = packet_build(packet, packet_offset, ptr, size); + free(ptr); + if (!packet) { + ret = -EFAULT; + break; + } + + packet_offset += recv_offset; + + recv_state = RECV_PAYLOAD; + recv_offset = 0; + size = packet_size(packet); + + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + ret = -ENOMEM; + } + } + break; + case RECV_PAYLOAD: + ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid); + if (ret <= 0) { + free(ptr); + break; + } + + recv_offset += ret; + + if (recv_offset == size) { + packet = packet_build(packet, packet_offset, ptr, size); + free(ptr); + if (!packet) { + ret = -EFAULT; + break; + } + + packet_offset += recv_offset; + + recv_state = RECV_DONE; + recv_offset = 0; + } + break; + case RECV_DONE: + /*! + * Push this packet to the packet list with TCB + * Then the service main function will get this. + */ + packet_info = malloc(sizeof(*packet_info)); + if (!packet_info) { + ret = -errno; + ErrPrint("Heap: %s\n", strerror(errno)); + packet_destroy(packet); + break; + } + + packet_info->packet = packet; + packet_info->tcb = tcb; + + CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock); + svc_ctx->packet_list = eina_list_append(svc_ctx->packet_list, packet_info); + CRITICAL_SECTION_END(&svc_ctx->packet_list_lock); + + if (write(svc_ctx->evt_pipe[EVT_WRITE], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) { + ret = -errno; + ErrPrint("Unable to write a pipe\n"); + CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock); + svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info); + CRITICAL_SECTION_END(&svc_ctx->packet_list_lock); + + packet_destroy(packet); + free(packet_info); + break; + } + DbgPrint("Packet received: %d bytes\n", packet_offset); + break; + default: + break; + } + } + + tcb_destroy(svc_ctx, tcb); + return (void *)ret; +} + +/*! + * \note + * Called from Server Main Thread + */ +static inline struct tcb *tcb_create(struct service_context *svc_ctx, int fd) +{ + struct tcb *tcb; + int status; + + tcb = malloc(sizeof(*tcb)); + if (!tcb) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + tcb->fd = fd; + tcb->svc_ctx = svc_ctx; + + status = pthread_create(&tcb->thid, NULL, client_packet_pump_main, tcb); + if (status != 0) { + ErrPrint("Unable to create a new thread: %s\n", strerror(status)); + free(tcb); + return NULL; + } + + CRITICAL_SECTION_BEGIN(&svc_ctx->tcb_list_lock); + svc_ctx->tcb_list = eina_list_append(svc_ctx->tcb_list, tcb); + CRITICAL_SECTION_END(&svc_ctx->tcb_list_lock); + + return tcb; +} + +/*! + * \note + * Called from Main Thread + */ +static inline void tcb_teminate_all(struct service_context *svc_ctx) +{ + struct tcb *tcb; + void *ret; + int status; + + /*! + * We don't need to make critical section on here. + * If we call this after terminate the server thread first. + * Then there is no other thread to access tcb_list. + */ + CRITICAL_SECTION_BEGIN(&svc_ctx->tcb_list_lock); + EINA_LIST_FREE(svc_ctx->tcb_list, tcb) { + /*! + * ASSERT(tcb->fd >= 0); + */ + secure_socket_destroy_handle(tcb->fd); + + status = pthread_join(tcb->thid, &ret); + if (status != 0) + ErrPrint("Unable to join a thread: %s\n", strerror(status)); + + free(tcb); + } + CRITICAL_SECTION_END(&svc_ctx->tcb_list_lock); +} + +/*! + * \NOTE + * Packet consuming thread. service thread. + */ +static void *service_main(void *data) +{ + struct service_context *svc_ctx = data; + fd_set set; + int ret; + char evt_ch; + struct packet_info *packet_info; + + while (1) { + FD_ZERO(&set); + FD_SET(svc_ctx->evt_pipe[EVT_READ], &set); + ret = select(svc_ctx->evt_pipe[EVT_READ] + 1, &set, NULL, NULL, NULL); + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("INTERRUPTED\n"); + continue; + } + ErrPrint("Error: %s\n",strerror(errno)); + break; + } else if (ret == 0) { + ErrPrint("Timeout\n"); + ret = -ETIMEDOUT; + break; + } + + if (FD_ISSET(svc_ctx->evt_pipe[EVT_READ], &set)) { + ErrPrint("Unexpected handler is toggled\n"); + ret = -EINVAL; + break; + } + + if (read(svc_ctx->evt_pipe[EVT_READ], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) { + ErrPrint("Unable to read pipe: %s\n", strerror(errno)); + ret = -EIO; + break; + } + + if (evt_ch == EVT_END_CH) { + ErrPrint("Thread is terminated\n"); + ret = 0; + break; + } + + CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock); + packet_info = eina_list_nth(svc_ctx->packet_list, 0); + svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info); + CRITICAL_SECTION_END(&svc_ctx->packet_list_lock); + + ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data); + if (ret < 0) + ErrPrint("Service thread returns: %d\n", ret); + + packet_destroy(packet_info->packet); + free(packet_info); + } + + return (void *)ret; +} + +/*! + * Accept new client connections + * And create a new thread for service. + */ +static void *server_main(void *data) +{ + struct service_context *svc_ctx = data; + fd_set set; + int ret; + int client_fd; + struct tcb *tcb; + + while (1) { + FD_ZERO(&set); + FD_SET(svc_ctx->fd, &set); + ret = select(svc_ctx->fd + 1, &set, NULL, NULL, NULL); + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("INTERRUPTED\n"); + continue; + } + ErrPrint("Error: %s\n", strerror(errno)); + break; + } else if (ret == 0) { + ErrPrint("Timeout\n"); + ret = -ETIMEDOUT; + break; + } + + if (FD_ISSET(svc_ctx->fd, &set)) { + ErrPrint("Unexpected handler is toggled\n"); + ret = -EINVAL; + break; + } + + client_fd = secure_socket_get_connection_handle(svc_ctx->fd); + DbgPrint("New client connection arrived (%d)\n", client_fd); + if (client_fd < 0) { + ErrPrint("Failed to establish the client connection\n"); + ret = -EFAULT; + break; + } + + tcb = tcb_create(svc_ctx, client_fd); + if (!tcb) + secure_socket_destroy_handle(client_fd); + } + + return (void *)ret; +} + +HAPI struct service_context *service_common_create(const char *addr, int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data), void *data) +{ + int status; + struct service_context *svc_ctx; + + if (!service_thread_main || !addr) { + ErrPrint("Invalid argument\n"); + return NULL; + } + + svc_ctx = malloc(sizeof(*svc_ctx)); + if (!svc_ctx) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + svc_ctx->fd = secure_socket_create_server(addr); + if (svc_ctx->fd < 0) { + free(svc_ctx); + return NULL; + } + + svc_ctx->service_thread_main = service_thread_main; + svc_ctx->service_thread_data = data; + + if (fcntl(svc_ctx->fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + if (fcntl(svc_ctx->fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + if (pipe2(svc_ctx->evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) { + ErrPrint("pipe: %d\n", strerror(errno)); + secure_socket_destroy_handle(svc_ctx->fd); + free(svc_ctx); + return NULL; + } + + status = pthread_mutex_init(&svc_ctx->packet_list_lock, NULL); + if (status != 0) { + ErrPrint("Unable to create a mutex: %s\n", strerror(status)); + status = close(svc_ctx->evt_pipe[EVT_READ]); + if (status < 0) + ErrPrint("Error: %s\n", strerror(errno)); + status = close(svc_ctx->evt_pipe[EVT_WRITE]); + if (status < 0) + ErrPrint("Error: %s\n", strerror(errno)); + secure_socket_destroy_handle(svc_ctx->fd); + free(svc_ctx); + return NULL; + } + + status = pthread_mutex_init(&svc_ctx->tcb_list_lock, NULL); + if (status != 0) { + ErrPrint("Unable to initiate the mutex: %s\n", strerror(status)); + status = pthread_mutex_destroy(&svc_ctx->packet_list_lock); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + status = close(svc_ctx->evt_pipe[EVT_READ]); + if (status < 0) + ErrPrint("Error: %s\n", strerror(errno)); + status = close(svc_ctx->evt_pipe[EVT_WRITE]); + if (status < 0) + ErrPrint("Error: %s\n", strerror(errno)); + secure_socket_destroy_handle(svc_ctx->fd); + free(svc_ctx); + return NULL; + } + + status = pthread_create(&svc_ctx->thid, NULL, server_main, svc_ctx); + if (status != 0) { + ErrPrint("Unable to create a thread for shortcut service: %s\n", strerror(status)); + status = pthread_mutex_destroy(&svc_ctx->tcb_list_lock); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + status = pthread_mutex_destroy(&svc_ctx->packet_list_lock); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + status = close(svc_ctx->evt_pipe[EVT_READ]); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + status = close(svc_ctx->evt_pipe[EVT_WRITE]); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + secure_socket_destroy_handle(svc_ctx->fd); + free(svc_ctx); + return NULL; + } + + status = pthread_create(&svc_ctx->service_thid, NULL, service_main, svc_ctx); + if (status != 0) { + void *ret; + ErrPrint("Unable to create a thread for shortcut service: %s\n", strerror(status)); + + secure_socket_destroy_handle(svc_ctx->fd); + + status = pthread_join(svc_ctx->thid, &ret); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + + status = pthread_mutex_destroy(&svc_ctx->tcb_list_lock); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + status = pthread_mutex_destroy(&svc_ctx->packet_list_lock); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + status = close(svc_ctx->evt_pipe[EVT_READ]); + if (status < 0) + ErrPrint("Error: %s\n", strerror(errno)); + status = close(svc_ctx->evt_pipe[EVT_WRITE]); + if (status < 0) + ErrPrint("Error: %s\n", strerror(errno)); + secure_socket_destroy_handle(svc_ctx->fd); + free(svc_ctx); + return NULL; + } + + return svc_ctx; +} + +HAPI int service_common_destroy(struct service_context *svc_ctx) +{ + int status; + void *ret; + char evt_ch = EVT_END_CH; + + if (!svc_ctx) + return -EINVAL; + + /*! + * \note + * Terminate server thread + */ + secure_socket_destroy_handle(svc_ctx->fd); + status = pthread_join(svc_ctx->thid, &ret); + if (status != 0) + ErrPrint("Join: %s\n", strerror(status)); + /*! + * \note + * Terminate all client threads. + */ + tcb_teminate_all(svc_ctx); + + /* Emit a finish event */ + if (write(svc_ctx->evt_pipe[EVT_WRITE], &evt_ch, sizeof(evt_ch)) == sizeof(evt_ch)) + ErrPrint("write: %s\n", strerror(errno)); + + /* Waiting */ + status = pthread_join(svc_ctx->service_thid, &ret); + if (status != 0) + ErrPrint("Join: %s\n", strerror(status)); + + status = pthread_mutex_destroy(&svc_ctx->tcb_list_lock); + if (status != 0) + ErrPrint("Unable to destroy a mutex: %s\n", strerror(status)); + + status = pthread_mutex_destroy(&svc_ctx->packet_list_lock); + if (status != 0) + ErrPrint("Unable to destroy a mutex: %s\n", strerror(status)); + + status = close(svc_ctx->evt_pipe[EVT_WRITE]); + status = close(svc_ctx->evt_pipe[EVT_READ]); + free(svc_ctx); + return 0; +} + +HAPI int tcb_fd(struct tcb *tcb) +{ + if (!tcb) + return -EINVAL; + + return tcb->fd; +} + +HAPI int tcb_client_type(struct tcb *tcb) +{ + if (!tcb) + return -EINVAL; + + return tcb->type; +} + +HAPI int tcb_client_set_type(struct tcb *tcb, enum tcb_type type) +{ + if (!tcb) + return -EINVAL; + + tcb->type = type; + return 0; +} + +HAPI struct service_context *tcb_svc_ctx(struct tcb *tcb) +{ + if (!tcb) + return NULL; + + return tcb->svc_ctx; +} + +HAPI int service_common_unicast_packet(struct tcb *tcb, struct packet *packet) +{ + if (!tcb || !packet) + return -EINVAL; + return secure_socket_send(tcb->fd, (void *)packet_data(packet), packet_size(packet)); +} + +HAPI int service_common_multicast_packet(struct tcb *tcb, struct packet *packet, int type) +{ + Eina_List *l; + struct tcb *target; + struct service_context *svc_ctx; + int ret; + + if (!tcb || !packet) + return -EINVAL; + + svc_ctx = tcb->svc_ctx; + + CRITICAL_SECTION_BEGIN(&svc_ctx->tcb_list_lock); + EINA_LIST_FOREACH(svc_ctx->tcb_list, l, target) { + if (target == tcb || target->type != type) + continue; + + ret = secure_socket_send(target->fd, (void *)packet_data(packet), packet_size(packet)); + if (ret < 0) + ErrPrint("Failed to send packet: %d\n", ret); + } + CRITICAL_SECTION_END(&svc_ctx->tcb_list_lock); + + return 0; +} + +/* End of a file */ diff --git a/src/shortcut_service.c b/src/shortcut_service.c new file mode 100644 index 0000000..2e926dc --- /dev/null +++ b/src/shortcut_service.c @@ -0,0 +1,77 @@ +#include + +#include + +#include +#include +#include + +#include "service_common.h" +#include "debug.h" +#include "util.h" + +#define SHORTCUT_ADDR "/tmp/.shortcut.service" + +/*! + * SERVICE THREAD + */ +static int service_thread_main(struct tcb *tcb, struct packet *packet, void *data) +{ + DbgPrint("Command: [%s]\n", packet_command(packet)); + + switch (packet_type(packet)) { + case PACKET_REQ: + /* Need to send reply packet */ + break; + case PACKET_REQ_NOACK: + /* Doesn't need to send reply packet */ + break; + default: + ErrPrint("Packet type is not valid\n"); + break; + } + + /*! + * return value has no meanning, + * it will be printed by dlogutil. + */ + return 0; +} + + +/*! + * MAIN THREAD + * Do not try to do anyother operation in these functions + */ + +static struct info { + struct service_context *svc_ctx; +} s_info = { + .svc_ctx = NULL, +}; + + +int service_shortcut_init(void) +{ + if (s_info.svc_ctx) { + ErrPrint("Already initialized\n"); + return LB_STATUS_ERROR_ALREADY; + } + + s_info.svc_ctx = service_common_create(SHORTCUT_ADDR, service_thread_main, NULL); + if (!s_info.svc_ctx) + return LB_STATUS_ERROR_FAULT; + + return LB_STATUS_SUCCESS; +} + +int service_shortcut_fini(void) +{ + if (!s_info.svc_ctx) + return LB_STATUS_ERROR_INVALID; + + service_common_destroy(s_info.svc_ctx); + return LB_STATUS_SUCCESS; +} + +/* End of a file */ -- 2.7.4