4 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
23 * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
28 #include <Ecore_File.h>
32 #include "proc-process.h"
33 #include "proc-main.h"
35 #include "proc-noti.h"
37 #include "proc-handler.h"
38 #include "proc-monitor.h"
41 #include "appid-helper.h"
42 #include "lowmem-handler.h"
44 pthread_mutex_t proc_mutex = PTHREAD_MUTEX_INITIALIZER;
45 static GHashTable *proc_exclude_list;
46 static Ecore_File_Monitor *exclude_list_monitor;
47 static const unsigned int exclude_list_limit = 1024;
48 static int proc_notifd;
49 #define BASE_UGPATH_PREFIX "/usr/ug/bin"
53 PROC_STATE_FOREGROUND,
54 PROC_STATE_BACKGROUND,
58 * @brief pid_info_list is only for pid_info_t
60 GSList *proc_process_list;
62 struct pid_info_t *new_pid_info(const pid_t pid, const int type)
64 struct pid_info_t *result = (struct pid_info_t *)malloc(
65 sizeof(struct pid_info_t));
67 _E("Malloc of new_pid_info failed\n");
76 static gint compare_pid(gconstpointer a, gconstpointer b)
78 const struct pid_info_t *pida = (struct pid_info_t *)a;
79 const struct pid_info_t *pidb = (struct pid_info_t *)b;
80 return pida->pid == pidb->pid ? 0 :
81 pida->pid > pidb->pid ? 1 : -1;
84 static struct pid_info_t *find_pid_info(pid_info_list pids, const pid_t pid)
86 struct pid_info_t pid_to_find = {
88 /* now it doesn't matter */
89 .type = RESOURCED_APP_TYPE_UNKNOWN,
93 ret_value_msg_if(!pids, NULL, "Please provide valid pointer.");
95 found = g_slist_find_custom((GSList *)pids,
96 &pid_to_find, compare_pid);
99 return (struct pid_info_t *)(found->data);
103 void proc_add_pid_list(struct proc_process_info_t *process_info, int pid, enum application_type type)
105 struct pid_info_t pid_to_find = {
107 /* now it doesn't matter */
108 .type = RESOURCED_APP_TYPE_UNKNOWN,
110 GSList *found = NULL;
112 if (process_info->pids)
113 found = g_slist_find_custom((GSList *)process_info->pids,
114 &pid_to_find, compare_pid);
119 pthread_mutex_lock(&proc_mutex);
120 process_info->pids = g_slist_prepend(process_info->pids, new_pid_info(pid, type));
121 pthread_mutex_unlock(&proc_mutex);
124 static int equal_process_info(const char *appid_a, const char *appid_b)
126 return !strcmp(appid_a, appid_b);
129 static resourced_ret_c proc_check_ug(pid_t pid)
131 char buf[PROC_BUF_MAX];
132 char cmdline_buf[PROC_NAME_MAX];
135 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
136 fp = fopen(buf, "r");
138 return RESOURCED_ERROR_FAIL;
140 if (fgets(cmdline_buf, PROC_NAME_MAX-1, fp) == NULL) {
142 return RESOURCED_ERROR_FAIL;
145 if (strstr(cmdline_buf, BASE_UGPATH_PREFIX)) {
146 _D("pid(%d) is ug process. don't freeze this process", pid);
147 return RESOURCED_ERROR_NONFREEZABLE;
149 return RESOURCED_ERROR_NONE;
152 static void proc_set_service_oomscore(const pid_t pid, const int state)
154 int oom_score = OOMADJ_SERVICE_DEFAULT;
156 case PROC_STATE_DEFAULT:
157 oom_score = OOMADJ_SERVICE_DEFAULT;
159 case PROC_STATE_FOREGROUND:
160 oom_score = OOMADJ_SERVICE_FOREGRD;
162 case PROC_STATE_BACKGROUND:
163 oom_score = OOMADJ_SERVICE_BACKGRD;
166 proc_set_oom_score_adj(pid, oom_score);
169 static pid_t get_service_pid(struct proc_process_info_t *info_t)
174 _D("Can't find process_info");
175 return RESOURCED_ERROR_FAIL;
178 gslist_for_each_item(iter, info_t->pids) {
179 struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data);
181 if (pid_info->type == RESOURCED_APP_TYPE_SERVICE) {
182 _D("get_service_pid : pid (%d), type (%d)", pid_info->pid, pid_info->type);
183 return pid_info->pid;
186 return RESOURCED_ERROR_NO_DATA;
189 void proc_set_process_info_memcg(struct proc_process_info_t *process_info, int memcg_idx)
193 process_info->memcg_idx = memcg_idx;
196 struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid, const char *pkgid)
199 struct proc_process_info_t *info_t = NULL;
202 gslist_for_each_item(iter, proc_process_list) {
203 info_t = (struct proc_process_info_t *)iter->data;
204 if (equal_process_info(info_t->pkgname, pkgid))
211 gslist_for_each_item(iter, proc_process_list) {
212 info_t = (struct proc_process_info_t *)iter->data;
213 if (equal_process_info(info_t->appid, appid))
219 gslist_for_each_item(iter, proc_process_list) {
220 info_t = (struct proc_process_info_t *)iter->data;
221 if (info_t->pids && find_pid_info(info_t->pids, pid))
227 static resourced_ret_c proc_update_process_state(const pid_t pid, const int state)
229 struct proc_process_info_t *process_info = NULL;
231 process_info = find_process_info(NULL, pid, NULL);
233 _E("Current pid (%d) didn't have any process list", pid);
234 return RESOURCED_ERROR_INVALID_PARAMETER;
236 process_info->state = state;
237 service_pid = get_service_pid(process_info);
239 proc_set_service_oomscore(service_pid, state);
240 return RESOURCED_ERROR_NONE;
243 resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type)
246 struct proc_process_info_t *process_info = NULL;
247 struct pid_info_t *found_pid = NULL;
249 gslist_for_each_item(iter, proc_process_list) {
250 process_info = (struct proc_process_info_t *)iter->data;
251 if (!process_info->pids)
254 found_pid = find_pid_info(process_info->pids, pid);
258 if(process_info->runtime_exclude) {
259 if (type == PROC_EXCLUDE)
260 process_info->runtime_exclude++;
262 process_info->runtime_exclude--;
264 process_info->runtime_exclude = type;
266 _D("found_pid %d, set proc exclude list, type = %d, exclude = %d",
267 found_pid->pid, type, process_info->runtime_exclude);
270 return RESOURCED_ERROR_NONE;
273 struct proc_process_info_t * proc_add_process_list(const int type, const pid_t pid, const char *appid, const char *pkgid)
275 struct proc_process_info_t *process_info;
280 process_info = find_process_info(appid, pid, pkgid);
281 /* do not add if it already in list */
282 if (process_info && find_pid_info(process_info->pids, pid))
286 process_info = malloc(sizeof(struct proc_process_info_t));
290 memset(process_info, 0, sizeof(struct proc_process_info_t));
291 strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1);
292 process_info->proc_exclude = resourced_proc_excluded(appid);
294 strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1);
296 extract_pkgname(process_info->appid, process_info->pkgname,
298 pthread_mutex_lock(&proc_mutex);
299 proc_process_list = g_slist_prepend(proc_process_list,
301 pthread_mutex_unlock(&proc_mutex);
302 process_info->state = PROC_STATE_DEFAULT;
304 if (proc_check_ug(pid) == RESOURCED_ERROR_NONFREEZABLE)
305 process_info->runtime_exclude = PROC_EXCLUDE;
306 if (type == RESOURCED_APP_TYPE_SERVICE)
307 proc_set_service_oomscore(pid, process_info->state);
309 proc_add_pid_list(process_info, pid, type);
313 struct proc_process_info_t * proc_create_process_list(const char *appid, const char *pkgid)
315 struct proc_process_info_t *process_info;
320 process_info = find_process_info(appid, 0, pkgid);
321 /* do not add if it already in list */
326 process_info = malloc(sizeof(struct proc_process_info_t));
330 memset(process_info, 0, sizeof(struct proc_process_info_t));
331 strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1);
332 process_info->proc_exclude = resourced_proc_excluded(appid);
334 strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1);
336 extract_pkgname(process_info->appid, process_info->pkgname,
338 pthread_mutex_lock(&proc_mutex);
339 proc_process_list = g_slist_prepend(proc_process_list,
341 pthread_mutex_unlock(&proc_mutex);
342 process_info->state = PROC_STATE_DEFAULT;
347 int proc_remove_process_list(const pid_t pid)
350 struct proc_process_info_t *process_info = NULL;
351 struct pid_info_t *found_pid = NULL;
353 pthread_mutex_lock(&proc_mutex);
354 gslist_for_each_item(iter, proc_process_list) {
355 process_info = (struct proc_process_info_t *)iter->data;
356 if (!process_info->pids)
359 found_pid = find_pid_info(process_info->pids, pid);
363 _D("found_pid %d", found_pid->pid);
364 /* Introduce function for removing and cleaning */
365 process_info->pids = g_slist_remove(process_info->pids,
368 if (!process_info->pids) {
369 proc_process_list = g_slist_remove(
376 pthread_mutex_unlock(&proc_mutex);
380 static void proc_free_exclude_key(gpointer data)
386 static gboolean find_excluded(gpointer key, gpointer value, gpointer user_data)
388 return (gboolean)strstr((char*)user_data, (char*)key);
391 int resourced_proc_excluded(const char *app_name)
394 if (proc_exclude_list)
395 ret = g_hash_table_find(proc_exclude_list, find_excluded, (gpointer)app_name);
397 return RESOURCED_ERROR_NONE;
398 return ret ? RESOURCED_ERROR_NONMONITOR : RESOURCED_ERROR_NONE;
401 static void _prepare_appid(char *appid, const int length)
403 if (!appid || length - 1 <= 0)
405 appid[length - 1] = '\0'; /*remove ending new line*/
408 static void fill_exclude_list_by_path(const char *exclude_file_name,
411 char *exclude_app_id = 0;
413 unsigned int excluded_count = 0;
415 FILE *exclude_file = NULL;
418 _D("Please initialize exclude list!");
422 exclude_file = fopen(exclude_file_name, "r");
425 _E("Can't open %s.", exclude_file_name);
429 while (excluded_count++ < exclude_list_limit) {
430 ret = getline(&exclude_app_id, &buf_size, exclude_file);
433 _prepare_appid(exclude_app_id, ret);
434 _SD("append %s to proc exclude list", exclude_app_id);
436 g_hash_table_insert(list, g_strdup(exclude_app_id),
440 if (excluded_count >= exclude_list_limit)
441 _E("Exclude list is exceed the limit of %u application",
445 free(exclude_app_id);
447 fclose(exclude_file);
450 static void _fill_exclude_list(GHashTable *list)
452 fill_exclude_list_by_path(EXCLUDE_LIST_FULL_PATH, list);
453 fill_exclude_list_by_path(EXCLUDE_LIST_OPT_FULL_PATH, list);
456 static void _exclude_list_change_cb(void *data, Ecore_File_Monitor *em,
457 Ecore_File_Event event, const char *path)
459 _SD("file %s changed, path: %s, event: %d ", EXCLUDE_LIST_OPT_FULL_PATH,
462 g_hash_table_remove_all(proc_exclude_list);
464 _fill_exclude_list(proc_exclude_list);
467 static void _init_exclude_list_noti(void)
469 if (ecore_file_init() == 0) {
470 _E("ecore_file_init() failed");
473 exclude_list_monitor = ecore_file_monitor_add(EXCLUDE_LIST_OPT_FULL_PATH,
474 _exclude_list_change_cb,
476 if (exclude_list_monitor == NULL)
477 _E("Dynamic exclude list is not supported. Can not add "
478 "notification callback");
481 static void proc_exclude_init(void)
483 proc_exclude_list = g_hash_table_new_full(
486 proc_free_exclude_key,
489 if (proc_exclude_list == NULL) {
490 _E("Can't initialize exclude_list!");
494 _init_exclude_list_noti();
495 _fill_exclude_list(proc_exclude_list);
498 int resourced_proc_init(const struct daemon_opts *opts)
502 proc_notifd = proc_noti_init( );
504 ret = proc_monitor_init();
506 _E("proc_monitor_init failed : %d", ret);
512 int resourced_proc_exit(const struct daemon_opts *opts)
516 g_hash_table_destroy(proc_exclude_list);
517 ecore_file_monitor_del(exclude_list_monitor);
518 g_slist_free_full(proc_process_list, free);
519 return RESOURCED_ERROR_NONE;
522 void proc_set_apptype(const char *appid, const char *pkgid, int type)
524 struct proc_process_info_t *process_info =
525 proc_create_process_list(appid, pkgid);
527 process_info->type = type;
530 int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_name)
532 int ret = 0, oom_score_adj = 0;
534 struct proc_status proc_data;;
536 if (pid && (proc_get_oom_score_adj(pid, &oom_score_adj) < 0)) {
537 /* due process with pid is no longer exits
538 * we need to remove it from
539 * freezer_process_list */
540 proc_remove_process_list(pid);
541 _E("Empty pid or process not exists. %d", pid);
542 return RESOURCED_ERROR_FAIL;
546 _E("invalid pid : %d of %s", pid, app_name ? app_name : "noprocess");
547 return RESOURCED_ERROR_FAIL;
551 proc_data.appid = app_name;
552 proc_data.processinfo = NULL;
554 case PROC_CGROUP_SET_FOREGRD:
555 _SD("set foreground : %d", pid);
556 snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
557 dbus_proc_handler(PREDEF_FOREGRD, pidbuf);
558 ret = proc_set_foregrd(pid, oom_score_adj);
560 return RESOURCED_ERROR_NO_DATA;
561 proc_update_process_state(pid, PROC_STATE_FOREGROUND);
562 resourced_notify(RESOURCED_NOTIFIER_APP_FOREGRD, &proc_data);
564 case PROC_CGROUP_SET_LAUNCH_REQUEST:
565 proc_set_oom_score_adj(pid, OOMADJ_INIT);
567 _E("need application name!pid = %d", pid);
568 return RESOURCED_ERROR_NO_DATA;
570 _SD("launch request %s, %d", app_name, pid);
572 _SD("launch request %s with pkgname", pkg_name);
573 ret = resourced_proc_excluded(app_name);
575 proc_data.processinfo = proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name);
576 resourced_notify(RESOURCED_NOTIFIER_APP_LAUNCH, &proc_data);
577 _E("available memory = %u", get_available());
579 case PROC_CGROUP_SET_SERVICE_REQUEST:
581 _E("need application name!pid = %d", pid);
582 return RESOURCED_ERROR_NO_DATA;
584 _SD("service launch request %s, %d", app_name, pid);
586 _SD("launch request %s with pkgname", pkg_name);
587 proc_add_process_list(RESOURCED_APP_TYPE_SERVICE, pid, app_name, pkg_name);
588 if (resourced_proc_excluded(app_name) == RESOURCED_ERROR_NONE)
589 resourced_notify(RESOURCED_NOTIFIER_SERVICE_LAUNCH, &proc_data);
591 case PROC_CGROUP_SET_RESUME_REQUEST:
592 _SD("resume request %d", pid);
593 /* init oom_score_value */
594 if (oom_score_adj >= OOMADJ_BACKGRD_UNLOCKED) {
595 resourced_notify(RESOURCED_NOTIFIER_APP_RESUME, &proc_data);
596 proc_set_oom_score_adj(pid, OOMADJ_INIT);
600 _E("need application name!pid = %d", pid);
601 return RESOURCED_ERROR_NO_DATA;
604 proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name);
605 if (ret != RESOURCED_ERROR_NONE)
606 _D("Failed to add to freezer list: pid %d", pid);
609 case PROC_CGROUP_SET_TERMINATE_REQUEST:
610 resourced_notify(RESOURCED_NOTIFIER_APP_TERMINATE, &proc_data);
611 proc_remove_process_list(pid);
613 case PROC_CGROUP_SET_ACTIVE:
614 ret = proc_set_active(pid, oom_score_adj);
615 if (ret != RESOURCED_ERROR_OK)
617 resourced_notify(RESOURCED_NOTIFIER_APP_ACTIVE, &proc_data);
618 proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
620 case PROC_CGROUP_SET_BACKGRD:
621 snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
622 dbus_proc_handler(PREDEF_BACKGRD, pidbuf);
623 ret = proc_set_backgrd(pid, oom_score_adj);
626 proc_update_process_state(pid, PROC_STATE_BACKGROUND);
628 case PROC_CGROUP_SET_INACTIVE:
629 ret = proc_set_inactive(pid, oom_score_adj);
630 if (ret != RESOURCED_ERROR_OK)
632 resourced_notify(RESOURCED_NOTIFIER_APP_INACTIVE, &proc_data);
634 case PROC_CGROUP_GET_MEMSWEEP:
635 ret = proc_sweep_memory(PROC_SWEEP_EXCLUDE_ACTIVE, pid);
637 case PROC_CGROUP_SET_NOTI_REQUEST:
639 case PROC_CGROUP_SET_PROC_EXCLUDE_REQUEST:
640 proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
643 ret = RESOURCED_ERROR_INVALID_PARAMETER;
648 int resourced_proc_action(int type, int argnum, char **arg)
651 char *pidbuf = NULL, *cgroup_name = NULL, *pkg_name = NULL;
653 _E("Unsupported number of arguments!");
654 return RESOURCED_ERROR_INVALID_PARAMETER;
658 if ((pid = atoi(pidbuf)) < 0) {
659 _E("Invalid pid argument!");
660 return RESOURCED_ERROR_INVALID_PARAMETER;
665 /* It's possible to get appid from arg */
666 cgroup_name = arg[1];
669 _SD("appid %s, pid %d, type %d \n", cgroup_name, pid, type);
670 return resourced_proc_status_change(type, pid, cgroup_name, pkg_name);