1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
|
// SPDX-License-Identifier: MIT
/* Parse /proc/meminfo
* Returned values are in kiB */
#include <errno.h>
#include <stddef.h> // for size_t
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "meminfo.h"
#include "msg.h"
/* Parse the contents of /proc/meminfo (in buf), return value of "name"
* (example: MemTotal) */
static long get_entry(const char* name, const char* buf)
{
char* hit = strstr(buf, name);
if (hit == NULL) {
return -1;
}
errno = 0;
long val = strtol(hit + strlen(name), NULL, 10);
if (errno != 0) {
perror("get_entry: strtol() failed");
return -1;
}
return val;
}
/* Like get_entry(), but exit if the value cannot be found */
static long get_entry_fatal(const char* name, const char* buf)
{
long val = get_entry(name, buf);
if (val == -1) {
fatal(104, "could not find entry '%s' in /proc/meminfo\n");
}
return val;
}
/* If the kernel does not provide MemAvailable (introduced in Linux 3.14),
* approximate it using other data we can get */
static long available_guesstimate(const char* buf)
{
long Cached = get_entry_fatal("Cached:", buf);
long MemFree = get_entry_fatal("MemFree:", buf);
long Buffers = get_entry_fatal("Buffers:", buf);
long Shmem = get_entry_fatal("Shmem:", buf);
return MemFree + Cached + Buffers - Shmem;
}
meminfo_t parse_meminfo()
{
static FILE* fd;
static char buf[8192];
static int guesstimate_warned = 0;
meminfo_t m;
if (fd == NULL)
fd = fopen("/proc/meminfo", "r");
if (fd == NULL) {
fatal(102, "could not open /proc/meminfo: %s\n", strerror(errno));
}
rewind(fd);
size_t len = fread(buf, 1, sizeof(buf) - 1, fd);
if (len == 0) {
fatal(102, "could not read /proc/meminfo: %s\n", strerror(errno));
}
buf[len] = 0; // Make sure buf is zero-terminated
m.MemTotalKiB = get_entry_fatal("MemTotal:", buf);
m.SwapTotalKiB = get_entry_fatal("SwapTotal:", buf);
long SwapFree = get_entry_fatal("SwapFree:", buf);
long MemAvailable = get_entry("MemAvailable:", buf);
if (MemAvailable == -1) {
MemAvailable = available_guesstimate(buf);
if (guesstimate_warned == 0) {
fprintf(stderr, "Warning: Your kernel does not provide MemAvailable data (needs 3.14+)\n"
" Falling back to guesstimate\n");
guesstimate_warned = 1;
}
}
// Calculate percentages
m.MemAvailablePercent = MemAvailable * 100 / m.MemTotalKiB;
if (m.SwapTotalKiB > 0) {
m.SwapFreePercent = SwapFree * 100 / m.SwapTotalKiB;
} else {
m.SwapFreePercent = 0;
}
// Convert kiB to MiB
m.MemTotalMiB = m.MemTotalKiB / 1024;
m.MemAvailableMiB = MemAvailable / 1024;
m.SwapTotalMiB = m.SwapTotalKiB / 1024;
m.SwapFreeMiB = SwapFree / 1024;
return m;
}
|