Initial import
authorRusty Lynch <rusty.lynch@intel.com>
Fri, 6 Sep 2013 19:10:18 +0000 (12:10 -0700)
committerRusty Lynch <rusty.lynch@intel.com>
Fri, 6 Sep 2013 19:10:54 +0000 (12:10 -0700)
Change-Id: I7e3c9fb0bbe124c596fe8b17c3de96c9ced752e8
Signed-off-by: Rusty Lynch <rusty.lynch@intel.com>
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
main.c [new file with mode: 0644]
notification_service.c [new file with mode: 0644]
notification_service.h [new file with mode: 0644]
notifications.service.in [new file with mode: 0644]
notifications.socket.in [new file with mode: 0644]
service_common.c [new file with mode: 0644]
service_common.h [new file with mode: 0644]

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..c0b4bbc
--- /dev/null
@@ -0,0 +1,22 @@
+bin_PROGRAMS = notification-service
+
+AM_CFLAGS = $(GCC_CFLAGS)
+AM_CPPFLAGS = $(GCC_CFLAGS)
+
+notification_service_SOURCES = \
+    main.c \
+    notification_service.c \
+    service_common.c
+notification_service_CFLAGS = -I. $(TIZEN_CFLAGS)
+notification_service_LDADD = $(TIZEN_LIBS)
+
+SCRIPT_IN_FILES = \
+     notifications.service.in \
+     notifications.socket.in
+
+install-data-hook:
+       $(mkinstalldirs) $(DESTDIR)/usr/lib/systemd/system/sockets.target.wants
+       install -m 0644 notifications.service $(DESTDIR)/usr/lib/systemd/system/notifications.service
+       install -m 0644 notifications.socket $(DESTDIR)/usr/lib/systemd/system/notifications.socket
+       ln -fs ../notifications.socket $(DESTDIR)/usr/lib/systemd/system/sockets.target.wants/notifications.socket
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..0c9f207
--- /dev/null
+++ b/README
@@ -0,0 +1,8 @@
+This package consist of the notification-service implementation picked out of the Tizen data-provider-master project at:
+https://review.tizen.org/git/?p=platform/framework/web/data-provider-master.git;a=summary
+
+This implementation had no real dependencies on the rest of the services provided by data-provider-master which were very mobile profile specific, so it was forked into this new project and adopted as an IVI component.
+
+Eventually this project might be proposed as a generic Tizen 3.0 component, with the old implementation in data-provider-master removed, but first the implementation will be proven and optimized in the IVI profile (even though there is nothing IVI specific about this code.)
+
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..3ca9aba
--- /dev/null
@@ -0,0 +1,4 @@
+#! /bin/sh
+
+autoreconf --force -v --install
+test -n "$NOCONFIGURE" || "./configure" "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..394cdfe
--- /dev/null
@@ -0,0 +1,19 @@
+AC_PREREQ([2.68])
+AC_INIT([notification-service], [0.0.1], [])
+AM_INIT_AUTOMAKE([foreign 1.11])
+AC_CONFIG_SRCDIR([configure.ac])
+AC_CONFIG_HEADERS([config.h])
+
+AC_PROG_CC
+
+LT_PREREQ([2.2])
+LT_INIT([disable-static])
+
+PKG_PROG_PKG_CONFIG()
+PKG_CHECK_MODULES([TIZEN], [eina ecore com-core notification])
+AC_SUBST(TIZEN_CFLAGS)
+AC_SUBST(TIZEN_LIBS)
+
+AC_CONFIG_FILES([Makefile notifications.service notifications.socket])
+AC_PROG_RANLIB([ranlib])
+AC_OUTPUT
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..2af826c
--- /dev/null
+++ b/main.c
@@ -0,0 +1,23 @@
+#include <Ecore.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+    if (!ecore_init()) {
+        fprintf(stderr, "ERROR: Cannot init Ecore!\n");
+        return -1;
+    }
+
+    if (notification_service_init() != 0) {
+        fprintf(stderr, "Unable to initialize notification service!\n");
+        goto shutdown;
+    }
+
+    ecore_main_loop_begin();
+
+ shutdown:
+    ecore_shutdown();
+    return 0;
+}
+
diff --git a/notification_service.c b/notification_service.c
new file mode 100644 (file)
index 0000000..93e8d52
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2013  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <Eina.h>
+#include <packet.h>
+#include <notification_ipc.h>
+#include <notification_noti.h>
+#include <notification_error.h>
+
+#include "service_common.h"
+
+#ifndef NOTIFICATION_ADDR
+#define NOTIFICATION_ADDR "/tmp/.notification.service"
+#endif
+
+#ifndef NOTIFICATION_DEL_PACKET_UNIT
+#define NOTIFICATION_DEL_PACKET_UNIT 10
+#endif
+
+static struct info {
+    Eina_List *context_list;
+    struct service_context *svc_ctx;
+} s_info = {
+       .context_list = NULL, /*!< \WARN: This is only used for SERVICE THREAD */
+       .svc_ctx = NULL, /*!< \WARN: This is only used for MAIN THREAD */
+};
+
+struct context {
+       struct tcb *tcb;
+       double seq;
+};
+
+struct noti_service {
+       const char *cmd;
+       void (*handler)(struct tcb *tcb, struct packet *packet, void *data);
+};
+
+/*!
+ * FUNCTIONS to handle notifcation
+ */
+static inline int get_priv_id(int num_deleted, int *list_deleted, int index) {
+       if (index < num_deleted) {
+               return *(list_deleted + index);
+       } else {
+               return -1;
+       }
+}
+
+static inline char *get_string(char *string)
+{
+       if (string == NULL) {
+               return NULL;
+       }
+       if (string[0] == '\0') {
+               return NULL;
+       }
+
+       return string;
+}
+
+static inline struct packet *create_packet_from_deleted_list(int op_num, int *list, int start_index) {
+       return packet_create(
+               "del_noti_multiple",
+               "iiiiiiiiiii",
+               ((op_num - start_index) > NOTIFICATION_DEL_PACKET_UNIT) ? NOTIFICATION_DEL_PACKET_UNIT : op_num - start_index,
+               get_priv_id(op_num, list, start_index),
+               get_priv_id(op_num, list, start_index + 1),
+               get_priv_id(op_num, list, start_index + 2),
+               get_priv_id(op_num, list, start_index + 3),
+               get_priv_id(op_num, list, start_index + 4),
+               get_priv_id(op_num, list, start_index + 5),
+               get_priv_id(op_num, list, start_index + 6),
+               get_priv_id(op_num, list, start_index + 7),
+               get_priv_id(op_num, list, start_index + 8),
+               get_priv_id(op_num, list, start_index + 9)
+               );
+}
+
+/*!
+ * SERVICE HANDLER
+ */
+static void _handler_insert(struct tcb *tcb, struct packet *packet, void *data)
+{
+       int ret = 0;
+       int priv_id = 0;
+       struct packet *packet_reply = NULL;
+       struct packet *packet_service = NULL;
+       notification_h noti = NULL;
+
+       noti = notification_create(NOTIFICATION_TYPE_NOTI);
+       if (noti != NULL) {
+               if (notification_ipc_make_noti_from_packet(noti, packet) == NOTIFICATION_ERROR_NONE) {
+                       ret = notification_noti_insert(noti);
+                       if (ret != NOTIFICATION_ERROR_NONE) {
+                               notification_free(noti);
+                               return ;
+                       }
+
+                       notification_get_id(noti, NULL, &priv_id);
+                       packet_reply = packet_create_reply(packet, "ii", ret, priv_id);
+                       if (packet_reply) {
+                               service_common_unicast_packet(tcb, packet_reply);
+                               packet_destroy(packet_reply);
+                       }
+
+                       packet_service = notification_ipc_make_packet_from_noti(noti, "add_noti", 2);
+                       if (packet_service != NULL) {
+                               service_common_multicast_packet(tcb, packet_service, TCB_CLIENT_TYPE_SERVICE);
+                               packet_destroy(packet_service);
+                       }
+               }
+               notification_free(noti);
+       }
+}
+
+static void _handler_update(struct tcb *tcb, struct packet *packet, void *data)
+{
+       int ret = 0;
+       int priv_id = 0;
+       struct packet *packet_reply = NULL;
+       struct packet *packet_service = NULL;
+       notification_h noti = NULL;
+
+       noti = notification_create(NOTIFICATION_TYPE_NOTI);
+       if (noti != NULL) {
+               if (notification_ipc_make_noti_from_packet(noti, packet) == NOTIFICATION_ERROR_NONE) {
+                       ret = notification_noti_update(noti);
+                       if (ret != NOTIFICATION_ERROR_NONE) {
+                               notification_free(noti);
+                               return ;
+                       }
+
+                       notification_get_id(noti, NULL, &priv_id);
+                       packet_reply = packet_create_reply(packet, "ii", ret, priv_id);
+                       if (packet_reply) {
+                               service_common_unicast_packet(tcb, packet_reply);
+                               packet_destroy(packet_reply);
+                       }
+
+                       packet_service = notification_ipc_make_packet_from_noti(noti, "update_noti", 2);
+                       if (packet_service != NULL) {
+                               service_common_multicast_packet(tcb, packet_service, TCB_CLIENT_TYPE_SERVICE);
+                               packet_destroy(packet_service);
+                       }
+               }
+               notification_free(noti);
+       }
+}
+
+static void _handler_refresh(struct tcb *tcb, struct packet *packet, void *data)
+{
+       int ret = NOTIFICATION_ERROR_NONE;
+       struct packet *packet_reply = NULL;
+
+       packet_reply = packet_create_reply(packet, "i", ret);
+       if (packet_reply) {
+               service_common_unicast_packet(tcb, packet_reply);
+               packet_destroy(packet_reply);
+       }
+
+       service_common_multicast_packet(tcb, packet, TCB_CLIENT_TYPE_SERVICE);
+}
+
+static void _handler_delete_single(struct tcb *tcb, struct packet *packet, void *data)
+{
+       int ret = 0;
+       int priv_id = 0;
+       struct packet *packet_reply = NULL;
+       struct packet *packet_service = NULL;
+       char *pkgname = NULL;
+
+       if (packet_get(packet, "si", &pkgname, &priv_id) == 2) {
+               pkgname = get_string(pkgname);
+
+               ret = notification_noti_delete_by_priv_id(pkgname, priv_id);
+
+               packet_reply = packet_create_reply(packet, "ii", ret, priv_id);
+               if (packet_reply) {
+                       service_common_unicast_packet(tcb, packet_reply);
+                       packet_destroy(packet_reply);
+               }
+
+               packet_service = packet_create("del_noti_single", "ii", 1, priv_id);
+               if (packet_service != NULL) {
+                       service_common_multicast_packet(tcb, packet_service, TCB_CLIENT_TYPE_SERVICE);
+                       packet_destroy(packet_service);
+               }
+       }
+}
+
+static void _handler_delete_multiple(struct tcb *tcb, struct packet *packet, void *data)
+{
+       int ret = 0;
+       struct packet *packet_reply = NULL;
+       struct packet *packet_service = NULL;
+       char *pkgname = NULL;
+       notification_type_e type = 0;
+       int num_deleted = 0;
+       int *list_deleted = NULL;
+
+       if (packet_get(packet, "si", &pkgname, &type) == 2) {
+               pkgname = get_string(pkgname);
+
+               ret = notification_noti_delete_all(type, pkgname, &num_deleted, &list_deleted);
+
+               packet_reply = packet_create_reply(packet, "ii", ret, num_deleted);
+               if (packet_reply) {
+                       service_common_unicast_packet(tcb, packet_reply);
+                       packet_destroy(packet_reply);
+               }
+
+               if (num_deleted > 0) {
+                       if (num_deleted <= NOTIFICATION_DEL_PACKET_UNIT) {
+                               packet_service = create_packet_from_deleted_list(num_deleted, list_deleted, 0);
+
+                               if (packet_service) {
+                                       service_common_multicast_packet(tcb, packet_service, TCB_CLIENT_TYPE_SERVICE);
+                                       packet_destroy(packet_service);
+                               }
+                       } else {
+                               int set = 0;
+                               int set_total = num_deleted / NOTIFICATION_DEL_PACKET_UNIT;
+
+                               for (set = 0; set <= set_total; set++) {
+                                       packet_service = create_packet_from_deleted_list(num_deleted,
+                                                       list_deleted, set * NOTIFICATION_DEL_PACKET_UNIT);
+
+                                       if (packet_service) {
+                                               service_common_multicast_packet(tcb, packet_service, TCB_CLIENT_TYPE_SERVICE);
+                                               packet_destroy(packet_service);
+                                       }
+                               }
+                       }
+               }
+
+               if (list_deleted != NULL) {
+                       free(list_deleted);
+                       list_deleted = NULL;
+               }
+       }
+}
+
+static void _handler_service_register(struct tcb *tcb, struct packet *packet, void *data)
+{
+       struct packet *packet_reply;
+       int ret;
+
+       ret = tcb_client_type_set(tcb, TCB_CLIENT_TYPE_SERVICE);
+
+       packet_reply = packet_create_reply(packet, "i", ret);
+       if (packet_reply) {
+               service_common_unicast_packet(tcb, packet_reply);
+               packet_destroy(packet_reply);
+       }
+}
+
+/*!
+ * SERVICE THREAD
+ */
+static int service_thread_main(struct tcb *tcb, struct packet *packet, void *data)
+{
+       int i = 0;
+       const char *command;
+       static struct noti_service service_req_table[] = {
+               {
+                       .cmd = "add_noti",
+                       .handler = _handler_insert,
+               },
+               {
+                       .cmd = "update_noti",
+                       .handler = _handler_update,
+               },
+               {
+                       .cmd = "refresh_noti",
+                       .handler = _handler_refresh,
+               },
+               {
+                       .cmd = "del_noti_single",
+                       .handler = _handler_delete_single,
+               },
+               {
+                       .cmd = "del_noti_multiple",
+                       .handler = _handler_delete_multiple,
+               },
+               {
+                       .cmd = "service_register",
+                       .handler = _handler_service_register,
+               },
+               {
+                       .cmd = NULL,
+                       .handler = NULL,
+               },
+       };
+
+       if (!packet) {
+               return 0;
+       }
+
+       command = packet_command(packet);
+       if (!command) {
+               return -EINVAL;
+       }
+
+       switch (packet_type(packet)) {
+       case PACKET_REQ:
+               /* Need to send reply packet */
+               for (i = 0; service_req_table[i].cmd; i++) {
+                       if (strcmp(service_req_table[i].cmd, command))
+                               continue;
+
+                       service_req_table[i].handler(tcb, packet, data);
+                       break;
+               }
+
+               break;
+       case PACKET_REQ_NOACK:
+               break;
+       case PACKET_ACK:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /*!
+        * 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
+ */
+int notification_service_init(void)
+{
+       if (s_info.svc_ctx) {
+               return -1;
+       }
+
+       s_info.svc_ctx = service_common_create(NOTIFICATION_ADDR, service_thread_main, NULL);
+       if (!s_info.svc_ctx) {
+               return -1;
+       }
+
+       return 0;
+}
+
+int notification_service_fini(void)
+{
+       if (!s_info.svc_ctx)
+               return -1;
+
+       service_common_destroy(s_info.svc_ctx);
+       return 0;
+}
+
+/* End of a file */
diff --git a/notification_service.h b/notification_service.h
new file mode 100644 (file)
index 0000000..e02dfa3
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern int notification_service_init(void);
+extern int notification_service_fini(void);
+
+/* End of a file */
diff --git a/notifications.service.in b/notifications.service.in
new file mode 100644 (file)
index 0000000..47db370
--- /dev/null
@@ -0,0 +1,2 @@
+[Service]
+ExecStart=/usr/bin/notification-service
diff --git a/notifications.socket.in b/notifications.socket.in
new file mode 100644 (file)
index 0000000..cdd863f
--- /dev/null
@@ -0,0 +1,5 @@
+[Socket]
+ListenStream=/tmp/.notification.service
+
+[Install]
+WantedBy=sockets.target
diff --git a/service_common.c b/service_common.c
new file mode 100644 (file)
index 0000000..db79011
--- /dev/null
@@ -0,0 +1,809 @@
+/*
+ * Copyright 2013  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <pthread.h>
+#include <secure_socket.h>
+#include <packet.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <Eina.h>
+
+#include "service_common.h"
+
+#define EVT_CH         'e'
+#define EVT_END_CH     'x'
+
+int errno;
+
+struct service_event_item {
+       enum {
+               SERVICE_EVENT_TIMER,
+       } type;
+
+       union {
+               struct {
+                       int fd;
+               } timer;
+       } info;
+
+       int (*event_cb)(struct service_context *svc_cx, void *data);
+       void *cbdata;
+};
+
+/*!
+ * \note
+ * Server information and global (only in this file-scope) variables are defined
+ */
+struct service_context {
+       pthread_t server_thid; /*!< Server thread Id */
+       int fd; /*!< Server socket handle */
+
+       Eina_List *tcb_list; /*!< TCB list, list of every thread for client connections */
+
+       Eina_List *packet_list;
+       pthread_mutex_t packet_list_lock;
+       int evt_pipe[PIPE_MAX];
+       int tcb_pipe[PIPE_MAX];
+
+       int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data);
+       void *service_thread_data;
+
+       Eina_List *event_list;
+};
+
+struct packet_info {
+       struct tcb *tcb;
+       struct packet *packet;
+};
+
+/*!
+ * \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;
+};
+
+/*!
+ * Do services for clients
+ * Routing packets to destination processes.
+ * CLIENT THREAD
+ */
+static void *client_packet_pump_main(void *data)
+{
+       struct tcb *tcb = data;
+       struct service_context *svc_ctx = tcb->svc_ctx;
+       struct packet *packet;
+       fd_set set;
+       char *ptr;
+       int size;
+       int packet_offset;
+       int recv_offset;
+       int pid;
+       long ret;
+       char evt_ch = EVT_CH;
+       enum {
+               RECV_INIT,
+               RECV_HEADER,
+               RECV_PAYLOAD,
+               RECV_DONE,
+       } recv_state;
+       struct packet_info *packet_info;
+       Eina_List *l;
+
+       ret = 0;
+       recv_state = RECV_INIT;
+       /*!
+        * \note
+        * To escape from the switch statement, we use this ret value
+        */
+       while (ret == 0) {
+               FD_ZERO(&set);
+               FD_SET(tcb->fd, &set);
+               ret = select(tcb->fd + 1, &set, NULL, NULL, NULL);
+               if (ret < 0) {
+                       ret = -errno;
+                       if (errno == EINTR) {
+                               ret = 0;
+                               continue;
+                       }
+                       free(ptr);
+                       ptr = NULL;
+                       break;
+               } else if (ret == 0) {
+                       ret = -ETIMEDOUT;
+                       free(ptr);
+                       ptr = NULL;
+                       break;
+               }
+
+               if (!FD_ISSET(tcb->fd, &set)) {
+                       ret = -EINVAL;
+                       free(ptr);
+                       ptr = NULL;
+                       break;
+               }
+               
+               /*!
+                * \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) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+                       recv_state = RECV_HEADER;
+                       /* Go through, don't break from here */
+               case RECV_HEADER:
+                       ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
+                       if (ret <= 0) {
+                               if (ret == 0)
+                                       ret = -ECANCELED;
+                               free(ptr);
+                               ptr = NULL;
+                               break;
+                       }
+
+                       recv_offset += ret;
+                       ret = 0;
+
+                       if (recv_offset == size) {
+                               packet = packet_build(packet, packet_offset, ptr, size);
+                               free(ptr);
+                               ptr = NULL;
+                               if (!packet) {
+                                       ret = -EFAULT;
+                                       break;
+                               }
+
+                               packet_offset += recv_offset;
+
+                               size = packet_payload_size(packet);
+                               if (size <= 0) {
+                                       recv_state = RECV_DONE;
+                                       recv_offset = 0;
+                                       break;
+                               }
+
+                               recv_state = RECV_PAYLOAD;
+                               recv_offset = 0;
+
+                               ptr = malloc(size);
+                               if (!ptr) {
+                                       ret = -ENOMEM;
+                               }
+                       }
+                       break;
+               case RECV_PAYLOAD:
+                       ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
+                       if (ret <= 0) {
+                               if (ret == 0)
+                                       ret = -ECANCELED;
+                               free(ptr);
+                               ptr = NULL;
+                               break;
+                       }
+
+                       recv_offset += ret;
+                       ret = 0;
+
+                       if (recv_offset == size) {
+                               packet = packet_build(packet, packet_offset, ptr, size);
+                               free(ptr);
+                               ptr = NULL;
+                               if (!packet) {
+                                       ret = -EFAULT;
+                                       break;
+                               }
+
+                               packet_offset += recv_offset;
+
+                               recv_state = RECV_DONE;
+                               recv_offset = 0;
+                       }
+                       break;
+               case RECV_DONE:
+               default:
+                       /* Dead code */
+                       break;
+               }
+
+               if (recv_state == 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;
+                               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[PIPE_WRITE], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
+                               ret = -errno;
+                               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;
+                       } else {
+                               recv_state = RECV_INIT;
+                       }
+               }
+       }
+
+       CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
+       EINA_LIST_FOREACH(svc_ctx->packet_list, l, packet_info) {
+               if (packet_info->tcb == tcb) {
+                       packet_info->tcb = NULL;
+               }
+       }
+       CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
+
+       /*!
+        * \note
+        * Emit a signal to collect this TCB from the SERVER THREAD.
+        */
+       write(svc_ctx->tcb_pipe[PIPE_WRITE], &tcb, sizeof(tcb)) != sizeof(tcb);
+
+       return (void *)ret;
+}
+
+/*!
+ * \note
+ * SERVER 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) {
+               return NULL;
+       }
+
+       tcb->fd = fd;
+       tcb->svc_ctx = svc_ctx;
+       tcb->type = TCB_CLIENT_TYPE_APP;
+
+       status = pthread_create(&tcb->thid, NULL, client_packet_pump_main, tcb);
+       if (status != 0) {
+               free(tcb);
+               return NULL;
+       }
+
+       svc_ctx->tcb_list = eina_list_append(svc_ctx->tcb_list, tcb);
+       return tcb;
+}
+
+/*!
+ * \note
+ * SERVER 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.
+        */
+       EINA_LIST_FREE(svc_ctx->tcb_list, tcb) {
+               /*!
+                * ASSERT(tcb->fd >= 0);
+                */
+               secure_socket_destroy_handle(tcb->fd);
+
+               status = pthread_join(tcb->thid, &ret);
+
+               free(tcb);
+       }
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline void tcb_destroy(struct service_context *svc_ctx, struct tcb *tcb)
+{
+       void *ret;
+       int status;
+
+       svc_ctx->tcb_list = eina_list_remove(svc_ctx->tcb_list, tcb);
+       /*!
+        * ASSERT(tcb->fd >= 0);
+        * Close the connection, and then collecting the return value of thread
+        */
+       secure_socket_destroy_handle(tcb->fd);
+
+       status = pthread_join(tcb->thid, &ret);
+
+       free(tcb);
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline int find_max_fd(struct service_context *svc_ctx)
+{
+       int fd;
+       Eina_List *l;
+       struct service_event_item *item;
+
+       fd = svc_ctx->fd > svc_ctx->tcb_pipe[PIPE_READ] ? svc_ctx->fd : svc_ctx->tcb_pipe[PIPE_READ];
+       fd = fd > svc_ctx->evt_pipe[PIPE_READ] ? fd : svc_ctx->evt_pipe[PIPE_READ];
+
+       EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
+               if (item->type == SERVICE_EVENT_TIMER && fd < item->info.timer.fd)
+                       fd = item->info.timer.fd;
+       }
+
+       fd += 1;
+       return fd;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline void update_fdset(struct service_context *svc_ctx, fd_set *set)
+{
+       Eina_List *l;
+       struct service_event_item *item;
+
+       FD_ZERO(set);
+       FD_SET(svc_ctx->fd, set);
+       FD_SET(svc_ctx->tcb_pipe[PIPE_READ], set);
+       FD_SET(svc_ctx->evt_pipe[PIPE_READ], set);
+
+       EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
+               if (item->type == SERVICE_EVENT_TIMER)
+                       FD_SET(item->info.timer.fd, set);
+       }
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+static inline void processing_timer_event(struct service_context *svc_ctx, fd_set *set)
+{
+       uint64_t expired_count;
+       Eina_List *l;
+       Eina_List *n;
+       struct service_event_item *item;
+
+       EINA_LIST_FOREACH_SAFE(svc_ctx->event_list, l, n, item) {
+               switch (item->type) {
+               case SERVICE_EVENT_TIMER:
+                       if (!FD_ISSET(item->info.timer.fd, set))
+                               break;
+
+                       if (read(item->info.timer.fd, &expired_count, sizeof(expired_count)) == sizeof(expired_count)) {
+                               if (item->event_cb(svc_ctx, item->cbdata) >= 0)
+                                       break;
+                       }
+
+                       if (!eina_list_data_find(svc_ctx->event_list, item))
+                               break;
+
+                       svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
+                       close(item->info.timer.fd);
+
+                       free(item);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+/*!
+ * Accept new client connections
+ * And create a new thread for service.
+ *
+ * Create Client threads & Destroying them
+ * SERVER THREAD
+ */
+static void *server_main(void *data)
+{
+       struct service_context *svc_ctx = data;
+       fd_set set;
+       long ret;
+       int client_fd;
+       struct tcb *tcb;
+       int fd;
+       char evt_ch;
+       struct packet_info *packet_info;
+
+       while (1) {
+               fd = find_max_fd(svc_ctx);
+               update_fdset(svc_ctx, &set);
+
+               ret = select(fd, &set, NULL, NULL, NULL);
+               if (ret < 0) {
+                       ret = -errno;
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       break;
+               } else if (ret == 0) {
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+
+               if (FD_ISSET(svc_ctx->fd, &set)) {
+                       client_fd = secure_socket_get_connection_handle(svc_ctx->fd);
+                       if (client_fd < 0) {
+                               ret = -EFAULT;
+                               break;
+                       }
+
+                       tcb = tcb_create(svc_ctx, client_fd);
+                       if (!tcb)
+                               secure_socket_destroy_handle(client_fd);
+               } 
+
+               if (FD_ISSET(svc_ctx->tcb_pipe[PIPE_READ], &set)) {
+                       if (read(svc_ctx->tcb_pipe[PIPE_READ], &tcb, sizeof(tcb)) != sizeof(tcb)) {
+                               ret = -EFAULT;
+                               break;
+                       }
+
+                       /*!
+                        * \note
+                        * Invoke the service thread main, to notify the termination of a TCB
+                        */
+                       ret = svc_ctx->service_thread_main(tcb, NULL, svc_ctx->service_thread_data);
+
+                       /*!
+                        * at this time, the client thread can access this tcb.
+                        * how can I protect this TCB from deletion without disturbing the server thread?
+                        */
+                       tcb_destroy(svc_ctx, tcb);
+               } 
+
+               if (FD_ISSET(svc_ctx->evt_pipe[PIPE_READ], &set)) {
+                       if (read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
+                               ret = -EFAULT;
+                               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);
+
+                       /*!
+                        * \CRITICAL
+                        * What happens if the client thread is terminated, so the packet_info->tcb is deleted
+                        * while processing svc_ctx->service_thread_main?
+                        */
+                       ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
+
+                       packet_destroy(packet_info->packet);
+                       free(packet_info);
+               }
+
+               processing_timer_event(svc_ctx, &set);
+               /* If there is no such triggered FD? */
+       }
+
+       /*!
+        * Consuming all pended packets before terminates server thread.
+        *
+        * If the server thread is terminated, we should flush all pended packets.
+        * And we should services them.
+        * While processing this routine, the mutex is locked.
+        * So every other client thread will be slowed down, sequently, every clients can meet problems.
+        * But in case of termination of server thread, there could be systemetic problem.
+        * This only should be happenes while terminating the master daemon process.
+        */
+       CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
+       EINA_LIST_FREE(svc_ctx->packet_list, packet_info) {
+               ret = read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch));
+               ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
+               packet_destroy(packet_info->packet);
+               free(packet_info);
+       }
+       CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
+
+       tcb_teminate_all(svc_ctx);
+       return (void *)ret;
+}
+
+/*!
+ * \NOTE
+ * MAIN THREAD
+ */
+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) {
+               return NULL;
+       }
+
+       svc_ctx = calloc(1, sizeof(*svc_ctx));
+       if (!svc_ctx) {
+               return NULL;
+       }
+
+    /* 
+     * cleanup old socket file
+     */
+    struct stat tmp;
+    if (!stat(addr, &tmp)) {
+        if (unlink(addr)) {
+            perror("Unable to unlink old socket file");
+            free(svc_ctx);
+            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;
+
+       fcntl(svc_ctx->fd, F_SETFD, FD_CLOEXEC);
+
+       fcntl(svc_ctx->fd, F_SETFL, O_NONBLOCK);
+
+       if (pipe2(svc_ctx->evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
+               secure_socket_destroy_handle(svc_ctx->fd);
+               free(svc_ctx);
+               return NULL;
+       }
+
+       if (pipe2(svc_ctx->tcb_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
+               CLOSE_PIPE(svc_ctx->evt_pipe);
+               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) {
+               CLOSE_PIPE(svc_ctx->evt_pipe);
+               CLOSE_PIPE(svc_ctx->tcb_pipe);
+               secure_socket_destroy_handle(svc_ctx->fd);
+               free(svc_ctx);
+               return NULL;
+       }
+
+       status = pthread_create(&svc_ctx->server_thid, NULL, server_main, svc_ctx);
+       if (status != 0) {
+               status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
+               CLOSE_PIPE(svc_ctx->evt_pipe);
+               CLOSE_PIPE(svc_ctx->tcb_pipe);
+               secure_socket_destroy_handle(svc_ctx->fd);
+               free(svc_ctx);
+               return NULL;
+       }
+
+       return svc_ctx;
+}
+
+/*!
+ * \note
+ * MAIN THREAD
+ */
+int service_common_destroy(struct service_context *svc_ctx)
+{
+       int status;
+       void *ret;
+
+       if (!svc_ctx)
+               return -EINVAL;
+
+       /*!
+        * \note
+        * Terminate server thread
+        */
+       secure_socket_destroy_handle(svc_ctx->fd);
+
+       status = pthread_join(svc_ctx->server_thid, &ret);
+
+       status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
+
+       CLOSE_PIPE(svc_ctx->evt_pipe);
+       CLOSE_PIPE(svc_ctx->tcb_pipe);
+       free(svc_ctx);
+       return 0;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+int tcb_fd(struct tcb *tcb)
+{
+       if (!tcb)
+               return -EINVAL;
+
+       return tcb->fd;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+int tcb_client_type(struct tcb *tcb)
+{
+       if (!tcb)
+               return -EINVAL;
+
+       return tcb->type;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+int tcb_client_type_set(struct tcb *tcb, enum tcb_type type)
+{
+       if (!tcb)
+               return -EINVAL;
+
+       tcb->type = type;
+       return 0;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+struct service_context *tcb_svc_ctx(struct tcb *tcb)
+{
+       if (!tcb)
+               return NULL;
+
+       return tcb->svc_ctx;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+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));
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+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;
+
+       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));
+       }
+       return 0;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+struct service_event_item *service_common_add_timer(struct service_context *svc_ctx, double timer, int (*timer_cb)(struct service_context *svc_cx, void *data), void *data)
+{
+       struct service_event_item *item;
+       struct itimerspec spec;
+
+       item = calloc(1, sizeof(*item));
+       if (!item) {
+               return NULL;
+       }
+
+       item->type = SERVICE_EVENT_TIMER;
+       item->info.timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+       if (item->info.timer.fd < 0) {
+               free(item);
+               return NULL;
+       }
+
+       spec.it_interval.tv_sec = (time_t)timer;
+       spec.it_interval.tv_nsec = (timer - spec.it_interval.tv_sec) * 1000000000;
+       spec.it_value.tv_sec = 0;
+       spec.it_value.tv_nsec = 0;
+
+       if (timerfd_settime(item->info.timer.fd, 0, &spec, NULL) < 0) {
+               close(item->info.timer.fd);
+               free(item);
+               return NULL;
+       }
+
+       item->event_cb = timer_cb;
+       item->cbdata = data;
+
+       svc_ctx->event_list = eina_list_append(svc_ctx->event_list, item);
+       return item;
+}
+
+/*!
+ * \note
+ * SERVER THREAD
+ */
+int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item)
+{
+       if (!eina_list_data_find(svc_ctx->event_list, item)) {
+               return -EINVAL;
+       }
+
+       svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
+
+       close(item->info.timer.fd);
+       free(item);
+       return 0;
+}
+
+/* End of a file */
diff --git a/service_common.h b/service_common.h
new file mode 100644 (file)
index 0000000..3dc7d46
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+enum tcb_type {
+       TCB_CLIENT_TYPE_APP     = 0x00,
+       TCB_CLIENT_TYPE_SERVICE = 0x01,
+       TCB_CLIENT_TYPE_UNKNOWN = 0xff,
+};
+
+struct tcb;
+struct service_context;
+struct service_event_item;
+
+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 int tcb_client_type_set(struct tcb *tcb, enum tcb_type type);
+
+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);
+
+extern struct service_event_item *service_common_add_timer(struct service_context *svc_ctx, double timer, int (*timer_cb)(struct service_context *svc_cx, void *data), void *data);
+extern int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item);
+
+#define CRITICAL_SECTION_BEGIN(handle) \
+do { \
+       int ret; \
+       ret = pthread_mutex_lock(handle); \
+       if (ret != 0) \
+               fprintf(stderr, "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) \
+               fprintf(stderr, "Failed to unlock: %s\n", strerror(ret)); \
+} while (0)
+
+#define CANCEL_SECTION_BEGIN() do { \
+       int ret; \
+       ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); \
+       if (ret != 0) \
+               fprintf(stderr, "Unable to set cancelate state: %s\n", strerror(ret)); \
+} while (0)
+
+#define CANCEL_SECTION_END() do { \
+       int ret; \
+       ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); \
+       if (ret != 0) \
+               fprintf(stderr, "Unable to set cancelate state: %s\n", strerror(ret)); \
+} while (0)
+
+#define CLOSE_PIPE(p)  do { \
+       int status; \
+       status = close(p[PIPE_READ]); \
+       if (status < 0) \
+               fprintf(stderr, "close: %s\n", strerror(errno)); \
+       status = close(p[PIPE_WRITE]); \
+       if (status < 0) \
+               fprintf(stderr, "close: %s\n", strerror(errno)); \
+} while (0)
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+#define PIPE_MAX 2
+
+/* End of a file */