Add worker thread for cleanup app 89/240589/1
authorHwankyu Jhun <h.jhun@samsung.com>
Mon, 10 Aug 2020 00:34:05 +0000 (09:34 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Mon, 10 Aug 2020 00:34:05 +0000 (09:34 +0900)
To avoid blocking launchpad, a new worker thread is added for
calling security_manager_cleanup_app().
While installing / uninstalling / updating the package, security-manager
daemon can be blocked. If launchpad calls security_manager_cleanup_app()
at that time, launchpad will be blocked.

Change-Id: I1e88ec38f61eabe21afa754f39f511606c85f148
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/launchpad/inc/launchpad_worker.h [new file with mode: 0644]
src/launchpad/src/launchpad.c
src/launchpad/src/launchpad_worker.c [new file with mode: 0644]

diff --git a/src/launchpad/inc/launchpad_worker.h b/src/launchpad/inc/launchpad_worker.h
new file mode 100644 (file)
index 0000000..cee3000
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LAUNCHPAD_WORKER_H__
+#define __LAUNCHPAD_WORKER_H__
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef bool (*worker_job_cb)(void *user_data);
+
+int _worker_add_job(worker_job_cb callback, void *user_data);
+
+int _worker_init(void);
+
+void _worker_fini(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LAUNCHPAD_WORKER_H__ */
index 804aa23..b74dbb2 100644 (file)
@@ -54,6 +54,7 @@
 #include "launchpad_proc.h"
 #include "launchpad_signal.h"
 #include "launchpad_types.h"
+#include "launchpad_worker.h"
 #include "loader_info.h"
 #include "perf.h"
 
@@ -159,6 +160,11 @@ struct app_info {
        bool exists;
 };
 
+struct cleanup_info_s {
+       char *appid;
+       int pid;
+};
+
 static int __sys_hwacc;
 static GList *loader_info_list;
 static GList *app_defined_loader_info_list;
@@ -1510,16 +1516,68 @@ static bool __handle_hydra_event(int fd, io_condition_e cond, void *data)
        return true;
 }
 
+static void __destroy_cleanup_info(struct cleanup_info_s *info)
+{
+       if (!info)
+               return;
+
+       free(info->appid);
+       free(info);
+}
+
+static struct cleanup_info_s *__create_cleanup_info(const char *appid, int pid)
+{
+       struct cleanup_info_s *info;
+
+       info = malloc(sizeof(struct cleanup_info_s));
+       if (!info) {
+               _E("Out of memory");
+               return NULL;
+       }
+
+       info->appid = strdup(appid);
+       if (!info->appid) {
+               _E("strdup(%s) is failed", appid);
+               __destroy_cleanup_info(info);
+               return NULL;
+       }
+
+       info->pid = pid;
+
+       return info;
+}
+
+static bool __cleanup_app_cb(void *user_data)
+{
+       struct cleanup_info_s *info = (struct cleanup_info_s *)user_data;
+
+       _W("security_manager_cleanup_app() ++");
+       security_manager_cleanup_app(info->appid, getuid(), info->pid);
+       _W("security_manager_cleanup_app() --");
+       __destroy_cleanup_info(info);
+       return false;
+}
+
 static void __handle_sigchild(int pid, void *user_data)
 {
        candidate_process_context_t *cpc;
+       struct cleanup_info_s *info;
        char *appid;
+       int ret = -1;
 
        appid = g_hash_table_lookup(__pid_table, GINT_TO_POINTER(pid));
        if (appid) {
-               _W("security_manager_cleanup_app() ++");
-               security_manager_cleanup_app(appid, getuid(), pid);
-               _W("security_manager_cleanup_app() --");
+               info = __create_cleanup_info(appid, pid);
+               if (info)
+                       ret = _worker_add_job(__cleanup_app_cb, info);
+
+               if (ret != 0) {
+                       __destroy_cleanup_info(info);
+                       _W("security_manager_cleanup_app() ++");
+                       security_manager_cleanup_app(appid, getuid(), pid);
+                       _W("security_manager_cleanup_app() --");
+               }
+
                g_hash_table_remove(__pid_table, GINT_TO_POINTER(pid));
        }
 
@@ -3077,6 +3135,10 @@ static int __before_loop(int argc, char **argv)
                return -1;
        }
 
+       ret = _worker_init();
+       if (ret < 0)
+               return ret;
+
        __register_vconf_events();
        __init_app_defined_loader_monitor();
        _memory_monitor_init();
@@ -3091,6 +3153,7 @@ static void __after_loop(void)
        _log_fini();
        _memory_monitor_fini();
        __unregister_vconf_events();
+       _worker_fini();
        if (__pid_table)
                g_hash_table_destroy(__pid_table);
 
diff --git a/src/launchpad/src/launchpad_worker.c b/src/launchpad/src/launchpad_worker.c
new file mode 100644 (file)
index 0000000..3f600d7
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "launchpad_worker.h"
+#include "log_private.h"
+
+struct job_s {
+       worker_job_cb callback;
+       void *user_data;
+};
+
+struct worker_s {
+       GThread *thread;
+       GMutex mutex;
+       GCond cond;
+       GQueue *queue;
+};
+
+static struct worker_s __worker;
+
+int _worker_add_job(worker_job_cb callback, void *user_data)
+{
+       struct job_s *job;
+
+       if (!callback) {
+               _E("Invalid parameter");
+               return -EINVAL;
+       }
+
+       job = malloc(sizeof(struct job_s));
+       if (!job) {
+               _E("Out of memory");
+               return -ENOMEM;
+       }
+
+       job->callback = callback;
+       job->user_data = user_data;
+
+       g_mutex_lock(&__worker.mutex);
+       g_queue_push_tail(__worker.queue, job);
+       g_cond_signal(&__worker.cond);
+       g_mutex_unlock(&__worker.mutex);
+
+       return 0;
+}
+
+static int __set_comm(const char *name)
+{
+       int fd;
+       ssize_t bytes_written;
+       char path[PATH_MAX];
+       pid_t tid = syscall(__NR_gettid);
+
+       _I("[%s] TID(%d)", name, tid);
+       snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+       fd = open(path, O_WRONLY);
+       if (fd < 0) {
+               _E("Failed to open %s. error(%d)", path, errno);
+               return -1;
+       }
+
+       bytes_written = write(fd, name, strlen(name) + 1);
+       if (bytes_written < 0) {
+               _E("Failed to write name(%s)", name);
+               close(fd);
+               return -1;
+       }
+
+       close(fd);
+       return 0;
+}
+
+static gpointer __worker_thread_cb(gpointer data)
+{
+       struct worker_s *worker = (struct worker_s *)data;
+       struct job_s *job;
+       bool done = false;
+
+       __set_comm("worker");
+       do {
+               g_mutex_lock(&worker->mutex);
+               if (g_queue_is_empty(worker->queue))
+                       g_cond_wait(&worker->cond, &worker->mutex);
+
+               job = (struct job_s *)g_queue_pop_head(worker->queue);
+               g_mutex_unlock(&worker->mutex);
+               done = job->callback(job->user_data);
+               free(job);
+       } while (!done);
+
+       return NULL;
+}
+
+int _worker_init(void)
+{
+       _W("WORKER_INIT");
+
+       g_mutex_init(&__worker.mutex);
+       g_cond_init(&__worker.cond);
+
+       __worker.queue = g_queue_new();
+       if (!__worker.queue) {
+               _E("g_queue_new() is failed");
+               return -ENOMEM;
+       }
+
+       __worker.thread = g_thread_new("worker", __worker_thread_cb, &__worker);
+       if (!__worker.thread) {
+               _E("g_thread_new() is failed");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static bool __worker_done_cb(void *user_data)
+{
+       _W("Done");
+       return true;
+}
+
+void _worker_fini(void)
+{
+       _W("WORKER_FINI");
+
+       if (__worker.thread) {
+               _worker_add_job(__worker_done_cb, NULL);
+               g_thread_join(__worker.thread);
+       }
+
+       if (__worker.queue)
+               g_queue_free_full(__worker.queue, (GDestroyNotify)free);
+
+       g_cond_clear(&__worker.cond);
+       g_mutex_clear(&__worker.mutex);
+}