Fix signal handler for handling all dead childs
[platform/core/appfw/pkgmgr-server.git] / src / pkgmgr-server.c
index f55c88d..7f6a2ae 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/signalfd.h>
 #include <signal.h>
 #include <grp.h>
 #include <fcntl.h>
@@ -81,15 +82,10 @@ is dynamically determined.
 */
 static char backend_busy = 0;
 extern int num_of_backends;
+bool is_drm_busy = false;
 
-struct signal_info_t {
-       pid_t pid;
-       int status;
-};
-
-static int pipe_sig[2];
-static GIOChannel *pipe_io;
-static guint pipe_wid;
+static GIOChannel *sio;
+static guint swid;
 static GHashTable *backend_info_table;
 static GMainLoop *mainloop;
 
@@ -98,7 +94,6 @@ static int __check_queue_status_for_exit(void);
 static int __is_backend_busy(int position);
 static void __set_backend_busy(int position);
 static void __set_backend_free(int position);
-static void sighandler(int signo);
 
 gboolean exit_server(void *data);
 
@@ -179,50 +174,56 @@ static int __setup_size_info_io(struct backend_job *job)
        return 0;
 }
 
-static gboolean pipe_io_handler(GIOChannel *io, GIOCondition cond,
+static gboolean __signal_handler(GIOChannel *io, GIOCondition cond,
                gpointer data)
 {
        GError *err = NULL;
        GIOStatus s;
        gsize len;
-       struct signal_info_t info;
+       struct signalfd_siginfo fdsi;
        struct backend_job *job;
+       pid_t pid;
+       int status;
 
-       s = g_io_channel_read_chars(io, (gchar *)&info,
-                       sizeof(struct signal_info_t), &len, &err);
-       if (s != G_IO_STATUS_NORMAL) {
-               ERR("Signal pipe read failed: %s", err->message);
+       s = g_io_channel_read_chars(io, (gchar *)&fdsi,
+                       sizeof(struct signalfd_siginfo), &len, &err);
+       if (s != G_IO_STATUS_NORMAL || len != sizeof(struct signalfd_siginfo)) {
+               ERR("Signal read failed");
                g_error_free(err);
                return TRUE;
        }
 
-       job = (struct backend_job *)g_hash_table_lookup(backend_info_table,
-                       (gconstpointer)info.pid);
-       if (job == NULL) {
-               ERR("Unknown child exit");
-               return -1;
-       }
+       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+               job = (struct backend_job *)g_hash_table_lookup(
+                       backend_info_table, (gconstpointer)pid);
+               if (job == NULL) {
+                       ERR("Unknown child exit");
+                       continue;
+               }
 
-       __set_backend_free(job->backend_slot);
-       if (WIFSIGNALED(info.status)) {
-               _send_fail_signal(job);
-               DBG("backend[%s] exit with signal[%d]", job->backend_type,
-                               WTERMSIG(info.status));
-       } else if (WEXITSTATUS(info.status)) {
-               DBG("backend[%s] exit with error", job->backend_type);
-       } else {
-               DBG("backend[%s] exit", job->backend_type);
-       }
+               __set_backend_free(job->backend_slot);
+               if (WIFSIGNALED(status)) {
+                       _send_fail_signal(job);
+                       INFO("backend[%s][%d] exit with signal[%d]",
+                               job->backend_type, pid, WTERMSIG(status));
+               } else if (WEXITSTATUS(status)) {
+                       INFO("backend[%s][%d] exit with error",
+                               job->backend_type, pid);
+               } else {
+                       INFO("backend[%s][%d] exit", job->backend_type, pid);
+               }
 
-       if (job->extra) {
-               if (job->extra->getsize_fifo) {
-                       ERR("invalid backend close");
-                       _return_value_to_caller(job->req_id,
-                               g_variant_new("(ix)", PKGMGR_R_ERROR, -1));
+               if (job->extra) {
+                       if (job->extra->getsize_fifo) {
+                               ERR("invalid backend close");
+                               _return_value_to_caller(job->req_id,
+                                       g_variant_new("(ix)", PKGMGR_R_ERROR,
+                                       -1));
+                       }
                }
-       }
 
-       g_hash_table_remove(backend_info_table, (gconstpointer)info.pid);
+               g_hash_table_remove(backend_info_table, (gconstpointer)pid);
+       }
        g_idle_add(queue_job, NULL);
 
        return TRUE;
@@ -234,60 +235,42 @@ static int __init_backend_info(void)
                        g_direct_equal, NULL,
                        (GDestroyNotify)_free_backend_job);
 
-       if (pipe(pipe_sig)) {
-               ERR("create pipe failed");
-               return -1;
-       }
-
-       pipe_io = g_io_channel_unix_new(pipe_sig[0]);
-       g_io_channel_set_encoding(pipe_io, NULL, NULL);
-       g_io_channel_set_buffered(pipe_io, FALSE);
-       pipe_wid = g_io_add_watch(pipe_io, G_IO_IN, pipe_io_handler, NULL);
-
        return 0;
 }
 
 static void __fini_backend_info(void)
 {
-       g_source_remove(pipe_wid);
-       g_io_channel_unref(pipe_io);
-       close(pipe_sig[0]);
-       close(pipe_sig[1]);
-
        /*Free backend info */
        g_hash_table_destroy(backend_info_table);
 }
 
-static void sighandler(int signo)
-{
-       struct signal_info_t info;
-       char buf[1024] = {0, };
-
-       info.pid = waitpid(-1, &info.status, WNOHANG);
-       if (write(pipe_sig[1], &info, sizeof(struct signal_info_t)) < 0)
-               ERR("failed to write result: %s",
-                               strerror_r(errno, buf, sizeof(buf)));
-}
-
 static int __register_signal_handler(void)
 {
-       static int sig_reg = 0;
-       struct sigaction act;
+       sigset_t mask;
+       int sfd;
 
-       if (sig_reg)
-               return 0;
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGCHLD);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
+               ERR("sigprocmask failed");
+               return -1;
+       }
 
-       act.sa_handler = sighandler;
-       sigemptyset(&act.sa_mask);
-       act.sa_flags = SA_NOCLDSTOP;
-       if (sigaction(SIGCHLD, &act, NULL) < 0) {
-               ERR("signal: SIGCHLD failed");
+       sfd = signalfd(-1, &mask, SFD_NONBLOCK);
+       if (sfd == -1) {
+               ERR("signalfd failed");
                return -1;
        }
 
-       g_timeout_add_seconds(2, exit_server, NULL);
+       sio = g_io_channel_unix_new(sfd);
+       g_io_channel_set_close_on_unref(sio, TRUE);
+       g_io_channel_set_encoding(sio, NULL, NULL);
+       g_io_channel_set_buffered(sio, FALSE);
+       swid = g_io_add_watch(sio, G_IO_IN, __signal_handler, NULL);
+
+       g_timeout_add_seconds_full(G_PRIORITY_LOW, 2, exit_server, NULL, NULL);
 
-       sig_reg = 1;
        return 0;
 }
 
@@ -315,7 +298,7 @@ gboolean exit_server(void *data)
 {
        DBG("exit_server Start");
        if (__check_backend_status_for_exit() &&
-                       __check_queue_status_for_exit()) {
+                       __check_queue_status_for_exit() && !is_drm_busy) {
                g_main_loop_quit(mainloop);
                return FALSE;
        }
@@ -591,8 +574,8 @@ static char **__generate_argv(const char *args)
 
        ret_parse = g_shell_parse_argv(args, &argcp, &argvp, &gerr);
        if (FALSE == ret_parse) {
-               DBG("Failed to split args: %s", args);
-               DBG("messsage: %s", gerr->message);
+               ERR("Failed to split args: %s", args);
+               ERR("messsage: %s", gerr->message);
                exit(1);
        }
 
@@ -608,7 +591,7 @@ void __set_environment(gpointer user_data)
        user_ctx *ctx = (user_ctx *)user_data;
 
        if (set_environement(ctx))
-               DBG("Failed to set env for the user : %d", ctx->uid);
+               ERR("Failed to set env for the user : %d", ctx->uid);
 }
 
 static int __fork_and_exec_with_args(char **argv, uid_t uid)
@@ -620,7 +603,7 @@ static int __fork_and_exec_with_args(char **argv, uid_t uid)
 
        user_context = get_user_context(uid);
        if (!user_context) {
-               DBG("Failed to getenv");
+               ERR("Failed to getenv");
                return -1;
        }
 
@@ -689,19 +672,28 @@ static int __process_install(struct backend_job *job)
        char *backend_cmd;
        char **argv;
        char args[MAX_PKG_ARGS_LEN];
+       gchar *req_id;
+       gchar *pkgid;
        int pid;
 
        backend_cmd = job->backend_path;
        if (backend_cmd == NULL)
                return -1;
 
+       req_id = g_shell_quote(job->req_id);
+       pkgid = g_shell_quote(job->pkgid);
+       if (!req_id || !pkgid)
+               return -1;
+
        snprintf(args, sizeof(args), "%s -k %s -i %s -u %d %s", backend_cmd,
-                       job->req_id, job->pkgid, (int)job->target_uid, job->args);
+                       req_id, pkgid, (int)job->target_uid, job->args);
 
        argv = __generate_argv(args);
 
        pid = __fork_and_exec_with_args(argv, APPFW_UID);
        g_strfreev(argv);
+       g_free(req_id);
+       g_free(pkgid);
 
        return pid;
 }
@@ -711,19 +703,28 @@ static int __process_mount_install(struct backend_job *job)
        char *backend_cmd;
        char **argv;
        char args[MAX_PKG_ARGS_LEN];
+       gchar *req_id;
+       gchar *pkgid;
        int pid;
 
        backend_cmd = job->backend_path;
        if (backend_cmd == NULL)
                return -1;
 
+       req_id = g_shell_quote(job->req_id);
+       pkgid = g_shell_quote(job->pkgid);
+       if (!req_id || !pkgid)
+               return -1;
+
        snprintf(args, sizeof(args), "%s -k %s -w %s -u %d %s", backend_cmd,
-                       job->req_id, job->pkgid, (int)job->target_uid, job->args);
+                       req_id, pkgid, (int)job->target_uid, job->args);
 
        argv = __generate_argv(args);
 
        pid = __fork_and_exec_with_args(argv, APPFW_UID);
        g_strfreev(argv);
+       g_free(req_id);
+       g_free(pkgid);
 
        return pid;
 }
@@ -733,19 +734,28 @@ static int __process_reinstall(struct backend_job *job)
        char *backend_cmd;
        char **argv;
        char args[MAX_PKG_ARGS_LEN];
+       gchar *req_id;
+       gchar *pkgid;
        int pid;
 
        backend_cmd = job->backend_path;
        if (backend_cmd == NULL)
                return -1;
 
+       req_id = g_shell_quote(job->req_id);
+       pkgid = g_shell_quote(job->pkgid);
+       if (!req_id || !pkgid)
+               return -1;
+
        snprintf(args, sizeof(args), "%s -k %s -r %s -u %d", backend_cmd,
-                       job->req_id, job->pkgid, (int)job->target_uid);
+                       req_id, pkgid, (int)job->target_uid);
        argv = __generate_argv(args);
 
        pid = __fork_and_exec_with_args(argv, APPFW_UID);
 
        g_strfreev(argv);
+       g_free(req_id);
+       g_free(pkgid);
 
        return pid;
 }
@@ -905,6 +915,71 @@ static int __process_disable_pkg(struct backend_job *job)
        return pid;
 }
 
+static int __process_register_pkg_update_info(struct backend_job *job)
+{
+       int ret;
+
+       if (!job->extra_data) {
+               _return_value_to_caller(job->req_id,
+                               g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+               return -1;
+       }
+
+       ret = pkgmgr_parser_register_pkg_update_info_in_usr_db(
+                       (pkgmgrinfo_updateinfo_h)job->extra_data, job->target_uid);
+       if (ret == PMINFO_R_OK)
+               _return_value_to_caller(job->req_id,
+                               g_variant_new("(i)", PKGMGR_R_OK));
+       else
+               _return_value_to_caller(job->req_id,
+                               g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+
+       pkgmgrinfo_updateinfo_destroy(job->extra_data);
+       job->extra_data = NULL;
+
+       return ret;
+}
+
+static int __process_unregister_pkg_update_info(struct backend_job *job)
+{
+       int ret = pkgmgr_parser_unregister_pkg_update_info_in_usr_db
+                       (job->pkgid, job->target_uid);
+
+       if (ret == PMINFO_R_OK)
+               _return_value_to_caller(job->req_id,
+                               g_variant_new("(i)", PKGMGR_R_OK));
+       else
+               _return_value_to_caller(job->req_id,
+                               g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+       return ret;
+}
+
+static int __process_unregister_all_pkg_update_info(struct backend_job *job)
+{
+       int ret = pkgmgr_parser_unregister_all_pkg_update_info_in_usr_db(
+                       job->target_uid);
+
+       if (ret != PMINFO_R_OK) {
+               _return_value_to_caller(job->req_id,
+                               g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+               return ret;
+       }
+
+       if (__is_admin_user(job->caller_uid)) {
+               ret = pkgmgr_parser_unregister_all_pkg_update_info_in_usr_db(
+                               GLOBAL_USER);
+               if (ret != PMINFO_R_OK) {
+                       _return_value_to_caller(job->req_id,
+                                       g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+                       return ret;
+               }
+       }
+       _return_value_to_caller(job->req_id,
+                       g_variant_new("(i)", PKGMGR_R_OK));
+
+       return ret;
+}
+
 static int __process_enable_app(struct backend_job *job)
 {
        int ret = -1;
@@ -1007,7 +1082,7 @@ static int __process_enable_global_app_for_uid(struct backend_job *job)
        if (ret != PMINFO_R_OK || strlen(job->appid) == 0) {
                _send_app_signal(job->target_uid, job->req_id, job->pkgid, job->pkgid,
                                PKGMGR_INSTALLER_START_KEY_STR,
-                               PKGMGR_INSTALLER_GLOBAL_APP_ENABLE_FOR_UID,
+                               PKGMGR_INSTALLER_APP_ENABLE_EVENT_STR,
                                job->req_type);
                _send_app_signal(job->target_uid, job->req_id, job->pkgid, job->pkgid,
                                PKGMGR_INSTALLER_END_KEY_STR,
@@ -1018,7 +1093,7 @@ static int __process_enable_global_app_for_uid(struct backend_job *job)
 
        _send_app_signal(job->target_uid, job->req_id, job->pkgid, job->appid,
                        PKGMGR_INSTALLER_START_KEY_STR,
-                       PKGMGR_INSTALLER_GLOBAL_APP_ENABLE_FOR_UID,
+                       PKGMGR_INSTALLER_APP_ENABLE_EVENT_STR,
                        job->req_type);
 
        ret = pkgmgr_parser_update_global_app_disable_for_uid_info_in_db(
@@ -1050,7 +1125,7 @@ static int __process_disable_global_app_for_uid(struct backend_job *job)
                _send_app_signal(job->target_uid, job->req_id,
                                job->pkgid, job->pkgid,
                                PKGMGR_INSTALLER_START_KEY_STR,
-                               PKGMGR_INSTALLER_GLOBAL_APP_DISABLE_FOR_UID,
+                               PKGMGR_INSTALLER_APP_DISABLE_EVENT_STR,
                                job->req_type);
                _send_app_signal(job->target_uid, job->req_id, job->pkgid, job->pkgid,
                                PKGMGR_INSTALLER_END_KEY_STR,
@@ -1062,7 +1137,7 @@ static int __process_disable_global_app_for_uid(struct backend_job *job)
        _send_app_signal(job->target_uid, job->req_id,
                        job->pkgid, job->appid,
                        PKGMGR_INSTALLER_START_KEY_STR,
-                       PKGMGR_INSTALLER_GLOBAL_APP_DISABLE_FOR_UID,
+                       PKGMGR_INSTALLER_APP_DISABLE_EVENT_STR,
                        job->req_type);
 
        ret = __kill_app(job->appid, job->target_uid);
@@ -1307,13 +1382,14 @@ static int __process_generate_license_request(struct backend_job *job)
                ERR("drm_tizen_generate_license_request failed: %d", ret);
                _return_value_to_caller(job->req_id, g_variant_new("(iss)",
                                        PKGMGR_R_ESYSTEM, "", ""));
+               is_drm_busy = false;
                return -1;
        }
 
        _return_value_to_caller(job->req_id,
                        g_variant_new("(iss)", PKGMGR_R_OK, req_data,
                                license_url));
-
+       is_drm_busy = true;
        return 0;
 }
 
@@ -1329,6 +1405,7 @@ static int __process_register_license(struct backend_job *job)
                ERR("drm_tizen_register_license failed: %d", ret);
                _return_value_to_caller(job->req_id,
                                g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+               is_drm_busy = false;
                return -1;
        }
 
@@ -1354,11 +1431,13 @@ static int __process_decrypt_package(struct backend_job *job)
                ERR("drm_tizen_register_license failed: %d", ret);
                _return_value_to_caller(job->req_id,
                                g_variant_new("(i)", PKGMGR_R_ESYSTEM));
+               is_drm_busy = false;
                return -1;
        }
 
        _return_value_to_caller(job->req_id,
                        g_variant_new("(i)", PKGMGR_R_OK));
+       is_drm_busy = false;
 
        return 0;
 }
@@ -1449,6 +1528,29 @@ static int __process_set_app_label(struct backend_job *job)
        return ret;
 }
 
+static int __process_migrate_external_image(struct backend_job *job)
+{
+       char *backend_cmd;
+       char **argv;
+       char args[MAX_PKG_ARGS_LEN];
+       int pid;
+
+       backend_cmd = job->backend_path;
+       if (backend_cmd == NULL)
+               return -1;
+
+       snprintf(args, sizeof(args), "%s -k %s --migrate-extimg %s -u %d %s",
+                       backend_cmd, job->req_id, job->pkgid,
+                       (int)job->target_uid, job->args);
+
+       argv = __generate_argv(args);
+
+       pid = __fork_and_exec_with_args(argv, APPFW_UID);
+       g_strfreev(argv);
+
+       return pid;
+}
+
 static int __post_process(int ret, int x, struct backend_job *job)
 {
        if (ret < 0) {
@@ -1540,6 +1642,18 @@ gboolean queue_job(void *data)
                        _send_fail_signal(job);
                __post_process(ret, x, job);
                break;
+       case REQUEST_TYPE_REGISTER_PKG_UPDATE_INFO:
+               ret = __process_register_pkg_update_info(job);
+               _free_backend_job(job);
+               break;
+       case REQUEST_TYPE_UNREGISTER_PKG_UPDATE_INFO:
+               ret = __process_unregister_pkg_update_info(job);
+               _free_backend_job(job);
+               break;
+       case REQUEST_TYPE_UNREGISTER_ALL_PKG_UPDATE_INFO:
+               ret = __process_unregister_all_pkg_update_info(job);
+               _free_backend_job(job);
+               break;
        case REQUEST_TYPE_ENABLE_APP:
                ret = __process_enable_app(job);
                _free_backend_job(job);
@@ -1622,6 +1736,11 @@ gboolean queue_job(void *data)
                ret = __process_set_app_label(job);
                _free_backend_job(job);
                break;
+       case REQUEST_TYPE_MIGRATE_EXTERNAL_IMAGE:
+               __set_backend_busy(x);
+               ret = __process_migrate_external_image(job);
+               __post_process(ret, x, job);
+               break;
        default:
                ret = -1;
                break;
@@ -1636,15 +1755,20 @@ int main(int argc, char *argv[])
 
        DBG("server start");
 
+       if (__register_signal_handler()) {
+               ERR("failed to register signal handler");
+               return -1;
+       }
+
        r = _init_backend_queue();
        if (r) {
-               DBG("Queue Initialization Failed");
+               ERR("Queue Initialization Failed");
                return -1;
        }
 
        r = __init_backend_info();
        if (r) {
-               DBG("backend info init failed");
+               ERR("backend info init failed");
                return -1;
        }
 
@@ -1654,11 +1778,6 @@ int main(int argc, char *argv[])
                return -1;
        }
 
-       if (__register_signal_handler()) {
-               ERR("failed to register signal handler");
-               return -1;
-       }
-
 #if !GLIB_CHECK_VERSION(2, 35, 0)
        g_type_init();
 #endif
@@ -1677,6 +1796,9 @@ int main(int argc, char *argv[])
        __fini_backend_info();
        _fini_backend_queue();
 
+       g_source_remove(swid);
+       g_io_channel_unref(sio);
+
        DBG("package manager server terminated.");
 
        return 0;