]> git.proxmox.com Git - mirror_lxcfs.git/blobdiff - src/proc_fuse.c
tree-wide: set _GNU_SOURCE in meson.build
[mirror_lxcfs.git] / src / proc_fuse.c
index 0ad21b928658f087ac54cbd256656436a062fcb4..4ac5d44b0a10c32033044f89627e3b303a8d51b4 100644 (file)
@@ -1,28 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
 #include "config.h"
 
-#ifdef HAVE_FUSE3
-#ifndef FUSE_USE_VERSION
-#define FUSE_USE_VERSION 30
-#endif
-#else
-#ifndef FUSE_USE_VERSION
-#define FUSE_USE_VERSION 26
-#endif
-#endif
-
-#define _FILE_OFFSET_BITS 64
-
-#define __STDC_FORMAT_MACROS
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <fuse.h>
 #include <inttypes.h>
 #include <libgen.h>
 #include <pthread.h>
@@ -47,6 +29,8 @@
 #include <sys/sysinfo.h>
 #include <sys/vfs.h>
 
+#include "proc_fuse.h"
+
 #include "bindings.h"
 #include "cgroup_fuse.h"
 #include "cgroups/cgroup.h"
@@ -80,6 +64,23 @@ struct memory_stat {
        uint64_t total_unevictable;
 };
 
+static off_t get_procfile_size(const char *path)
+{
+       __do_fclose FILE *f = NULL;
+       __do_free char *line = NULL;
+       size_t len = 0;
+       ssize_t sz, answer = 0;
+
+       f = fopen(path, "re");
+       if (!f)
+               return 0;
+
+       while ((sz = getline(&line, &len, f)) != -1)
+               answer += sz;
+
+       return answer;
+}
+
 __lxcfs_fuse_ops int proc_getattr(const char *path, struct stat *sb)
 {
        struct timespec now;
@@ -102,8 +103,9 @@ __lxcfs_fuse_ops int proc_getattr(const char *path, struct stat *sb)
            strcmp(path, "/proc/stat")          == 0 ||
            strcmp(path, "/proc/diskstats")     == 0 ||
            strcmp(path, "/proc/swaps")         == 0 ||
-           strcmp(path, "/proc/loadavg")       == 0) {
-               sb->st_size = 4096;
+           strcmp(path, "/proc/loadavg")       == 0 ||
+           strcmp(path, "/proc/slabinfo")      == 0) {
+               sb->st_size = get_procfile_size(path);
                sb->st_mode = S_IFREG | 00444;
                sb->st_nlink = 1;
                return 0;
@@ -124,29 +126,13 @@ __lxcfs_fuse_ops int proc_readdir(const char *path, void *buf,
            DIR_FILLER(filler, buf, "uptime",   NULL, 0) != 0 ||
            DIR_FILLER(filler, buf, "diskstats",        NULL, 0) != 0 ||
            DIR_FILLER(filler, buf, "swaps",    NULL, 0) != 0 ||
-           DIR_FILLER(filler, buf, "loadavg",  NULL, 0) != 0)
+           DIR_FILLER(filler, buf, "loadavg",  NULL, 0) != 0 ||
+           DIR_FILLER(filler, buf, "slabinfo", NULL, 0) != 0)
                return -EINVAL;
 
        return 0;
 }
 
-static off_t get_procfile_size(const char *path)
-{
-       __do_fclose FILE *f = NULL;
-       __do_free char *line = NULL;
-       size_t len = 0;
-       ssize_t sz, answer = 0;
-
-       f = fopen(path, "re");
-       if (!f)
-               return 0;
-
-       while ((sz = getline(&line, &len, f)) != -1)
-               answer += sz;
-
-       return answer;
-}
-
 __lxcfs_fuse_ops int proc_open(const char *path, struct fuse_file_info *fi)
 {
        __do_free struct file_info *info = NULL;
@@ -166,6 +152,8 @@ __lxcfs_fuse_ops int proc_open(const char *path, struct fuse_file_info *fi)
                type = LXC_TYPE_PROC_SWAPS;
        else if (strcmp(path, "/proc/loadavg") == 0)
                type = LXC_TYPE_PROC_LOADAVG;
+       else if (strcmp(path, "/proc/slabinfo") == 0)
+               type = LXC_TYPE_PROC_SLABINFO;
        if (type == -1)
                return -ENOENT;
 
@@ -318,20 +306,25 @@ static inline bool startswith(const char *line, const char *pref)
 static int proc_swaps_read(char *buf, size_t size, off_t offset,
                           struct fuse_file_info *fi)
 {
-       __do_free char *cgroup = NULL, *memusage_str = NULL, *memswusage_str = NULL;
+       __do_free char *cgroup = NULL, *memusage_str = NULL,
+                *memswusage_str = NULL, *memswpriority_str = NULL;
        struct fuse_context *fc = fuse_get_context();
-       struct lxcfs_opts *opts = (struct lxcfs_opts *)fuse_get_context()->private_data;
-       bool wants_swap = opts && !opts->swap_off && liblxcfs_can_use_swap();
+       bool wants_swap = lxcfs_has_opt(fuse_get_context()->private_data, LXCFS_SWAP_ON);
        struct file_info *d = INTTYPE_TO_PTR(fi->fh);
        uint64_t memswlimit = 0, memlimit = 0, memusage = 0, memswusage = 0,
-                swtotal = 0, swfree = 0, swusage = 0;
+                swtotal = 0, swusage = 0, memswpriority = 1,
+                hostswtotal = 0, hostswfree = 0;
        ssize_t total_len = 0;
        ssize_t l = 0;
        char *cache = d->buf;
        int ret;
+       __do_free char *line = NULL;
+       __do_free void *fopen_cache = NULL;
+       __do_fclose FILE *f = NULL;
+       size_t linelen = 0;
 
        if (offset) {
-               int left;
+               size_t left;
 
                if (offset > d->size)
                        return -EINVAL;
@@ -377,30 +370,41 @@ static int proc_swaps_read(char *buf, size_t size, off_t offset,
                                        swusage = 0;
                                else
                                        swusage = (memswusage - memusage) / 1024;
-                               if (swtotal >= swusage)
-                                       swfree = swtotal - swusage;
                        }
+
+                       ret = cgroup_ops->get_memory_swappiness(cgroup_ops, cgroup, &memswpriority_str);
+                       if (ret >= 0)
+                               safe_uint64(memswpriority_str, &memswpriority, 10);
                }
        }
 
        total_len = snprintf(d->buf, d->size, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
 
-       /* When no mem + swap limit is specified or swapaccount=0*/
-       if (!memswlimit) {
-               __do_free char *line = NULL;
-               __do_free void *fopen_cache = NULL;
-               __do_fclose FILE *f = NULL;
-               size_t linelen = 0;
+       /* Read host total and free values */
+       f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
+       if (!f)
+               return 0;
 
-               f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
-               if (!f)
-                       return 0;
+       while (getline(&line, &linelen, f) != -1) {
+               if (startswith(line, "SwapTotal:"))
+                       sscanf(line, "SwapTotal:      %8" PRIu64 " kB", &hostswtotal);
+               else if (startswith(line, "SwapFree:"))
+                       sscanf(line, "SwapFree:      %8" PRIu64 " kB", &hostswfree);
+       }
+
+       if (wants_swap) {
+               /* The total amount of swap is always reported to be the
+                  lesser of the RAM+SWAP limit or the SWAP device size.
+                  This is because the kernel can swap as much as it
+                  wants and not only up to swtotal. */
+               swtotal = memlimit / 1024 + swtotal;
+               if (hostswtotal < swtotal) {
+                       swtotal = hostswtotal;
+               }
 
-               while (getline(&line, &linelen, f) != -1) {
-                       if (startswith(line, "SwapTotal:"))
-                               sscanf(line, "SwapTotal:      %8" PRIu64 " kB", &swtotal);
-                       else if (startswith(line, "SwapFree:"))
-                               sscanf(line, "SwapFree:      %8" PRIu64 " kB", &swfree);
+               /* When swappiness is 0, pretend we can't swap. */
+               if (memswpriority == 0) {
+                       swtotal = swusage;
                }
        }
 
@@ -417,7 +421,7 @@ static int proc_swaps_read(char *buf, size_t size, off_t offset,
        d->cached = 1;
        d->size = (int)total_len;
 
-       if (total_len > size)
+       if ((size_t)total_len > size)
                total_len = size;
        memcpy(buf, d->buf, total_len);
 
@@ -491,7 +495,7 @@ static int proc_diskstats_read(char *buf, size_t size, off_t offset,
        int ret;
 
        if (offset) {
-               int left;
+               size_t left;
 
                if (offset > d->size)
                        return -EINVAL;
@@ -622,7 +626,7 @@ static int proc_diskstats_read(char *buf, size_t size, off_t offset,
                l = snprintf(cache, cache_size, "%s", lbuf);
                if (l < 0)
                        return log_error(0, "Failed to write cache");
-               if (l >= cache_size)
+               if ((size_t)l >= cache_size)
                        return log_error(0, "Write to cache was truncated");
 
                cache += l;
@@ -639,7 +643,7 @@ static int proc_diskstats_read(char *buf, size_t size, off_t offset,
        return total_len;
 }
 
-#if RELOADTEST
+#ifdef RELOADTEST
 static inline void iwashere(void)
 {
        mknod("/tmp/lxcfs-iwashere", S_IFREG, 0644);
@@ -794,12 +798,12 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
        ssize_t total_len = 0, ret = 0;
        double busytime, idletime, reaperage;
 
-#if RELOADTEST
+#ifdef RELOADTEST
        iwashere();
 #endif
 
        if (offset) {
-               int left;
+               size_t left;
 
                if (offset > d->size)
                        return -EINVAL;
@@ -831,7 +835,7 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
 
        d->cached = 1;
        d->size = total_len;
-       if (total_len > size)
+       if ((size_t)total_len > size)
                total_len = size;
        memcpy(buf, d->buf, total_len);
 
@@ -864,7 +868,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
        int cg_cpu_usage_size = 0;
 
        if (offset) {
-               int left;
+               size_t left;
 
                if (offset > d->size)
                        return -EINVAL;
@@ -938,7 +942,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
                        l = snprintf(cache, cache_size, "%s", line);
                        if (l < 0)
                                return log_error(0, "Failed to write cache");
-                       if (l >= cache_size)
+                       if ((size_t)l >= cache_size)
                                return log_error(0, "Write to cache was truncated");
 
                        cache += l;
@@ -975,7 +979,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
                        l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c);
                        if (l < 0)
                                return log_error(0, "Failed to write cache");
-                       if (l >= cache_size)
+                       if ((size_t)l >= cache_size)
                                return log_error(0, "Write to cache was truncated");
 
                        cache += l;
@@ -995,9 +999,8 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
 
                        if (all_used >= cg_used) {
                                new_idle = idle + (all_used - cg_used);
-
                        } else {
-                               lxcfs_error("cpu%d from %s has unexpected cpu time: %" PRIu64 " in /proc/stat, %" PRIu64 " in cpuacct.usage_all; unable to determine idle time",
+                               lxcfs_debug("cpu%d from %s has unexpected cpu time: %" PRIu64 " in /proc/stat, %" PRIu64 " in cpuacct.usage_all; unable to determine idle time",
                                            curcpu, cg, all_used, cg_used);
                                new_idle = idle;
                        }
@@ -1008,7 +1011,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
                                     cg_cpu_usage[physcpu].system, new_idle);
                        if (l < 0)
                                return log_error(0, "Failed to write cache");
-                       if (l >= cache_size)
+                       if ((size_t)l >= cache_size)
                                return log_error(0, "Write to cache was truncated");
 
                        cache += l;
@@ -1136,15 +1139,15 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
                             struct fuse_file_info *fi)
 {
        __do_free char *cgroup = NULL, *line = NULL, *memusage_str = NULL,
-                      *memswusage_str = NULL;
+                      *memswusage_str = NULL, *memswpriority_str = NULL;
        __do_free void *fopen_cache = NULL;
        __do_fclose FILE *f = NULL;
        struct fuse_context *fc = fuse_get_context();
-       struct lxcfs_opts *opts = (struct lxcfs_opts *)fuse_get_context()->private_data;
-       bool wants_swap = opts && !opts->swap_off && liblxcfs_can_use_swap(), host_swap = false;
+       bool wants_swap = lxcfs_has_opt(fuse_get_context()->private_data, LXCFS_SWAP_ON);
        struct file_info *d = INTTYPE_TO_PTR(fi->fh);
        uint64_t memlimit = 0, memusage = 0, memswlimit = 0, memswusage = 0,
-                hosttotal = 0, swfree = 0, swusage = 0, swtotal = 0;
+                hosttotal = 0, swfree = 0, swusage = 0, swtotal = 0,
+                memswpriority = 1;
        struct memory_stat mstat = {};
        size_t linelen = 0, total_len = 0;
        char *cache = d->buf;
@@ -1152,7 +1155,7 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
        int ret;
 
        if (offset) {
-               int left;
+               size_t left;
 
                if (offset > d->size)
                        return -EINVAL;
@@ -1209,6 +1212,10 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
                                        swusage = (memswusage - memusage) / 1024;
                        }
                }
+
+               ret = cgroup_ops->get_memory_swappiness(cgroup_ops, cgroup, &memswpriority_str);
+               if (ret >= 0)
+                       safe_uint64(memswpriority_str, &memswpriority, 10);
        }
 
        f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
@@ -1243,19 +1250,19 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
 
                                sscanf(line + STRLITERALLEN("SwapTotal:"), "%" PRIu64, &hostswtotal);
 
-                               /*
-                                * If swtotal is 0 it should mean that
-                                * memory.memsw.limit_in_bytes and
-                                * memory.limit_in_bytes are both unlimited or
-                                * both set to the same value. In both cases we
-                                * have no idea what the technical swap limit
-                                * is supposed to be (It's a shared limit
-                                * anyway.) so fallback to the host's values in
-                                * that case too.
-                                */
-                               if ((hostswtotal < swtotal) || swtotal == 0) {
+                               /* The total amount of swap is always reported to be the
+                                  lesser of the RAM+SWAP limit or the SWAP device size.
+                                  This is because the kernel can swap as much as it
+                                  wants and not only up to swtotal. */
+
+                               swtotal = memlimit + swtotal;
+                               if (hostswtotal < swtotal) {
                                        swtotal = hostswtotal;
-                                       host_swap = true;
+                               }
+
+                               /* When swappiness is 0, pretend we can't swap. */
+                               if (memswpriority == 0) {
+                                       swtotal = swusage;
                                }
                        }
 
@@ -1263,20 +1270,13 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
                        printme = lbuf;
                } else if (startswith(line, "SwapFree:")) {
                        if (wants_swap) {
-                               uint64_t hostswfree = 0;
-
-                               if (host_swap) {
-                                       sscanf(line + STRLITERALLEN("SwapFree:"), "%" PRIu64, &hostswfree);
-                                       swfree = hostswfree;
-                               } else if (swtotal >= swusage) {
-                                       swfree = swtotal - swusage;
-                               }
+                               swfree = swtotal - swusage;
                        }
 
                        snprintf(lbuf, 100, "SwapFree:       %8" PRIu64 " kB\n", swfree);
                        printme = lbuf;
                } else if (startswith(line, "Slab:")) {
-                       snprintf(lbuf, 100, "Slab:        %8" PRIu64 " kB\n", (uint64_t)0);
+                       snprintf(lbuf, 100, "Slab:           %8" PRIu64 " kB\n", (uint64_t)0);
                        printme = lbuf;
                } else if (startswith(line, "Buffers:")) {
                        snprintf(lbuf, 100, "Buffers:        %8" PRIu64 " kB\n", (uint64_t)0);
@@ -1365,7 +1365,76 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
                l = snprintf(cache, cache_size, "%s", printme);
                if (l < 0)
                        return log_error(0, "Failed to write cache");
-               if (l >= cache_size)
+               if ((size_t)l >= cache_size)
+                       return log_error(0, "Write to cache was truncated");
+
+               cache += l;
+               cache_size -= l;
+               total_len += l;
+       }
+
+       d->cached = 1;
+       d->size = total_len;
+       if (total_len > size)
+               total_len = size;
+       memcpy(buf, d->buf, total_len);
+
+       return total_len;
+}
+
+static int proc_slabinfo_read(char *buf, size_t size, off_t offset,
+                             struct fuse_file_info *fi)
+{
+       __do_free char *cgroup = NULL, *line = NULL;
+       __do_free void *fopen_cache = NULL;
+       __do_fclose FILE *f = NULL;
+       __do_close int fd = -EBADF;
+       struct fuse_context *fc = fuse_get_context();
+       struct file_info *d = INTTYPE_TO_PTR(fi->fh);
+       size_t linelen = 0, total_len = 0;
+       char *cache = d->buf;
+       size_t cache_size = d->buflen;
+       pid_t initpid;
+
+       if (offset) {
+               size_t left;
+
+               if (offset > d->size)
+                       return -EINVAL;
+
+               if (!d->cached)
+                       return 0;
+
+               left = d->size - offset;
+               total_len = left > size ? size : left;
+               memcpy(buf, cache + offset, total_len);
+
+               return total_len;
+       }
+
+       initpid = lookup_initpid_in_store(fc->pid);
+       if (initpid <= 1 || is_shared_pidns(initpid))
+               initpid = fc->pid;
+
+       cgroup = get_pid_cgroup(initpid, "memory");
+       if (!cgroup)
+               return read_file_fuse("/proc/slabinfo", buf, size, d);
+
+       prune_init_slice(cgroup);
+
+       fd = cgroup_ops->get_memory_slabinfo_fd(cgroup_ops, cgroup);
+       if (fd < 0)
+               return read_file_fuse("/proc/slabinfo", buf, size, d);
+
+       f = fdopen_cached(fd, "re", &fopen_cache);
+       if (!f)
+               return read_file_fuse("/proc/slabinfo", buf, size, d);
+
+       while (getline(&line, &linelen, f) != -1) {
+               ssize_t l = snprintf(cache, cache_size, "%s", line);
+               if (l < 0)
+                       return log_error(0, "Failed to write cache");
+               if ((size_t)l >= cache_size)
                        return log_error(0, "Write to cache was truncated");
 
                cache += l;
@@ -1430,6 +1499,12 @@ __lxcfs_fuse_ops int proc_read(const char *path, char *buf, size_t size,
 
                return read_file_fuse_with_offset(LXC_TYPE_PROC_LOADAVG_PATH,
                                                  buf, size, offset, f);
+       case LXC_TYPE_PROC_SLABINFO:
+               if (liblxcfs_functional())
+                       return proc_slabinfo_read(buf, size, offset, fi);
+
+               return read_file_fuse_with_offset(LXC_TYPE_PROC_SLABINFO_PATH,
+                                                 buf, size, offset, f);
        }
 
        return -EINVAL;