From f27c4d0e7a962c23662c7e1aeb61a9aaa8529a68 Mon Sep 17 00:00:00 2001 From: Jeonghoon Park Date: Tue, 15 May 2018 16:14:40 +0900 Subject: [PATCH] add app interface module Change-Id: Ia603c56c06b6fdd55fae492a8b29b441fc52cc0f --- daemon/include/ttd-app-interface.h | 25 ++ daemon/src/ttd-app-interface.c | 630 +++++++++++++++++++++++++++++++++++++ 2 files changed, 655 insertions(+) create mode 100644 daemon/include/ttd-app-interface.h create mode 100644 daemon/src/ttd-app-interface.c diff --git a/daemon/include/ttd-app-interface.h b/daemon/include/ttd-app-interface.h new file mode 100644 index 0000000..916592e --- /dev/null +++ b/daemon/include/ttd-app-interface.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef __TT_DAEMON_APP_INTERFACE_H__ +#define __TT_DAEMON_APP_INTERFACE_H__ + +typedef struct _ttd_app_inf_h *ttd_app_inf_h; + +int ttd_app_interface_init(ttd_app_inf_h *handle); +int ttd_app_interface_fini(ttd_app_inf_h handle); + +#endif /* __TT_DAEMON_APP_INTERFACE_H__ */ diff --git a/daemon/src/ttd-app-interface.c b/daemon/src/ttd-app-interface.c new file mode 100644 index 0000000..25f44d8 --- /dev/null +++ b/daemon/src/ttd-app-interface.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2018 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 +#include +#include +#include "ttd-log.h" +#include "ttd-app-interface.h" +#include "ttd-http.h" + +/* TODO : move this one to common module */ +#define TTD_APP_MSG_DELIMITER "|" +#define TTD_APP_INF_BUS_NAME "org.tizen.ttd.bus" +#define TTD_APP_INF_BUS_INF "org.tizen.ttd.bus.appinf" +#define TTD_APP_INF_OBJECT_PATH "/org/tizen/ttd/bus/appinf/object" + +#define POST_DATA_URL "http://apitest.showiot.xyz/api/data" +#define __POST_THREAD_RUN 1 +#define __POST_THREAD_STOP 0 +#define __QUEUE_TIME_OUT 500 + +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +typedef struct __thread_data_s { + char *name; + int disconnected; + GMainContext *context; + GMainLoop *loop; + GThread *thread; + GSocket *socket; + GSocketAddress *addr; + GAsyncQueue *data_queue; + void *main_data; +} thread_data; + +struct _ttd_app_inf_h { + guint gdbus_id; + GDBusNodeInfo *introspection_data; + GHashTable *app_sock_hash; + GThread *post_thread; + GAsyncQueue *data_queue; + int post_thread_run; +}; + +static char * +__get_addr_name(const gchar *token, const gchar *appID, guint appPID) +{ + /* TODO : move this one to common module and improve it*/ + return g_strdup_printf("%5u-%s", appPID, token); +} + +static gboolean +__on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer user_data) +{ + thread_data *data = user_data; + + _E("connection of thread[%s] is broken", data->name); + + data->disconnected = 1; + g_main_loop_quit(data->loop); + + return FALSE; +} + +static gboolean +__receive_msg(GIOChannel *ch, GIOCondition cond, gpointer user_data) +{ + thread_data *data = user_data; + GSocket *socket = data->socket; + GString *str = NULL; + char buf[1024] = {'\0', }; + char *msg = NULL; + gssize size = 0; + gboolean connected = FALSE; + + connected = g_socket_is_connected(socket); + if (!connected) { + _E("socket is disconnected"); + g_main_loop_quit(data->loop); + } + + while (0 < (size = g_socket_receive(socket, buf, 1024, NULL, NULL))) { + _D("size : %d", size); + if (!str) + str = g_string_new(NULL); + g_string_append_len(str, buf, size); + } + + if (str) + msg = g_string_free(str, FALSE); + + if (!msg) + _E("Empty message"); + else { + char **strv = NULL; + int i; + + strv = g_strsplit(msg, "|", -1); + for (i = 0; strv[i]; i++) { + if (strv[i][0] == '\0') { + g_free(strv[i]); + continue; + } else { + _D("strv[%d] : %s", i, strv[i]); + g_async_queue_push(data->data_queue, strv[i]); + } + } + } + g_free(msg); + + return TRUE; +} + +static guint +io_add_watch_with_context(GMainContext *context, + GIOChannel *ch, + GIOCondition condition, + GIOFunc func, + gpointer user_data) +{ + GSource *source = NULL; + guint id = 0; + + source = g_io_create_watch(ch, condition); + g_source_set_priority(source, G_PRIORITY_DEFAULT); + g_source_set_callback(source, (GSourceFunc)func, user_data, NULL); + id = g_source_attach(source, context); + g_source_unref(source); + + return id; +} + +static gboolean __thread_terminated(gpointer user_data) +{ + thread_data *data = user_data; + ttd_app_inf_h handle = (ttd_app_inf_h) data->main_data; + + retv_if(!data, FALSE); + retv_if(!handle, FALSE); + retv_if(!data->name, FALSE); + + _D("thread for [%s] is terminated", data->name); + + g_hash_table_remove(handle->app_sock_hash, data->name); + + return FALSE; +} + +static gpointer new_socket_thread(gpointer user_data) +{ + thread_data *data = user_data; + GError *error = NULL; + GIOChannel *ch = NULL; + int fd = 0; + + _D("thread for [%s] created", + g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr))); + + g_main_context_push_thread_default(data->context); + + if (!g_socket_connect(data->socket, data->addr, NULL, &error)) { + _E("failed to g_socket_connect - %s", error->message); + g_error_free(error); + goto THREAD_EXIT; + } + + fd = g_socket_get_fd(data->socket); + ch = g_io_channel_unix_new(fd); + io_add_watch_with_context(data->context, ch, G_IO_IN, __receive_msg, data); + io_add_watch_with_context(data->context, ch, + (GIOCondition) (G_IO_ERR | G_IO_HUP), __on_disconnect, data); + + g_main_loop_run(data->loop); + + g_io_channel_shutdown(ch, FALSE, &error); + g_io_channel_unref(ch); + +THREAD_EXIT: + g_main_context_pop_thread_default(data->context); + + g_socket_shutdown(data->socket, FALSE, TRUE, NULL); + g_socket_close(data->socket, NULL); + + if (data->disconnected) + g_main_context_invoke(NULL, __thread_terminated, data); + + return NULL; +} + +static void thread_data_set_name(thread_data *data, const char *name) +{ + ret_if(!data); + ret_if(!name); + + data->name = g_strdup(name); +} + +static void thread_data_set_queue(thread_data *data, GAsyncQueue *queue) +{ + ret_if(!data); + ret_if(!queue); + + data->data_queue = queue; +} + +static void thread_data_set_main_data(thread_data *data, void *main_data) +{ + ret_if(!data); + ret_if(!data); + + data->main_data = main_data; +} + +static void thread_quit(thread_data *data) +{ + gboolean running = FALSE; + + running = g_main_loop_is_running(data->loop); + if (running) { + g_main_loop_quit(data->loop); + _D("quit thread for [%s]", + g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr))); + } else { + _D("thread for [%s] is already quitted", + g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr))); + } +} + +static void thread_data_free(thread_data *data) +{ + g_main_loop_unref(data->loop); + g_main_context_unref(data->context); + g_object_unref(data->socket); + _D("remove data for thread related to [%s]", + g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr))); + g_object_unref(data->addr); + g_free(data->name); + g_free(data); +} + +static void thread_quit_and_free(thread_data *data) +{ + thread_quit(data); + g_thread_join(data->thread); + thread_data_free(data); +} + +static thread_data * +create_thread_for_socket(GSocket *sock, GSocketAddress *addr) +{ + thread_data *data = NULL; + GError *error = NULL; + + data = g_try_new0(thread_data, 1); + if (!data) { + _E("failed to get memory for thread_data"); + return NULL; + } + data->disconnected = 0; + data->socket = g_object_ref(sock); + data->addr = g_object_ref(addr); + data->context = g_main_context_new(); + data->loop = g_main_loop_new(data->context, FALSE); + data->thread = g_thread_try_new(NULL, new_socket_thread, data, &error); + if (!data->thread) { + _E("failed to create thread - %s", error->message); + g_error_free(error); + thread_data_free(data); + data = NULL; + } + + return data; +} + +static int +__get_new_connection_thread(ttd_app_inf_h handle, + const gchar *token, const gchar *appID, guint appPID) +{ + char *addr_name = NULL; + GSocket *socket = NULL; + GSocketAddress *addr = NULL; + GError *error = NULL; + thread_data *thread_d = NULL; + + retv_if(!handle, -1); + retv_if(!token, -1); + retv_if(!appID, -1); + retv_if(!appPID, -1); + + addr_name = __get_addr_name(token, appID, appPID); + retv_if(!addr_name, -1); + + socket = g_socket_new(G_SOCKET_FAMILY_UNIX, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + if (!socket) { + _E("failed to create socket - %s", error->message); + g_error_free(error); + goto FREE_N_RETURN_ERROR; + } + g_socket_set_blocking(socket, FALSE); + + addr = g_unix_socket_address_new_with_type( + addr_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT); + if (!addr) { + _E("failed to create socket address"); + goto FREE_N_RETURN_ERROR; + } + + thread_d = create_thread_for_socket(socket, addr); + if (!thread_d) { + _E("failed to create thread"); + goto FREE_N_RETURN_ERROR; + } + thread_data_set_queue(thread_d, handle->data_queue); + thread_data_set_main_data(thread_d, handle); + thread_data_set_name(thread_d, addr_name); + g_hash_table_insert(handle->app_sock_hash, addr_name, thread_d); + + g_object_unref(socket); + g_object_unref(addr); + + return 0; + +FREE_N_RETURN_ERROR: + g_free(addr_name); + + if (socket) + g_object_unref(socket); + + if (addr) + g_object_unref(addr); + + if (error) + g_error_free(error); + + return -1; +} + +static void __handle_method_register(GVariant *parameters, + GDBusMethodInvocation *invocation, ttd_app_inf_h handle) +{ + const gchar *token = NULL; + const gchar *appID = NULL; + guint appPID = 0; + gint response = NULL; + + g_variant_get(parameters, "(&s&su)", &token, &appID, &appPID); + _D("received register request from [%s-%u] - token[%s]", + appID, appPID, token); + + if (!appID) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to get appID"); + return; + } + + if (!appPID) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to get appPID"); + return; + } + + if (!token) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to get token"); + return; + } + + response = __get_new_connection_thread(handle, token, appID, appPID); + if (!response) + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(i)", response)); + else + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to connect"); +} + +static void __handle_method_unregister(GVariant *parameters, + GDBusMethodInvocation *invocation, ttd_app_inf_h handle) +{ + const gchar *token = NULL; + const gchar *appID = NULL; + guint appPID = 0; + char *key = NULL; + + g_variant_get(parameters, "(&s&su)", &token, &appID, &appPID); + _D("received unregister request from [%s-%u] - token[%s]", + appID, appPID, token); + + if (!appID) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to get appID"); + return; + } + + if (!appPID) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to get appPID"); + return; + } + + if (!token) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, "failed to get token"); + return; + } + + key = __get_addr_name(token, appID, appPID); + g_hash_table_remove(handle->app_sock_hash, key); + g_free(key); + + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(i)", 0)); +} + +static void +_ttd_handle_method_call(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + ttd_app_inf_h handle = user_data; + ret_if(!handle); + ret_if(!parameters); + ret_if(!invocation); + + _D("method[%s] called by [%s]", method_name, sender); + + if (g_strcmp0(method_name, "RegisterApp") == 0) { + __handle_method_register(parameters, invocation, handle); + } else if (g_strcmp0(method_name, "UnregisterApp") == 0) { + __handle_method_unregister(parameters, invocation, handle); + } else { + _E("Unkwon Method - %s", method_name); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown Method"); + } +} + +static const GDBusInterfaceVTable interface_vtable = { + _ttd_handle_method_call, NULL, NULL +}; + +static void +_ttd_app_inf_on_name_acquired(GDBusConnection *connection, + const gchar *name, gpointer user_data) +{ + _D("Aquried the name[%s]", name); +} + +static void +_ttd_app_inf_on_name_lost(GDBusConnection *connection, + const gchar *name, gpointer user_data) +{ + + /* TODO : how to handle this situation? */ + + if (connection) + _E("Lost the name[%s]", name); + else + _E("failed to connect to the bus [%s]", name); +} + +static void +_ttd_app_inf_on_bus_acquired(GDBusConnection *connection, + const gchar *name, gpointer user_data) +{ + ttd_app_inf_h handle = user_data; + guint r_id = 0; /* TODO : store it or not? */ + + _D("Acquired a message bus connection - [%s]", name); + + r_id = g_dbus_connection_register_object(connection, + TTD_APP_INF_OBJECT_PATH, + handle->introspection_data->interfaces[0], + &interface_vtable, + handle, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + + if (r_id == 0) + _E("failed to g_dbus_connection_register_object()"); +} + +static gpointer post_thread(gpointer user_data) +{ + ttd_app_inf_h handle = user_data; + + retv_if(!handle, NULL); + + while (g_atomic_int_get(&(handle->post_thread_run))) { + char *msg = NULL; + int ret = 0; + + msg = g_async_queue_timeout_pop( + handle->data_queue, (guint64)__QUEUE_TIME_OUT*1000); + if (!msg) + continue; +#if 0 /* Enable it if API url for posting data is created */ + /* TODO : get API url for posting data to replace POST_DATA_URL */ + ret = ttd_http_post_data(POST_DATA_URL, msg); + if (!ret) { + _E("failed to post data, retry it"); + g_async_queue_push_front(handle->data_queue, msg); + g_usleep((gulong)(G_USEC_PER_SEC/2)); + /* 0.5 sec sleep before retry it */ + continue; + } +#endif + _D("msg posted : %s", msg); + g_free(msg); + } + return NULL; +} + +int ttd_app_interface_fini(ttd_app_inf_h handle) +{ + retv_if(!handle, -1); + + if (handle->gdbus_id > 0) + g_bus_unown_name(handle->gdbus_id); + + if (handle->introspection_data) + g_dbus_node_info_unref(handle->introspection_data); + + if (handle->app_sock_hash) { + g_hash_table_remove_all(handle->app_sock_hash); + g_hash_table_unref(handle->app_sock_hash); + } + + if (handle->post_thread) { + g_atomic_int_set(&(handle->post_thread_run), __POST_THREAD_STOP); + g_thread_join(handle->post_thread); + } + + if (handle->data_queue) + g_async_queue_ref(handle->data_queue); + + g_free(handle); + + return 0; +} + +int ttd_app_interface_init(ttd_app_inf_h *handle) +{ + ttd_app_inf_h _handle = NULL; + GError *error = NULL; + guint id = 0; + GDBusNodeInfo *introspection_data = NULL; + + retv_if(!handle, -1); + + _handle = g_try_malloc0(sizeof(struct _ttd_app_inf_h)); + retv_if(!_handle, -1); + + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + + id = g_bus_own_name(G_BUS_TYPE_SYSTEM, + TTD_APP_INF_BUS_NAME, + (GBusNameOwnerFlags)(G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT + | G_BUS_NAME_OWNER_FLAGS_REPLACE), + _ttd_app_inf_on_bus_acquired, + _ttd_app_inf_on_name_acquired, + _ttd_app_inf_on_name_lost, + _handle, + NULL); + if (id == 0) { + _E("failed to g_bus_own_name()"); + g_dbus_node_info_unref(introspection_data); + g_free(_handle); + return -1; + } + + _handle->gdbus_id = id; + _handle->introspection_data = introspection_data; + _handle->app_sock_hash = + g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, (GDestroyNotify)thread_quit_and_free); + + _handle->data_queue = g_async_queue_new_full(g_free); + _handle->post_thread = + g_thread_try_new(NULL, (GThreadFunc)post_thread, _handle, &error); + if (!_handle->post_thread) { + _E("failed to create post thread - %s", error->message); + g_error_free(error); + ttd_app_interface_fini(_handle); + return -1; + } + + g_atomic_int_set(&(_handle->post_thread_run), __POST_THREAD_RUN); + + *handle = _handle; + + return 0; +} -- 2.7.4