Merge "Add configure module"
[apps/native/tizen-things-daemon.git] / daemon / src / ttd-app-interface.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://floralicense.org/license/
9  *
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.
15  */
16
17 #include <glib.h>
18 #include <gio/gio.h>
19 #include <gio/gunixsocketaddress.h>
20 #include "ttd-log.h"
21 #include "ttd-app-interface.h"
22 #include "ttd-http.h"
23 #include "common-app-inf.h"
24
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
29
30 #define APP_INF_INTROSPECTION \
31 "<node>" \
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'/>" \
38 "    </method>" \
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'/>" \
44 "    </method>" \
45 "  </interface>" \
46 "</node>"
47
48 typedef struct __thread_data_s {
49         char *name;
50         int disconnected;
51         GMainContext *context;
52         GMainLoop *loop;
53         GThread *thread;
54         GSocket *socket;
55         GSocketAddress *addr;
56         GAsyncQueue *data_queue;
57         void *main_data;
58 } thread_data;
59
60 struct _ttd_app_inf_h {
61         guint gdbus_id;
62         GDBusNodeInfo *introspection_data;
63         GHashTable *app_sock_hash;
64         GThread *post_thread;
65         GAsyncQueue *data_queue;
66         int post_thread_run;
67 };
68
69 static char *
70 __get_addr_name(const gchar *token, const gchar *appID, guint appPID)
71 {
72         return common_make_socket_addr_name(token, appID, appPID);
73 }
74
75 static gboolean
76 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer user_data)
77 {
78         thread_data *data = user_data;
79
80         _E("connection of thread[%s] is broken", data->name);
81
82         data->disconnected = 1;
83         g_main_loop_quit(data->loop);
84
85         return FALSE;
86 }
87
88 static gboolean
89 __receive_msg(GIOChannel *ch, GIOCondition cond, gpointer user_data)
90 {
91         thread_data *data = user_data;
92         GSocket *socket = data->socket;
93         GString *str = NULL;
94         char buf[1024] = {'\0', };
95         char *msg = NULL;
96         gssize size = 0;
97         gboolean connected = FALSE;
98
99         connected = g_socket_is_connected(socket);
100         if (!connected) {
101                 _E("socket is disconnected");
102                 g_main_loop_quit(data->loop);
103         }
104
105         while (0 < (size = g_socket_receive(socket, buf, 1024, NULL, NULL))) {
106                 _D("size : %d", size);
107                 if (!str)
108                         str = g_string_new(NULL);
109                 g_string_append_len(str, buf, size);
110         }
111
112         if (str)
113                 msg = g_string_free(str, FALSE);
114
115         if (!msg)
116                 _E("Empty message");
117         else {
118                 char **strv = NULL;
119                 int i;
120
121                 strv = g_strsplit(msg, "|", -1);
122                 for (i = 0; strv[i]; i++) {
123                         if (strv[i][0] == '\0') {
124                                 g_free(strv[i]);
125                                 continue;
126                         } else {
127                                 _D("strv[%d] : %s", i, strv[i]);
128                                 g_async_queue_push(data->data_queue, strv[i]);
129                         }
130                 }
131         }
132         g_free(msg);
133
134         return TRUE;
135 }
136
137 static guint
138 io_add_watch_with_context(GMainContext *context,
139         GIOChannel *ch,
140         GIOCondition condition,
141         GIOFunc func,
142         gpointer user_data)
143 {
144         GSource *source = NULL;
145         guint id = 0;
146
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);
152
153         return id;
154 }
155
156 static gboolean __thread_terminated(gpointer user_data)
157 {
158         thread_data *data = user_data;
159         ttd_app_inf_h handle = (ttd_app_inf_h) data->main_data;
160
161         retv_if(!data, FALSE);
162         retv_if(!handle, FALSE);
163         retv_if(!data->name, FALSE);
164
165         _D("thread for [%s] is terminated", data->name);
166
167         g_hash_table_remove(handle->app_sock_hash, data->name);
168
169         return FALSE;
170 }
171
172 static gpointer new_socket_thread(gpointer user_data)
173 {
174         thread_data *data = user_data;
175         GError *error = NULL;
176         GIOChannel *ch = NULL;
177         int fd = 0;
178
179         _D("thread for [%s] created",
180                 g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr)));
181
182         g_main_context_push_thread_default(data->context);
183
184         if (!g_socket_connect(data->socket, data->addr, NULL, &error)) {
185                 _E("failed to g_socket_connect - %s", error->message);
186                 g_error_free(error);
187                 goto THREAD_EXIT;
188         }
189
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);
195
196         g_main_loop_run(data->loop);
197
198         g_io_channel_shutdown(ch, FALSE, &error);
199         g_io_channel_unref(ch);
200
201 THREAD_EXIT:
202         g_main_context_pop_thread_default(data->context);
203
204         g_socket_shutdown(data->socket, FALSE, TRUE, NULL);
205         g_socket_close(data->socket, NULL);
206
207         if (data->disconnected)
208                 g_main_context_invoke(NULL, __thread_terminated, data);
209
210         return NULL;
211 }
212
213 static void thread_data_set_name(thread_data *data, const char *name)
214 {
215         ret_if(!data);
216         ret_if(!name);
217
218         data->name = g_strdup(name);
219 }
220
221 static void thread_data_set_queue(thread_data *data, GAsyncQueue *queue)
222 {
223         ret_if(!data);
224         ret_if(!queue);
225
226         data->data_queue = queue;
227 }
228
229 static void thread_data_set_main_data(thread_data *data, void *main_data)
230 {
231         ret_if(!data);
232         ret_if(!data);
233
234         data->main_data = main_data;
235 }
236
237 static void thread_quit(thread_data *data)
238 {
239         gboolean running = FALSE;
240
241         running = g_main_loop_is_running(data->loop);
242         if (running) {
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)));
246         } else {
247                 _D("thread for [%s] is already quitted",
248                         g_unix_socket_address_get_path(G_UNIX_SOCKET_ADDRESS(data->addr)));
249         }
250 }
251
252 static void thread_data_free(thread_data *data)
253 {
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);
260         g_free(data->name);
261         g_free(data);
262 }
263
264 static void thread_quit_and_free(thread_data *data)
265 {
266         thread_quit(data);
267         g_thread_join(data->thread);
268         thread_data_free(data);
269 }
270
271 static thread_data *
272 create_thread_for_socket(GSocket *sock, GSocketAddress *addr)
273 {
274         thread_data *data = NULL;
275         GError *error = NULL;
276
277         data = g_try_new0(thread_data, 1);
278         if (!data) {
279                 _E("failed to get memory for thread_data");
280                 return NULL;
281         }
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);
288         if (!data->thread) {
289                 _E("failed to create thread - %s", error->message);
290                 g_error_free(error);
291                 thread_data_free(data);
292                 data = NULL;
293         }
294
295         return data;
296 }
297
298 static int
299 __get_new_connection_thread(ttd_app_inf_h handle,
300         const gchar *token, const gchar *appID, guint appPID)
301 {
302         char *addr_name = NULL;
303         GSocket *socket = NULL;
304         GSocketAddress *addr = NULL;
305         GError *error = NULL;
306         thread_data *thread_d = NULL;
307
308         retv_if(!handle, -1);
309         retv_if(!token, -1);
310         retv_if(!appID, -1);
311         retv_if(!appPID, -1);
312
313         addr_name = __get_addr_name(token, appID, appPID);
314         retv_if(!addr_name, -1);
315
316         socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
317                                 G_SOCKET_TYPE_STREAM,
318                                 G_SOCKET_PROTOCOL_DEFAULT,
319                                 &error);
320         if (!socket) {
321                 _E("failed to create socket - %s", error->message);
322                 g_error_free(error);
323                 goto FREE_N_RETURN_ERROR;
324         }
325         g_socket_set_blocking(socket, FALSE);
326
327         addr = g_unix_socket_address_new_with_type(
328                         addr_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
329         if (!addr) {
330                 _E("failed to create socket address");
331                 goto FREE_N_RETURN_ERROR;
332         }
333
334         thread_d = create_thread_for_socket(socket, addr);
335         if (!thread_d) {
336                 _E("failed to create thread");
337                 goto FREE_N_RETURN_ERROR;
338         }
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);
343
344         g_object_unref(socket);
345         g_object_unref(addr);
346
347         return 0;
348
349 FREE_N_RETURN_ERROR:
350         g_free(addr_name);
351
352         if (socket)
353                 g_object_unref(socket);
354
355         if (addr)
356                 g_object_unref(addr);
357
358         if (error)
359                 g_error_free(error);
360
361         return -1;
362 }
363
364 static void __handle_method_register(GVariant *parameters,
365         GDBusMethodInvocation *invocation, ttd_app_inf_h handle)
366 {
367         const gchar *token = NULL;
368         const gchar *appID = NULL;
369         guint appPID = 0;
370         gint response = NULL;
371
372         g_variant_get(parameters, "(&s&su)", &token, &appID, &appPID);
373         _D("received register request from [%s-%u] - token[%s]",
374                 appID, appPID, token);
375
376         if (!appID) {
377                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
378                         G_DBUS_ERROR_FAILED, "failed to get appID");
379                 return;
380         }
381
382         if (!appPID) {
383                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
384                         G_DBUS_ERROR_FAILED, "failed to get appPID");
385                 return;
386         }
387
388         if (!token) {
389                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
390                         G_DBUS_ERROR_FAILED, "failed to get token");
391                 return;
392         }
393
394         response = __get_new_connection_thread(handle, token, appID, appPID);
395         if (!response)
396                 g_dbus_method_invocation_return_value(invocation,
397                         g_variant_new("(i)", response));
398         else
399                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
400                         G_DBUS_ERROR_FAILED, "failed to connect");
401 }
402
403 static void __handle_method_unregister(GVariant *parameters,
404         GDBusMethodInvocation *invocation, ttd_app_inf_h handle)
405 {
406         const gchar *token = NULL;
407         const gchar *appID = NULL;
408         guint appPID = 0;
409         char *key = NULL;
410
411         g_variant_get(parameters, "(&s&su)", &token, &appID, &appPID);
412         _D("received unregister request from [%s-%u] - token[%s]",
413                 appID, appPID, token);
414
415         if (!appID) {
416                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
417                         G_DBUS_ERROR_FAILED, "failed to get appID");
418                 return;
419         }
420
421         if (!appPID) {
422                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
423                         G_DBUS_ERROR_FAILED, "failed to get appPID");
424                 return;
425         }
426
427         if (!token) {
428                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
429                         G_DBUS_ERROR_FAILED, "failed to get token");
430                 return;
431         }
432
433         key = __get_addr_name(token, appID, appPID);
434         g_hash_table_remove(handle->app_sock_hash, key);
435         g_free(key);
436
437         g_dbus_method_invocation_return_value(invocation,
438                 g_variant_new("(i)", 0));
439 }
440
441 static void
442 _ttd_handle_method_call(GDBusConnection *connection,
443         const gchar *sender,
444         const gchar *object_path,
445         const gchar *interface_name,
446         const gchar *method_name,
447         GVariant *parameters,
448         GDBusMethodInvocation *invocation,
449         gpointer user_data)
450 {
451         ttd_app_inf_h handle = user_data;
452         ret_if(!handle);
453         ret_if(!parameters);
454         ret_if(!invocation);
455
456         _D("method[%s] called by [%s]", method_name, sender);
457
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);
462         } else {
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");
466         }
467 }
468
469 static const GDBusInterfaceVTable interface_vtable = {
470         _ttd_handle_method_call, NULL, NULL
471 };
472
473 static void
474 _ttd_app_inf_on_name_acquired(GDBusConnection *connection,
475         const gchar *name, gpointer user_data)
476 {
477         _D("Aquried the name[%s]", name);
478 }
479
480 static void
481 _ttd_app_inf_on_name_lost(GDBusConnection *connection,
482         const gchar *name, gpointer user_data)
483 {
484
485         /* TODO : how to handle this situation? */
486
487         if (connection)
488                 _E("Lost the name[%s]", name);
489         else
490                 _E("failed to connect to the bus [%s]", name);
491 }
492
493 static void
494 _ttd_app_inf_on_bus_acquired(GDBusConnection *connection,
495         const gchar *name, gpointer user_data)
496 {
497         ttd_app_inf_h handle = user_data;
498         guint r_id = 0; /* TODO : store it or not? */
499
500         _D("Acquired a message bus connection - [%s]", name);
501
502         r_id = g_dbus_connection_register_object(connection,
503                         TTD_APP_INF_OBJECT_PATH,
504                         handle->introspection_data->interfaces[0],
505                         &interface_vtable,
506                         handle, /* user_data */
507                         NULL,   /* user_data_free_func */
508                         NULL);  /* GError** */
509
510         if (r_id == 0)
511                 _E("failed to g_dbus_connection_register_object()");
512 }
513
514 static gpointer post_thread(gpointer user_data)
515 {
516         ttd_app_inf_h handle = user_data;
517
518         retv_if(!handle, NULL);
519
520         while (g_atomic_int_get(&(handle->post_thread_run))) {
521                 char *msg = NULL;
522                 int ret = 0;
523
524                 msg = g_async_queue_timeout_pop(
525                                 handle->data_queue, (guint64)__QUEUE_TIME_OUT*1000);
526                 if (!msg)
527                         continue;
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);
531                 if (!ret) {
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 */
536                         continue;
537                 }
538 #endif
539                 _D("msg posted : %s", msg);
540                 g_free(msg);
541         }
542         return NULL;
543 }
544
545 int ttd_app_interface_fini(ttd_app_inf_h handle)
546 {
547         retv_if(!handle, -1);
548
549         if (handle->gdbus_id > 0)
550                 g_bus_unown_name(handle->gdbus_id);
551
552         if (handle->introspection_data)
553                 g_dbus_node_info_unref(handle->introspection_data);
554
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);
558         }
559
560         if (handle->post_thread) {
561                 g_atomic_int_set(&(handle->post_thread_run), __POST_THREAD_STOP);
562                 g_thread_join(handle->post_thread);
563         }
564
565         if (handle->data_queue)
566                 g_async_queue_ref(handle->data_queue);
567
568         g_free(handle);
569
570         return 0;
571 }
572
573 int ttd_app_interface_init(ttd_app_inf_h *handle)
574 {
575         ttd_app_inf_h _handle = NULL;
576         GError *error = NULL;
577         guint id = 0;
578         GDBusNodeInfo *introspection_data = NULL;
579
580         retv_if(!handle, -1);
581
582         _handle = g_try_malloc0(sizeof(struct _ttd_app_inf_h));
583         retv_if(!_handle, -1);
584
585         introspection_data =
586                 g_dbus_node_info_new_for_xml(APP_INF_INTROSPECTION, NULL);
587
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,
595                         _handle,
596                         NULL);
597         if (id == 0) {
598                 _E("failed to g_bus_own_name()");
599                 g_dbus_node_info_unref(introspection_data);
600                 g_free(_handle);
601                 return -1;
602         }
603
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);
609
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);
615                 g_error_free(error);
616                 ttd_app_interface_fini(_handle);
617                 return -1;
618         }
619
620         g_atomic_int_set(&(_handle->post_thread_run), __POST_THREAD_RUN);
621
622         *handle = _handle;
623
624         return 0;
625 }