2 * Copyright (c) 2018 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.
19 #include "ttd-cmd-mgr.h"
21 #include "ttd-cmd-type.h"
22 #include "ttd-cmd-func.h"
24 #include "ttd-parse-cmd.h"
25 #include "ttd-build-json.h"
26 #include "common-util.h"
28 #define CMD_MGR_GET_INTERVAL_SEC (3600 * 3)
29 #define RESULT_WAIT_IN_SEC (10)
30 #define RESULT_WAIT_TIME (RESULT_WAIT_IN_SEC * 1000000)
31 #define RESULT_WAIT_TRY_MAX (10)
33 // #define CMD_FETCH_TEST
35 typedef struct _ttd_result_data_s {
37 ttd_cmd_result_e result;
42 GAsyncQueue *cmd_id_queue;
44 GAsyncQueue *result_queue;
45 const char *current_cmd_id;
48 int get_thread_running;
52 GThread *launch_thread;
55 /* Just a pointer value to make unblocking async queue and exiting thread,
56 * it could be any address as long, as it isn't a valid queue item
58 static const gpointer quit_marker = (gpointer) &ttd_cmd_mgr_init;
61 static struct __cmd_mgr_h *g_handle = NULL;
63 static void __cmd_id_item_free(gpointer data)
68 if (data == quit_marker)
74 static void __result_item_free(gpointer data)
76 ttd_result_data_s *item = data;
81 if (data == quit_marker)
85 _D("free result item[%s]", item->cmd_id);
92 int ttd_cmd_mgr_push_result(const char *id,
93 ttd_cmd_result_e result, const char *data)
95 ttd_result_data_s *result_item = NULL;
96 const char *curr_id = NULL;
98 retvm_if(!g_handle, -1, "cmd mgr is not initialized yet");
99 retvm_if(!id, -1, "cmd id is NULL");
101 g_mutex_lock(&g_handle->mutex);
102 curr_id = g_handle->current_cmd_id;
103 g_mutex_unlock(&g_handle->mutex);
105 retvm_if(!curr_id, -1, "current cmd id is NULL");
107 if (0 != g_strcmp0(id, curr_id)) {
108 _E("invaild cmd id[%s] - current cmd id[%s]", id, curr_id);
112 result_item = g_try_new0(ttd_result_data_s, 1);
114 result_item->cmd_id = g_strdup(id);
115 result_item->result = result;
117 result_item->data = g_strdup(data);
119 g_async_queue_push(g_handle->result_queue, result_item);
124 int ttd_cmd_mgr_get_cmd(void)
126 retvm_if(!g_handle, -1, "cmd mgr is not initialized yet");
127 _D("unblocking get_thread");
128 g_mutex_lock(&g_handle->get_mutex);
129 g_handle->get_by_request = 1;
130 g_cond_broadcast(&g_handle->get_cond);
131 g_mutex_unlock(&g_handle->get_mutex);
132 // wait here to get result of getting cmd or not ???
137 static int _get_cloud_cmd(struct __cmd_mgr_h *handle, long *res_code)
142 GList *cmd_list = NULL;
145 retv_if(!handle, -1);
147 ret = ttd_http_command_get(&cmd, &r_code);
149 _E("failed to get cmd [%ld]", r_code);
158 retvm_if(!cmd, 0, "there is no new cmd now");
160 ret = ttd_parse_json_to_cmd(cmd, &cmd_list);
162 _E("failed to parse cmd");
170 _D("Thers is no command now");
174 for (l = cmd_list; l != NULL; l = l->next) {
175 ttd_cmd_data *cmd_data = NULL;
176 ttd_cmd_data *item = NULL;
177 const char *cmd_id = NULL;
179 cmd_data = (ttd_cmd_data *)l->data;
180 continue_if(!cmd_data);
182 cmd_id = ttd_cmd_get_id(cmd_data);
184 g_mutex_lock(&handle->mutex);
185 item = g_hash_table_lookup(handle->cmd_hash, cmd_id);
187 _D("cmd[%s] is pushed in cmd queue", cmd_id);
188 g_async_queue_push(handle->cmd_id_queue, g_strdup(cmd_id));
189 g_hash_table_insert(handle->cmd_hash, g_strdup(cmd_id), cmd_data);
191 _D("cmd[%s] is already in cmd queue", cmd_id);
192 ttd_cmd_free(cmd_data);
194 g_mutex_unlock(&handle->mutex);
196 g_list_free(cmd_list);
201 static gpointer _get_thread(gpointer data)
203 struct __cmd_mgr_h *handle = data;
205 g_mutex_lock(&handle->get_mutex);
209 unsigned int retry = 0;
211 until = common_get_monotonic_coarse_time() +
212 CMD_MGR_GET_INTERVAL_SEC * G_TIME_SPAN_SECOND;
213 _D("thread blocked for 3 hours");
214 g_cond_wait_until(&handle->get_cond, &handle->get_mutex, until);
215 _D("thread unblocked");
217 if (!handle->get_thread_running)
220 if (handle->get_by_request)
228 ret = _get_cloud_cmd(handle, &res_code);
230 _E("failed to get cmd - %ld", res_code);
232 _D("res_code: %ld", res_code);
234 if (res_code == TTD_HTTP_OK) // HTTP OK - success to get cmd
239 g_usleep(G_USEC_PER_SEC); // sleep in 1 second before retry
242 handle->get_by_request = 0;
244 g_mutex_unlock(&handle->get_mutex);
245 _D("thread terminated");
249 static gpointer _launch_thread(gpointer data)
251 struct __cmd_mgr_h *handle = data;
255 ttd_cmd_data *cmd_data = NULL;
259 ttd_cmd_launch_func launch_func = NULL;
260 ttd_cmd_type_e cmd_type = TTD_CMD_TYPE_UNKNOWN;
263 // get pop oldest cmd from cmd_id_queue
264 _D("block launch thread");
265 cmd_id = g_async_queue_pop(handle->cmd_id_queue);
267 _D("unblock launch thread");
268 _D("cmd queue is empty");
271 _D("unblock launch thread");
273 if (cmd_id == quit_marker)
276 // get cmd data from cmd_hash
277 g_mutex_lock(&handle->mutex);
278 cmd_data = g_hash_table_lookup(handle->cmd_hash, cmd_id);
279 g_mutex_unlock(&handle->mutex);
281 _E("data for cmd[%s] is not exist", cmd_id);
285 // report 'running' state of the cmd id to cloud
286 cmd_type = ttd_cmd_get_type(cmd_data);
287 report = ttd_build_json_create_report(cmd_id, cmd_type,
288 TTD_CMD_STATE_RUNNING, 0, "state update", NULL);
289 ret = ttd_http_report_post(report, &r_code);
290 if (r_code != TTD_HTTP_OK) {
291 _E("failed to post report [%ld]", r_code);
297 ttd_cmd_set_state(cmd_data, TTD_CMD_STATE_RUNNING);
300 launch_func = ttd_cmd_get_launch_func(cmd_type);
302 _E("cmd[%s] no proper launch function", cmd_id);
303 report = ttd_build_json_create_report(cmd_id, cmd_type,
304 TTD_CMD_STATE_FAILED, 0, "no proper launch function", NULL);
305 ttd_http_report_post(report, NULL);
311 g_mutex_lock(&handle->mutex);
312 g_handle->current_cmd_id = cmd_id;
313 g_mutex_unlock(&handle->mutex);
314 ret = launch_func(cmd_data);
316 _E("cmd[%s] launch failed", cmd_id);
317 report = ttd_build_json_create_report(cmd_id, cmd_type,
318 TTD_CMD_STATE_FAILED, 0, "command launch failed", NULL);
319 ttd_http_report_post(report, NULL);
325 while (result_wait) {
326 ttd_result_data_s *result_item = NULL;
327 static unsigned int count = 0;
329 result_item = g_async_queue_timeout_pop(
330 g_handle->result_queue, RESULT_WAIT_TIME); /* 10 sec */
335 if (count <= RESULT_WAIT_TRY_MAX) /* 10 times */
338 /* Wait 100 sec to receive a result,
339 * IS IT(100 sec) "ENOUGH" to install pkgs or task monitoring ???
341 /* timeout to wait result, report fail */
342 report = ttd_build_json_create_report(cmd_id, cmd_type,
343 TTD_CMD_STATE_FAILED, 0, "timeout to wait result ", NULL);
344 ttd_http_report_post(report, NULL);
352 if (result_item == quit_marker)
355 switch (result_item->result) {
356 case TTD_CMD_RESULT_RUNNING:
357 /* report running and wait more result */
358 report = ttd_build_json_create_report(cmd_id, cmd_type,
359 TTD_CMD_STATE_RUNNING, 0,
360 "report in progress", result_item->data);
361 ttd_http_report_post(report, NULL);
363 case TTD_CMD_RESULT_SUCCESS:
365 report = ttd_build_json_create_report(cmd_id, cmd_type,
366 TTD_CMD_STATE_DONE, 0,
367 "done", result_item->data);
368 ttd_http_report_post(report, NULL);
371 case TTD_CMD_RESULT_FAIL:
373 report = ttd_build_json_create_report(cmd_id, cmd_type,
374 TTD_CMD_STATE_FAILED, 0,
375 "failed to process command", result_item->data);
376 ttd_http_report_post(report, NULL);
379 /* unhandled states */
385 __result_item_free(result_item);
390 g_mutex_lock(&handle->mutex);
391 g_handle->current_cmd_id = NULL;
392 g_hash_table_remove(handle->cmd_hash, cmd_id);
393 g_mutex_unlock(&handle->mutex);
399 _D("get quit_marker");
402 _D("thread terminated");
406 static void __free_cmd_mgr_handle(void)
411 if (g_handle->get_thread) {
412 g_mutex_lock(&g_handle->get_mutex);
413 g_handle->get_thread_running = 0;
414 g_cond_broadcast(&g_handle->get_cond);
415 g_mutex_unlock(&g_handle->get_mutex);
417 g_thread_join(g_handle->get_thread);
419 g_mutex_clear(&g_handle->get_mutex);
420 g_cond_clear(&g_handle->get_cond);
422 if (g_handle->launch_thread) {
423 g_async_queue_push(g_handle->cmd_id_queue, quit_marker);
424 g_async_queue_push(g_handle->result_queue, quit_marker);
426 g_thread_join(g_handle->launch_thread);
429 g_mutex_clear(&g_handle->mutex);
431 if (g_handle->result_queue)
432 g_async_queue_unref(g_handle->result_queue);
434 if (g_handle->cmd_id_queue)
435 g_async_queue_unref(g_handle->cmd_id_queue);
437 if (g_handle->cmd_hash) {
438 g_hash_table_remove_all(g_handle->cmd_hash);
439 g_hash_table_unref(g_handle->cmd_hash);
446 #ifdef CMD_FETCH_TEST
447 static gboolean __get_cmd_first_time(gpointer data)
449 ttd_cmd_mgr_get_cmd();
451 return G_SOURCE_REMOVE;
453 #endif /* CMD_FETCH_TEST */
455 int ttd_cmd_mgr_init(void)
457 GError *error = NULL;
459 retvm_if(g_handle, -1, "cmd mgr already initialized, finalized it first");
461 g_handle = g_try_malloc0(sizeof(struct __cmd_mgr_h));
462 retvm_if(!g_handle, -1, "failed to malloc");
464 g_handle->cmd_id_queue = g_async_queue_new_full(__cmd_id_item_free);
465 goto_if(!g_handle->cmd_id_queue, ERROR_N_EXIT);
467 g_handle->cmd_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
468 g_free, (GDestroyNotify)ttd_cmd_free);
469 goto_if(!g_handle->cmd_hash, ERROR_N_EXIT);
471 g_handle->result_queue = g_async_queue_new_full(__result_item_free);
472 goto_if(!g_handle->result_queue, ERROR_N_EXIT);
474 g_mutex_init(&g_handle->mutex);
476 g_mutex_init(&g_handle->get_mutex);
477 g_cond_init(&g_handle->get_cond);
478 g_handle->get_thread_running = 1;
480 g_handle->get_thread =
481 g_thread_try_new(NULL, (GThreadFunc)_get_thread, g_handle, &error);
482 if (!g_handle->get_thread) {
483 _E("failed to create get thread - %s",
484 error && error->message ? error->message : "Unknown Error");
488 g_handle->launch_thread =
489 g_thread_try_new(NULL, (GThreadFunc)_launch_thread, g_handle, &error);
490 if (!g_handle->launch_thread) {
491 _E("failed to create launch_thread - %s",
492 error && error->message ? error->message : "Unknown Error");
496 #ifdef CMD_FETCH_TEST
497 g_idle_add(__get_cmd_first_time, NULL);
498 #endif /* CMD_FETCH_TEST */
505 __free_cmd_mgr_handle();
509 int ttd_cmd_mgr_fini(void)
511 __free_cmd_mgr_handle();