]> git.ipfire.org Git - pakfire.git/commitdiff
os: Implement getting memory stats from /proc/meminfo
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 28 Oct 2023 10:52:24 +0000 (10:52 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 28 Oct 2023 10:52:24 +0000 (10:52 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/include/pakfire/os.h
src/libpakfire/os.c
tests/libpakfire/os.c

index 8cf6021e3c9fab28b7c33de73226b2afebed4587..dd600e633850463f490732331da2d96fedc6f40b 100644 (file)
@@ -23,6 +23,8 @@
 
 #ifdef PAKFIRE_PRIVATE
 
+#include <stdint.h>
+
 // CPU info
 
 #define CPU_VENDOR_MAX 16
@@ -35,6 +37,26 @@ struct pakfire_cpuinfo {
 
 int pakfire_cpuinfo(struct pakfire_cpuinfo* cpuinfo);
 
+// Meminfo
+
+struct pakfire_meminfo {
+       uint64_t total;
+       uint64_t used;
+       uint64_t free;
+       uint64_t available;
+       uint64_t buffers;
+       uint64_t cached;
+       uint64_t active;
+       uint64_t inactive;
+
+       // Swap
+       uint64_t swap_total;
+       uint64_t swap_used;
+       uint64_t swap_free;
+};
+
+int pakfire_meminfo(struct pakfire_meminfo* meminfo);
+
 #endif /* PAKFIRE_PRIVATE */
 
 #endif /* PAKFIRE_OS_H */
index 66d03789fba579280d36d1c6551548fd3c1a7987..63a225e66ef24b44aa1bc11d4531ce3f03fc1ba8 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -139,3 +140,105 @@ static int pakfire_parse_cpuinfo(char* line, size_t length, void* data) {
 int pakfire_cpuinfo(struct pakfire_cpuinfo* cpuinfo) {
        return pakfire_parse_file("/proc/cpuinfo", pakfire_parse_cpuinfo, cpuinfo);
 }
+
+// Meminfo
+
+static int pakfire_parse_meminfo_value(uint64_t* mem, const char* s) {
+       char* remainder = NULL;
+       uint64_t m = 0;
+
+       // Parse the numeric value
+       m = strtoul(s, &remainder, 10);
+       if (m == ULONG_MAX)
+               return -errno;
+
+       // Convert into bytes
+       if (strcmp(remainder, " kB") == 0) {
+               // Check if the multiplication won't overflow
+               if (m > UINT64_MAX / 1024)
+                       return -EOVERFLOW;
+
+               m *= 1024;
+
+       // Fail on anything else
+       } else if (*remainder)
+               return -EINVAL;
+
+       // Store the value
+       *mem = m;
+
+       return 0;
+}
+
+static int pakfire_parse_meminfo(char* line, size_t length, void* data) {
+       struct pakfire_meminfo* meminfo = data;
+       int r;
+
+       // Key & Value
+       const char* k = NULL;
+       const char* v = NULL;
+
+       // Split the line
+       r = pakfire_split_line(line, length, &k, &v);
+       if (r)
+               return r;
+
+       // If we didn't get a result we skip this line
+       if (!k || !v)
+               return 0;
+
+       // Total
+       if (strcmp(k, "MemTotal") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->total, v);
+
+       // Free
+       else if (strcmp(k, "MemFree") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->free, v);
+
+       // Available
+       else if (strcmp(k, "MemAvailable") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->available, v);
+
+       // Buffers
+       else if (strcmp(k, "Buffers") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->buffers, v);
+
+       // Cached
+       else if (strcmp(k, "Cached") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->cached, v);
+
+       // Active
+       else if (strcmp(k, "Active") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->active, v);
+
+       // Inactive
+       else if (strcmp(k, "Inactive") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->inactive, v);
+
+       // Swap Total
+       else if (strcmp(k, "SwapTotal") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->swap_total, v);
+
+       // Swap Free
+       else if (strcmp(k, "SwapFree") == 0)
+               return pakfire_parse_meminfo_value(&meminfo->swap_free, v);
+
+       return 0;
+}
+
+int pakfire_meminfo(struct pakfire_meminfo* meminfo) {
+       int r;
+
+       // Parse /proc/meminfo
+       r = pakfire_parse_file("/proc/meminfo", pakfire_parse_meminfo, meminfo);
+       if (r)
+               return r;
+
+       // Set used memory
+       meminfo->used = meminfo->total - meminfo->free;
+
+       // Set used swap
+       meminfo->swap_used = meminfo->swap_total - meminfo->swap_free;
+
+       return 0;
+}
index 940aec589d60a2ce5087eca52c2e434f699d5b4e..e631bad1e0920ae5cb7f37d7f9d991810e4544ad 100644 (file)
@@ -38,8 +38,34 @@ FAIL:
        return EXIT_FAILURE;
 }
 
+// This test parses /proc/meminfo
+static int test_meminfo(const struct test* t) {
+       struct pakfire_meminfo meminfo = {};
+
+       ASSERT_SUCCESS(pakfire_meminfo(&meminfo));
+
+       // We can only check is something has been read
+       ASSERT(meminfo.total > 0);
+       ASSERT(meminfo.used > 0);
+       ASSERT(meminfo.free > 0);
+       ASSERT(meminfo.available > 0);
+       ASSERT(meminfo.buffers > 0);
+       ASSERT(meminfo.cached > 0);
+       ASSERT(meminfo.active > 0);
+       ASSERT(meminfo.inactive > 0);
+       ASSERT(meminfo.swap_total >= 0);
+       ASSERT(meminfo.swap_used >= 0);
+       ASSERT(meminfo.swap_free >= 0);
+
+       return EXIT_SUCCESS;
+
+FAIL:
+       return EXIT_FAILURE;
+}
+
 int main(int argc, const char* argv[]) {
        testsuite_add_test(test_cpuinfo);
+       testsuite_add_test(test_meminfo);
 
        return testsuite_run(argc, argv);
 }