2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Flora License, Version 1.1 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <gio/gunixsocketaddress.h>
21 #include "ttd-app-interface.h"
23 #include "common-app-inf.h"
25 #define POST_DATA_URL "http://apitest.showiot.xyz/api/data"
26 #define __POST_THREAD_RUN 1
27 #define __POST_THREAD_STOP 0
28 #define __QUEUE_TIME_OUT 500
30 #define APP_INF_INTROSPECTION \
32 " <interface name='"TTD_APP_INF_BUS_INF"'>" \
33 " <method name='"TTD_APP_INF_METHOD_REG"'>" \
34 " <arg type='s' name='token' direction='in'/>" \
35 " <arg type='s' name='appID' direction='in'/>" \
36 " <arg type='u' name='appPID' direction='in'/>" \
37 " <arg type='i' name='response' direction='out'/>" \
39 " <method name='"TTD_APP_INF_METHOD_UNREG"'>" \
40 " <arg type='s' name='token' direction='in'/>" \
41 " <arg type='s' name='appID' direction='in'/>" \
42 " <arg type='u' name='appPID' direction='in'/>" \
43 " <arg type='i' name='response' direction='out'/>" \
48 typedef struct __thread_data_s {
51 GMainContext *context;
56 GAsyncQueue *data_queue;
60 struct _ttd_app_inf_h {
62 GDBusNodeInfo *introspection_data;
63 GHashTable *app_sock_hash;
65 GAsyncQueue *data_queue;
70 __get_addr_name(const gchar *token, const gchar *appID, guint appPID)
72 return common_make_socket_addr_name(token, appID, appPID);
76 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer user_data)
78 thread_data *data = user_data;
80 _E("connection of thread[%s] is broken", data->name);
82 data->disconnected = 1;
83 g_main_loop_quit(data->loop);
89 __receive_msg(GIOChannel *ch, GIOCondition cond, gpointer user_data)
91 thread_data *data = user_data;
92 GSocket *socket = data->socket;
94 char buf[1024] = {'\0', };
97 gboolean connected = FALSE;
99 connected = g_socket_is_connected(socket);
101 _E("socket is disconnected");
102 g_main_loop_quit(data->loop);
105 while (0 < (size = g_socket_receive(socket, buf, 1024, NULL, NULL))) {
106 _D("size : %d", size);
108 str = g_string_new(NULL);
109 g_string_append_len(str, buf, size);
113 msg = g_string_free(str, FALSE);
121 strv = g_strsplit(msg, "|", -1);
122 for (i = 0; strv[i]; i++) {
123 if (strv[i][0] == '\0') {
127 _D("strv[%d] : %s", i, strv[i]);
128 g_async_queue_push(data->data_queue, strv[i]);
138 io_add_watch_with_context(GMainContext *context,
140 GIOCondition condition,
144 GSource *source = NULL;
147 source = g_io_create_watch(ch, condition);
148 g_source_set_priority(source, G_PRIORITY_DEFAULT);
149 g_source_set_callback(source, (GSourceFunc)func, user_data, NULL);
150 id = g_source_attach(source, context);
151 g_source_unref(source);
156 static gboolean __thread_terminated(gpointer user_data)
158 thread_data *data = user_data;
159 ttd_app_inf_h handle = (ttd_app_inf_h) data->main_data;
161 retv_if(!data, FALSE);
162 retv_if(!handle, FALSE);
163 retv_if(!data->name, FALSE);
165 _D("thread for [%s] is terminated", data->name);
167 g_hash_table_remove(handle->app_sock_hash, data->name);
172 static gpointer new_socket_thread(gpointer user_data)
174 thread_data *data = user_data;
175 GError *error = NULL;
176 GIOChannel *ch = NULL;
179 _D("thread for [%s] created",
180 g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr)));
182 g_main_context_push_thread_default(data->context);
184 if (!g_socket_connect(data->socket, data->addr, NULL, &error)) {
185 _E("failed to g_socket_connect - %s", error->message);
190 fd = g_socket_get_fd(data->socket);
191 ch = g_io_channel_unix_new(fd);
192 io_add_watch_with_context(data->context, ch, G_IO_IN, __receive_msg, data);
193 io_add_watch_with_context(data->context, ch,
194 (GIOCondition) (G_IO_ERR | G_IO_HUP), __on_disconnect, data);
196 g_main_loop_run(data->loop);
198 g_io_channel_shutdown(ch, FALSE, &error);
199 g_io_channel_unref(ch);
202 g_main_context_pop_thread_default(data->context);
204 g_socket_shutdown(data->socket, FALSE, TRUE, NULL);
205 g_socket_close(data->socket, NULL);
207 if (data->disconnected)
208 g_main_context_invoke(NULL, __thread_terminated, data);
213 static void thread_data_set_name(thread_data *data, const char *name)
218 data->name = g_strdup(name);
221 static void thread_data_set_queue(thread_data *data, GAsyncQueue *queue)
226 data->data_queue = queue;
229 static void thread_data_set_main_data(thread_data *data, void *main_data)
234 data->main_data = main_data;
237 static void thread_quit(thread_data *data)
239 gboolean running = FALSE;
241 running = g_main_loop_is_running(data->loop);
243 g_main_loop_quit(data->loop);
244 _D("quit thread for [%s]",
245 g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr)));
247 _D("thread for [%s] is already quitted",
248 g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr)));
252 static void thread_data_free(thread_data *data)
254 g_main_loop_unref(data->loop);
255 g_main_context_unref(data->context);
256 g_object_unref(data->socket);
257 _D("remove data for thread related to [%s]",
258 g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr)));
259 g_object_unref(data->addr);
264 static void thread_quit_and_free(thread_data *data)
267 g_thread_join(data->thread);
268 thread_data_free(data);
272 create_thread_for_socket(GSocket *sock, GSocketAddress *addr)
274 thread_data *data = NULL;
275 GError *error = NULL;
277 data = g_try_new0(thread_data, 1);
279 _E("failed to get memory for thread_data");
282 data->disconnected = 0;
283 data->socket = g_object_ref(sock);
284 data->addr = g_object_ref(addr);
285 data->context = g_main_context_new();
286 data->loop = g_main_loop_new(data->context, FALSE);
287 data->thread = g_thread_try_new(NULL, new_socket_thread, data, &error);
289 _E("failed to create thread - %s", error->message);
291 thread_data_free(data);
299 __get_new_connection_thread(ttd_app_inf_h handle,
300 const gchar *token, const gchar *appID, guint appPID)
302 char *addr_name = NULL;
303 GSocket *socket = NULL;
304 GSocketAddress *addr = NULL;
305 GError *error = NULL;
306 thread_data *thread_d = NULL;
308 retv_if(!handle, -1);
311 retv_if(!appPID, -1);
313 addr_name = __get_addr_name(token, appID, appPID);
314 retv_if(!addr_name, -1);
316 socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
317 G_SOCKET_TYPE_STREAM,
318 G_SOCKET_PROTOCOL_DEFAULT,
321 _E("failed to create socket - %s", error->message);
323 goto FREE_N_RETURN_ERROR;
325 g_socket_set_blocking(socket, FALSE);
327 addr = g_unix_socket_address_new_with_type(
328 addr_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
330 _E("failed to create socket address");
331 goto FREE_N_RETURN_ERROR;
334 thread_d = create_thread_for_socket(socket, addr);
336 _E("failed to create thread");
337 goto FREE_N_RETURN_ERROR;
339 thread_data_set_queue(thread_d, handle->data_queue);
340 thread_data_set_main_data(thread_d, handle);
341 thread_data_set_name(thread_d, addr_name);
342 g_hash_table_insert(handle->app_sock_hash, addr_name, thread_d);
344 g_object_unref(socket);
345 g_object_unref(addr);
353 g_object_unref(socket);
356 g_object_unref(addr);
364 static void __handle_method_register(GVariant *parameters,
365 GDBusMethodInvocation *invocation, ttd_app_inf_h handle)
367 const gchar *token = NULL;
368 const gchar *appID = NULL;
370 gint response = NULL;
372 g_variant_get(parameters, "(&s&su)", &token, &appID, &appPID);
373 _D("received register request from [%s-%u] - token[%s]",
374 appID, appPID, token);
377 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
378 G_DBUS_ERROR_FAILED, "failed to get appID");
383 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
384 G_DBUS_ERROR_FAILED, "failed to get appPID");
389 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
390 G_DBUS_ERROR_FAILED, "failed to get token");
394 response = __get_new_connection_thread(handle, token, appID, appPID);
396 g_dbus_method_invocation_return_value(invocation,
397 g_variant_new("(i)", response));
399 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
400 G_DBUS_ERROR_FAILED, "failed to connect");
403 static void __handle_method_unregister(GVariant *parameters,
404 GDBusMethodInvocation *invocation, ttd_app_inf_h handle)
406 const gchar *token = NULL;
407 const gchar *appID = NULL;
411 g_variant_get(parameters, "(&s&su)", &token, &appID, &appPID);
412 _D("received unregister request from [%s-%u] - token[%s]",
413 appID, appPID, token);
416 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
417 G_DBUS_ERROR_FAILED, "failed to get appID");
422 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
423 G_DBUS_ERROR_FAILED, "failed to get appPID");
428 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
429 G_DBUS_ERROR_FAILED, "failed to get token");
433 key = __get_addr_name(token, appID, appPID);
434 g_hash_table_remove(handle->app_sock_hash, key);
437 g_dbus_method_invocation_return_value(invocation,
438 g_variant_new("(i)", 0));
442 _ttd_handle_method_call(GDBusConnection *connection,
444 const gchar *object_path,
445 const gchar *interface_name,
446 const gchar *method_name,
447 GVariant *parameters,
448 GDBusMethodInvocation *invocation,
451 ttd_app_inf_h handle = user_data;
456 _D("method[%s] called by [%s]", method_name, sender);
458 if (g_strcmp0(method_name, TTD_APP_INF_METHOD_REG) == 0) {
459 __handle_method_register(parameters, invocation, handle);
460 } else if (g_strcmp0(method_name, TTD_APP_INF_METHOD_UNREG) == 0) {
461 __handle_method_unregister(parameters, invocation, handle);
463 _E("Unkwon Method - %s", method_name);
464 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
465 G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown Method");
469 static const GDBusInterfaceVTable interface_vtable = {
470 _ttd_handle_method_call, NULL, NULL
474 _ttd_app_inf_on_name_acquired(GDBusConnection *connection,
475 const gchar *name, gpointer user_data)
477 _D("Aquried the name[%s]", name);
481 _ttd_app_inf_on_name_lost(GDBusConnection *connection,
482 const gchar *name, gpointer user_data)
485 /* TODO : how to handle this situation? */
488 _E("Lost the name[%s]", name);
490 _E("failed to connect to the bus [%s]", name);
494 _ttd_app_inf_on_bus_acquired(GDBusConnection *connection,
495 const gchar *name, gpointer user_data)
497 ttd_app_inf_h handle = user_data;
498 guint r_id = 0; /* TODO : store it or not? */
500 _D("Acquired a message bus connection - [%s]", name);
502 r_id = g_dbus_connection_register_object(connection,
503 TTD_APP_INF_OBJECT_PATH,
504 handle->introspection_data->interfaces[0],
506 handle, /* user_data */
507 NULL, /* user_data_free_func */
508 NULL); /* GError** */
511 _E("failed to g_dbus_connection_register_object()");
514 static gpointer post_thread(gpointer user_data)
516 ttd_app_inf_h handle = user_data;
518 retv_if(!handle, NULL);
520 while (g_atomic_int_get(&(handle->post_thread_run))) {
524 msg = g_async_queue_timeout_pop(
525 handle->data_queue, (guint64)__QUEUE_TIME_OUT*1000);
528 #if 0 /* Enable it if API url for posting data is created */
529 /* TODO : get API url for posting data to replace POST_DATA_URL */
530 ret = ttd_http_post_data(POST_DATA_URL, msg);
532 _E("failed to post data, retry it");
533 g_async_queue_push_front(handle->data_queue, msg);
534 g_usleep((gulong)(G_USEC_PER_SEC/2));
535 /* 0.5 sec sleep before retry it */
539 _D("msg posted : %s", msg);
545 int ttd_app_interface_fini(ttd_app_inf_h handle)
547 retv_if(!handle, -1);
549 if (handle->gdbus_id > 0)
550 g_bus_unown_name(handle->gdbus_id);
552 if (handle->introspection_data)
553 g_dbus_node_info_unref(handle->introspection_data);
555 if (handle->app_sock_hash) {
556 g_hash_table_remove_all(handle->app_sock_hash);
557 g_hash_table_unref(handle->app_sock_hash);
560 if (handle->post_thread) {
561 g_atomic_int_set(&(handle->post_thread_run), __POST_THREAD_STOP);
562 g_thread_join(handle->post_thread);
565 if (handle->data_queue)
566 g_async_queue_ref(handle->data_queue);
573 int ttd_app_interface_init(ttd_app_inf_h *handle)
575 ttd_app_inf_h _handle = NULL;
576 GError *error = NULL;
578 GDBusNodeInfo *introspection_data = NULL;
580 retv_if(!handle, -1);
582 _handle = g_try_malloc0(sizeof(struct _ttd_app_inf_h));
583 retv_if(!_handle, -1);
586 g_dbus_node_info_new_for_xml(APP_INF_INTROSPECTION, NULL);
588 id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
589 TTD_APP_INF_BUS_NAME,
590 (GBusNameOwnerFlags)(G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT
591 | G_BUS_NAME_OWNER_FLAGS_REPLACE),
592 _ttd_app_inf_on_bus_acquired,
593 _ttd_app_inf_on_name_acquired,
594 _ttd_app_inf_on_name_lost,
598 _E("failed to g_bus_own_name()");
599 g_dbus_node_info_unref(introspection_data);
604 _handle->gdbus_id = id;
605 _handle->introspection_data = introspection_data;
606 _handle->app_sock_hash =
607 g_hash_table_new_full(g_str_hash, g_str_equal,
608 g_free, (GDestroyNotify)thread_quit_and_free);
610 _handle->data_queue = g_async_queue_new_full(g_free);
611 _handle->post_thread =
612 g_thread_try_new(NULL, (GThreadFunc)post_thread, _handle, &error);
613 if (!_handle->post_thread) {
614 _E("failed to create post thread - %s", error->message);
616 ttd_app_interface_fini(_handle);
620 g_atomic_int_set(&(_handle->post_thread_run), __POST_THREAD_RUN);