2 * Copyright (c) 2017 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.
17 #include <sys/types.h>
22 #include <gio/gunixsocketaddress.h>
23 #include <app_common.h>
25 #include "log-private.h"
26 #include "things-service.h"
27 #include "common-app-inf.h"
28 #include "common-util.h"
39 GThread *accept_thread;
40 int accept_thread_running;
43 struct __timebased_int {
48 struct __timebased_double {
53 struct __timebased_data {
59 static void __quit_n_join_accept_thread(ts_handle handle);
60 static int _create_accept_thread(ts_handle handle);
62 static void __timebase_value_free(gpointer data)
68 __add_timebased_int_to_json_object(gpointer data, gpointer user_data)
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;
79 obj = json_object_new_object();
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);
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);
90 json_object_array_add(a_obj, obj);
99 __add_timebased_double_to_json_object(gpointer data, gpointer user_data)
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;
110 obj = json_object_new_object();
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);
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);
121 json_object_array_add(a_obj, obj);
126 json_object_put(obj);
130 static char *__timebased_data_to_json(timebased_data *tb_data)
132 struct json_object *obj = NULL;
133 struct json_object *a_obj = NULL;
134 char *json_str = NULL;
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);
141 obj = json_object_new_object();
144 a_obj = json_object_new_array();
145 goto_if(!a_obj, ERROR);
146 json_object_object_add(obj, "data", a_obj);
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);
153 case TS_VALUE_TYPE_DOUBLE:
154 g_list_foreach(tb_data->v_list,
155 __add_timebased_double_to_json_object, a_obj);
158 _E("invalid type [%d]", tb_data->type);
163 json_str = g_strdup(json_object_to_json_string(obj));
166 json_object_put(obj);
172 json_object_put(obj);
177 static char *_get_socket_addr_name(const char *appID, const char *token)
179 return common_make_socket_addr_name(token, appID, (guint)getpid());
182 static char *_get_app_id(void)
190 static char *_get_token(const char *appID)
192 char *checksum = NULL;
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);
204 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
206 ts_handle handle = data;
207 gboolean connected = FALSE;
209 connected = g_socket_is_connected(handle->cl_socket);
211 _E("client socket is disconnected");
213 if (cond == G_IO_ERR)
214 _E("connection has error");
216 if (cond == G_IO_HUP)
217 _E("connection has been broken");
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);
223 handle->cl_socket = NULL;
225 _create_accept_thread(handle);
230 static void _ts_handle_free(ts_handle handle)
234 __quit_n_join_accept_thread(handle);
237 g_free(handle->app_id);
240 g_free(handle->project);
243 g_free(handle->token);
245 if (handle->socket) {
246 g_socket_close(handle->socket, NULL);
247 g_object_unref(handle->socket);
251 g_object_unref(handle->addr);
254 g_object_unref(handle->bus);
257 g_object_unref(handle->proxy);
262 static void __quit_n_join_accept_thread(ts_handle handle)
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;
271 static gpointer accept_thread(gpointer data)
273 ts_handle handle = data;
274 retv_if(!handle, NULL);
277 GSocket *new_socket = NULL;
278 GIOChannel *new_ch = NULL;
280 GError *error = NULL;
282 if (!g_atomic_int_get(&(handle->accept_thread_running)))
285 new_socket = g_socket_accept(handle->socket, NULL, &error);
288 if (error->code != G_IO_ERROR_WOULD_BLOCK)
289 _E("err on g_socket_accept() - %s", error->message);
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 */
304 _D("exiting accept thread");
309 static int _create_accept_thread(ts_handle handle)
311 GThread *new_thread = NULL;
312 GError *error = NULL;
314 retv_if(!handle, -1);
316 if (handle->accept_thread)
317 __quit_n_join_accept_thread(handle);
319 g_atomic_int_set(&(handle->accept_thread_running), 1);
320 new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
322 _E("failed to create accept thread - %s", error->message);
326 handle->accept_thread = new_thread;
331 static int __client_method_call(const char *method, ts_handle handle)
333 GVariant *response = NULL;
335 const char *ret_msg = 0;
336 GError *error = NULL;
338 _D("call method [%s]", method);
339 response = g_dbus_proxy_call_sync(handle->proxy,
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 */
348 _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
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);
362 static int _client_call_register(ts_handle handle)
364 return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
367 static int _client_call_unregister(ts_handle handle)
369 return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
372 int things_service_init(ts_handle *handle, const char *project)
374 ts_handle _handle = NULL;
375 GError *error = NULL;
376 char *socket_name = NULL;
378 retv_if(!handle, -1);
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);
388 _D("libthings_service with - project[%s], app[%s]", _handle->app_id);
390 _handle->token = _get_token(_handle->app_id);
391 if (!_handle->token) {
392 _E("failed to get token");
393 _ts_handle_free(_handle);
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,
402 if (!_handle->socket) {
403 _E("failed to create socket - %s", error->message);
405 _ts_handle_free(_handle);
408 g_socket_set_blocking(_handle->socket, FALSE);
410 socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
412 _E("failed to get socket_name");
413 _ts_handle_free(_handle);
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);
421 g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
423 _E("failed to g_socket_bind() - %s", error->message);
425 _ts_handle_free(_handle);
429 g_socket_listen(_handle->socket, &error);
431 _E("failed to g_socket_listen() - %s", error->message);
433 _ts_handle_free(_handle);
438 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
440 _E("failed to g_bus_get_sync() - %s", error->message);
442 _ts_handle_free(_handle);
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,
452 NULL, /* cancellable */
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);
461 if (_create_accept_thread(_handle)) {
462 _E("failed to _create_accept_thread()");
463 _ts_handle_free(_handle);
467 if (_client_call_register(_handle)) {
468 _E("failed to call register");
469 _ts_handle_free(_handle);
478 int things_service_fini(ts_handle handle)
480 retv_if(!handle, -1);
482 _client_call_unregister(handle);
483 _ts_handle_free(handle);
488 static int __ts_send_data(ts_handle handle, const char *json_data)
490 gboolean connected = FALSE;
492 GError *error = NULL;
494 retv_if(!handle, -1);
495 retv_if(!handle->socket, -1);
496 retv_if(!handle->cl_socket, -1);
497 retv_if(!json_data, -1);
499 connected = g_socket_is_connected(handle->cl_socket);
500 retv_if(!connected, -1);
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);
505 _E("failed to g_socket_send() - %s", error->message);
509 _D("msg is sent - %s", msg);
515 int things_service_send_data(ts_handle handle, const char *json_data)
517 return __ts_send_data(handle, json_data);
520 timebased_data *things_service_timebased_data_new(ts_value_type_e type)
522 timebased_data *t_data = NULL;
524 retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
525 NULL, "invalid type[%d]", type);
527 t_data = g_try_malloc0(sizeof(timebased_data));
537 void things_service_timebased_data_free(timebased_data *tb_data)
542 g_list_free_full(tb_data->v_list, __timebase_value_free);
547 int things_service_timebased_data_get_length(timebased_data *tb_data)
549 retv_if(!tb_data, -1);
551 return tb_data->length;
555 things_service_timebased_data_append_int(timebased_data *tb_data, int ivalue)
557 struct __timebased_int *tb_int = NULL;
559 retv_if(!tb_data, -1);
560 retv_if(tb_data->type != TS_VALUE_TYPE_INT, -1);
562 tb_int = g_try_malloc0(sizeof(struct __timebased_int));
563 retv_if(!tb_int, -1);
565 tb_int->i_time = common_get_epoch_coarse_time();
566 tb_int->value = ivalue;
568 tb_data->v_list = g_list_append(tb_data->v_list, tb_int);
574 int things_service_timebased_data_append_double(
575 timebased_data *tb_data, double dvalue)
577 struct __timebased_double *tb_double = NULL;
579 retv_if(!tb_data, -1);
580 retv_if(tb_data->type != TS_VALUE_TYPE_DOUBLE, -1);
582 tb_double = g_try_malloc0(sizeof(struct __timebased_double));
583 retv_if(!tb_double, -1);
585 tb_double->i_time = common_get_epoch_coarse_time();
586 tb_double->value = dvalue;
588 tb_data->v_list = g_list_append(tb_data->v_list, tb_double);
595 things_service_send_timebased_data(ts_handle handle, timebased_data *tb_data)
598 char *json_data = NULL;
600 retv_if(!handle, -1);
601 retv_if(!tb_data, -1);
603 json_data = __timebased_data_to_json(tb_data);
604 ret = __ts_send_data(handle, json_data);
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;