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"
30 #define TEST_DEVICE_ID "test-page-device"
41 GThread *accept_thread;
42 int accept_thread_running;
45 struct __timebased_data {
48 struct json_object *arr_obj;
51 static void __quit_n_join_accept_thread(ts_handle handle);
52 static int _create_accept_thread(ts_handle handle);
54 static char *_get_socket_addr_name(const char *appID, const char *token)
56 return common_make_socket_addr_name(token, appID, (guint)getpid());
59 static char *_get_app_id(void)
67 static char *_get_token(const char *appID, const char *project)
69 char *checksum = NULL;
72 key = g_strdup_printf("%s-%s-%u-%lld",
73 appID, project, getpid(), common_get_monotonic_coarse_time());
74 checksum = g_compute_checksum_for_string(G_CHECKSUM_MD5, key, -1);
81 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
83 ts_handle handle = data;
84 gboolean connected = FALSE;
86 connected = g_socket_is_connected(handle->cl_socket);
88 _E("client socket is disconnected");
91 _E("connection has error");
94 _E("connection has been broken");
96 g_socket_shutdown(handle->cl_socket, FALSE, TRUE, NULL);
97 g_socket_close(handle->cl_socket, NULL);
98 g_object_unref(handle->cl_socket);
100 handle->cl_socket = NULL;
102 _create_accept_thread(handle);
107 static void _ts_handle_free(ts_handle handle)
111 __quit_n_join_accept_thread(handle);
114 g_free(handle->app_id);
117 g_free(handle->project);
120 g_free(handle->token);
122 if (handle->socket) {
123 g_socket_close(handle->socket, NULL);
124 g_object_unref(handle->socket);
128 g_object_unref(handle->addr);
131 g_object_unref(handle->bus);
134 g_object_unref(handle->proxy);
139 static void __quit_n_join_accept_thread(ts_handle handle)
141 if (handle->accept_thread) {
142 g_atomic_int_set(&(handle->accept_thread_running), 0);
143 g_thread_join(handle->accept_thread);
144 handle->accept_thread = NULL;
148 static gpointer accept_thread(gpointer data)
150 ts_handle handle = data;
151 retv_if(!handle, NULL);
154 GSocket *new_socket = NULL;
155 GIOChannel *new_ch = NULL;
157 GError *error = NULL;
159 if (!g_atomic_int_get(&(handle->accept_thread_running)))
162 new_socket = g_socket_accept(handle->socket, NULL, &error);
165 if (error->code != G_IO_ERROR_WOULD_BLOCK)
166 _E("err on g_socket_accept() - %s", error->message);
173 _D("accepted new socket[%p]", new_socket);
174 fd = g_socket_get_fd(new_socket);
175 new_ch = g_io_channel_unix_new(fd);
176 g_io_add_watch(new_ch, (GIOCondition) (G_IO_ERR | G_IO_HUP),
177 __on_disconnect, handle);
178 handle->cl_socket = new_socket;
179 break; /* accept and exit thread */
181 _D("exiting accept thread");
186 static int _create_accept_thread(ts_handle handle)
188 GThread *new_thread = NULL;
189 GError *error = NULL;
191 retv_if(!handle, -1);
193 if (handle->accept_thread)
194 __quit_n_join_accept_thread(handle);
196 g_atomic_int_set(&(handle->accept_thread_running), 1);
197 new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
199 _E("failed to create accept thread - %s", error->message);
203 handle->accept_thread = new_thread;
208 static int __client_method_call(const char *method, ts_handle handle)
210 GVariant *response = NULL;
212 const char *ret_msg = 0;
213 GError *error = NULL;
215 _D("call method [%s]", method);
216 response = g_dbus_proxy_call_sync(handle->proxy,
218 g_variant_new("(sssu)",
219 handle->project, handle->token, handle->app_id, (guint)getpid()),
220 G_DBUS_CALL_FLAGS_NONE,
221 -1, /* The timeout in milliseconds or -1 to use the proxy default timeout. */
222 NULL, /* cancellable */
225 _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
232 g_variant_get(response, "(i&s)", ret, &ret_msg);
233 _D("method[%s] get response ret[%d] - %s", method, ret, ret_msg);
234 g_variant_unref(response);
239 static int _client_call_register(ts_handle handle)
241 return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
244 static int _client_call_unregister(ts_handle handle)
246 return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
249 int things_service_init(ts_handle *handle, const char *project)
251 ts_handle _handle = NULL;
252 GError *error = NULL;
253 char *socket_name = NULL;
255 retv_if(!handle, -1);
257 _handle = g_try_malloc0(sizeof(struct _ts_data));
258 _handle->project = g_strdup(project);
259 _handle->app_id = _get_app_id();
260 if (!_handle->app_id) {
261 _E("failed to get app id");
262 _ts_handle_free(_handle);
265 _D("libthings_service with - project[%s], app[%s]", _handle->app_id);
267 _handle->token = _get_token(_handle->app_id, project);
268 if (!_handle->token) {
269 _E("failed to get token");
270 _ts_handle_free(_handle);
273 _D("get token - %s", _handle->token);
274 _handle->cl_socket = NULL;
275 _handle->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
276 G_SOCKET_TYPE_STREAM,
277 G_SOCKET_PROTOCOL_DEFAULT,
279 if (!_handle->socket) {
280 _E("failed to create socket - %s", error->message);
282 _ts_handle_free(_handle);
285 g_socket_set_blocking(_handle->socket, FALSE);
287 socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
289 _E("failed to get socket_name");
290 _ts_handle_free(_handle);
293 _handle->addr = g_unix_socket_address_new_with_type(
294 socket_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
295 _D("got socket addr - [%s]", socket_name);
298 g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
300 _E("failed to g_socket_bind() - %s", error->message);
302 _ts_handle_free(_handle);
306 g_socket_listen(_handle->socket, &error);
308 _E("failed to g_socket_listen() - %s", error->message);
310 _ts_handle_free(_handle);
315 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
317 _E("failed to g_bus_get_sync() - %s", error->message);
319 _ts_handle_free(_handle);
323 _handle->proxy = g_dbus_proxy_new_sync(_handle->bus,
324 G_DBUS_PROXY_FLAGS_NONE,
325 NULL, /* GDBusInterfaceInfo */
326 TTD_APP_INF_BUS_NAME,
327 TTD_APP_INF_OBJECT_PATH,
329 NULL, /* cancellable */
331 if (!_handle->proxy) {
332 _E("failed to g_dbus_proxy_new_sync() - %s",
333 error ? error->message : "error not set");
334 _ts_handle_free(_handle);
338 if (_create_accept_thread(_handle)) {
339 _E("failed to _create_accept_thread()");
340 _ts_handle_free(_handle);
344 if (_client_call_register(_handle)) {
345 _E("failed to call register");
346 _ts_handle_free(_handle);
355 int things_service_fini(ts_handle handle)
357 retv_if(!handle, -1);
359 _client_call_unregister(handle);
360 _ts_handle_free(handle);
365 static int __ts_send_data(ts_handle handle, const char *json_data)
367 gboolean connected = FALSE;
369 GError *error = NULL;
371 retv_if(!handle, -1);
372 retv_if(!handle->socket, -1);
373 retv_if(!handle->cl_socket, -1);
374 retv_if(!json_data, -1);
376 connected = g_socket_is_connected(handle->cl_socket);
377 retv_if(!connected, -1);
379 msg = g_strdup_printf("%s %s", json_data, TTD_APP_MSG_DELIMITER);
380 g_socket_send(handle->cl_socket, msg, strlen(msg), NULL, &error);
382 _E("failed to g_socket_send() - %s", error->message);
386 _D("msg is sent - %s", msg);
392 int things_service_send_data(ts_handle handle, const char *json_data)
394 return __ts_send_data(handle, json_data);
397 timebased_data *things_service_timebased_data_new(ts_value_type_e type)
399 timebased_data *tb_data = NULL;
401 retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
402 NULL, "invalid type[%d]", type);
404 tb_data = g_try_malloc(sizeof(timebased_data));
405 retv_if(!tb_data, NULL);
407 tb_data->type = type;
409 tb_data->arr_obj = NULL;
414 int things_service_timebased_data_reset(
415 timebased_data *tb_data, ts_value_type_e type)
417 retv_if(!tb_data, -1);
419 retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
420 -1, "invalid type[%d]", type);
422 tb_data->type = type;
425 if (tb_data->arr_obj) {
426 json_object_put(tb_data->arr_obj);
427 tb_data->arr_obj = NULL;
433 void things_service_timebased_data_free(timebased_data *tb_data)
437 if (tb_data->arr_obj)
438 json_object_put(tb_data->arr_obj);
443 int things_service_timebased_data_get_length(timebased_data *tb_data)
445 retv_if(!tb_data, -1);
447 return tb_data->length;
451 things_service_timebased_data_append_int(timebased_data *tb_data, int ivalue)
453 struct json_object *tmp_o = NULL;
454 struct json_object *time_o = NULL;
455 struct json_object *value_o = NULL;
457 retv_if(!tb_data, -1);
458 retv_if(tb_data->type != TS_VALUE_TYPE_INT, -1);
460 if (!tb_data->arr_obj) {
461 struct json_object *obj = NULL;
462 obj = json_object_new_array();
463 retvm_if(!obj, -1, "failed to create json arry object");
464 tb_data->arr_obj = obj;
467 tmp_o = json_object_new_object();
468 retvm_if (!tmp_o, -1, "failed to create json object");
470 time_o = json_object_new_int64(common_get_epoch_coarse_time());
471 goto_if(!time_o, ERROR);
472 value_o = json_object_new_int64(ivalue);
473 goto_if(!value_o, ERROR);
475 json_object_object_add(tmp_o, "x", time_o);
476 json_object_object_add(tmp_o, "y", value_o);
477 json_object_array_add(tb_data->arr_obj, tmp_o);
485 json_object_put(tmp_o);
488 json_object_put(time_o);
493 int things_service_timebased_data_append_double(
494 timebased_data *tb_data, double dvalue)
496 struct json_object *tmp_o = NULL;
497 struct json_object *time_o = NULL;
498 struct json_object *value_o = NULL;
500 retv_if(!tb_data, -1);
501 retv_if(tb_data->type != TS_VALUE_TYPE_DOUBLE, -1);
503 if (!tb_data->arr_obj) {
504 struct json_object *obj = NULL;
505 obj = json_object_new_array();
506 retvm_if(!obj, -1, "failed to create json arry object");
507 tb_data->arr_obj = obj;
510 tmp_o = json_object_new_object();
511 retvm_if (!tmp_o, -1, "failed to create json object");
513 time_o = json_object_new_int64(common_get_epoch_coarse_time());
514 goto_if(!time_o, ERROR);
515 value_o = json_object_new_double(dvalue);
516 goto_if(!value_o, ERROR);
518 json_object_object_add(tmp_o, "x", time_o);
519 json_object_object_add(tmp_o, "y", value_o);
520 json_object_array_add(tb_data->arr_obj, tmp_o);
528 json_object_put(tmp_o);
531 json_object_put(time_o);
536 int things_service_send_timebased_data(
537 ts_handle handle, timebased_data *tb_data, const char *project_key)
540 struct json_object *msg_o = NULL;
541 struct json_object *key_o = NULL;
543 retv_if(!handle, -1);
544 retv_if(!tb_data, -1);
545 retv_if(!tb_data->arr_obj, -1);
546 retv_if(!project_key, -1);
548 msg_o = json_object_new_object();
549 retvm_if(!msg_o, -1, "failed to create json object for message");
551 key_o = json_object_new_string(project_key);
553 _E("failed to create json object for project_key");
554 json_object_put(msg_o);
558 json_object_object_add(msg_o, "key", key_o);
559 json_object_object_add(msg_o, "data", tb_data->arr_obj);
560 #ifdef TEST_DEVICE_ID
561 /* only for test, SHOULD BE REMOVED after device auth method is applied on cloud */
562 struct json_object *did_o = json_object_new_string(TEST_DEVICE_ID);
563 json_object_object_add(msg_o, "deviceId", did_o);
564 #endif /* TEST_DEVICE_ID */
566 ret = __ts_send_data(handle, json_object_to_json_string(msg_o));
568 _E("failed to send data");
569 /* keep appended data */
570 json_object_get(tb_data->arr_obj);
571 } else { /* if success to send data, remove all items in tb_data */
572 tb_data->arr_obj = NULL;
576 json_object_put(msg_o);