ab45b7c6359d40bd2597b851ce84aff53402c805
[apps/native/tizen-things-daemon.git] / lib / things-service / src / things-service.c
1 /*
2  * Copyright (c) 2017 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 <sys/types.h>
18 #include <unistd.h>
19 #include <time.h>
20 #include <glib.h>
21 #include <gio/gio.h>
22 #include <gio/gunixsocketaddress.h>
23 #include <app_common.h>
24 #include <json.h>
25 #include "log-private.h"
26 #include "things-service.h"
27 #include "common-app-inf.h"
28 #include "common-util.h"
29
30 struct _ts_data {
31         char *app_id;
32         char *token;
33         char *project;
34         GSocket *socket;
35         GSocketAddress *addr;
36         GSocket *cl_socket;
37         GDBusConnection *bus;
38         GDBusProxy *proxy;
39         GThread *accept_thread;
40         int accept_thread_running;
41 };
42
43 struct __timebased_int {
44         gint64 i_time;
45         gint value;
46 };
47
48 struct __timebased_double {
49         gint64 i_time;
50         gdouble value;
51 };
52
53 struct __timebased_data {
54         ts_value_type_e type;
55         unsigned int length;
56         GList *v_list;
57 };
58
59 static void __quit_n_join_accept_thread(ts_handle handle);
60 static int _create_accept_thread(ts_handle handle);
61
62 static void __timebase_value_free(gpointer data)
63 {
64         g_free(data);
65 }
66
67 static void
68 __add_timebased_int_to_json_object(gpointer data, gpointer user_data)
69 {
70         struct __timebased_int *tb_int = data;
71         struct json_object *a_obj = user_data;
72         struct json_object *obj = NULL;
73         struct json_object *value_o = NULL;
74         struct json_object *time_o = NULL;
75
76         ret_if(!tb_int);
77         ret_if(!a_obj);
78
79         obj = json_object_new_object();
80         ret_if(!obj);
81
82         time_o = json_object_new_int64(tb_int->i_time);
83         goto_if(!time_o, ERROR);
84         json_object_object_add(obj, "x", time_o);
85
86         value_o = json_object_new_int64(tb_int->value);
87         goto_if(!value_o, ERROR);
88         json_object_object_add(obj, "y", value_o);
89
90         json_object_array_add(a_obj, obj);
91         return;
92
93 ERROR:
94         json_object_put(obj);
95         return;
96 }
97
98 static void
99 __add_timebased_double_to_json_object(gpointer data, gpointer user_data)
100 {
101         struct __timebased_double *tb_double = data;
102         struct json_object *a_obj = user_data;
103         struct json_object *obj = NULL;
104         struct json_object *value_o = NULL;
105         struct json_object *time_o = NULL;
106
107         ret_if(!tb_double);
108         ret_if(!a_obj);
109
110         obj = json_object_new_object();
111         ret_if(!obj);
112
113         time_o = json_object_new_int64(tb_double->i_time);
114         goto_if(!time_o, ERROR);
115         json_object_object_add(obj, "x", time_o);
116
117         value_o = json_object_new_double(tb_double->value);
118         goto_if(!value_o, ERROR);
119         json_object_object_add(obj, "y", value_o);
120
121         json_object_array_add(a_obj, obj);
122
123         return;
124
125 ERROR:
126         json_object_put(obj);
127         return;
128 }
129
130 static char *__timebased_data_to_json(timebased_data *tb_data)
131 {
132         struct json_object *obj = NULL;
133         struct json_object *a_obj = NULL;
134         char *json_str = NULL;
135
136         retv_if(!tb_data, NULL);
137         retvm_if(!((tb_data->type == TS_VALUE_TYPE_INT) ||
138                         (tb_data->type == TS_VALUE_TYPE_DOUBLE)),
139                         NULL, "invalid type[%d]", tb_data->type);
140
141         obj = json_object_new_object();
142         retv_if(!obj, NULL);
143
144         a_obj = json_object_new_array();
145         goto_if(!a_obj, ERROR);
146         json_object_object_add(obj, "data", a_obj);
147
148         switch (tb_data->type) {
149         case TS_VALUE_TYPE_INT:
150                 g_list_foreach(tb_data->v_list,
151                         __add_timebased_int_to_json_object, a_obj);
152                 break;
153         case TS_VALUE_TYPE_DOUBLE:
154                 g_list_foreach(tb_data->v_list,
155                         __add_timebased_double_to_json_object, a_obj);
156                 break;
157         default:
158                 _E("invalid type [%d]", tb_data->type);
159                 goto ERROR;
160                 break;
161         }
162
163         json_str = g_strdup(json_object_to_json_string(obj));
164
165         if (obj)
166                 json_object_put(obj);
167
168         return json_str;
169
170 ERROR:
171         if (obj)
172                 json_object_put(obj);
173
174         return NULL;
175 }
176
177 static char *_get_socket_addr_name(const char *appID, const char *token)
178 {
179         return common_make_socket_addr_name(token, appID, (guint)getpid());
180 }
181
182 static char *_get_app_id(void)
183 {
184         char *app_id = NULL;
185         app_get_id(&app_id);
186
187         return app_id;
188 }
189
190 static char *_get_token(const char *appID)
191 {
192         char *checksum = NULL;
193         char *key = NULL;
194
195         key = g_strdup_printf("%s-%u-%lld",
196                         appID, getpid(), common_get_monotonic_coarse_time());
197         checksum = g_compute_checksum_for_string(G_CHECKSUM_MD5, key, -1);
198         g_free(key);
199
200         return checksum;
201 }
202
203 static gboolean
204 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
205 {
206         ts_handle handle = data;
207         gboolean connected = FALSE;
208
209         connected = g_socket_is_connected(handle->cl_socket);
210         if (!connected)
211                 _E("client socket is disconnected");
212
213         if (cond == G_IO_ERR)
214                 _E("connection has error");
215
216         if (cond == G_IO_HUP)
217                 _E("connection has been broken");
218
219         g_socket_shutdown(handle->cl_socket, FALSE, TRUE, NULL);
220         g_socket_close(handle->cl_socket, NULL);
221         g_object_unref(handle->cl_socket);
222
223         handle->cl_socket = NULL;
224
225         _create_accept_thread(handle);
226
227         return FALSE;
228 }
229
230 static void _ts_handle_free(ts_handle handle)
231 {
232         ret_if(!handle);
233
234         __quit_n_join_accept_thread(handle);
235
236         if (handle->app_id)
237                 g_free(handle->app_id);
238
239         if (handle->project)
240                 g_free(handle->project);
241
242         if (handle->token)
243                 g_free(handle->token);
244
245         if (handle->socket) {
246                 g_socket_close(handle->socket, NULL);
247                 g_object_unref(handle->socket);
248         }
249
250         if (handle->addr)
251                 g_object_unref(handle->addr);
252
253         if (handle->bus)
254                 g_object_unref(handle->bus);
255
256         if (handle->proxy)
257                 g_object_unref(handle->proxy);
258
259         g_free(handle);
260 }
261
262 static void __quit_n_join_accept_thread(ts_handle handle)
263 {
264         if (handle->accept_thread) {
265                 g_atomic_int_set(&(handle->accept_thread_running), 0);
266                 g_thread_join(handle->accept_thread);
267                 handle->accept_thread = NULL;
268         }
269 }
270
271 static gpointer accept_thread(gpointer data)
272 {
273         ts_handle handle = data;
274         retv_if(!handle, NULL);
275
276         while (TRUE) {
277                 GSocket *new_socket = NULL;
278                 GIOChannel *new_ch = NULL;
279                 int fd = 0;
280                 GError *error = NULL;
281
282                 if (!g_atomic_int_get(&(handle->accept_thread_running)))
283                         break;
284
285                 new_socket = g_socket_accept(handle->socket, NULL, &error);
286                 if (!new_socket) {
287                         if (error) {
288                                 if (error->code !=  G_IO_ERROR_WOULD_BLOCK)
289                                         _E("err on g_socket_accept() - %s", error->message);
290
291                                 g_error_free(error);
292                         }
293                         continue;
294                 }
295
296                 _D("accepted new socket[%p]", new_socket);
297                 fd = g_socket_get_fd(new_socket);
298                 new_ch = g_io_channel_unix_new(fd);
299                 g_io_add_watch(new_ch, (GIOCondition) (G_IO_ERR | G_IO_HUP),
300                         __on_disconnect, handle);
301                 handle->cl_socket = new_socket;
302                 break; /* accept and exit thread */
303         }
304         _D("exiting accept thread");
305
306         return NULL;
307 }
308
309 static int _create_accept_thread(ts_handle handle)
310 {
311         GThread *new_thread = NULL;
312         GError *error = NULL;
313
314         retv_if(!handle, -1);
315
316         if (handle->accept_thread)
317                 __quit_n_join_accept_thread(handle);
318
319         g_atomic_int_set(&(handle->accept_thread_running), 1);
320         new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
321         if (!new_thread) {
322                 _E("failed to create accept thread - %s", error->message);
323                 g_error_free(error);
324                 return -1;
325         }
326         handle->accept_thread = new_thread;
327
328         return 0;
329 }
330
331 static int __client_method_call(const char *method, ts_handle handle)
332 {
333         GVariant *response = NULL;
334         int ret = 0;
335         const char *ret_msg = 0;
336         GError *error = NULL;
337
338         _D("call method [%s]", method);
339         response = g_dbus_proxy_call_sync(handle->proxy,
340                 method,
341                 g_variant_new("(sssu)",
342                         handle->project, handle->token, handle->app_id, (guint)getpid()),
343                 G_DBUS_CALL_FLAGS_NONE,
344                 -1, /* The timeout in milliseconds or -1 to use the proxy default timeout. */
345                 NULL, /* cancellable */
346                 &error);
347         if (error) {
348                 _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
349                 g_error_free(error);
350                 return -1;
351         }
352         if (!response)
353                 return -1;
354
355         g_variant_get(response, "(i&s)", ret, &ret_msg);
356         _D("method[%s] get response ret[%d] - %s", method, ret, ret_msg);
357         g_variant_unref(response);
358
359         return ret;
360 }
361
362 static int _client_call_register(ts_handle handle)
363 {
364         return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
365 }
366
367 static int _client_call_unregister(ts_handle handle)
368 {
369         return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
370 }
371
372 int things_service_init(ts_handle *handle, const char *project)
373 {
374         ts_handle _handle = NULL;
375         GError *error = NULL;
376         char *socket_name = NULL;
377
378         retv_if(!handle, -1);
379
380         _handle = g_try_malloc0(sizeof(struct _ts_data));
381         _handle->project = g_strdup(project);
382         _handle->app_id = _get_app_id();
383         if (!_handle->app_id) {
384                 _E("failed to get app id");
385                 _ts_handle_free(_handle);
386                 return -1;
387         }
388         _D("libthings_service with - project[%s], app[%s]", _handle->app_id);
389
390         _handle->token = _get_token(_handle->app_id);
391         if (!_handle->token) {
392                 _E("failed to get token");
393                 _ts_handle_free(_handle);
394                 return -1;
395         }
396         _D("get token - %s", _handle->token);
397         _handle->cl_socket = NULL;
398         _handle->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
399                                 G_SOCKET_TYPE_STREAM,
400                                 G_SOCKET_PROTOCOL_DEFAULT,
401                                 &error);
402         if (!_handle->socket) {
403                 _E("failed to create socket - %s", error->message);
404                 g_error_free(error);
405                 _ts_handle_free(_handle);
406                 return -1;
407         }
408         g_socket_set_blocking(_handle->socket, FALSE);
409
410         socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
411         if (!socket_name) {
412                 _E("failed to get socket_name");
413                 _ts_handle_free(_handle);
414                 return -1;
415         }
416         _handle->addr = g_unix_socket_address_new_with_type(
417                         socket_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
418         _D("got socket addr - [%s]", socket_name);
419         g_free(socket_name);
420
421         g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
422         if (error) {
423                 _E("failed to g_socket_bind() - %s", error->message);
424                 g_error_free(error);
425                 _ts_handle_free(_handle);
426                 return -1;
427         }
428
429         g_socket_listen(_handle->socket, &error);
430         if (error) {
431                 _E("failed to g_socket_listen() - %s", error->message);
432                 g_error_free(error);
433                 _ts_handle_free(_handle);
434                 return -1;
435         }
436
437         _handle->bus =
438                 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
439         if (!_handle->bus) {
440                 _E("failed to g_bus_get_sync() - %s", error->message);
441                 g_error_free(error);
442                 _ts_handle_free(_handle);
443                 return -1;
444         }
445
446         _handle->proxy = g_dbus_proxy_new_sync(_handle->bus,
447                 G_DBUS_PROXY_FLAGS_NONE,
448                 NULL, /* GDBusInterfaceInfo */
449                 TTD_APP_INF_BUS_NAME,
450                 TTD_APP_INF_OBJECT_PATH,
451                 TTD_APP_INF_BUS_INF,
452                 NULL, /* cancellable */
453                 &error);
454         if (!_handle->proxy) {
455                 _E("failed to g_dbus_proxy_new_sync() - %s",
456                         error ? error->message : "error not set");
457                 _ts_handle_free(_handle);
458                 return -1;
459         }
460
461         if (_create_accept_thread(_handle)) {
462                 _E("failed to _create_accept_thread()");
463                 _ts_handle_free(_handle);
464                 return -1;
465         }
466
467         if (_client_call_register(_handle)) {
468                 _E("failed to call register");
469                 _ts_handle_free(_handle);
470                 return -1;
471         }
472
473         *handle = _handle;
474
475         return 0;
476 }
477
478 int things_service_fini(ts_handle handle)
479 {
480         retv_if(!handle, -1);
481
482         _client_call_unregister(handle);
483         _ts_handle_free(handle);
484
485         return 0;
486 }
487
488 static int __ts_send_data(ts_handle handle, const char *json_data)
489 {
490         gboolean connected = FALSE;
491         char *msg = NULL;
492         GError *error = NULL;
493
494         retv_if(!handle, -1);
495         retv_if(!handle->socket, -1);
496         retv_if(!handle->cl_socket, -1);
497         retv_if(!json_data, -1);
498
499         connected = g_socket_is_connected(handle->cl_socket);
500         retv_if(!connected, -1);
501
502         msg = g_strdup_printf("%s %s", json_data, TTD_APP_MSG_DELIMITER);
503         g_socket_send(handle->cl_socket, msg, strlen(msg), NULL, &error);
504         if (error) {
505                 _E("failed to g_socket_send() - %s", error->message);
506                 g_error_free(error);
507                 return -1;
508         }
509         _D("msg is sent - %s", msg);
510         g_free(msg);
511
512         return 0;
513 }
514
515 int things_service_send_data(ts_handle handle, const char *json_data)
516 {
517         return __ts_send_data(handle, json_data);
518 }
519
520 timebased_data *things_service_timebased_data_new(ts_value_type_e type)
521 {
522         timebased_data *t_data = NULL;
523
524         retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
525                 NULL, "invalid type[%d]", type);
526
527         t_data = g_try_malloc0(sizeof(timebased_data));
528         if (!t_data)
529                 return NULL;
530
531         t_data->type = type;
532         t_data->length = 0;
533
534         return t_data;
535 }
536
537 void things_service_timebased_data_free(timebased_data *tb_data)
538 {
539         ret_if(!tb_data);
540
541         if (tb_data->v_list)
542                 g_list_free_full(tb_data->v_list, __timebase_value_free);
543
544         g_free(tb_data);
545 }
546
547 int things_service_timebased_data_get_length(timebased_data *tb_data)
548 {
549         retv_if(!tb_data, -1);
550
551         return tb_data->length;
552 }
553
554 int
555 things_service_timebased_data_append_int(timebased_data *tb_data, int ivalue)
556 {
557         struct __timebased_int *tb_int = NULL;
558
559         retv_if(!tb_data, -1);
560         retv_if(tb_data->type != TS_VALUE_TYPE_INT, -1);
561
562         tb_int = g_try_malloc0(sizeof(struct __timebased_int));
563         retv_if(!tb_int, -1);
564
565         tb_int->i_time = common_get_epoch_coarse_time();
566         tb_int->value = ivalue;
567
568         tb_data->v_list = g_list_append(tb_data->v_list, tb_int);
569         tb_data->length++;
570
571         return 0;
572 }
573
574 int things_service_timebased_data_append_double(
575         timebased_data *tb_data, double dvalue)
576 {
577         struct __timebased_double *tb_double = NULL;
578
579         retv_if(!tb_data, -1);
580         retv_if(tb_data->type != TS_VALUE_TYPE_DOUBLE, -1);
581
582         tb_double = g_try_malloc0(sizeof(struct __timebased_double));
583         retv_if(!tb_double, -1);
584
585         tb_double->i_time = common_get_epoch_coarse_time();
586         tb_double->value = dvalue;
587
588         tb_data->v_list = g_list_append(tb_data->v_list, tb_double);
589         tb_data->length++;
590
591         return 0;
592 }
593
594 int
595 things_service_send_timebased_data(ts_handle handle, timebased_data *tb_data)
596 {
597         int ret = 0;
598         char *json_data = NULL;
599
600         retv_if(!handle, -1);
601         retv_if(!tb_data, -1);
602
603         json_data = __timebased_data_to_json(tb_data);
604         ret  = __ts_send_data(handle, json_data);
605         g_free(json_data);
606
607         if (!ret) { /* if success to send data, remove all items in tb_data */
608                 g_list_free_full(tb_data->v_list, __timebase_value_free);
609                 tb_data->v_list = NULL;
610                 tb_data->length = 0;
611         }
612
613         return ret;
614 }