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