implements functions for application library
[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 "log-private.h"
25 #include "things-service.h"
26 #include "common-app-inf.h"
27 #include "common-util.h"
28
29 struct _ts_data {
30         char *app_id;
31         char *token;
32         GSocket *socket;
33         GSocketAddress *addr;
34         GSocket *cl_socket;
35         GDBusConnection *bus;
36         GDBusProxy *proxy;
37         GThread *accept_thread;
38         int accept_thread_running;
39 };
40
41 static void __quit_n_join_accept_thread(ts_handle handle);
42 static int _create_accept_thread(ts_handle handle);
43
44 static char *_get_socket_addr_name(const char *appID, const char *token)
45 {
46         return common_make_socket_addr_name(token, appID, (guint)getpid());
47 }
48
49 static char *_get_app_id(void)
50 {
51         char *app_id = NULL;
52         app_get_id(&app_id);
53
54         return app_id;
55 }
56
57 static char *_get_token(const char *appID)
58 {
59         char *checksum = NULL;
60         char *key = NULL;
61
62         key = g_strdup_printf("%s-%u-%llu",
63                         appID, getpid(), common_get_monotonic_coarse_time());
64         checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, key, -1);
65         g_free(key);
66
67         return checksum;
68 }
69
70 static gboolean
71 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
72 {
73         ts_handle handle = data;
74         gboolean connected = FALSE;
75
76         connected = g_socket_is_connected(handle->cl_socket);
77         if (!connected)
78                 _E("client socket is disconnected");
79
80         if (cond == G_IO_ERR)
81                 _E("connection has error");
82
83         if (cond == G_IO_HUP)
84                 _E("connection has been broken");
85
86         g_socket_shutdown(handle->cl_socket, FALSE, TRUE, NULL);
87         g_socket_close(handle->cl_socket, NULL);
88         g_object_unref(handle->cl_socket);
89
90         handle->cl_socket = NULL;
91
92         _create_accept_thread(handle);
93
94         return FALSE;
95 }
96
97 static void _ts_handle_free(ts_handle handle)
98 {
99         ret_if(!handle);
100
101         __quit_n_join_accept_thread(handle);
102
103         if (handle->app_id)
104                 g_free(handle->app_id);
105
106         if (handle->token)
107                 g_free(handle->token);
108
109         if (handle->socket) {
110                 g_socket_close(handle->socket, NULL);
111                 g_object_unref(handle->socket);
112         }
113
114         if (handle->addr)
115                 g_object_unref(handle->addr);
116
117         if (handle->bus)
118                 g_object_unref(handle->bus);
119
120         if (handle->proxy)
121                 g_object_unref(handle->proxy);
122
123         g_free(handle);
124 }
125
126 static void __quit_n_join_accept_thread(ts_handle handle)
127 {
128         if (handle->accept_thread) {
129                 g_atomic_int_set(&(handle->accept_thread_running), 0);
130                 g_thread_join(handle->accept_thread);
131                 handle->accept_thread = NULL;
132         }
133 }
134
135 static gpointer accept_thread(gpointer data)
136 {
137         ts_handle handle = data;
138         retv_if(!handle, NULL);
139
140         while (TRUE) {
141                 GSocket *new_socket = NULL;
142                 GIOChannel *new_ch = NULL;
143                 int fd = 0;
144                 GError *error = NULL;
145
146                 if (!g_atomic_int_get(&(handle->accept_thread_running)))
147                         break;
148
149                 new_socket = g_socket_accept(handle->socket, NULL, &error);
150                 if (!new_socket) {
151                         if (error) {
152                                 if (error->code !=  G_IO_ERROR_WOULD_BLOCK)
153                                         _E("err on g_socket_accept() - %s", error->message);
154
155                                 g_error_free(error);
156                         }
157                         continue;
158                 }
159
160                 _D("accepted new socket[%p]", new_socket);
161                 fd = g_socket_get_fd(new_socket);
162                 new_ch = g_io_channel_unix_new(fd);
163                 g_io_add_watch(new_ch, (GIOCondition) (G_IO_ERR | G_IO_HUP),
164                         __on_disconnect, handle);
165                 handle->cl_socket = new_socket;
166                 break; /* accept and exit thread */
167         }
168         _D("exiting accept thread");
169
170         return NULL;
171 }
172
173 static int _create_accept_thread(ts_handle handle)
174 {
175         GThread *new_thread = NULL;
176         GError *error = NULL;
177
178         retv_if(!handle, -1);
179
180         if (handle->accept_thread)
181                 __quit_n_join_accept_thread(handle);
182
183         g_atomic_int_set(&(handle->accept_thread_running), 1);
184         new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
185         if (!new_thread) {
186                 _E("failed to create accept thread - %s", error->message);
187                 g_error_free(error);
188                 return -1;
189         }
190
191         return 0;
192 }
193
194 static int __client_method_call(const char *method, ts_handle handle)
195 {
196         GVariant *response = NULL;
197         int ret = 0;
198         GError *error = NULL;
199
200         _D("call method [%s]", method);
201         response = g_dbus_proxy_call_sync(handle->proxy,
202                 method,
203                 g_variant_new("(ssu)", handle->token, handle->app_id, (guint)getpid()),
204                 G_DBUS_CALL_FLAGS_NONE,
205                 -1, /* The timeout in milliseconds or -1 to use the proxy default timeout. */
206                 NULL, /* cancellable */
207                 &error);
208         if (error) {
209                 _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
210                 g_error_free(error);
211                 return -1;
212         }
213         g_variant_get(response, "(i)", ret);
214         _D("method[%s] get response ret[%d]", method, ret);
215         g_variant_unref(response);
216
217         return 0;
218 }
219
220 static int _client_call_register(ts_handle handle)
221 {
222         return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
223 }
224
225 static int _client_call_unregister(ts_handle handle)
226 {
227         return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
228 }
229
230 int things_service_init(ts_handle *handle)
231 {
232         ts_handle _handle = NULL;
233         GError *error = NULL;
234         char *socket_name = NULL;
235
236         retv_if(!handle, -1);
237
238         _handle = g_try_malloc0(sizeof(ts_handle));
239         _handle->app_id = _get_app_id();
240         if (!_handle->app_id) {
241                 _E("failed to get app id");
242                 _ts_handle_free(_handle);
243                 return -1;
244         }
245         _D("libthings_service with - %s", _handle->app_id);
246
247         _handle->token = _get_token(_handle->app_id);
248         if (!_handle->token) {
249                 _E("failed to get token");
250                 _ts_handle_free(_handle);
251                 return -1;
252         }
253         _D("get token - %s", _handle->token);
254         _handle->cl_socket = NULL;
255         _handle->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
256                                 G_SOCKET_TYPE_STREAM,
257                                 G_SOCKET_PROTOCOL_DEFAULT,
258                                 &error);
259         if (!_handle->socket) {
260                 _E("failed to create socket - %s", error->message);
261                 g_error_free(error);
262                 _ts_handle_free(_handle);
263                 return -1;
264         }
265         g_socket_set_blocking(_handle->socket, FALSE);
266
267         socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
268         if (!socket_name) {
269                 _E("failed to get socket_name");
270                 _ts_handle_free(_handle);
271                 return -1;
272         }
273         _handle->addr = g_unix_socket_address_new_with_type(
274                         socket_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
275         _D("got socket addr - [%s]", socket_name);
276         g_free(socket_name);
277
278         g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
279         if (error) {
280                 _E("failed to g_socket_bind() - %s", error->message);
281                 g_error_free(error);
282                 _ts_handle_free(_handle);
283                 return -1;
284         }
285
286         g_socket_listen(_handle->socket, &error);
287         if (error) {
288                 _E("failed to g_socket_listen() - %s", error->message);
289                 g_error_free(error);
290                 _ts_handle_free(_handle);
291                 return -1;
292         }
293
294         _handle->bus =
295                 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
296         if (!_handle->bus) {
297                 _E("failed to g_bus_get_sync() - %s", error->message);
298                 g_error_free(error);
299                 _ts_handle_free(_handle);
300                 return -1;
301         }
302
303         _handle->proxy = g_dbus_proxy_new_sync(_handle->bus,
304                 G_DBUS_PROXY_FLAGS_NONE,
305                 NULL, /* GDBusInterfaceInfo */
306                 TTD_APP_INF_BUS_NAME,
307                 TTD_APP_INF_OBJECT_PATH,
308                 TTD_APP_INF_BUS_INF,
309                 NULL, /* cancellable */
310                 &error);
311         if (!_handle->proxy) {
312                 _E("failed to g_dbus_proxy_new_sync() - %s",
313                         error ? error->message : "error not set");
314                 _ts_handle_free(_handle);
315                 return -1;
316         }
317
318         if (_create_accept_thread(_handle)) {
319                 _E("failed to _create_accept_thread()");
320                 _ts_handle_free(_handle);
321                 return -1;
322         }
323
324         if (_client_call_register(_handle)) {
325                 _E("failed to call register");
326                 _ts_handle_free(_handle);
327                 return -1;
328         }
329
330         *handle = _handle;
331
332         return 0;
333 }
334
335 int things_service_fini(ts_handle handle)
336 {
337         retv_if(!handle, -1);
338
339         _client_call_unregister(handle);
340         _ts_handle_free(handle);
341
342         return 0;
343 }
344
345 int things_service_send_data(ts_handle handle, const char *json_data)
346 {
347         gboolean connected = FALSE;
348         char *msg = NULL;
349         GError *error = NULL;
350
351         retv_if(!handle, -1);
352         retv_if(!handle->socket, -1);
353         retv_if(!handle->cl_socket, -1);
354         retv_if(!json_data, -1);
355
356         connected = g_socket_is_connected(handle->cl_socket);
357         retv_if(!connected, -1);
358
359         msg = g_strdup_printf("%s %s", json_data, TTD_APP_MSG_DELIMITER);
360         g_socket_send(handle->cl_socket, msg, strlen(msg), NULL, &error);
361         if (error) {
362                 _E("failed to g_socket_send() - %s", error->message);
363                 g_error_free(error);
364                 return -1;
365         }
366         _D("msg is sent - %s", msg);
367         g_free(msg);
368
369         return 0;
370 }