tizen 2.3 release
[kernel/api/system-resource.git] / src / proc-stat / proc-main.c
index 18651b3..01a47b7 100644 (file)
 
 #include <Ecore.h>
 #include <Ecore_File.h>
+#include <pthread.h>
 
+#include "notifier.h"
 #include "proc-process.h"
 #include "proc-main.h"
-
+#include "cgroup.h"
 #include "proc-noti.h"
 #include "trace.h"
-#include "proc-winstate.h"
 #include "proc-handler.h"
 #include "proc-monitor.h"
 #include "module.h"
+#include "macro.h"
+#include "appid-helper.h"
+#include "lowmem-handler.h"
 
+pthread_mutex_t        proc_mutex      = PTHREAD_MUTEX_INITIALIZER;
 static GHashTable *proc_exclude_list;
 static Ecore_File_Monitor *exclude_list_monitor;
 static const unsigned int exclude_list_limit = 1024;
 static int proc_notifd;
+#define BASE_UGPATH_PREFIX "/usr/ug/bin"
+
+enum proc_state {
+       PROC_STATE_DEFAULT,
+       PROC_STATE_FOREGROUND,
+       PROC_STATE_BACKGROUND,
+};
+
+/*
+ * @brief pid_info_list is only for pid_info_t
+ */
+GSList *proc_process_list;
+
+struct pid_info_t *new_pid_info(const pid_t pid, const int type)
+{
+       struct pid_info_t *result = (struct pid_info_t *)malloc(
+                       sizeof(struct pid_info_t));
+       if (!result) {
+               _E("Malloc of new_pid_info failed\n");
+               return NULL;
+       }
+
+       result->pid = pid;
+       result->type = type;
+       return result;
+}
+
+static gint compare_pid(gconstpointer a, gconstpointer b)
+{
+       const struct pid_info_t *pida = (struct pid_info_t *)a;
+       const struct pid_info_t *pidb = (struct pid_info_t *)b;
+       return pida->pid == pidb->pid ? 0 :
+               pida->pid > pidb->pid ? 1 : -1;
+}
+
+static struct pid_info_t *find_pid_info(pid_info_list pids, const pid_t pid)
+{
+       struct pid_info_t pid_to_find = {
+               .pid = pid,
+               /* now it doesn't matter */
+               .type = RESOURCED_APP_TYPE_UNKNOWN,
+       };
+       GSList *found = NULL;
+
+       ret_value_msg_if(!pids, NULL, "Please provide valid pointer.");
+
+       found = g_slist_find_custom((GSList *)pids,
+               &pid_to_find, compare_pid);
+
+       if (found)
+               return (struct pid_info_t *)(found->data);
+       return NULL;
+}
+
+void proc_add_pid_list(struct proc_process_info_t *process_info, int pid, enum application_type type)
+{
+       struct pid_info_t pid_to_find = {
+               .pid = pid,
+               /* now it doesn't matter */
+               .type = RESOURCED_APP_TYPE_UNKNOWN,
+       };
+       GSList *found = NULL;
+
+       if (process_info->pids)
+               found = g_slist_find_custom((GSList *)process_info->pids,
+                       &pid_to_find, compare_pid);
+
+       if (found)
+               return;
+
+       pthread_mutex_lock(&proc_mutex);
+       process_info->pids = g_slist_prepend(process_info->pids, new_pid_info(pid, type));
+       pthread_mutex_unlock(&proc_mutex);
+}
+
+static int equal_process_info(const char *appid_a, const char *appid_b)
+{
+       return !strcmp(appid_a, appid_b);
+}
+
+static resourced_ret_c proc_check_ug(pid_t pid)
+{
+       char buf[PROC_BUF_MAX];
+       char cmdline_buf[PROC_NAME_MAX];
+       FILE *fp;
+
+       snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
+       fp = fopen(buf, "r");
+       if (fp == NULL)
+               return RESOURCED_ERROR_FAIL;
+
+       if (fgets(cmdline_buf, PROC_NAME_MAX-1, fp) == NULL) {
+               fclose(fp);
+               return RESOURCED_ERROR_FAIL;
+       }
+       fclose(fp);
+       if (strstr(cmdline_buf, BASE_UGPATH_PREFIX)) {
+               _D("pid(%d) is ug process. don't freeze this process", pid);
+               return RESOURCED_ERROR_NONFREEZABLE;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+static void proc_set_service_oomscore(const pid_t pid, const int state)
+{
+       int oom_score = OOMADJ_SERVICE_DEFAULT;
+       switch(state) {
+       case PROC_STATE_DEFAULT:
+               oom_score = OOMADJ_SERVICE_DEFAULT;
+               break;
+       case PROC_STATE_FOREGROUND:
+               oom_score = OOMADJ_SERVICE_FOREGRD;
+               break;
+       case PROC_STATE_BACKGROUND:
+               oom_score = OOMADJ_SERVICE_BACKGRD;
+               break;
+       }
+       proc_set_oom_score_adj(pid, oom_score);
+}
+
+static pid_t get_service_pid(struct proc_process_info_t *info_t)
+{
+       GSList *iter = NULL;
+
+       if (!info_t) {
+               _D("Can't find process_info");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       gslist_for_each_item(iter, info_t->pids) {
+               struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data);
+
+               if (pid_info->type == RESOURCED_APP_TYPE_SERVICE) {
+                       _D("get_service_pid : pid (%d), type (%d)", pid_info->pid, pid_info->type);
+                       return pid_info->pid;
+               }
+       }
+       return RESOURCED_ERROR_NO_DATA;
+}
+
+void proc_set_process_info_memcg(struct proc_process_info_t *process_info, int memcg_idx)
+{
+       if (!process_info)
+               return;
+       process_info->memcg_idx = memcg_idx;
+}
+
+struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid, const char *pkgid)
+{
+       GSList *iter = NULL;
+       struct proc_process_info_t *info_t = NULL;
+
+       if (pkgid) {
+               gslist_for_each_item(iter, proc_process_list) {
+                       info_t = (struct proc_process_info_t *)iter->data;
+                       if (equal_process_info(info_t->pkgname, pkgid))
+                               return info_t;
+               }
+               return NULL;
+       }
+
+       if (!pid) {
+               gslist_for_each_item(iter, proc_process_list) {
+                       info_t = (struct proc_process_info_t *)iter->data;
+                       if (equal_process_info(info_t->appid, appid))
+                               return info_t;
+               }
+               return NULL;
+       }
+
+       gslist_for_each_item(iter, proc_process_list) {
+               info_t = (struct proc_process_info_t *)iter->data;
+               if (info_t->pids && find_pid_info(info_t->pids, pid))
+                       return info_t;
+       }
+       return NULL;
+}
+
+static resourced_ret_c proc_update_process_state(const pid_t pid, const int state)
+{
+       struct proc_process_info_t *process_info = NULL;
+       pid_t service_pid;
+       process_info = find_process_info(NULL, pid, NULL);
+       if (!process_info) {
+               _E("Current pid (%d) didn't have any process list", pid);
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+       }
+       process_info->state = state;
+       service_pid = get_service_pid(process_info);
+       if (service_pid)
+               proc_set_service_oomscore(service_pid, state);
+       return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type)
+{
+       GSList *iter = NULL;
+       struct proc_process_info_t *process_info = NULL;
+       struct pid_info_t *found_pid = NULL;
+
+       gslist_for_each_item(iter, proc_process_list) {
+               process_info = (struct proc_process_info_t *)iter->data;
+               if (!process_info->pids)
+                       continue;
+
+               found_pid = find_pid_info(process_info->pids, pid);
+               if(!found_pid)
+                       continue;
+
+               if(process_info->runtime_exclude) {
+                       if (type == PROC_EXCLUDE)
+                               process_info->runtime_exclude++;
+                       else
+                               process_info->runtime_exclude--;
+               } else
+                       process_info->runtime_exclude = type;
+
+               _D("found_pid %d, set proc exclude list, type = %d, exclude = %d",
+                           found_pid->pid, type, process_info->runtime_exclude);
+               break;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+struct proc_process_info_t * proc_add_process_list(const int type, const pid_t pid, const char *appid, const char *pkgid)
+{
+       struct proc_process_info_t *process_info;
+
+       if (!appid)
+               return NULL;
+
+       process_info = find_process_info(appid, pid, pkgid);
+       /* do not add if it already in list */
+       if (process_info && find_pid_info(process_info->pids, pid))
+               return process_info;
+
+       if (!process_info) {
+               process_info = malloc(sizeof(struct proc_process_info_t));
+               if (!process_info)
+                       return NULL;
+
+               memset(process_info, 0, sizeof(struct proc_process_info_t));
+               strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1);
+               process_info->proc_exclude = resourced_proc_excluded(appid);
+               if (pkgid)
+                       strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1);
+               else
+                       extract_pkgname(process_info->appid, process_info->pkgname,
+                               MAX_NAME_LENGTH);
+               pthread_mutex_lock(&proc_mutex);
+               proc_process_list = g_slist_prepend(proc_process_list,
+                       process_info);
+               pthread_mutex_unlock(&proc_mutex);
+               process_info->state = PROC_STATE_DEFAULT;
+       }
+       if (proc_check_ug(pid) == RESOURCED_ERROR_NONFREEZABLE)
+               process_info->runtime_exclude = PROC_EXCLUDE;
+       if (type == RESOURCED_APP_TYPE_SERVICE)
+               proc_set_service_oomscore(pid, process_info->state);
+
+       proc_add_pid_list(process_info, pid, type);
+       return process_info;
+}
+
+struct proc_process_info_t * proc_create_process_list(const char *appid, const char *pkgid)
+{
+       struct proc_process_info_t *process_info;
+
+       if (!appid)
+               return NULL;
+
+       process_info = find_process_info(appid, 0, pkgid);
+       /* do not add if it already in list */
+       if (process_info)
+               return process_info;
+
+       if (!process_info) {
+               process_info = malloc(sizeof(struct proc_process_info_t));
+               if (!process_info)
+                       return NULL;
+
+               memset(process_info, 0, sizeof(struct proc_process_info_t));
+               strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1);
+               process_info->proc_exclude = resourced_proc_excluded(appid);
+               if (pkgid)
+                       strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1);
+               else
+                       extract_pkgname(process_info->appid, process_info->pkgname,
+                               MAX_NAME_LENGTH);
+               pthread_mutex_lock(&proc_mutex);
+               proc_process_list = g_slist_prepend(proc_process_list,
+                       process_info);
+               pthread_mutex_unlock(&proc_mutex);
+               process_info->state = PROC_STATE_DEFAULT;
+       }
+       return process_info;
+}
+
+int proc_remove_process_list(const pid_t pid)
+{
+       GSList *iter = NULL;
+       struct proc_process_info_t *process_info = NULL;
+       struct pid_info_t *found_pid = NULL;
+
+       pthread_mutex_lock(&proc_mutex);
+       gslist_for_each_item(iter, proc_process_list) {
+               process_info = (struct proc_process_info_t *)iter->data;
+               if (!process_info->pids)
+                       continue;
+
+               found_pid = find_pid_info(process_info->pids, pid);
+               if(!found_pid)
+                       continue;
+
+               _D("found_pid %d", found_pid->pid);
+               /* Introduce function for removing and cleaning */
+               process_info->pids = g_slist_remove(process_info->pids,
+                       found_pid);
+               free(found_pid);
+               if (!process_info->pids) {
+                       proc_process_list = g_slist_remove(
+                               proc_process_list,
+                               process_info);
+                       free(process_info);
+               }
+               break;
+       }
+       pthread_mutex_unlock(&proc_mutex);
+       return 0;
+}
 
 static void proc_free_exclude_key(gpointer data)
 {
@@ -63,16 +398,13 @@ int resourced_proc_excluded(const char *app_name)
        return ret ? RESOURCED_ERROR_NONMONITOR : RESOURCED_ERROR_NONE;
 }
 
-#if 0
 static void _prepare_appid(char *appid, const int length)
 {
        if (!appid || length - 1 <= 0)
                return;
        appid[length - 1] = '\0'; /*remove ending new line*/
 }
-#endif
 
-#if 0
 static void fill_exclude_list_by_path(const char *exclude_file_name,
        GHashTable *list)
 {
@@ -114,13 +446,11 @@ static void fill_exclude_list_by_path(const char *exclude_file_name,
 
        fclose(exclude_file);
 }
-#endif
 
 static void _fill_exclude_list(GHashTable *list)
 {
-#if 0
-       fill_exclude_list_by_path( , list);
-#endif
+       fill_exclude_list_by_path(EXCLUDE_LIST_FULL_PATH, list);
+       fill_exclude_list_by_path(EXCLUDE_LIST_OPT_FULL_PATH, list);
 }
 
 static void _exclude_list_change_cb(void *data, Ecore_File_Monitor *em,
@@ -171,8 +501,6 @@ int resourced_proc_init(const struct daemon_opts *opts)
 
        proc_notifd = proc_noti_init( );
 
-       proc_win_status_init();
-
        ret = proc_monitor_init();
        if (ret)
                _E("proc_monitor_init failed : %d", ret);
@@ -187,18 +515,41 @@ int resourced_proc_exit(const struct daemon_opts *opts)
                close(proc_notifd);
        g_hash_table_destroy(proc_exclude_list);
        ecore_file_monitor_del(exclude_list_monitor);
+       g_slist_free_full(proc_process_list, free);
        return RESOURCED_ERROR_NONE;
 }
 
-int resourced_proc_status_change(int type, pid_t pid, char* app_name)
+void proc_set_apptype(const char *appid, const char *pkgid, int type)
+{
+       struct proc_process_info_t *process_info =
+               proc_create_process_list(appid, pkgid);
+       if (process_info)
+               process_info->type = type;
+}
+
+int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_name)
 {
        int ret = 0, oom_score_adj = 0;
        char pidbuf[32];
+       struct proc_status proc_data;;
 
        if (pid && (proc_get_oom_score_adj(pid, &oom_score_adj) < 0)) {
+               /* due process with pid is no longer exits
+                * we need to remove it from
+                * freezer_process_list  */
+               proc_remove_process_list(pid);
+               _E("Empty pid or process not exists. %d", pid);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       if (!pid) {
+               _E("invalid pid : %d of %s", pid, app_name ? app_name : "noprocess");
                return RESOURCED_ERROR_FAIL;
        }
 
+       proc_data.pid = pid;
+       proc_data.appid = app_name;
+       proc_data.processinfo = NULL;
        switch (type) {
        case PROC_CGROUP_SET_FOREGRD:
                _SD("set foreground : %d", pid);
@@ -206,10 +557,9 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name)
                dbus_proc_handler(PREDEF_FOREGRD, pidbuf);
                ret = proc_set_foregrd(pid, oom_score_adj);
                if (ret != 0)
-                       _E("Failed to handle proc foreground action!");
-               if ( oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)
-                       break;
-
+                       return RESOURCED_ERROR_NO_DATA;
+               proc_update_process_state(pid, PROC_STATE_FOREGROUND);
+               resourced_notify(RESOURCED_NOTIFIER_APP_FOREGRD, &proc_data);
                break;
        case PROC_CGROUP_SET_LAUNCH_REQUEST:
                proc_set_oom_score_adj(pid, OOMADJ_INIT);
@@ -218,33 +568,54 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name)
                        return RESOURCED_ERROR_NO_DATA;
                }
                _SD("launch request %s, %d", app_name, pid);
-
+               if (pkg_name)
+                       _SD("launch request %s with pkgname", pkg_name);
+               ret = resourced_proc_excluded(app_name);
+               if (!ret)
+                       proc_data.processinfo = proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name);
+               resourced_notify(RESOURCED_NOTIFIER_APP_LAUNCH, &proc_data);
+               _E("available memory = %u", get_available());
                break;
        case PROC_CGROUP_SET_SERVICE_REQUEST:
-               proc_set_oom_score_adj(pid, OOMADJ_INIT);
                if (!app_name) {
                        _E("need application name!pid = %d", pid);
                        return RESOURCED_ERROR_NO_DATA;
                }
                _SD("service launch request %s, %d", app_name, pid);
-
+               if (pkg_name)
+                       _SD("launch request %s with pkgname", pkg_name);
+               proc_add_process_list(RESOURCED_APP_TYPE_SERVICE, pid, app_name, pkg_name);
+               if (resourced_proc_excluded(app_name) == RESOURCED_ERROR_NONE)
+                       resourced_notify(RESOURCED_NOTIFIER_SERVICE_LAUNCH, &proc_data);
                break;
        case PROC_CGROUP_SET_RESUME_REQUEST:
                _SD("resume request %d", pid);
                /* init oom_score_value */
-               if (oom_score_adj >= OOMADJ_BACKGRD_UNLOCKED)
+               if (oom_score_adj >= OOMADJ_BACKGRD_UNLOCKED) {
+                       resourced_notify(RESOURCED_NOTIFIER_APP_RESUME, &proc_data);
                        proc_set_oom_score_adj(pid, OOMADJ_INIT);
+               }
 
                if (!app_name) {
                        _E("need application name!pid = %d", pid);
                        return RESOURCED_ERROR_NO_DATA;
                }
 
+               proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name);
+               if (ret != RESOURCED_ERROR_NONE)
+                       _D("Failed to add to freezer list: pid %d", pid);
+
                break;
        case PROC_CGROUP_SET_TERMINATE_REQUEST:
+               resourced_notify(RESOURCED_NOTIFIER_APP_TERMINATE, &proc_data);
+               proc_remove_process_list(pid);
                break;
        case PROC_CGROUP_SET_ACTIVE:
                ret = proc_set_active(pid, oom_score_adj);
+               if (ret != RESOURCED_ERROR_OK)
+                       break;
+               resourced_notify(RESOURCED_NOTIFIER_APP_ACTIVE, &proc_data);
+               proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
                break;
        case PROC_CGROUP_SET_BACKGRD:
                snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
@@ -252,17 +623,21 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name)
                ret = proc_set_backgrd(pid, oom_score_adj);
                if (ret != 0)
                        break;
-
+               proc_update_process_state(pid, PROC_STATE_BACKGROUND);
                break;
        case PROC_CGROUP_SET_INACTIVE:
                ret = proc_set_inactive(pid, oom_score_adj);
+               if (ret != RESOURCED_ERROR_OK)
+                       break;
+               resourced_notify(RESOURCED_NOTIFIER_APP_INACTIVE, &proc_data);
                break;
        case PROC_CGROUP_GET_MEMSWEEP:
-               ret = proc_sweep_memory(pid);
+               ret = proc_sweep_memory(PROC_SWEEP_EXCLUDE_ACTIVE, pid);
                break;
        case PROC_CGROUP_SET_NOTI_REQUEST:
                break;
        case PROC_CGROUP_SET_PROC_EXCLUDE_REQUEST:
+               proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
                break;
        default:
                ret = RESOURCED_ERROR_INVALID_PARAMETER;
@@ -272,8 +647,8 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name)
 
 int resourced_proc_action(int type, int argnum, char **arg)
 {
-       int pid;
-       char *pidbuf = NULL, *cgroup_name = NULL;
+       pid_t pid;
+       char *pidbuf = NULL, *cgroup_name = NULL, *pkg_name = NULL;
        if (argnum < 1) {
                _E("Unsupported number of arguments!");
                return RESOURCED_ERROR_INVALID_PARAMETER;
@@ -286,11 +661,12 @@ int resourced_proc_action(int type, int argnum, char **arg)
        }
 
        /* Getting appid */
-       if (argnum > 1) {
+       if (argnum > 1)
                /* It's possible to get appid from arg */
                cgroup_name = arg[1];
-       }
+       if (argnum == 3)
+               pkg_name = arg[2];
        _SD("appid %s, pid %d, type %d \n", cgroup_name, pid, type);
-       return resourced_proc_status_change(type, pid, cgroup_name);
+       return resourced_proc_status_change(type, pid, cgroup_name, pkg_name);
 }