Add unit(in variable) & fix bugs
[platform/core/system/resourced.git] / src / common / procfs.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2015 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 /**
21  * @file procfs.c
22  *
23  * @desc communicate with procfs in resourced
24  *
25  * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
26  *
27  */
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <dirent.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <errno.h>
41
42 #include "resourced.h"
43 #include "trace.h"
44 #include "macro.h"
45 #include "util.h"
46 #include "procfs.h"
47 #include "proc-common.h"
48 #include "memory-cgroup.h"
49 #include "module.h"
50 #include "file-helper.h"
51 #include "swap-common.h"
52 #include "smaps.h"
53 #include "notifier.h"
54 #include "lowmem-handler.h"
55
56 #define MEM_SWAP_RATIO                  0.5
57
58 static struct sys_node_table sys_node_tables[] = {
59         { SYS_VM_SHRINK_MEMORY, "/proc/sys/vm/shrink_memory", 1, 1 },
60         { SYS_VM_COMPACT_MEMORY, "/proc/sys/vm/compact_memory", 1, 1 },
61         { },
62 };
63
64 int proc_get_cmdline(pid_t pid, char *cmdline, size_t maxcmdline)
65 {
66         assert(cmdline);
67         assert(maxcmdline > 0);
68
69         char buf[PROC_BUF_MAX];
70         char cmdline_buf[PROC_NAME_MAX];
71         char *filename;
72         FILE *fp;
73
74         if (pid == 0)
75                 snprintf(buf, sizeof(buf), "/proc/cmdline");
76         else
77                 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
78
79         fp = fopen(buf, "r");
80         if (fp == NULL)
81                 return RESOURCED_ERROR_FAIL;
82
83         if (fgets(cmdline_buf, sizeof cmdline_buf, fp) == NULL) {
84                 fclose(fp);
85                 return RESOURCED_ERROR_FAIL;
86         }
87         fclose(fp);
88
89         filename = strrchr(cmdline_buf, '/');
90         if (filename == NULL)
91                 filename = cmdline_buf;
92         else
93                 filename = filename + 1;
94
95         strncpy(cmdline, filename, maxcmdline - 1);
96         cmdline[maxcmdline - 1] = '\0';
97
98         return RESOURCED_ERROR_NONE;
99 }
100
101 pid_t find_pid_from_cmdline(char *cmdline)
102 {
103         pid_t pid = -1, foundpid = -1;
104         int ret = 0;
105         DIR *dp;
106         struct dirent *dentry;
107         char appname[PROC_NAME_MAX];
108
109         dp = opendir("/proc");
110         if (!dp) {
111                 _E("BACKGRD MANAGE : fail to open /proc");
112                 return RESOURCED_ERROR_FAIL;
113         }
114         while ((dentry = readdir(dp))) {
115                 if (!isdigit(dentry->d_name[0]))
116                         continue;
117
118                 pid = atoi(dentry->d_name);
119                 if (!pid)
120                         continue;
121                 ret = proc_get_cmdline(pid, appname, sizeof appname);
122                 if (ret == RESOURCED_ERROR_NONE) {
123                         if (!strncmp(cmdline, appname, strlen(appname)+1)) {
124                                 foundpid = pid;
125                                 break;
126                         }
127                 }
128         }
129         closedir(dp);
130         return foundpid;
131 }
132
133 int proc_get_oom_score_adj(int pid, int *oom_score_adj)
134 {
135         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
136         FILE *fp = NULL;
137
138         if (pid <= 0)
139                 return RESOURCED_ERROR_FAIL;
140
141         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
142         fp = fopen(buf, "r");
143
144         if (fp == NULL) {
145                 _E("fopen %s failed", buf);
146                 return RESOURCED_ERROR_FAIL;
147         }
148         if (fgets(buf, sizeof(buf), fp) == NULL) {
149                 fclose(fp);
150                 return RESOURCED_ERROR_FAIL;
151         }
152         (*oom_score_adj) = atoi(buf);
153         fclose(fp);
154         return RESOURCED_ERROR_NONE;
155 }
156
157 int proc_set_oom_score_adj(int pid, int oom_score_adj, struct proc_app_info *pai)
158 {
159         FILE *fp;
160         struct lowmem_control_data lowmem_data;
161         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
162
163         /* Don't touch OOM-fixed process' score */
164         if (pai && pai->app_memcg_update_exclude)
165                 return RESOURCED_ERROR_NONE;
166
167         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
168         fp = fopen(buf, "r+");
169         if (fp == NULL)
170                 return RESOURCED_ERROR_FAIL;
171         if (fgets(buf, sizeof(buf), fp) == NULL) {
172                 fclose(fp);
173                 return RESOURCED_ERROR_FAIL;
174         }
175         fprintf(fp, "%d", oom_score_adj);
176         fclose(fp);
177
178         if (oom_score_adj >= OOMADJ_SU) {
179                 lowmem_data.control_type = LOWMEM_MOVE_CGROUP;
180                 lowmem_data.pid = pid;
181                 lowmem_data.oom_score_adj = oom_score_adj;
182                 lowmem_data.pai = pai;
183                 resourced_notify(RESOURCED_NOTIFIER_MEM_CONTROL, &lowmem_data);
184         }
185
186         return RESOURCED_ERROR_NONE;
187 }
188
189 int proc_get_label(pid_t pid, char *label)
190 {
191         char buf[PROC_BUF_MAX];
192         FILE *fp;
193
194         snprintf(buf, sizeof(buf), "/proc/%d/attr/current", pid);
195         fp = fopen(buf, "r");
196         if (fp == NULL)
197                 return RESOURCED_ERROR_FAIL;
198
199         if (fgets(label, PROC_NAME_MAX-1, fp) == NULL) {
200                 fclose(fp);
201                 return RESOURCED_ERROR_FAIL;
202         }
203         fclose(fp);
204         return RESOURCED_ERROR_NONE;
205 }
206
207 int proc_get_mem_status(pid_t pid, unsigned int *vmswap_kb, unsigned int *vmrss_kb)
208 {
209         char filename[PROC_BUF_MAX];
210         _cleanup_fclose_ FILE *fp = NULL;
211         unsigned int swap_kb = 0, rss_kb = 0;
212
213         snprintf(filename, PROC_BUF_MAX, "/proc/%d/status", pid);
214         fp = fopen(filename, "r");
215         if (!fp)
216                 return RESOURCED_ERROR_FAIL;
217
218         if (vmrss_kb != NULL) {
219                 while (fgets(filename, sizeof(filename), fp)) {
220                         /* Skip the lines, until first match */
221                         if (!strstart_with(filename, "VmRSS:"))
222                                 continue;
223
224                         /* Read RSS value and end this loop. */
225                         if (sscanf(filename, "VmRSS: %u kB", &rss_kb) == 1)
226                                 break;
227
228                         return RESOURCED_ERROR_NO_DATA;
229                 }
230                 *vmrss_kb = rss_kb;
231         }
232
233         if (vmswap_kb != NULL) {
234                 /* Interate over rest of Vm* values */
235                 while (fgets(filename, sizeof(filename), fp)) {
236                         /* Read VmSwap and return with positive result */
237                         if (sscanf(filename, "VmSwap: %d kB", &swap_kb) == 1)
238                                 break;
239
240                         /* End of file before VmSwap read, return with error */
241                         if (feof(fp))
242                                 return RESOURCED_ERROR_NO_DATA;
243                 }
244                 *vmswap_kb = swap_kb;
245         }
246
247         return RESOURCED_ERROR_NONE;
248 }
249
250 int proc_get_uss(pid_t pid, unsigned int *uss)
251 {
252         _cleanup_smaps_free_ struct smaps *smaps = NULL;
253         int ret;
254
255         *uss = 0;
256
257         /*
258          * USS memory usage is number of KB accounted to a process
259          * where only private (non-shared) data are accounted.
260          *
261          * USS provides the value how much memory will be freed after
262          * termination of process.
263          */
264         ret = smaps_get(pid, &smaps,
265                         (SMAPS_MASK_PRIVATE_CLEAN | SMAPS_MASK_PRIVATE_DIRTY));
266
267         if (ret < 0) {
268                 _E("Error while reading smaps or totmaps for pid %d", pid);
269                 return RESOURCED_ERROR_FAIL;
270         }
271
272         *uss += smaps->sum[SMAPS_ID_PRIVATE_CLEAN];
273         *uss += smaps->sum[SMAPS_ID_PRIVATE_DIRTY];
274
275         return RESOURCED_ERROR_NONE;
276 }
277
278 int proc_get_zram_usage(pid_t pid, unsigned int *usage_kb)
279 {
280         int ret;
281         struct meminfo mi;
282         static unsigned int swap_total_kb = 0;
283         unsigned int proc_swap_usage_kb;
284         unsigned long long zram_usage_bytes;
285
286         /* Read total swap size just once and cache it */
287         if (!swap_total_kb) {
288                 ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_TOTAL);
289                 if (ret < 0) {
290                         _E("Failed to get %s: %m",
291                            meminfo_id_to_string(MEMINFO_ID_SWAP_TOTAL));
292                         return RESOURCED_ERROR_FAIL;
293                 }
294                 swap_total_kb = mi.value[MEMINFO_ID_SWAP_TOTAL];
295         }
296
297         /* Read usage of Swap (VmSwap) of interested process */
298         ret = proc_get_mem_status(pid, &proc_swap_usage_kb, NULL);
299         if (ret != RESOURCED_ERROR_NONE)
300                 return ret;
301
302         /* Read current total memory usage of zram device */
303         ret = fread_nth_ulonglong(SWAP_ZRAM_SYSFILE"mm_stat", 2, &zram_usage_bytes);
304         if (ret == -ENOENT) {
305                 ret = fread_ulonglong(SWAP_ZRAM_SYSFILE"mem_used_total", &zram_usage_bytes);
306         }
307
308         if (ret < 0)
309                 return RESOURCED_ERROR_FAIL;
310
311         /*
312          * Calculate aproximated value of zram usage for selected process
313          * by formula: proc_zram_usage = ( VmSwap x ZramMemoryUsage )/SwapTotal
314          */
315         *usage_kb = (int)((float)proc_swap_usage_kb * BYTE_TO_KBYTE(zram_usage_bytes) / swap_total_kb);
316
317         return RESOURCED_ERROR_NONE;
318 }
319
320 int proc_get_approx_mem_usage(pid_t pid, unsigned int *usage_kb)
321 {
322         int ret;
323         unsigned long resident_pages = 0, shared_pages = 0;
324         char filename[PROC_BUF_MAX];
325         _cleanup_fclose_ FILE *fp = NULL;
326
327         snprintf(filename, PROC_BUF_MAX, "/proc/%d/statm", pid);
328         fp = fopen(filename, "r");
329         if (!fp)
330                 return RESOURCED_ERROR_FAIL;
331
332         /*
333          * For quick read, open code by putting numbers directly
334          * expected format is
335          * seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n",
336          *               size, resident, shared, text, data);
337          */
338         ret = fscanf(fp, "%*s %lu %lu %*s %*s %*s %*s\n", &resident_pages, &shared_pages);
339         if (ret < 0)
340                 return RESOURCED_ERROR_FAIL;
341
342         /*
343          * The value resident - shared is mostly similar to Uss.
344          */
345         *usage_kb = BYTE_TO_KBYTE((unsigned long long)(resident_pages - shared_pages) << PAGE_SHIFT);
346         return RESOURCED_ERROR_NONE;
347 }
348
349 /**
350  * @desc get total memory usage from VmSwap and VmRSS.
351  * @return negative value if error or pid doesn't exist
352  */
353 int proc_get_mem_usage(pid_t pid, unsigned int *usage)
354 {
355         int ret;
356         unsigned int vmswap = 0, total = 0;
357
358         ret = proc_get_approx_mem_usage(pid, &total);
359         if (ret < 0) {
360                 _E("Failed to get usage : %d", pid);
361                 return ret;
362         }
363
364         if (swap_get_state() == SWAP_ON) {
365                 ret = proc_get_mem_status(pid, &vmswap, NULL);
366                 if (ret != RESOURCED_ERROR_NONE)
367                         goto out;
368
369                 total += vmswap;
370         }
371 out:
372         *usage = total;
373         return RESOURCED_ERROR_NONE;
374 }
375
376 /**
377  * @desc get how much ram is used in each application
378  * @return negative value if error or pid doesn't exist
379  */
380 int proc_get_ram_usage(pid_t pid, unsigned int *usage_kb)
381 {
382         int ret;
383         unsigned int vmswap_kb = 0, total_kb = 0;
384
385         ret = proc_get_approx_mem_usage(pid, &total_kb);
386         if (ret < 0) {
387                 _E("Failed to get usage : %d", pid);
388                 return ret;
389         }
390
391         if (swap_get_state() == SWAP_ON) {
392                 ret = proc_get_mem_status(pid, &vmswap_kb, NULL);
393                 if (ret != RESOURCED_ERROR_NONE)
394                         goto out;
395
396                 /*
397                  * If it is necessary to know real ram size about each application,
398                  * it should consider compression ratio.
399                  */
400                 vmswap_kb *= MEM_SWAP_RATIO;
401                 total_kb += vmswap_kb;
402         }
403 out:
404         *usage_kb = total_kb;
405         return RESOURCED_ERROR_NONE;
406 }
407
408 unsigned int proc_get_mem_available(void)
409 {
410         struct meminfo mi;
411         int ret;
412
413         ret = proc_get_meminfo(&mi, MEMINFO_MASK_MEM_AVAILABLE);
414         if (ret < 0) {
415                 _E("Failed to get %s: %m",
416                                 meminfo_id_to_string(MEMINFO_ID_MEM_AVAILABLE));
417                 return 0;
418         }
419
420         return KBYTE_TO_MBYTE(mi.value[MEMINFO_ID_MEM_AVAILABLE]);
421 }
422
423 unsigned int proc_get_swap_free(void)
424 {
425         struct meminfo mi;
426         int ret;
427
428         ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_FREE);
429         if (ret < 0) {
430                 _E("Failed to get %s: %m",
431                    meminfo_id_to_string(MEMINFO_ID_SWAP_FREE));
432                 return 0;
433         }
434
435         return mi.value[MEMINFO_ID_SWAP_FREE];
436 }
437
438 unsigned int proc_get_swap_total(void)
439 {
440         struct meminfo mi;
441         int ret;
442
443         ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_TOTAL);
444         if (ret < 0) {
445                 _E("Failed to get %s: %m",
446                    meminfo_id_to_string(MEMINFO_ID_SWAP_TOTAL));
447                 return 0;
448         }
449
450         return mi.value[MEMINFO_ID_SWAP_TOTAL];
451 }
452
453 int proc_get_cpu_time(pid_t pid, unsigned long *utime,
454                 unsigned long *stime, unsigned long *starttime)
455 {
456         char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
457         _cleanup_fclose_ FILE *fp = NULL;
458
459         assert(utime != NULL);
460         assert(stime != NULL);
461
462         snprintf(proc_path, sizeof(proc_path), PROC_STAT_PATH, pid);
463         fp = fopen(proc_path, "r");
464         if (fp == NULL)
465                 return RESOURCED_ERROR_FAIL;
466
467         if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0)
468                 return RESOURCED_ERROR_FAIL;
469
470         if (fscanf(fp, "%lu %lu", utime, stime) < 1)
471                 return RESOURCED_ERROR_FAIL;
472
473         if (fscanf(fp, "%*s %*s %*s %*s %*s %*s") < 0)
474                 return RESOURCED_ERROR_FAIL;
475
476         if (fscanf(fp, "%lu", starttime) < 1)
477                 return RESOURCED_ERROR_FAIL;
478
479         return RESOURCED_ERROR_NONE;
480 }
481
482 unsigned int proc_get_cpu_number(void)
483 {
484         char buf[PATH_MAX];
485         FILE *fp;
486         int cpu = 0;
487
488         fp = fopen("/proc/cpuinfo", "r");
489
490         if (!fp) {
491                 _E("/proc/cpuinfo open failed");
492                 return RESOURCED_ERROR_FAIL;
493         }
494
495         while (fgets(buf, PATH_MAX, fp) != NULL) {
496                 if (!strncmp(buf, "processor", 9))
497                         cpu++;
498         }
499
500         fclose(fp);
501         return cpu;
502 }
503
504 int proc_get_exepath(pid_t pid, char *buf, int len)
505 {
506         char path[PROC_BUF_MAX];
507         int ret = 0;
508
509         snprintf(path, sizeof(path), "/proc/%d/exe", pid);
510         ret = readlink(path, buf, len-1);
511         if (ret > 0)
512                 buf[ret] = '\0';
513         else
514                 buf[0] = '\0';
515         return RESOURCED_ERROR_NONE;
516 }
517
518 static int proc_get_data(char *path, char *buf, int len)
519 {
520         _cleanup_close_ int fd = -1;
521         int ret;
522
523         fd = open(path, O_RDONLY);
524         if (fd < 0)
525                 return RESOURCED_ERROR_FAIL;
526
527         ret = read(fd, buf, len-1);
528         if (ret < 0) {
529                 buf[0] = '\0';
530                 return RESOURCED_ERROR_FAIL;
531         }
532         buf[ret] = '\0';
533         return RESOURCED_ERROR_NONE;
534 }
535
536 int proc_get_raw_cmdline(pid_t pid, char *buf, int len)
537 {
538         char path[PROC_BUF_MAX];
539         snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
540         return proc_get_data(path, buf, len);
541 }
542
543 int proc_get_stat(pid_t pid, char *buf, int len)
544 {
545         char path[PROC_BUF_MAX];
546         snprintf(path, sizeof(path), "/proc/%d/stat", pid);
547         return proc_get_data(path, buf, len);
548 }
549
550 int proc_get_status(pid_t pid, char *buf, int len)
551 {
552         char path[PROC_BUF_MAX];
553         snprintf(path, sizeof(path), "/proc/%d/status", pid);
554         return proc_get_data(path, buf, len);
555 }
556
557 int proc_sys_node_trigger(enum sys_node_id sys_node_id)
558 {
559         FILE *fp = NULL;
560
561         if (sys_node_id >= ARRAY_SIZE(sys_node_tables)) {
562                 _E("sys_node_id[%d] is out of range.\n", sys_node_id);
563                 return RESOURCED_ERROR_FAIL;
564         }
565         if (!sys_node_tables[sys_node_id].valid) {
566                 _E("sys_node_id[%d] is not valid.\n", sys_node_id);
567                 return RESOURCED_ERROR_FAIL;
568         }
569
570         /* open and check if the path exists, else return fail */
571         fp = fopen(sys_node_tables[sys_node_id].path, "w");
572         if (fp == NULL) {
573                 _E("Failed to open %s: %m\n",
574                         sys_node_tables[sys_node_id].path);
575                 sys_node_tables[sys_node_id].valid = 0;
576                 return RESOURCED_ERROR_FAIL;
577         }
578         fputc(sys_node_tables[sys_node_id].value, fp);
579         fclose(fp);
580         return RESOURCED_ERROR_NONE;
581 }
582
583 static const char* const meminfo_string_lookup[MEMINFO_ID_MAX] = {
584         [MEMINFO_ID_MEM_TOTAL]     = "MemTotal",
585         [MEMINFO_ID_MEM_FREE]      = "MemFree",
586         [MEMINFO_ID_MEM_AVAILABLE] = "MemAvailable",
587         [MEMINFO_ID_BUFFERS]       = "Buffers",
588         [MEMINFO_ID_CACHED]        = "Cached",
589         [MEMINFO_ID_SWAP_CACHED]   = "SwapCached",
590         [MEMINFO_ID_ACTIVE]        = "Active",
591         [MEMINFO_ID_INACTIVE]      = "Inactive",
592         [MEMINFO_ID_ACTIVE_ANON]   = "Active(anon)",
593         [MEMINFO_ID_INACTIVE_ANON] = "Inactive(anon)",
594         [MEMINFO_ID_ACTIVE_FILE]   = "Active(file)",
595         [MEMINFO_ID_INACTIVE_FILE] = "Inactive(file)",
596         [MEMINFO_ID_UNEVICTABLE]   = "Unevictable",
597         [MEMINFO_ID_MLOCKED]       = "Mlocked",
598         [MEMINFO_ID_HIGH_TOTAL]    = "HighTotal",
599         [MEMINFO_ID_HIGH_FREE]     = "HighFree",
600         [MEMINFO_ID_LOW_TOTAL]     = "LowTotal",
601         [MEMINFO_ID_LOW_FREE]      = "LowFree",
602         [MEMINFO_ID_SWAP_TOTAL]    = "SwapTotal",
603         [MEMINFO_ID_SWAP_FREE]     = "SwapFree",
604         [MEMINFO_ID_DIRTY]         = "Dirty",
605         [MEMINFO_ID_WRITEBACK]     = "Writeback",
606         [MEMINFO_ID_ANON_PAGES]    = "AnonPages",
607         [MEMINFO_ID_MAPPED]        = "Mapped",
608         [MEMINFO_ID_SHMEM]         = "Shmem",
609         [MEMINFO_ID_SLAB]          = "Slab",
610         [MEMINFO_ID_SRECLAIMABLE]  = "SReclaimable",
611         [MEMINFO_ID_SUNRECLAIM]    = "SUnreclaim",
612         [MEMINFO_ID_KERNEL_STACK]  = "KernelStack",
613         [MEMINFO_ID_PAGE_TABLES]   = "PageTables",
614         [MEMINFO_ID_NFS_UNSTABLE]  = "NFS_Unstable",
615         [MEMINFO_ID_BOUNCE]        = "Bounce",
616         [MEMINFO_ID_WRITEBACK_TMP] = "WritebackTmp",
617         [MEMINFO_ID_COMMIT_LIMIT]  = "CommitLimit",
618         [MEMINFO_ID_COMMITTED_AS]  = "Committed_AS",
619         [MEMINFO_ID_VMALLOC_TOTAL] = "VmallocTotal",
620         [MEMINFO_ID_VMALLOC_USED]  = "VmallocUsed",
621         [MEMINFO_ID_VMALLOC_CHUNK] = "VmallocChunk",
622 };
623
624 const char *meminfo_id_to_string(enum meminfo_id id)
625 {
626         assert(id >= 0 && id < MEMINFO_ID_MAX);
627
628         return meminfo_string_lookup[id];
629 }
630
631 int proc_get_meminfo(struct meminfo *mi, unsigned long long mask)
632 {
633         _cleanup_fclose_ FILE *f = NULL;
634         unsigned long long remain_mask = mask;
635         char buf[LINE_MAX];
636
637         assert(mi);
638
639         memset(mi, 0x0, sizeof(struct meminfo));
640
641         f = fopen("/proc/meminfo", "r");
642         if (!f) {
643                 _E("Failed to read /proc/meminfo");
644                 return -errno;
645         }
646
647         if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE)
648                 remain_mask |= (MEMINFO_MASK_MEM_FREE |
649                                 MEMINFO_MASK_CACHED);
650
651         while (remain_mask) {
652                 _cleanup_free_ char *k = NULL;
653                 unsigned int v = 0;
654                 enum meminfo_id id;
655                 size_t l;
656
657                 if (!fgets(buf, sizeof(buf), f)) {
658                         if (ferror(f))
659                                 return -errno;
660                         break;
661                 }
662
663                 l = strcspn(buf, ":");
664                 if (!l)
665                         break;
666
667                 k = strndup(buf, l);
668                 if (!k)
669                         return -errno;
670
671                 id = meminfo_string_to_id(k);
672                 if (id < 0 || id >= MEMINFO_ID_MAX)
673                         continue;
674
675                 if (!(remain_mask & (1ULL << id)))
676                         continue;
677
678                 remain_mask &= ~((1ULL << id));
679
680                 if (sscanf(buf + l + 1, "%d", &v) != 1)
681                         break;
682
683                 mi->value[id] = v;
684         }
685
686         if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE) {
687                 mi->value[MEMINFO_ID_MEM_AVAILABLE] =
688                         mi->value[MEMINFO_ID_MEM_FREE]
689                         + mi->value[MEMINFO_ID_CACHED];
690
691                 remain_mask &= ~MEMINFO_MASK_MEM_AVAILABLE;
692         }
693
694         if (remain_mask) {
695                 enum meminfo_id i;
696
697                 for (i = 0; i < MEMINFO_ID_MAX; i++)
698                         if (remain_mask & (1ULL << i))
699                                 _E("Failed to get meminfo: '%s'",
700                                    meminfo_id_to_string(i));
701         }
702
703         return RESOURCED_ERROR_NONE;
704 }
705
706 static int parse_spanned_pages(const char *s, regmatch_t *match,
707                                 unsigned int parse_tag, void *data)
708 {
709         char *e;
710         unsigned long v;
711         unsigned long *parse_v = (unsigned long *)data;
712
713         v = strtoul(s + match[1].rm_so, &e, 0);
714         *parse_v += v;
715
716         return 0;
717 }
718
719 int proc_get_ram_total(unsigned int *total_kb)
720 {
721         unsigned long total_spanned = 0;
722         static unsigned int total_ram_kb = 0;
723         int ret;
724         const struct parse_arg args[] = {
725                 PARSE_TAG("spanned[[:blank:]]+([0-9]+)\n",
726                           parse_spanned_pages, SPANNED),
727                 PARSE_TAG_EMPTY(),
728         };
729
730         if (total_ram_kb > 0) {
731                 *total_kb = total_ram_kb;
732                 return RESOURCED_ERROR_NONE;
733         }
734
735         ret = proc_parse_zoneinfo(args, &total_spanned);
736         if (ret != RESOURCED_ERROR_NONE)
737                 return RESOURCED_ERROR_NO_DATA;
738
739         total_ram_kb = (unsigned int)BYTE_TO_KBYTE((unsigned long long)total_spanned << PAGE_SHIFT);
740         *total_kb = total_ram_kb;
741
742         return RESOURCED_ERROR_NONE;
743 }
744
745 static inline char *proc_skip_blanks(const char *s)
746 {
747         while (s && (isblank(*s) || !isalnum(*s)))
748                 ++s;
749         return (char*)s;
750 }
751
752 #define PROC_MAX_REG_MATCH      6
753
754 static int proc_find_match(char *s, const struct parse_arg *parse,
755                                 void *data, size_t *parsed)
756 {
757         regex_t regex;
758         regmatch_t match[PROC_MAX_REG_MATCH];
759         int result;
760
761         if (!s || !(*s != '\0'))
762                 return -EINVAL;
763
764         if (!parse->callback)
765                 return 0;
766
767         *parsed = 0;
768
769         result = regcomp(&regex, parse->re_exp, REG_EXTENDED);
770         if (!result) {
771                 result = regexec(&regex, s, ARRAY_SIZE(match), match, 0);
772                 if (!result) {
773                         regmatch_t *m = match;
774
775                         result =  parse->callback(s, match, parse->tag, data);
776                         if (result)
777                                 goto leave;
778                         while ((m - match) < PROC_MAX_REG_MATCH &&
779                             m->rm_so >= 0 && m->rm_eo > 0)
780                                 ++m;
781
782                         *parsed = m > match ? (--m)->rm_eo + 1 : 0;
783                 }
784 leave:
785                 regfree(&regex);
786         }
787         return result;
788 }
789
790 /**
791  * @brief  Rather basic parser that relies on provided arguments,
792  *         specifying the actual expression to look for.
793  *         Note that it does not make any assumptions as to how
794  *         the input to be parsed is actual formated and  what is the
795  *         layout of the information held within it.
796  *         - mind it as badly specified arguments might result in a failure
797  *         to identify potential match or finding a match but not one that
798  *         has been expected. It is up to the caller to perform
799  *         the actual formatting of data.
800  *         Parsing is done per line with possible multiple regexes.
801  * @return Returns number of found matches against supplied parse arguments
802  */
803 static int proc_parse_single(const char *buffer,
804                              const struct parse_arg *parse_args,
805                              void *data)
806 {
807         char *s = (char*)buffer;
808         size_t size;
809         unsigned int match = 0;
810
811         while (s && *s != '\n' && *s != '\0') {
812                 const struct parse_arg *p = parse_args;
813
814                 size = 0;
815                 s = proc_skip_blanks(s);
816
817                 while (proc_find_match(s, p, data, &size))
818                         ++p;
819
820                 if (!size)
821                         break;
822                 s += size;
823                 ++match;
824         }
825         return match;
826 }
827
828 /**
829  * @brief Simplified procfs parser
830  *
831  *  Reads the procfs entry line by line triggering regex match
832  *  function for each supplied argument.
833  *  Note: The set of arguments should end with an empty one.
834  *
835  * @return returns 0 upon successfully calling the actual parsing
836  *         procedure at least once, error code otherwise.
837  *         Note: it is up to the caller to properly handle
838  *         callbacks triggered upon each match found and to determine
839  *         if parsing itself actually succeeded.
840  */
841 int proc_parse_entry(const char *path, const struct parse_arg *parse_args,
842                         void *data)
843 {
844         char buf[LINE_MAX];
845         _cleanup_fclose_ FILE *f = NULL;
846
847         f = fopen(path, "r");
848         if (!f)
849                 return -errno;
850
851         while (fgets(buf, sizeof(buf), f))
852                 proc_parse_single(buf, parse_args, data);
853         return ferror(f) ? -errno : 0;
854 }
855
856 int proc_get_uptime(unsigned long *uptime)
857 {
858         _cleanup_fclose_ FILE *fp = NULL;
859         double stime;
860
861         fp = fopen("/proc/uptime", "r");
862         if (fp == NULL)
863                 return RESOURCED_ERROR_FAIL;
864
865         if (fscanf(fp, "%lf %*s", &stime) < 0)
866                 return RESOURCED_ERROR_FAIL;
867
868         *uptime = (unsigned long)stime;
869         return RESOURCED_ERROR_NONE;
870 }
871
872 /**
873  * @brief print simple memory info with rss and memtotal
874  *
875  *  Reads the procfs about all process id and print RSS usage.
876  *  Note: If fp is valid, the result is written by desired file.
877  *            Otherwise, it printed message through dlog.
878  */
879 void proc_print_meninfo(FILE *fp)
880 {
881         _cleanup_closedir_ DIR *dir = NULL;
882         struct dirent *de;
883         pid_t pid, caller;
884         char cmdline[PATH_MAX];
885         _cleanup_fclose_ FILE *output_file = NULL;
886         int oom_score_adj, ret;
887         unsigned int rss, swap;
888         struct meminfo mi;
889         unsigned int free_kb = 0;
890         unsigned int total_mem_kb = 0, available_kb = 0, used_kb;
891         unsigned int swap_total_kb = 0, swap_free_kb = 0, swap_used_kb;
892         unsigned long long zram_used_bytes;
893
894         dir = opendir("/proc");
895         if (dir == NULL) {
896                 _E("cannot read directory /proc.");
897                 return;
898         }
899
900         LOG_DUMP(fp, "     PID    RSS    SWAP    OOM_SCORE    COMMAND\n");
901
902         caller = getpid();
903         while ((de = readdir(dir)) != NULL) {
904                 pid = atoi(de->d_name);
905
906                 /* exclude negative value which means string value and kernel thread */
907                 if (pid < 1 || pid == caller)
908                         continue;
909
910                 ret = proc_get_cmdline(pid, cmdline, sizeof cmdline);
911                 if (ret)
912                         continue;
913
914                 ret = proc_get_mem_status(pid, &swap, &rss);
915                 if (ret)
916                         continue;
917
918                 ret = proc_get_oom_score_adj(pid, &oom_score_adj);
919                 if (ret)
920                         continue;
921
922                 LOG_DUMP(fp, "%8d %8u  %8u %8d          %s\n",
923                                 pid,
924                                 rss,
925                                 swap,
926                                 oom_score_adj,
927                                 cmdline);
928
929         } /* end of while */
930
931         ret = proc_get_meminfo(&mi,
932                         (MEMINFO_MASK_MEM_TOTAL |
933                          MEMINFO_MASK_MEM_FREE |
934                          MEMINFO_MASK_MEM_AVAILABLE |
935                          MEMINFO_MASK_CACHED |
936                          MEMINFO_MASK_SWAP_TOTAL |
937                          MEMINFO_MASK_SWAP_FREE));
938         if (ret < 0) {
939                 _E("Failed to get meminfo");
940                 return;
941         }
942
943         total_mem_kb = mi.value[MEMINFO_ID_MEM_TOTAL];
944         free_kb = mi.value[MEMINFO_ID_MEM_FREE];
945         available_kb = mi.value[MEMINFO_ID_MEM_AVAILABLE];
946         swap_total_kb = mi.value[MEMINFO_ID_SWAP_TOTAL];
947         swap_free_kb = mi.value[MEMINFO_ID_SWAP_FREE];
948
949         used_kb = total_mem_kb - available_kb;
950         swap_used_kb = swap_total_kb - swap_free_kb;
951
952         ret = fread_nth_ulonglong(SWAP_ZRAM_SYSFILE"mm_stat", 2, &zram_used_bytes);
953         if (ret == -ENOENT) {
954                 ret = fread_ulonglong(SWAP_ZRAM_SYSFILE"mem_used_total", &zram_used_bytes);
955         }
956
957         if (ret != RESOURCED_ERROR_NONE)
958                 zram_used_bytes = 0;
959
960         LOG_DUMP(fp, "====================================================================\n");
961         LOG_DUMP(fp, "Total RAM size: \t%15d MB( %6d kB)\n",
962                         KBYTE_TO_MBYTE(total_mem_kb), total_mem_kb);
963         LOG_DUMP(fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
964                         KBYTE_TO_MBYTE(total_mem_kb - free_kb), total_mem_kb - free_kb);
965         LOG_DUMP(fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
966                         KBYTE_TO_MBYTE(used_kb), used_kb);
967         LOG_DUMP(fp, "Used (Mem):  \t\t%15d MB( %6d kB)\n",
968                         KBYTE_TO_MBYTE(used_kb), used_kb);
969         LOG_DUMP(fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
970                         KBYTE_TO_MBYTE(swap_used_kb), swap_used_kb);
971         LOG_DUMP(fp, "Used (Zram block device): %13d MB( %6d kB)\n",
972                         (int)BYTE_TO_MBYTE(zram_used_bytes), (int)BYTE_TO_KBYTE(zram_used_bytes));
973         LOG_DUMP(fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
974                         KBYTE_TO_MBYTE(free_kb), free_kb);
975         LOG_DUMP(fp,  "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
976                         KBYTE_TO_MBYTE(available_kb), available_kb);
977         return;
978 }
979
980 int proc_get_buddyinfo(const char *zone, struct buddyinfo *bi)
981 {
982         _cleanup_fclose_ FILE *f = NULL;
983         char buf[LINE_MAX];
984
985         g_assert(zone);
986         g_assert(bi);
987
988         f = fopen("/proc/buddyinfo", "re");
989         if (!f)
990                 return -errno;
991
992         for (;;) {
993                 int n;
994                 char zonename[32] = { 0, };
995
996                 if (!fgets(buf, sizeof(buf), f)) {
997                         if (ferror(f))
998                                 return -errno;
999
1000                         break;
1001                 }
1002
1003                 n = sscanf(buf, "Node %d, zone %31s %d %d %d %d %d %d %d %d %d %d %d",
1004                            &bi->node,
1005                            zonename,
1006                            &bi->page[PAGE_4K],
1007                            &bi->page[PAGE_8K],
1008                            &bi->page[PAGE_16K],
1009                            &bi->page[PAGE_32K],
1010                            &bi->page[PAGE_64K],
1011                            &bi->page[PAGE_128K],
1012                            &bi->page[PAGE_256K],
1013                            &bi->page[PAGE_512K],
1014                            &bi->page[PAGE_1M],
1015                            &bi->page[PAGE_2M],
1016                            &bi->page[PAGE_4M]);
1017                 if (n != 13)
1018                         break;
1019
1020                 if (!streq(zone, zonename))
1021                         continue;
1022
1023                 return 0;
1024         }
1025
1026         return -ENODATA;
1027 }
1028
1029 void proc_swap_free(struct proc_swaps *swap)
1030 {
1031         if (!swap)
1032                 return;
1033
1034         free(swap->filename);
1035         free(swap->type);
1036         free(swap);
1037 }
1038
1039 void proc_swaps_free(struct proc_swaps **swaps)
1040 {
1041         int i;
1042
1043         if (!swaps)
1044                 return;
1045
1046         for (i = 0; swaps[i]; i++)
1047                 proc_swap_free(swaps[i]);
1048
1049         free(swaps);
1050 }
1051
1052 int proc_get_swaps(struct proc_swaps ***swaps)
1053 {
1054         _cleanup_proc_swaps_free_ struct proc_swaps **ss = NULL;
1055         _cleanup_fclose_ FILE *f = NULL;
1056         char buf[LINE_MAX];
1057         size_t cnt = 0;
1058
1059         assert(swaps);
1060         assert(!*swaps);
1061
1062         f = fopen("/proc/swaps", "re");
1063         if (!f)
1064                 return -errno;
1065
1066         for (;;) {
1067                 _cleanup_proc_swap_free_ struct proc_swaps *swap = NULL;
1068
1069                 if (!fgets(buf, sizeof(buf), f)) {
1070                         if (ferror(f))
1071                                 return -errno;
1072
1073                         break;
1074                 }
1075
1076                 swap = calloc(sizeof(struct proc_swaps), 1);
1077                 if (!swap)
1078                         return -ENOMEM;
1079
1080                 if (sscanf(buf, "%ms %ms %u %u %d",
1081                            &swap->filename,
1082                            &swap->type,
1083                            &swap->size,
1084                            &swap->used,
1085                            &swap->priority) != 5)
1086                         continue;
1087
1088                 ss = (struct proc_swaps **)realloc(ss, sizeof(struct proc_swaps *) * (cnt + 2));
1089                 if (!ss)
1090                         return -ENOMEM;
1091
1092                 ss[cnt++] = swap;
1093                 ss[cnt] = NULL;
1094                 swap = NULL;
1095         }
1096
1097         *swaps = ss;
1098         ss = NULL;
1099
1100         return cnt;
1101 }