Add the support of glib event loop 77/260177/5
authorCheoleun Moon <chleun.moon@samsung.com>
Mon, 21 Jun 2021 11:59:23 +0000 (20:59 +0900)
committerCheoleun Moon <chleun.moon@samsung.com>
Tue, 22 Jun 2021 02:33:36 +0000 (11:33 +0900)
Change-Id: I692b5a8edba1ebe751283044ea5461da53e38fa3
Signed-off-by: Cheoleun Moon <chleun.moon@samsung.com>
CMakeLists.txt
packaging/capi-network-vine.spec
src/include/vine-event-loop.h
src/vine-event-loop-epoll.cpp
src/vine-event-loop-glib.cpp [new file with mode: 0755]
src/vine-event-loop.cpp
tests/vine-test/CMakeLists.txt
tests/vine-test/vine-test-glib.cpp [new file with mode: 0755]

index de923f86d39cccc04ba0bd2121413b81a2590fb6..964c045899abd5631e47e354098ca9936fd0bfbc 100755 (executable)
@@ -27,6 +27,7 @@ SET(VINE_VERSION_PATCH "0")
 SET(VINE_VERSION ${VINE_VERSION_MAJOR}.${VINE_VERSION_MINOR}.${VINE_VERSION_PATCH})
 
 OPTION(USE_EVENT_LOOP_EPOLL "Use epoll event loop" ON)
+OPTION(USE_EVENT_LOOP_EXTERNAL_GLIB "Use glib event loop" OFF)
 OPTION(USE_LIBWEBSOCKETS "Use libwebsockets for data path" ON)
 OPTION(USE_LIBWEBSOCKETS_STATIC "Use libwebsockets static library" ON)
 OPTION(USE_LIBWEBSOCKETS_STATIC_PREBUILT "Use prebuilt static library(.a)" ON)
@@ -48,6 +49,16 @@ IF(TIZEN_OS)
     STRING(CONCAT DEPS ${DEPS} "dlog capi-base-common capi-system-info")
 ENDIF(TIZEN_OS)
 
+IF(USE_EVENT_LOOP_EPOLL)
+       ADD_DEFINITIONS("-DVINE_EVENT_LOOP_EPOLL")
+ENDIF(USE_EVENT_LOOP_EPOLL)
+
+IF(USE_EVENT_LOOP_EXTERNAL_GLIB)
+       REMOVE_DEFINITIONS("-DVINE_EVENT_LOOP_EPOLL")
+       ADD_DEFINITIONS("-DVINE_EVENT_LOOP_EXTERNAL_GLIB")
+       STRING(CONCAT DEPS ${DEPS} " glib-2.0 gio-2.0")
+ENDIF(USE_EVENT_LOOP_EXTERNAL_GLIB)
+
 pkg_check_modules(fw_name_deps REQUIRED ${DEPS})
 
 IF(NOT ANDROID)
@@ -106,10 +117,6 @@ IF(USE_LIBWEBSOCKETS)
        ADD_SUBDIRECTORY(plugins/libwebsockets)
 ENDIF(USE_LIBWEBSOCKETS)
 
-iF(USE_EVENT_LOOP_EPOLL)
-       ADD_DEFINITIONS("-DVINE_EVENT_LOOP_EPOLL")
-ENDIF(USE_EVENT_LOOP_EPOLL)
-
 ADD_SUBDIRECTORY(plugins/dns-sd)
 
 ADD_SUBDIRECTORY(include)
index 47913b513d2db2eff38a2bdde95dd10e0d04e102..bedbdf8507c51ad438a7eff3c54b62156c5177c9 100755 (executable)
@@ -1,8 +1,9 @@
 %bcond_without lws_static
 %bcond_without lws_static_prebuilt
+%bcond_without use_glib_event_loop
 Name:    capi-network-vine
 Summary: An service discovery framework
-Version: 1.0.7
+Version: 1.0.8
 Release: 0
 Group:   Network & Connectivity/API
 License: Apache-2.0
@@ -24,6 +25,10 @@ BuildRequires: pkgconfig(capi-system-info)
 BuildRequires: pkgconfig(dlog)
 %endif
 
+%if %{with use_glib_event_loop}
+BuildRequires: pkgconfig(glib-2.0)
+%endif
+
 BuildRequires: pkgconfig(gtest)
 
 %if 0%{?gcov:1}
@@ -85,6 +90,13 @@ export LDFLAGS+=" -lgcov"
         -DLIB_DIR:PATH=%{_libdir} \
         -DBIN_DIR:PATH=%{_bindir} \
         -DINCLUDE_DIR:PATH=%{_includedir} \
+%if %{with use_glib_event_loop}
+        -DUSE_EVENT_LOOP_EXTERNAL_GLIB=ON \
+        -DUSE_EVENT_LOOP_EPOLL=OFF \
+%else
+        -DUSE_EVENT_LOOP_EXTERNAL_GLIB=OF \
+        -DUSE_EVENT_LOOP_EPOLL=ON \
+%endif
 %if %{with lws_static}
         -DUSE_LIBWEBSOCKETS_STATIC=ON \
 %else
index 012c8eaf438964c89537635179426d2341344c32..f2a09d9bf30cce83e63b92bfdc05bbba8fafeb4b 100755 (executable)
@@ -74,6 +74,6 @@ typedef struct {
                void *user_data);
 } vine_event_loop_fn;
 
-#define MAX_VINE_EPOLL_EVENTS 512
+#define MAX_VINE_FDS 512
 
 #endif /* __VINE_EVENT_LOOP_H__ */
index c918a998b9daefe074a03e04f23e93ed168973b4..c3beefdcbf1488f276b350da563b730c0d1f79e1 100755 (executable)
@@ -53,7 +53,7 @@ typedef struct {
 #ifdef FD_SETSIZE
 #define MAX_IO_EVENT_HANDLERS FD_SETSIZE
 #else
-#define MAX_IO_EVENT_HANDLERS MAX_VINE_EPOLL_EVENTS
+#define MAX_IO_EVENT_HANDLERS MAX_VINE_FDS
 #endif
 
 static vine_epoll_io_event_handler *io_event_handlers[MAX_IO_EVENT_HANDLERS] = {0, };
@@ -62,11 +62,11 @@ static void *__vine_event_loop_epoll_run(void *arg)
 {
        VINE_LOGD("Run Vine event loop");
 
-       struct epoll_event events[MAX_VINE_EPOLL_EVENTS];
+       struct epoll_event events[MAX_VINE_FDS];
        int timeout = -1;
 
        do {
-               int n = epoll_wait(__vine_epoll_fd, events,     MAX_VINE_EPOLL_EVENTS, timeout);
+               int n = epoll_wait(__vine_epoll_fd, events,     MAX_VINE_FDS, timeout);
                if (n == -1) {
                        VINE_LOGE("Invalid return value");
                        break;
diff --git a/src/vine-event-loop-glib.cpp b/src/vine-event-loop-glib.cpp
new file mode 100755 (executable)
index 0000000..4f252f9
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+*/
+
+#if defined VINE_EVENT_LOOP_EXTERNAL_GLIB
+#include <glib.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "vine.h"
+#include "vine-event-loop.h"
+#include "vine-queue.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+typedef struct {
+       int fd;
+       vine_poll_handler handler;
+       void *user_data;
+       guint g_source_id;
+} vine_glib_io_event_handler;
+
+#define MAX_IO_EVENT_HANDLERS FD_SETSIZE
+static vine_glib_io_event_handler *io_event_handlers[MAX_IO_EVENT_HANDLERS] = {0, };
+
+#ifdef FD_SETSIZE
+#define MAX_IO_EVENT_HANDLERS FD_SETSIZE
+#else
+#define MAX_IO_EVENT_HANDLERS MAX_VINE_FDS
+#endif
+
+int vine_event_loop_glib_init()
+{
+       return VINE_ERROR_NONE;
+}
+
+void vine_event_loop_glib_deinit()
+{
+}
+
+int vine_event_loop_glib_start()
+{
+       return VINE_ERROR_NONE;
+}
+
+void vine_event_loop_glib_stop()
+{
+}
+
+int vine_event_queue_glib_create(vine_event_queue_h *event_queue)
+{
+       return VINE_ERROR_NONE;
+}
+
+void vine_event_queue_glib_destroy(vine_event_queue_h event_queue)
+{
+}
+
+void vine_event_loop_glib_get_eventfd(vine_event_queue_h event_queue, int *eventfd)
+{
+}
+
+static void _add_io_event_handler(int fd, vine_glib_io_event_handler *h)
+{
+       if (io_event_handlers[fd])
+               free(io_event_handlers[fd]);
+       io_event_handlers[fd] = h;
+}
+
+static void _del_io_event_handler(int fd)
+{
+       if (io_event_handlers[fd]) {
+               free(io_event_handlers[fd]);
+               io_event_handlers[fd] = NULL;
+       }
+}
+
+static vine_glib_io_event_handler *_find_io_event_handler(int fd)
+{
+       return io_event_handlers[fd];
+}
+
+static int convert_glib_cond_to_poll_events(GIOCondition condition)
+{
+       int events = 0;
+       if (condition & G_IO_IN)
+               events |= VINE_POLLIN;
+       if (condition & G_IO_OUT)
+               events |= VINE_POLLOUT;
+       if (condition & G_IO_HUP)
+               events |= VINE_POLLHUP;
+       return events;
+}
+
+static GIOCondition convert_poll_events_to_glib_cond(int events)
+{
+       int cond = 0;
+       if (events & VINE_POLLIN)
+               cond |= G_IO_IN;
+       if (events & VINE_POLLOUT)
+               cond |= G_IO_OUT;
+       if (events & VINE_POLLHUP)
+               cond |= G_IO_HUP;
+       return (GIOCondition)cond;
+}
+
+static gboolean __vine_glib_io_handler(GIOChannel *source,
+            GIOCondition condition,
+            gpointer data)
+{
+       RET_VAL_IF(data == NULL, TRUE, "vine_glib_io_event_handler is NULL");
+       vine_glib_io_event_handler *h = (vine_glib_io_event_handler *)data;
+       if (h->handler)
+               h->handler(h->fd, convert_glib_cond_to_poll_events(condition), h->user_data);
+       return TRUE;
+}
+
+int vine_event_loop_glib_add_io_handler(
+       int fd, int events, vine_poll_handler handler, void *user_data)
+{
+       RET_VAL_IF(fd < 0, VINE_ERROR_INVALID_PARAMETER,
+               "fd should be equal to or greater than zero");
+       RET_VAL_IF(handler == NULL, VINE_ERROR_INVALID_PARAMETER,
+               "handler should not be null");
+
+       vine_glib_io_event_handler *h =
+               (vine_glib_io_event_handler *)calloc(1, sizeof(vine_glib_io_event_handler));
+       RET_VAL_IF(h == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+       h->fd = fd;
+       h->handler = handler;
+       h->user_data = user_data;
+
+       GIOChannel *ch = g_io_channel_unix_new(fd);
+       g_io_channel_set_flags(ch, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_channel_set_close_on_unref(ch, FALSE);
+       h->g_source_id  = g_io_add_watch(ch,
+               convert_poll_events_to_glib_cond(events), __vine_glib_io_handler, h);
+
+       _add_io_event_handler(fd, h);
+       return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_glib_del_io_handler(int fd)
+{
+       RET_VAL_IF(fd < 0, VINE_ERROR_INVALID_PARAMETER,
+               "fd should be equal to or greater than zero");
+
+       vine_glib_io_event_handler *handler = _find_io_event_handler(fd);
+       if (handler == NULL) {
+               VINE_LOGE("vine_glib_io_event_handler is NULL for fd %d", fd);
+               return VINE_ERROR_INVALID_PARAMETER;
+       }
+       g_source_remove(handler->g_source_id);
+       _del_io_event_handler(fd);
+       VINE_LOGD("Del an glib event. fd: %d", fd);
+
+       return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_glib_mod_io_handler(
+       int fd, int events, vine_poll_handler handler, void *user_data)
+{
+       RET_VAL_IF(fd < 0, VINE_ERROR_INVALID_PARAMETER,
+               "fd should be equal to or greater than zero");
+       RET_VAL_IF(handler == NULL, VINE_ERROR_INVALID_PARAMETER,
+               "handler should not be null");
+
+       vine_event_loop_glib_del_io_handler(fd);
+       vine_event_loop_glib_add_io_handler(fd, events, handler, user_data);
+       VINE_LOGD("Modify an glib event. fd: %d event: %d", fd, events);
+
+       return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_glib_add_event(vine_event_queue_h event_queue, void *event_data,
+       vine_event_handler handler, vine_event_free_handler free_func,
+       void *user_data)
+{
+       // Ignore event_queue
+
+       RET_VAL_IF(handler == NULL, VINE_ERROR_INVALID_PARAMETER, "handler is NULL");
+       VINE_LOGD("event_data[%p]", event_data);
+
+       handler(event_data, user_data);
+       if (free_func)
+               free_func(event_data);
+
+       return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_glib_process(vine_event_queue_h event_queue)
+{
+       return VINE_ERROR_NONE;
+}
+
+vine_event_loop_fn vine_event_loop_glib = {
+       .init = vine_event_loop_glib_init,
+       .deinit = vine_event_loop_glib_deinit,
+       .start = vine_event_loop_glib_start,
+       .stop = vine_event_loop_glib_stop,
+       .event_queue_create = vine_event_queue_glib_create,
+       .event_queue_destroy = vine_event_queue_glib_destroy,
+       .get_eventfd = vine_event_loop_glib_get_eventfd,
+       .process = vine_event_loop_glib_process,
+       .add_io_handler = vine_event_loop_glib_add_io_handler,
+       .mod_io_handler = vine_event_loop_glib_mod_io_handler,
+       .del_io_handler = vine_event_loop_glib_del_io_handler,
+       .add_event = vine_event_loop_glib_add_event,
+};
+
+#endif // VINE_EVENT_LOOP_EXTERNAL_GLIB
\ No newline at end of file
index ba37ac3862e003ed8b35ba5a537cae7443e406c3..e31be407fc1c68bbfc72c72bf418a51396deb029 100755 (executable)
@@ -28,13 +28,16 @@ static vine_event_loop_fn __event_loop;
 #if defined VINE_EVENT_LOOP_EPOLL
 extern vine_event_loop_fn vine_event_loop_epoll;
 #endif
-#if defined VINE_EVENT_LOOP_GLIB
+#if defined VINE_EVENT_LOOP_EXTERNAL_GLIB
+extern vine_event_loop_fn vine_event_loop_glib;
 #endif
 
 int vine_event_loop_init()
 {
 #if defined VINE_EVENT_LOOP_EPOLL
        __event_loop = vine_event_loop_epoll;
+#elif defined VINE_EVENT_LOOP_EXTERNAL_GLIB
+       __event_loop = vine_event_loop_glib;
 #endif
 
        return __event_loop.init();
index b8f095347cbada642f8f5216f6135d987dd8b51c..939810308655e93cc397d64994614adb2c202ef2 100755 (executable)
@@ -31,6 +31,13 @@ LIST(REMOVE_ITEM VINE_TEST_SRCS ${VINE_TEST_UTILS_SRC})
 
 foreach(file ${VINE_TEST_SRCS})
        GET_FILENAME_COMPONENT(name ${file} NAME_WE)
+       IF(NOT USE_EVENT_LOOP_EXTERNAL_GLIB)
+               STRING(FIND ${name} "glib" output)
+               IF(NOT ${output} EQUAL -1)
+                       MESSAGE("${name} will not be added")
+                       CONTINUE()
+               ENDIF()
+       ENDIF()
        ADD_EXECUTABLE(${name} ${file} ${VINE_TEST_UTILS_SRC})
        TARGET_LINK_LIBRARIES(${name}
                ${TARGET_VINE}
diff --git a/tests/vine-test/vine-test-glib.cpp b/tests/vine-test/vine-test-glib.cpp
new file mode 100755 (executable)
index 0000000..36aade2
--- /dev/null
@@ -0,0 +1,830 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <glib.h>
+#include <list>
+#include <iterator>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <time.h>
+#include <vine.h>
+#include <vine-log.h>
+
+#include "vine-test-utils.h"
+
+#define MAX_EVENTS 16
+#define TEST_DEBUG
+
+#define CHECK_SESSION \
+       do { \
+               if (!g_session) { \
+                       _test_print_error("No Session"); \
+                       return; \
+               } \
+       } while (0)
+
+#define MAX_SERVICE_TYPE_LEN 16
+#define MAX_SERVICE_NAME_LEN 63
+#define MAX_IP_LEN 39 // for IPv6
+
+#define ROOT_CA_PATH "certs/rootCA/ca.pem"
+#define SERVER_CERT_PATH "certs/server/test-server.pem"
+#define SERVER_PRIVATE_KEY_PATH "certs/server/test-server.key"
+
+GMainLoop *main_loop = NULL;
+static vine_session_h g_session = NULL;
+static vine_service_h g_service = NULL;
+static vine_dp_h g_server_dp = NULL;
+static vine_dp_h g_client_dp = NULL;
+static vine_dp_h g_pubsub_dp = NULL;
+static std::list<vine_dp_h> g_datapath_list;
+static std::list<vine_service_h> g_service_list;
+
+static bool test_get_user_string(const char *msg, char *buf, int buf_size)
+{
+       if (msg == NULL || buf == NULL || buf_size < 2)
+               return false;
+
+       int ret;
+       printf("%s", msg);
+       fflush(stdout);
+       memset(buf, 0, buf_size);
+       ret = read(0, buf, buf_size - 1);
+
+       if (ret < 0 || buf[0] == '\0' || buf[0] == '\n' || buf[0] == '\r') {
+               buf[0] = '\0';
+               return false;
+       }
+
+       buf[ret - 1] = '\0';
+       return true;
+}
+
+static void __quit()
+{
+       PRINT_IF_ERROR(vine_deinitialize(), "vine_initialize()");
+       g_main_loop_quit(main_loop);
+       exit(1);
+}
+
+static void __event_handler(vine_session_h session)
+{
+       printf("Vine Event\n");
+       PRINT_IF_ERROR(vine_session_process_event(session), "vine_process_event");
+}
+
+static void __start_event_loop()
+{
+}
+
+static void __init()
+{
+       PRINT_IF_ERROR(vine_initialize(), "vine_initialize()");
+}
+
+static void __registered_cb(vine_session_h session,
+               const char *service_name, vine_error_e error, void *user_data)
+{
+       if (error == VINE_ERROR_NONE) {
+               PRINT_RESULT(error, "Successfully registered");
+               printf("Service Name: %s\n", service_name);
+       } else if (error == VINE_ERROR_NAME_CONFLICT) {
+               PRINT_RESULT(error, "Successfully registered, but service name conflicted");
+               printf("Service Name: %s\n", service_name);
+       } else {
+               PRINT_IF_ERROR(error, "Failed to register a service");
+       }
+}
+
+static bool __print_attr(const char *key, const char *value, void *user_data)
+{
+       printf("\t   > Attributes: %s=%s\n", key, value);
+       return true;
+}
+
+static void __ip_resolved_cb(vine_session_h session, vine_service_h service,
+       const char *ip, vine_address_family_e address_family, /* availability */
+       void *user_data)
+{
+       printf("\t++ IP resolved\n");
+       int ret;
+       char *service_type;
+       char *service_name;
+       int port;
+
+       ret = vine_service_get_type(service, &service_type);
+       PRINT_IF_ERROR(ret, "vine_service_get_type");
+       ret = vine_service_get_name(service, &service_name);
+       PRINT_IF_ERROR(ret, "vine_service_get_name");
+       ret = vine_service_get_port(service, &port);
+       PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+       printf("\t   > Service Type: %s\n", service_type);
+       printf("\t   > Service Name: %s\n", service_name);
+       printf("\t   > IP Address: %s\n", ip);
+       printf("\t   > Port: %d\n", port);
+       ret = vine_service_foreach_attribute(service, __print_attr, NULL);
+       PRINT_IF_ERROR(ret, "vine_service_foreach_attribute");
+       printf("\n");
+       fflush(stdout);
+
+       free(service_type);
+       free(service_name);
+}
+
+static void __discovered_cb(vine_session_h session, vine_service_h service,
+               vine_service_state_e state, void *user_data)
+{
+       printf("\t++ Service Discovered\n");
+
+       int ret;
+       char *service_type;
+       char *service_name;
+       int port;
+
+       ret = vine_service_get_type(service, &service_type);
+       PRINT_IF_ERROR(ret, "vine_service_get_type");
+       ret = vine_service_get_name(service, &service_name);
+       PRINT_IF_ERROR(ret, "vine_service_get_name");
+       ret = vine_service_get_port(service, &port);
+       PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+       printf("\t   > State: %s\n",
+                       state == VINE_SERVICE_UNAVAILABLE ? "Unavailable" : "Available");
+       printf("\t   > Service Type: %s\n", service_type);
+       printf("\t   > Service Name: %s\n", service_name);
+       printf("\t   > Port: %d\n", port);
+       ret = vine_service_foreach_attribute(service, __print_attr, NULL);
+       PRINT_IF_ERROR(ret, "vine_service_foreach_attribute");
+       printf("\n");
+       fflush(stdout);
+
+       vine_service_h s;
+       vine_service_clone(service, &s);
+       g_service_list.push_back(s);
+       free(service_type);
+       free(service_name);
+}
+
+static void __print_received_data(unsigned char *buf, size_t len)
+{
+       for (int i = 0; i < (int)len; i++)
+               printf("%c", buf[i]);
+       printf("\n");
+}
+
+static void __received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+       unsigned char buf[20] = {0, };
+       size_t bytes = 0;
+
+       printf(COLOR_GRN "[RECV_CB] %p received %zd bytes." COLOR_RESET "\n", dp, received_len);
+       do {
+               vine_dp_recv(dp, buf, 20, &bytes);
+               __print_received_data(buf, bytes);
+               received_len -= bytes;
+       } while (received_len > 0);
+}
+
+static void __terminated_cb(vine_dp_h dp, void *user_data)
+{
+       printf(COLOR_GRN "[TERMINATED_CB] %p is terminated." COLOR_RESET "\n", dp);
+       g_datapath_list.remove(dp);
+       vine_dp_destroy(dp);
+}
+
+static void __add_new_dp(vine_dp_h dp)
+{
+       g_datapath_list.push_back(dp);
+       vine_dp_set_received_cb(dp, __received_cb, NULL);
+       vine_dp_set_terminated_cb(dp, __terminated_cb, NULL);
+}
+
+static void __opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+       int port;
+
+       vine_dp_get_port(dp, &port);
+       printf(COLOR_GRN "[OPENED_CB] %p is %s. port[%d]" COLOR_RESET "\n",
+                       dp, (result == VINE_ERROR_NONE) ? "opened" : "not opened", port);
+       if (dp == g_client_dp || dp == g_pubsub_dp)
+               __add_new_dp(dp);
+}
+
+static void __set_callbacks()
+{
+       CHECK_SESSION;
+       PRINT_IF_ERROR(vine_session_set_registered_cb(g_session, __registered_cb, NULL),
+                       "vine_session_set_registered_cb");
+       PRINT_IF_ERROR(vine_session_set_discovered_cb(g_session, __discovered_cb, NULL),
+                       "vine_session_set_discovered_cb");
+}
+
+static void __create_session()
+{
+       int ret;
+       ret = vine_session_create(&g_session);
+       if (ret != VINE_ERROR_NONE) {
+               PRINT_IF_ERROR(ret,  "vine_session_create");
+               return;
+       }
+
+       __set_callbacks();
+       printf("Session Created\n");
+}
+
+static void __destroy_session()
+{
+       CHECK_SESSION;
+       PRINT_IF_ERROR(vine_session_destroy(g_session),
+                       "vine_session_destroy");
+}
+
+static void __set_service_type()
+{
+       CHECK_SESSION;
+       char type[64];
+       printf(" >> Service Type: ");
+       if (scanf(" %63s", type) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+       PRINT_RESULT(vine_service_set_type(g_service, type),
+                       "vine_service_set_type");
+}
+
+static void __set_service_name()
+{
+       CHECK_SESSION;
+       char name[64];
+       printf(" >> Service Name (Max length 64): ");
+       if (scanf(" %63s", name) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+       PRINT_RESULT(vine_service_set_name(g_service, name),
+                       "vine_service_set_name");
+}
+
+static void __set_port()
+{
+       CHECK_SESSION;
+       int port;
+       printf(" >> Port Number (0 ~ 65535): ");
+       if (scanf(" %d", &port) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+       PRINT_RESULT(vine_service_set_port(g_service, port), "vine_session_set_port");
+}
+
+static void __add_attribute()
+{
+       CHECK_SESSION;
+
+       while (true) {
+               char key[10];
+               char value[256] = {0, };
+               if (!test_get_user_string(" >> Key (Max length 9. Enter if no attribute): ", key, 10))
+                       break;
+               test_get_user_string(" >> Value (Enter if empty): ", value, 256);
+               PRINT_RESULT(vine_service_add_attribute(g_service, key, value),
+                               "vine_service_add_attribute");
+       }
+}
+
+static void __create_service()
+{
+       int ret;
+       ret = vine_service_create(&g_service);
+       if (ret != VINE_ERROR_NONE) {
+               PRINT_IF_ERROR(ret,  "vine_service_create");
+               return;
+       }
+
+       __set_service_type();
+       __set_service_name();
+       __set_port();
+       __add_attribute();
+
+       printf("Service Created\n");
+}
+
+static void __destroy_service()
+{
+       CHECK_SESSION;
+       PRINT_IF_ERROR(vine_service_destroy(g_service), "vine_service_destroy");
+}
+
+static void __register()
+{
+       CHECK_SESSION;
+       PRINT_IF_ERROR(vine_session_register(g_session, g_service, NULL), "vine_session_register");
+       printf("Registered\n");
+}
+
+static void __unregister()
+{
+       CHECK_SESSION;
+       PRINT_IF_ERROR(vine_session_unregister(g_session), "vine_session_unregister");
+       printf("Unregistered\n");
+}
+
+static void __start_discovery()
+{
+       CHECK_SESSION;
+       char type[20];
+       printf(" >> Service Type: ");
+       if (scanf(" %19s", type) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       PRINT_IF_ERROR(vine_session_start_discovery(g_session, type, NULL),
+               "vine_session_start_discovery");
+       printf("Discovery started\n");
+}
+
+static void __stop_discovery()
+{
+       CHECK_SESSION;
+       PRINT_IF_ERROR(vine_session_stop_discovery(g_session), "vine_session_stop_discovery");
+       printf("Discovery stopped\n");
+}
+
+static void __print_service_list()
+{
+       int idx = -1;
+       int ret = VINE_ERROR_NONE;
+       for (const auto &service : g_service_list) {
+               char *service_type;
+               char *service_name;
+               int port;
+
+               ret = vine_service_get_type(service, &service_type);
+               PRINT_IF_ERROR(ret, "vine_service_get_type");
+               ret = vine_service_get_name(service, &service_name);
+               PRINT_IF_ERROR(ret, "vine_service_get_name");
+               ret = vine_service_get_port(service, &port);
+               PRINT_IF_ERROR(ret, "vine_service_get_port");
+               printf("\t %d: type[%s] name[%s] port[%d]\n", ++idx, service_type, service_name, port);
+               free(service_type);
+               free(service_name);
+       }
+}
+
+static vine_service_h __get_service()
+{
+       __print_service_list();
+
+       int idx;
+       printf(" >> Select service: \n");
+       if (scanf("%d", &idx) < 0)
+               return NULL;
+
+       auto it = g_service_list.begin();
+       std::advance(it, idx);
+
+       return (vine_service_h) *it;
+}
+
+static void __resolve_ip()
+{
+       CHECK_SESSION;
+       int ret = vine_session_set_ip_resolved_cb(g_session, __get_service(),
+                       VINE_ADDRESS_FAMILY_DEFAULT,__ip_resolved_cb, NULL);
+       PRINT_IF_ERROR(ret, "vine_session_set_ip_resolved_cb");
+}
+
+static void __stop_resolve_ip()
+{
+       CHECK_SESSION;
+       int ret = vine_session_unset_ip_resolved_cb(g_session, __get_service());
+       PRINT_IF_ERROR(ret, "vine_session_unset_ip_resolved_cb");
+}
+
+#ifdef TEST_DEBUG
+#define VINE_LOGGER_TIME_MESSAGE_LEN 17
+static void __get_current_time(char *buf)
+{
+       int hour, min, sec, day, month;
+       time_t now;
+       struct tm *local;
+
+       time(&now);
+       local = localtime(&now);
+
+       hour = local->tm_hour;
+       min = local->tm_min;
+       sec = local->tm_sec;
+       day = local->tm_mday;
+       month = local->tm_mon + 1;
+
+       snprintf(buf, VINE_LOGGER_TIME_MESSAGE_LEN,
+                       "[%02u-%02u %02u:%02u:%02u]", month, day, hour, min, sec);
+}
+
+void __logger(int log_level, const char *log)
+{
+       char timestamp[VINE_LOGGER_TIME_MESSAGE_LEN];
+       __get_current_time(timestamp);
+       printf("%s[T%5u] ", timestamp, (unsigned int)syscall(__NR_gettid));
+
+       switch (log_level) {
+       case VINE_LOG_DEBUG:
+               printf("D/");
+               break;
+       case VINE_LOG_INFO:
+               printf("I/");
+               break;
+       case VINE_LOG_ERROR:
+               printf(COLOR_RED "E/");
+               break;
+       }
+       printf("%s" COLOR_RESET "\n", log);
+}
+#endif
+
+static void __debug_on_off()
+{
+       static int __debug_on = 0;
+       __debug_on = 1 - __debug_on;
+       printf("Debug %s\n", __debug_on ? "ON" : "OFF");
+#ifdef TEST_DEBUG
+       if (__debug_on) {
+               vine_set_logger(__logger);
+               vine_set_log_level(VINE_LOG_DEBUG | VINE_LOG_INFO | VINE_LOG_ERROR);
+       } else {
+               vine_set_logger(NULL);
+       }
+#endif
+}
+
+static vine_security_h __create_security_handle(int type, bool is_server)
+{
+       vine_security_h s = NULL;
+
+       if (type == VINE_SECURITY_TYPE_NONE)
+               return NULL;
+       if (vine_security_create(&s) != VINE_ERROR_NONE)
+               return NULL;
+
+       if (vine_security_set_type(s, (vine_security_type_e)type) != VINE_ERROR_NONE) {
+               vine_security_destroy(s);
+               return NULL;
+       }
+
+       if (type >= VINE_SECURITY_TYPE_TLS) {
+               vine_security_set_ca_path(s, ROOT_CA_PATH);
+               vine_security_set_cert_path(s, SERVER_CERT_PATH);
+               vine_security_set_verification_flags(s, VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED | VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK);
+               vine_security_set_tls_version(s, VINE_SECURITY_TLS_VERSION_1_3);
+               if (is_server)
+                       vine_security_set_private_key(s, SERVER_PRIVATE_KEY_PATH);
+       }
+
+       if (type == VINE_SECURITY_TYPE_PSK_OVER_TLS) {
+               char psk[256] = {0, };
+               while (!test_get_user_string(" >> PSK (Max length 256): ", psk, 256)) {
+               }
+               vine_security_set_psk(s, psk);
+       }
+
+       return s;
+}
+
+static void __accepted_cb(vine_dp_h dp, vine_dp_h accepted_dp, void *user_data)
+{
+       printf(COLOR_GRN "[ACCEPTED_CB] %p is accepted." COLOR_RESET "\n", accepted_dp);
+       vine_address_family_e addr_family;
+       char *ip;
+       int ret = vine_dp_get_remote_ip(accepted_dp, &addr_family, &ip);
+       PRINT_IF_ERROR(ret, "vine_dp_get_remote_ip");
+       printf(COLOR_GRN "  Client IP[%s] Family[%d]" COLOR_RESET "\n", ip,  (int)addr_family);
+       free(ip);
+       __add_new_dp(accepted_dp);
+}
+
+static void __open_server()
+{
+       CHECK_SESSION;
+       int addr_family = 0;
+       int port = 0;
+       int security_type = 0;
+
+       printf(" >> Address type(0-default, 1-IPv4, 2-IPv6): ");
+       if (scanf(" %d", &addr_family) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       printf(" >> listen port: ");
+       if (scanf(" %d", &port) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       printf("Set security type(0.NONE, 1.TLS, 2.PSK): ");
+       if (scanf("%d", &security_type) < 0) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       vine_dp_create(g_session, VINE_DP_TYPE_SERVER, &g_server_dp);
+       vine_dp_set_accepted_cb(g_server_dp, __accepted_cb, NULL);
+       vine_dp_set_terminated_cb(g_server_dp, __terminated_cb, NULL);
+       vine_dp_set_address_family(g_server_dp, (vine_address_family_e)addr_family);
+       vine_dp_set_port(g_server_dp, port);
+       vine_security_h security = __create_security_handle(security_type, true);
+       vine_dp_set_security(g_server_dp, security);
+       vine_security_destroy(security);
+
+       PRINT_RESULT(vine_dp_open(g_server_dp, __opened_cb, NULL), "vine_dp_open");
+}
+
+static void __connect_server()
+{
+       CHECK_SESSION;
+       int addr_type;
+       char ip[40] = {0, };
+       int port = 0;
+       int security_type = 0;
+
+       printf(" >> Address Family(0-IPv4, 1-IPv6): ");
+       if (scanf(" %d", &addr_type) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+       printf(" >> Peer IP: ");
+       if (scanf(" %39s", ip) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       printf(" >> Peer port: ");
+       if (scanf(" %d", &port) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       printf("Set security type(0.NONE, 1.TLS, 2.PSK): ");
+       if (scanf("%d", &security_type) < 0) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       vine_dp_create(g_session, VINE_DP_TYPE_CLIENT, &g_client_dp);
+       vine_dp_set_remote_ip(g_client_dp,
+                       addr_type ? VINE_ADDRESS_FAMILY_IPV6 : VINE_ADDRESS_FAMILY_IPV4, ip);
+       PRINT_RESULT(vine_dp_set_remote_port(g_client_dp, port), "vine_dp_set_remote_port");
+       vine_security_h security = __create_security_handle(security_type, true);
+       vine_dp_set_security(g_client_dp, security);
+       vine_security_destroy(security);
+       PRINT_RESULT(vine_dp_open(g_client_dp, __opened_cb, NULL), "vine_dp_open");
+}
+
+static void __join_service()
+{
+       CHECK_SESSION;
+       int port = 0;
+       int addr_family = 0;
+       char topic[64];
+
+       printf(" >> Address type(0-default, 1-IPv4, 2-IPv6): ");
+       if (scanf(" %d", &addr_family) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       printf(" >> Listen port: ");
+       if (scanf(" %d", &port) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       printf(" >> Set topic: ");
+       if (scanf(" %63s", topic) < 1) {
+               _test_print_error("Scan failed");
+               return;
+       }
+
+       vine_dp_create(g_session, VINE_DP_TYPE_PUBSUB, &g_pubsub_dp);
+       vine_dp_set_address_family(g_pubsub_dp, (vine_address_family_e)addr_family);
+       vine_dp_set_port(g_pubsub_dp, port);
+       vine_dp_set_topic(g_pubsub_dp, topic);
+       // TODO: set security.
+
+       vine_dp_set_terminated_cb(g_pubsub_dp, __terminated_cb, NULL);
+       PRINT_RESULT(vine_dp_open(g_pubsub_dp, __opened_cb, NULL), "vine_dp_open");
+}
+
+void __print_datapath_list()
+{
+       int idx = -1;
+       for (const auto &dp : g_datapath_list)
+               printf("\t %d: datapath[%p]\n", ++idx, dp);
+}
+
+static void __send_data()
+{
+       CHECK_SESSION;
+
+       const char *test_str[3] = {
+               "12345678910",
+               "Hello! This is a test string.",
+               "Tizen is an open and flexible operating system" \
+               "built from the ground up to address the needs of all stakeholders of the mobile"\
+               "and connected device ecosystem, including device manufacturers, mobile operators,"\
+               "application developers and independent software vendors (ISVs)."\
+               "Tizen is developed by a community of developers, under open source governance,"\
+               "and is open to all members who wish to participate."
+       };
+
+       __print_datapath_list();
+
+       int idx;
+       printf(" >> Select peer: \n");
+       if (scanf("%d", &idx) < 0)
+               return;
+
+       auto it = g_datapath_list.begin();
+       std::advance(it, idx);
+       vine_dp_h dp = *it;
+
+       for (int i = 0; i < 3; i++) {
+               int ret = vine_dp_send(dp,
+                       (unsigned char *)test_str[i], strlen(test_str[i]));
+               PRINT_IF_ERROR(ret, "vine_data_path_write");
+       }
+}
+
+static void __close_data_path()
+{
+       __print_datapath_list();
+
+       int idx;
+       printf(" >> Select peer: \n");
+       if (scanf("%d", &idx) < 0)
+               return;
+
+       auto it = g_datapath_list.begin();
+       std::advance(it, idx);
+       vine_dp_h dp = *it;
+
+       PRINT_RESULT(vine_dp_close(dp), "vine_dp_close");
+       g_datapath_list.remove(dp);
+       PRINT_RESULT(vine_dp_destroy(dp), "vine_dp_destroy");
+}
+
+enum {
+       CMD_QUIT = 0,
+       CMD_CREATE_SESSION,
+       CMD_DESTROY_SESSION,
+       CMD_CREATE_SERVICE,
+       CMD_DESTROY_SERVICE,
+       CMD_REGISTER,
+       CMD_UNREGISTER,
+       CMD_START_DISCOVERY,
+       CMD_STOP_DISCOVERY,
+       CMD_RESOLVE_IP,
+       CMD_STOP_RESOLVING_IP,
+       CMD_OPEN_SERVER,
+       CMD_CONNECT_SERVER,
+       CMD_JOIN_SERVICE,
+       CMD_SEND_DATA,
+       CMD_CLOSE_DATA_PATH,
+       CMD_DEBUG,
+       CMD_INVALID,
+};
+
+static struct {
+       const char *str;
+       void (*func)(void);
+} __menus[] = {
+       [CMD_QUIT] = {"quit", __quit},
+       [CMD_CREATE_SESSION] = {"Create session", __create_session},
+       [CMD_DESTROY_SESSION] = {"Destroy session", __destroy_session},
+       [CMD_CREATE_SERVICE] = {"Create service", __create_service},
+       [CMD_DESTROY_SERVICE] = {"Destroy service", __destroy_service},
+       [CMD_REGISTER] = {"Register a service", __register},
+       [CMD_UNREGISTER] = {"Unregister a service", __unregister},
+       [CMD_START_DISCOVERY] = {"Start discovery", __start_discovery},
+       [CMD_STOP_DISCOVERY] = {"Stop discovery", __stop_discovery},
+       [CMD_RESOLVE_IP] = {"Resolve IP", __resolve_ip},
+       [CMD_STOP_RESOLVING_IP] = {"Stop resolveing IP", __stop_resolve_ip},
+       [CMD_OPEN_SERVER] = {"Open server", __open_server},
+       [CMD_CONNECT_SERVER] = {"Connect to server", __connect_server},
+       [CMD_JOIN_SERVICE] = {"Join a service", __join_service},
+       [CMD_SEND_DATA] = {"Send data to peer", __send_data},
+       [CMD_CLOSE_DATA_PATH] = {"Close datapath", __close_data_path},
+       [CMD_DEBUG] = {"Debug on/off", __debug_on_off},
+  {NULL, NULL},
+};
+
+static void __show_menus()
+{
+       printf("\n-- Vine Test\n");
+       for (int i = 0; __menus[i].str; ++i)
+               printf("%02d %s\n", i, __menus[i].str);
+       printf("> ");
+       fflush(stdout);
+}
+
+static inline int __is_digit(const char* str)
+{
+       int len;
+       int i;
+
+       if (str == NULL)
+               return -1;
+
+       if (strlen(str) == 0)
+               return -1;
+
+       len = strlen(str);
+       for (i = 0; i < len; i++)       {
+               if (str[i] < '0' || str[i] > '9')
+                       return -2;
+       }
+
+       return 0;
+}
+
+static void __process_input(const char *input)
+{
+       int cmd = -1;
+
+       cmd = strtol(input, NULL, 0);
+       if (__is_digit(input) < 0 || strlen(input) == 0 || errno == ERANGE || errno
+                       == EINVAL)
+               cmd = CMD_INVALID;
+
+       if (cmd >= CMD_INVALID || cmd < CMD_QUIT) {
+               printf("Invalid CMD\n");
+               return;
+       }
+       __menus[cmd].func();
+}
+
+static void __terminal_read_std_input()
+{
+       int fd = 0;
+
+       static char buf[1024];
+       int n;
+
+       errno = 0;
+       n = read(fd, buf, 1024);
+       if (n == 0) {
+               printf("Error: read() from stdin returns 0.\n");
+       } else if (n < 0) {
+               printf("input: read, err\n");
+       } else if (n - 1 > 0 && n < 1024) {
+               buf[n - 1] = '\0'; /* remove new line... */
+               printf("\n\n");
+               __process_input(buf);
+       } else {
+               if (buf[0] == '\n' || buf[0] == '\r')
+                       __show_menus();
+               else
+               printf("invalid input\n");
+       }
+}
+
+static gboolean __io_handler(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+       __terminal_read_std_input();
+       return TRUE;
+}
+
+int main()
+{
+       __init();
+       __show_menus();
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       GIOChannel *channel = g_io_channel_unix_new(0);
+       g_io_add_watch(channel, (GIOCondition)(G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL),
+                       __io_handler, NULL);
+       g_main_loop_run(main_loop);
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}