1 /*
2  * mem_linux.c - module to get memory/swap usages, for GNU/Linxu
3  *
4  * Copyright(C) 2001,2002  Seiichi SATO <ssato@sh.rim.or.jp>
5  * Copyright(C) 2001       John McCutchan <ttb@tentacle.dhs.org>
6  *
7  * licensed under the GPL
8  */
9 
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13 
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 
18 #if defined(HAVE_STRING_H)
19 #include <string.h>
20 #elif defined(HAVE_STRINGS_H)
21 #include <strings.h>
22 #endif
23 
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <sys/utsname.h>
29 #include "mem.h"
30 
31 static enum {
32 	BEFORE_2_5_1,
33 	BETWEEN_2_5_1_AND_3_14,
34 	AFTER_3_14
35 } format;
36 
37 #ifdef DEBUG
38 #  define INLINE_STATIC static
39 #else
40 #  define INLINE_STATIC static inline
41 #endif
42 
43 /* initialize function */
mem_init(void)44 void mem_init(void)
45 {
46 	struct utsname un;
47 	int version, patchlevel, sublevel;
48 
49 	/* get kernel version */
50 	if (uname(&un) == -1)
51 		perror("uname()");
52 	sscanf(un.release, "%d.%d.%d", &version, &patchlevel, &sublevel);
53 
54 	/* new format ? (kernel >= 3.14 or 2.5.1pre?) */
55 	/* see linux/fs/proc/proc_misc.c */
56 	/* or linux/fs/proc/meminfo.c */
57 	if ((version == 3 && patchlevel >= 14) || version > 3)
58 		format = AFTER_3_14;
59 	else if ((version == 2 && patchlevel >= 5 && sublevel >= 1) ||
60 	    (version == 2 && patchlevel >= 6 && sublevel >= 0) ||
61 	    version > 2)
62 		format = BETWEEN_2_5_1_AND_3_14;
63 	else
64 		format = BEFORE_2_5_1;
65 }
66 
67 
skip_line(const char * p)68 INLINE_STATIC char *skip_line(const char *p)
69 {
70 	while (*p != '\n')
71 		p++;
72 	return (char *) ++p;
73 }
74 
skip_token(const char * p)75 INLINE_STATIC char *skip_token(const char *p)
76 {
77 	while (isspace(*p))
78 		p++;
79 	while (*p && !isspace(*p))
80 		p++;
81 	return (char *)p;
82 }
83 
skip_multiple_token(const char * p,int count)84 INLINE_STATIC char *skip_multiple_token(const char *p, int count)
85 {
86 	int i;
87 	for (i = 0; i < count; i++)
88 		p = skip_token(p);
89 	return (char *)p;
90 }
91 
92 /* return mem/swap usage in percent 0 to 100 */
mem_getusage(int * per_mem,int * per_swap,const struct mem_options * opts)93 void mem_getusage(int *per_mem, int *per_swap, const struct mem_options *opts)
94 {
95 	char buffer[BUFSIZ], *p;
96 	int fd, len, i;
97 	u_int64_t mtotal, mused, mfree, mbuffer, mcached;
98 	u_int64_t stotal, sused, sfree, scached = 0;
99 
100 	/* read /proc/meminfo */
101 	fd = open("/proc/meminfo", O_RDONLY);
102 	if (fd < 0) {
103 		perror("can't open /proc/meminfo");
104 		exit(1);
105 	}
106 	len = read(fd, buffer, BUFSIZ - 1);
107 	if (len < 0) {
108 		perror("can't read /proc/meminfo");
109 		exit(1);
110 	}
111 	close(fd);
112 
113 	buffer[len] = '\0';
114 	p = buffer;
115 
116 	switch (format) {
117 	case (BEFORE_2_5_1):
118 		/* skip 3 lines */
119 		for (i = 0; i < 3; i++)
120 			p = skip_line(p);
121 		p = skip_token(p);
122 		/* examine each line of file */
123 		mtotal  = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
124 		mfree   = strtoul(p, &p, 0); p = skip_multiple_token(p, 5);
125 		mbuffer = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
126 		mcached = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
127 		scached = strtoul(p, &p, 0);
128 		break;
129 
130 	case (BETWEEN_2_5_1_AND_3_14):
131 		p = skip_token(p);
132 		/* examine each line of file */
133 		mtotal  = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
134 		mfree   = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
135 		mbuffer = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
136 		mcached = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
137 		scached = strtoul(p, &p, 0);
138 		break;
139 
140 	case (AFTER_3_14):
141 		p = skip_token(p);
142 		/* examine each line of file */
143 		mtotal  = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
144 		mfree   = strtoul(p, &p, 0);
145 		p = skip_multiple_token(p, 5); /* skip MemAvailable line */
146 		mbuffer = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
147 		mcached = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
148 		scached = strtoul(p, &p, 0);
149 		break;
150 	}
151 
152 
153 	/* skip N lines and examine info about swap */
154 	/* kernel-2.4.2:N=8  2.4.16:N=7  */
155 	while (isprint(p[0])) {
156 		p = skip_line(p);
157 		if (strncmp(p, "SwapTotal", 9) == 0)
158 			break;
159 	}
160 
161 	p = skip_token(p);
162 	stotal = strtoul(p, &p, 0); p = skip_multiple_token(p, 2);
163 	sfree  = strtoul(p, &p, 0);
164 
165 	/* calculate memory usage in percent */
166 	mused = mtotal - mfree;
167 	if (opts->ignore_buffers)
168 		mused -= mbuffer;
169 	if (opts->ignore_cached)
170 		mused -= mcached;
171 	*per_mem = 100 * (double) mused / (double) mtotal;
172 
173 	/* calculate swap usage in percent */
174 	sused = stotal - sfree;
175 	if (opts->ignore_cached)
176 		sused -= scached;
177 	if (!stotal)
178 		*per_swap = 0;
179 	else
180 		*per_swap = 100 * (double) sused / (double) stotal;
181 
182 #if DEBUG
183 	printf("-----------------------\n");
184 	printf("MemTotal:  %12ld\n", (unsigned long)mtotal);
185 	printf("MemFree:   %12ld\n", (unsigned long)mfree);
186 	printf("Buffers:   %12ld\n", (unsigned long)mbuffer);
187 	printf("Cached:    %12ld\n", (unsigned long)mcached);
188 	printf("SwapTotal: %12ld\n", (unsigned long)stotal);
189 	printf("SwapFree:  %12ld\n", (unsigned long)sfree);
190 	printf("SwapCached:%12ld\n", (unsigned long)scached);
191 	printf("-----------------------\n\n");
192 #endif
193 
194 }
195