initial public commit
[free.git] / free.c
1 #include <sys/stat.h>
2 #include <sys/sysctl.h>
3 #include <sys/types.h>
4
5 #include <errno.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10
11 #include <err.h>
12 #include <kvm.h>
13
14 typedef enum {
15 NO_CONV,
16 KB_CONV,
17 MB_CONV,
18 GB_CONV,
19 TB_CONV,
20 PB_CONV,
21 HU_CONV
22 } ConvFlags;
23
24 static kvm_t *kd;
25 static long pgsz;
26 static char sbuff[32];
27
28 void
29 usage(void)
30 {
31 fprintf(stderr, "usage: %s [-bghkmpt]\n", getprogname());
32 exit(2);
33 }
34
35 long long
36 getphysmem(int unused)
37 {
38 (void)unused;
39
40 long long physcnt;
41 size_t len = sizeof(physcnt);
42
43 if (sysctlbyname("hw.physmem", &physcnt, &len, NULL, 0) < 0)
44 err(1, "hw.physmem");
45
46 return physcnt;
47 }
48
49 long long
50 getavailmem(int flag)
51 {
52 size_t len;
53 unsigned int freecnt, inactcnt;
54
55 len = sizeof(freecnt);
56 if (sysctlbyname("vm.stats.vm.v_free_count", &freecnt, &len, NULL, 0) < 0)
57 err(1, "vm.stats.vm.v_free_count");
58 if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactcnt, &len, NULL, 0) < 0)
59 err(1, "vm.stats.vm.v_inactive_count");
60
61 if (flag == 1)
62 return (long long)(getphysmem(0) - (freecnt + inactcnt) * pgsz);
63 else if (flag == 2)
64 return (long long)(freecnt * pgsz);
65 else
66 return (long long)((freecnt + inactcnt) * pgsz);
67 }
68
69 long long
70 getwiredmem(int unused)
71 {
72 (void)unused;
73
74 size_t len;
75 unsigned int wirecnt;
76
77 len = sizeof(wirecnt);
78 if (sysctlbyname("vm.stats.vm.v_wire_count", &wirecnt, &len, NULL, 0) < 0)
79 err(1, "vm.stats.vm.v_wire_count");
80
81 return (long long)(wirecnt * pgsz);
82
83 }
84
85 long long
86 getbufcachestats(int unused)
87 {
88 (void)unused;
89
90 size_t len;
91 long bufspace, arcspace;
92 unsigned int cachecnt;
93
94 len = sizeof(bufspace);
95 if (sysctlbyname("vfs.bufspace", &bufspace, &len, NULL, 0) < 0)
96 err(1, "vfs.bufspace");
97 /* Ignore failure in case ZFS is not used */
98 (void)sysctlbyname("kstat.zfs.misc.arcstats.size", &arcspace, &len, NULL, 0);
99
100 len = sizeof(cachecnt);
101 if (sysctlbyname("vm.stats.vm.v_cache_count", &cachecnt, &len, NULL, 0) < 0)
102 err(1, "vm.stats.vm.v_inactive_count");
103
104 return (long long)(cachecnt * pgsz + bufspace + arcspace);
105 }
106
107 long long
108 getswapstats(int flag)
109 {
110 int nswap;
111 struct kvm_swap swap;
112
113 if ((nswap = kvm_getswapinfo(kd, &swap, 1, 0)) < 0)
114 errx(1, "kvm_getswapinfo: %s", kvm_geterr(kd));
115
116 if (flag == 0)
117 return (long long)(swap.ksw_total * pgsz);
118 else if (flag == 1)
119 return (long long)(swap.ksw_used * pgsz);
120 else
121 return (long long)((swap.ksw_total - swap.ksw_used) * pgsz);
122 }
123
124 int
125 tohumansize(long long bytes, char **buff, ConvFlags flag)
126 {
127 int i = 0;
128 double size = (double)bytes;
129 char units[] = {'\0', 'K', 'M', 'G', 'T', 'P'};
130
131 if (flag != HU_CONV)
132 memset(units, '\0', sizeof(units));
133 if (flag == NO_CONV)
134 return asprintf(buff, "%.0f", size);
135
136 while (i < (int)flag) {
137 if (size <= 1024 && flag == HU_CONV) break;
138 size /= 1024;
139 i++;
140 }
141
142 if (i == 0 || size < 0.1)
143 return asprintf(buff, "%.0f", size);
144 else if (size < 10)
145 return asprintf(buff, "%.1f%c", size, units[i]);
146 else
147 return asprintf(buff, "%.0f%c", size, units[i]);
148 }
149
150 void
151 sprintfaligned(char *buff, size_t len)
152 {
153 size_t blen;
154 char *ind;
155
156 blen = strlen(buff);
157 ind = sbuff + len - blen;
158 if (ind < sbuff) {
159 size_t t = sbuff - ind;
160 ind = sbuff;
161 *ind = '%';
162 ind++; buff += t;
163 blen = 30;
164 }
165 strlcpy(ind, buff, blen + 1);
166
167 return;
168 }
169
170 int
171 main(int argc, char *argv[])
172 {
173 int c, i;
174 char *buff;
175 size_t offset;
176 ConvFlags flags;
177 long long (*getmemstats[])(int) =
178 {getphysmem, getavailmem, getavailmem, getwiredmem, getbufcachestats, getavailmem};
179
180 flags = KB_CONV;
181 while ((c = getopt(argc, argv, "bghkmpt")) >= 0) {
182 switch(c) {
183 case 'b':
184 flags = NO_CONV; break;
185 case 'k':
186 flags = KB_CONV; break;
187 case 'm':
188 flags = MB_CONV; break;
189 case 'g':
190 flags = GB_CONV; break;
191 case 't':
192 flags = TB_CONV; break;
193 case 'p':
194 flags = PB_CONV; break;
195 case 'h':
196 flags = HU_CONV; break;
197 default:
198 usage();
199 }
200 }
201
202 pgsz = sysconf(_SC_PAGESIZE);
203
204 /* initializing kvm device for swap stats */
205 if (!(kd = kvm_open(NULL, "/dev/null", NULL, 0, "kvm_open")))
206 err(1, "kvm_open");
207
208 printf("%16s%12s%12s%12s%12s%12s\nMem:",
209 "total", "used", "free", "wired", "buff/cache", "available");
210
211 offset = 12;
212 for (i = 0; i < 6; i++) {
213 memset(sbuff, ' ', sizeof(sbuff));
214 tohumansize(getmemstats[i](i), &buff, flags);
215 sprintfaligned(buff, offset);
216 printf("%s", sbuff);
217 free(buff);
218 }
219
220 offset = 11;
221 printf("\nSwap:");
222 for (i = 0; i < 3; i++) {
223 memset(sbuff, ' ', sizeof(sbuff));
224 tohumansize(getswapstats(i), &buff, flags);
225 sprintfaligned(buff, offset);
226 printf("%s", sbuff);
227 free(buff);
228 offset = 12;
229 }
230 putc('\n', stdout);
231
232 return 0;
233 }