tizen 2.3 release
[kernel/api/system-resource.git] / src / proc-stat / proc-process.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18 */
19
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <sys/types.h>
28 #include <Ecore.h>
29
30 #include "resourced.h"
31 #include "trace.h"
32 #include "proc-main.h"
33 #include "cgroup.h"
34 #include "proc-process.h"
35 #include "lowmem-common.h"
36 #include "logging-common.h"
37 #include "macro.h"
38 #include "swap-common.h"
39 #include "proc-noti.h"
40 #include "notifier.h"
41
42 #define PROC_OOM_SCORE_ADJ_PATH "/proc/%d/oom_score_adj"
43 #define PROC_SWEEP_TIMER        3
44 static GHashTable *proc_sweep_list;
45 static Ecore_Timer *proc_sweep_timer = NULL;
46
47 enum proc_background_type {
48         PROC_BACKGROUND_INACTIVE,
49         PROC_BACKGROUND_ACTIVE,
50 };
51
52 static int proc_backgrd_manage(int currentpid, int active)
53 {
54         int pid = -1, pgid, ret;
55         struct proc_status proc_data;;
56         DIR *dp;
57         struct dirent *dentry;
58         FILE *fp;
59         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
60         char appname[PROC_NAME_MAX];
61         int count = 0;
62         int candidatepid[16] = {0,};
63         int cur_oom = -1, prev_oom=OOMADJ_BACKGRD_UNLOCKED, select_pid=0;
64         static int checkprevpid = 0;
65         unsigned long swap_args[2] = {0,};
66         unsigned long logging_args[3] = {0,};
67
68         ret = proc_get_cmdline(currentpid, appname);
69         if (ret == RESOURCED_ERROR_NONE) {
70                 ret = resourced_proc_excluded(appname);
71                 if (!active && !ret) {
72                         proc_data.pid = currentpid;
73                         proc_data.appid = appname;
74                         resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD, &proc_data);
75                 }
76                 if (ret) {
77                         _D("BACKGRD MANAGE : don't manage background application by %d", currentpid);
78                         return RESOURCED_ERROR_NONE;
79                 }
80         }
81
82         dp = opendir("/proc");
83         if (!dp) {
84                 _E("BACKGRD MANAGE : fail to open /proc");
85                 return RESOURCED_ERROR_FAIL;
86         }
87         while ((dentry = readdir(dp)) != NULL) {
88
89                 if (!isdigit(dentry->d_name[0]))
90                         continue;
91
92                 pid = atoi(dentry->d_name);
93                 pgid = getpgid(pid);
94                 if (!pgid)
95                         continue;
96
97                 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
98                 fp = fopen(buf, "r+");
99                 if (fp == NULL)
100                         continue;
101                 if (fgets(buf, sizeof(buf), fp) == NULL) {
102                         fclose(fp);
103                         continue;
104                 }
105                 cur_oom = atoi(buf);
106
107                 logging_args[0] = (unsigned long)pid;
108                 logging_args[1] = (unsigned long)pgid;
109                 logging_args[2] = (unsigned long)cur_oom;
110                 logging_control(LOGGING_INSERT_PROC_LIST, logging_args);
111
112                 if (currentpid != pid && currentpid == pgid) {
113                         proc_set_group(currentpid, pid);
114                         fclose(fp);
115                         continue;
116                 }
117
118                 if (active || checkprevpid == currentpid) {
119                         fclose(fp);
120                         continue;
121                 }
122
123                 if (select_pid != pid && select_pid == pgid && count < 16)
124                 {
125                         _D("found candidate child pid = %d, pgid = %d", pid, pgid);
126                         candidatepid[count++] = pid;
127                         fclose(fp);
128                         continue;
129                 }
130
131                 swap_args[0] = (unsigned long)pid;
132                 if (cur_oom > OOMADJ_BACKGRD_UNLOCKED && cur_oom > prev_oom
133                                 && !swap_status(SWAP_CHECK_PID, swap_args)) {
134                         count = 0;
135                         candidatepid[count++] = pid;
136                         select_pid = pid;
137                         prev_oom = cur_oom;
138                 }
139
140                 if (cur_oom >= OOMADJ_APP_MAX) {
141                         fclose(fp);
142                         continue;
143                 } else if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) {
144                         _D("BACKGRD : process %d set score %d (before %d)",
145                                         pid, cur_oom+OOMADJ_APP_INCREASE, cur_oom);
146                         fprintf(fp, "%d", cur_oom+OOMADJ_APP_INCREASE);
147                 }
148                 fclose(fp);
149         }
150
151         if (select_pid) {
152                 _D("found candidate pid = %d, count = %d", candidatepid, count);
153                 swap_args[0] = (unsigned long)candidatepid;
154                 swap_args[1] = (unsigned long)count;
155                 resourced_notify(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_args);
156         }
157         checkprevpid = currentpid;
158         closedir(dp);
159
160         logging_control(LOGGING_UPDATE_PROC_INFO, NULL);
161
162         return RESOURCED_ERROR_NONE;
163 }
164
165 static int proc_foregrd_manage(int pid, int oom_score_adj)
166 {
167         int ret = 0;
168         GSList *iter;
169         struct proc_process_info_t *process_info =
170                 find_process_info(NULL, pid, NULL);
171
172         if (!process_info) {
173                 ret = proc_set_oom_score_adj(pid, oom_score_adj);
174                 return ret;
175         }
176
177         gslist_for_each_item(iter, process_info->pids) {
178                 struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data);
179                 ret = proc_set_oom_score_adj(pid_info->pid, oom_score_adj);
180         }
181         return ret;
182 }
183
184 void proc_kill_victiom(gpointer key, gpointer value, gpointer user_data)
185 {
186         int pid = *(gint*)key;
187         int cur_oom = -1;
188         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
189         FILE *fp;
190
191         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
192         fp = fopen(buf, "r+");
193         if (fp == NULL) {
194                 _D("sweep proc_kill_victim : background process %d already terminated", pid);
195                 return;
196         }
197         if (fgets(buf, sizeof(buf), fp) == NULL) {
198                 fclose(fp);
199                 return;
200         }
201         cur_oom = atoi(buf);
202         if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) {
203                 kill(pid, SIGKILL);
204                 _D("sweep memory : background process %d killed by sigkill", pid);
205         }
206         fclose(fp);
207 }
208
209 static Eina_Bool proc_check_sweep_cb(void *data)
210 {
211         GHashTable *List = (GHashTable *)data;
212         g_hash_table_foreach(List, proc_kill_victiom, NULL);
213         g_hash_table_destroy(List);
214         proc_sweep_list = NULL;
215         return ECORE_CALLBACK_CANCEL;
216 }
217
218 int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid)
219 {
220         pid_t pid = -1;
221         int count=0, ret;
222         DIR *dp;
223         struct dirent *dentry;
224         FILE *fp;
225         gint *piddata;
226         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
227         char appname[PROC_NAME_MAX];
228
229         int cur_oom = -1;
230         int select_sweep_limit;
231         dp = opendir("/proc");
232         if (!dp) {
233                 _E("BACKGRD MANAGE : fail to open /proc");
234                 return RESOURCED_ERROR_FAIL;
235         }
236         if (proc_sweep_timer)
237                 ecore_timer_del(proc_sweep_timer);
238         if (proc_sweep_list)
239                 g_hash_table_destroy(proc_sweep_list);
240         proc_sweep_list = g_hash_table_new(g_int_hash, g_int_equal);
241
242         if (type == PROC_SWEEP_EXCLUDE_ACTIVE)
243                 select_sweep_limit = OOMADJ_BACKGRD_UNLOCKED;
244         else
245                 select_sweep_limit = OOMADJ_BACKGRD_LOCKED;
246
247         while ((dentry = readdir(dp)) != NULL) {
248                 if (!isdigit(dentry->d_name[0]))
249                         continue;
250
251                 pid = atoi(dentry->d_name);
252                 if (pid == callpid)
253                         continue;
254
255                 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
256                 fp = fopen(buf, "r+");
257                 if (fp == NULL)
258                         continue;
259                 if (fgets(buf, sizeof(buf), fp) == NULL) {
260                         fclose(fp);
261                         continue;
262                 }
263                 cur_oom = atoi(buf);
264                 if (cur_oom >= select_sweep_limit) {
265                         ret = proc_get_cmdline(pid, appname);
266                         if (ret != 0) {
267                                 fclose(fp);
268                                 continue;
269                         }
270                         proc_remove_process_list(pid);
271                         piddata = g_new(gint, 1);
272                         *piddata = pid;
273                         g_hash_table_insert(proc_sweep_list, piddata, NULL);
274                         kill(pid, SIGTERM);
275                         _D("sweep memory : background process %d(%s) killed",
276                                         pid, appname);
277                         count++;
278                 }
279                 fclose(fp);
280         }
281         if (count > 0) {
282                 proc_sweep_timer =
283                             ecore_timer_add(PROC_SWEEP_TIMER, proc_check_sweep_cb, (void *)proc_sweep_list);
284         }
285         closedir(dp);
286         return count;
287 }
288
289
290 int proc_get_cmdline(pid_t pid, char *cmdline)
291 {
292         char buf[PROC_BUF_MAX];
293         char cmdline_buf[PROC_NAME_MAX];
294         char *filename;
295         FILE *fp;
296
297         sprintf(buf, "/proc/%d/cmdline", pid);
298         fp = fopen(buf, "r");
299         if (fp == NULL)
300                 return RESOURCED_ERROR_FAIL;
301
302         if (fgets(cmdline_buf, PROC_NAME_MAX-1, fp) == NULL) {
303                 fclose(fp);
304                 return RESOURCED_ERROR_FAIL;
305         }
306         fclose(fp);
307
308         filename = strrchr(cmdline_buf, '/');
309         if (filename == NULL)
310                 filename = cmdline_buf;
311         else
312                 filename = filename + 1;
313
314         strncpy(cmdline, filename, PROC_NAME_MAX-1);
315
316         return RESOURCED_ERROR_NONE;
317 }
318
319 int proc_get_oom_score_adj(int pid, int *oom_score_adj)
320 {
321         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
322         FILE *fp = NULL;
323
324         if (pid < 0)
325                 return RESOURCED_ERROR_FAIL;
326
327         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
328         fp = fopen(buf, "r");
329
330         if (fp == NULL) {
331                 _E("fopen %s failed", buf);
332                 return RESOURCED_ERROR_FAIL;
333         }
334         if (fgets(buf, sizeof(buf), fp) == NULL) {
335                 fclose(fp);
336                 return RESOURCED_ERROR_FAIL;
337         }
338         (*oom_score_adj) = atoi(buf);
339         fclose(fp);
340         return RESOURCED_ERROR_NONE;
341 }
342
343 int proc_set_oom_score_adj(int pid, int oom_score_adj)
344 {
345         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
346         FILE *fp;
347         unsigned long lowmem_args[2] = {0, };
348
349         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
350         fp = fopen(buf, "r+");
351         if (fp == NULL)
352                 return RESOURCED_ERROR_FAIL;
353         if (fgets(buf, sizeof(buf), fp) == NULL) {
354                 fclose(fp);
355                 return RESOURCED_ERROR_FAIL;
356         }
357         fprintf(fp, "%d", oom_score_adj);
358         fclose(fp);
359
360         if (oom_score_adj >= OOMADJ_SU) {
361                 lowmem_args[0] = (unsigned long)pid;
362                 lowmem_args[1] = (unsigned long)oom_score_adj;
363                 lowmem_control(LOWMEM_MOVE_CGROUP, lowmem_args);
364         }
365         return 0;
366 }
367
368 int proc_set_foregrd(pid_t pid, int oom_score_adj)
369 {
370         int ret = 0;
371
372         switch (oom_score_adj) {
373         case OOMADJ_FOREGRD_LOCKED:
374         case OOMADJ_FOREGRD_UNLOCKED:
375         case OOMADJ_SU:
376                 ret = 0;
377                 break;
378         case OOMADJ_BACKGRD_LOCKED:
379                 ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_LOCKED);
380                 break;
381         case OOMADJ_BACKGRD_UNLOCKED:
382         case OOMADJ_INIT:
383                 ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED);
384                 break;
385         default:
386                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
387                         ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED);
388                 } else {
389                         ret = -1;
390                 }
391                 break;
392
393         }
394         return ret;
395 }
396
397 int proc_set_backgrd(int pid, int oom_score_adj)
398 {
399         int ret = 0;
400
401         switch (oom_score_adj) {
402         case OOMADJ_BACKGRD_LOCKED:
403         case OOMADJ_BACKGRD_UNLOCKED:
404         case OOMADJ_SU:
405                 ret = -1;
406                 break;
407         case OOMADJ_FOREGRD_LOCKED:
408                 proc_backgrd_manage(pid, PROC_BACKGROUND_ACTIVE);
409                 ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
410                 break;
411         case OOMADJ_FOREGRD_UNLOCKED:
412                 proc_backgrd_manage(pid, PROC_BACKGROUND_INACTIVE);
413                 ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED);
414                 break;
415         case OOMADJ_INIT:
416                 ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED);
417                 break;
418         default:
419                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
420                         ret = 0;
421                 } else {
422                         ret = -1;
423                 }
424                 break;
425         }
426         return ret;
427 }
428
429 int proc_set_active(int pid, int oom_score_adj)
430 {
431         int ret = 0;
432
433         switch (oom_score_adj) {
434         case OOMADJ_FOREGRD_LOCKED:
435         case OOMADJ_BACKGRD_LOCKED:
436         case OOMADJ_SU:
437         case OOMADJ_INIT:
438                 /* don't change oom value pid */
439                 ret = -1;
440                 break;
441         case OOMADJ_FOREGRD_UNLOCKED:
442                 ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_LOCKED);
443                 break;
444         case OOMADJ_BACKGRD_UNLOCKED:
445                 ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
446                 break;
447         case OOMADJ_SERVICE_DEFAULT:
448         case OOMADJ_SERVICE_BACKGRD:
449                 ret = proc_set_oom_score_adj(pid, OOMADJ_SERVICE_FOREGRD);
450                 break;
451         default:
452                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED)
453                         ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
454                 else
455                         ret = -1;
456                 break;
457         }
458         return ret;
459 }
460
461 int proc_set_inactive(int pid, int oom_score_adj)
462 {
463         int ret = 0;
464         struct proc_process_info_t * processinfo;
465         switch (oom_score_adj) {
466         case OOMADJ_FOREGRD_UNLOCKED:
467         case OOMADJ_BACKGRD_UNLOCKED:
468         case OOMADJ_SU:
469         case OOMADJ_INIT:
470                 /* don't change oom value pid */
471                 ret = -1;
472                 break;
473         case OOMADJ_FOREGRD_LOCKED:
474                 ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED);
475                 break;
476         case OOMADJ_BACKGRD_LOCKED:
477                 processinfo = find_process_info(NULL, pid, NULL);
478                 if (processinfo)
479                         ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED);
480                 break;
481         case OOMADJ_SERVICE_FOREGRD:
482                 ret = proc_set_oom_score_adj(pid, OOMADJ_SERVICE_DEFAULT);
483                 break;
484         default:
485                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
486                         ret = 0;
487                 } else {
488                         ret = -1;
489                 }
490                 break;
491
492         }
493         return ret;
494 }
495
496 void proc_set_group(pid_t onwerpid, pid_t childpid)
497 {
498         int oom_score_adj = 0;
499         struct proc_process_info_t *process_info =
500                 find_process_info(NULL, onwerpid, NULL);
501
502         if (proc_get_oom_score_adj(onwerpid, &oom_score_adj) < 0) {
503                 _D("owner pid(%d) was already terminated", onwerpid);
504                 return;
505         }
506         if (process_info) {
507                 proc_add_pid_list(process_info, childpid, RESOURCED_APP_TYPE_GROUP);
508                 proc_set_oom_score_adj(childpid, oom_score_adj);
509         }
510 }
511
512 pid_t find_pid_from_cmdline(char *cmdline)
513 {
514         pid_t pid = -1, foundpid = -1;
515         int ret = 0;
516         DIR *dp;
517         struct dirent *dentry;
518         char appname[PROC_NAME_MAX];
519
520         dp = opendir("/proc");
521         if (!dp) {
522                 _E("BACKGRD MANAGE : fail to open /proc");
523                 return RESOURCED_ERROR_FAIL;
524         }
525         while ((dentry = readdir(dp)) != NULL) {
526                 if (!isdigit(dentry->d_name[0]))
527                         continue;
528
529                 pid = atoi(dentry->d_name);
530                 if (!pid)
531                         continue;
532                 ret = proc_get_cmdline(pid, appname);
533                 if (ret == RESOURCED_ERROR_NONE) {
534                         if (!strcmp(cmdline, appname)) {
535                                 foundpid = pid;
536                                 break;
537                         }
538                 }
539         }
540         closedir(dp);
541         return foundpid;
542 }