/* SPDX-License-Identifier: LGPL-2.1+ */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#ifndef FUSE_USE_VERSION
-#define FUSE_USE_VERSION 26
-#endif
-
-#define _FILE_OFFSET_BITS 64
+#include "config.h"
-#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>
#include <sys/sysinfo.h>
#include <sys/vfs.h>
+#include "proc_fuse.h"
+
#include "bindings.h"
-#include "config.h"
#include "cgroup_fuse.h"
#include "cgroups/cgroup.h"
#include "cgroups/cgroup_utils.h"
#include "cpuset_parse.h"
+#include "lxcfs_fuse_compat.h"
#include "memory_utils.h"
#include "proc_loadavg.h"
#include "proc_cpuview.h"
uint64_t total_unevictable;
};
-int proc_getattr(const char *path, struct stat *sb)
+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;
strcmp(path, "/proc/stat") == 0 ||
strcmp(path, "/proc/diskstats") == 0 ||
strcmp(path, "/proc/swaps") == 0 ||
- strcmp(path, "/proc/loadavg") == 0) {
- sb->st_size = 0;
+ 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;
return -ENOENT;
}
-int proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi)
+__lxcfs_fuse_ops int proc_readdir(const char *path, void *buf,
+ fuse_fill_dir_t filler, off_t offset,
+ struct fuse_file_info *fi)
{
- if (filler(buf, ".", NULL, 0) != 0 ||
- filler(buf, "..", NULL, 0) != 0 ||
- filler(buf, "cpuinfo", NULL, 0) != 0 ||
- filler(buf, "meminfo", NULL, 0) != 0 ||
- filler(buf, "stat", NULL, 0) != 0 ||
- filler(buf, "uptime", NULL, 0) != 0 ||
- filler(buf, "diskstats", NULL, 0) != 0 ||
- filler(buf, "swaps", NULL, 0) != 0 ||
- filler(buf, "loadavg", NULL, 0) != 0)
+ if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
+ DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
+ DIR_FILLER(filler, buf, "cpuinfo", NULL, 0) != 0 ||
+ DIR_FILLER(filler, buf, "meminfo", NULL, 0) != 0 ||
+ DIR_FILLER(filler, buf, "stat", NULL, 0) != 0 ||
+ 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, "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;
-}
-
-int proc_open(const char *path, struct fuse_file_info *fi)
+__lxcfs_fuse_ops int proc_open(const char *path, struct fuse_file_info *fi)
{
__do_free struct file_info *info = NULL;
int type = -1;
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;
- info = malloc(sizeof(*info));
+ info = zalloc(sizeof(*info));
if (!info)
return -ENOMEM;
- memset(info, 0, sizeof(*info));
info->type = type;
info->buflen = get_procfile_size(path) + BUF_RESERVE_SIZE;
- info->buf = malloc(info->buflen);
+ info->buf = zalloc(info->buflen);
if (!info->buf)
return -ENOMEM;
-
- memset(info->buf, 0, info->buflen);
/* set actual size to buffer size */
info->size = info->buflen;
return 0;
}
-int proc_access(const char *path, int mask)
+__lxcfs_fuse_ops int proc_access(const char *path, int mask)
{
if (strcmp(path, "/proc") == 0 && access(path, R_OK) == 0)
return 0;
return 0;
}
-int proc_release(const char *path, struct fuse_file_info *fi)
+__lxcfs_fuse_ops int proc_release(const char *path, struct fuse_file_info *fi)
{
do_release_file_info(fi);
return 0;
}
-static unsigned long get_memlimit(const char *cgroup, bool swap)
+static uint64_t get_memlimit(const char *cgroup, bool swap)
{
__do_free char *memlimit_str = NULL;
- unsigned long memlimit = -1;
+ uint64_t memlimit = 0;
int ret;
if (swap)
ret = cgroup_ops->get_memory_swap_max(cgroup_ops, cgroup, &memlimit_str);
else
ret = cgroup_ops->get_memory_max(cgroup_ops, cgroup, &memlimit_str);
- if (ret > 0)
- memlimit = strtoul(memlimit_str, NULL, 10);
+ if (ret > 0 && memlimit_str[0] && safe_uint64(memlimit_str, &memlimit, 10) < 0)
+ lxcfs_error("Failed to convert memlimit %s", memlimit_str);
return memlimit;
}
-static unsigned long get_min_memlimit(const char *cgroup, bool swap)
+/*
+ * This function taken from glibc-2.32, as POSIX dirname("/some-dir") will
+ * return "/some-dir" as opposed to "/", which breaks `get_min_memlimit()`
+ */
+static char *gnu_dirname(char *path)
+{
+ static const char dot[] = ".";
+ char *last_slash;
+
+ /* Find last '/'. */
+ last_slash = path != NULL ? strrchr(path, '/') : NULL;
+
+ if (last_slash != NULL && last_slash != path && last_slash[1] == '\0') {
+ /* Determine whether all remaining characters are slashes. */
+ char *runp;
+
+ for (runp = last_slash; runp != path; --runp)
+ if (runp[-1] != '/')
+ break;
+
+ /* The '/' is the last character, we have to look further. */
+ if (runp != path)
+ last_slash = memrchr(path, '/', runp - path);
+ }
+
+ if (last_slash != NULL) {
+ /* Determine whether all remaining characters are slashes. */
+ char *runp;
+
+ for (runp = last_slash; runp != path; --runp)
+ if (runp[-1] != '/')
+ break;
+
+ /* Terminate the path. */
+ if (runp == path) {
+ /*
+ * The last slash is the first character in the string.
+ * We have to return "/". As a special case we have to
+ * return "//" if there are exactly two slashes at the
+ * beginning of the string. See XBD 4.10 Path Name
+ * Resolution for more information
+ */
+ if (last_slash == path + 1)
+ ++last_slash;
+ else
+ last_slash = path + 1;
+ } else
+ last_slash = runp;
+
+ last_slash[0] = '\0';
+ } else {
+ /*
+ * This assignment is ill-designed but the XPG specs require to
+ * return a string containing "." in any case no directory part
+ * is found and so a static and constant string is required.
+ */
+ path = (char *)dot;
+ }
+
+ return path;
+}
+
+static uint64_t get_min_memlimit(const char *cgroup, bool swap)
{
__do_free char *copy = NULL;
- unsigned long memlimit = 0;
- unsigned long retlimit;
+ uint64_t memlimit = 0, retlimit = 0;
copy = strdup(cgroup);
if (!copy)
retlimit = get_memlimit(copy, swap);
- while (strcmp(copy, "/") != 0) {
+ /*
+ * If the cgroup doesn't start with / (probably won't happen), dirname()
+ * will terminate with "" instead of "/"
+ */
+ while (*copy && strcmp(copy, "/") != 0) {
char *it = copy;
- it = dirname(it);
+ it = gnu_dirname(it);
memlimit = get_memlimit(it, swap);
- if (memlimit != -1 && memlimit < retlimit)
+ if (memlimit > 0 && memlimit < retlimit)
retlimit = memlimit;
};
return strncmp(line, pref, strlen(pref)) == 0;
}
+static void get_swap_info(const char *cgroup, uint64_t memlimit,
+ uint64_t memusage, uint64_t *swtotal,
+ uint64_t *swusage, uint64_t *memswpriority)
+{
+ __do_free char *memswusage_str = NULL, *memswpriority_str = NULL;
+ size_t memswlimit = 0, memswusage = 0;
+ int ret;
+
+ *swtotal = *swusage = 0;
+ *memswpriority = 1;
+
+ memswlimit = get_min_memlimit(cgroup, true);
+ if (memswlimit > 0) {
+ ret = cgroup_ops->get_memory_swap_current(cgroup_ops, cgroup, &memswusage_str);
+ if (ret < 0 || safe_uint64(memswusage_str, &memswusage, 10) != 0)
+ return;
+
+ if (liblxcfs_memory_is_cgroupv2()) {
+ *swtotal = memswlimit / 1024;
+ *swusage = memswusage / 1024;
+ } else {
+ if (memlimit > memswlimit)
+ *swtotal = 0;
+ else
+ *swtotal = (memswlimit - memlimit) / 1024;
+ if (memusage > memswusage || swtotal == 0)
+ *swusage = 0;
+ else
+ *swusage = (memswusage - memusage) / 1024;
+ }
+
+ ret = cgroup_ops->get_memory_swappiness(cgroup_ops, cgroup, &memswpriority_str);
+ if (ret >= 0)
+ safe_uint64(memswpriority_str, memswpriority, 10);
+ }
+}
+
static int proc_swaps_read(char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
- __do_free char *cg = NULL, *memswlimit_str = 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();
+ bool wants_swap = lxcfs_has_opt(fuse_get_context()->private_data, LXCFS_SWAP_ON);
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
- unsigned long memswlimit = 0, memlimit = 0, memusage = 0,
- memswusage = 0, swap_total = 0, swap_free = 0;
+ uint64_t memlimit = 0, memusage = 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;
if (initpid <= 1 || is_shared_pidns(initpid))
initpid = fc->pid;
- cg = get_pid_cgroup(initpid, "memory");
- if (!cg)
+ cgroup = get_pid_cgroup(initpid, "memory");
+ if (!cgroup)
return read_file_fuse("/proc/swaps", buf, size, d);
- prune_init_slice(cg);
+ prune_init_slice(cgroup);
- memlimit = get_min_memlimit(cg, false);
+ memlimit = get_min_memlimit(cgroup, false);
- ret = cgroup_ops->get_memory_current(cgroup_ops, cg, &memusage_str);
+ ret = cgroup_ops->get_memory_current(cgroup_ops, cgroup, &memusage_str);
if (ret < 0)
return 0;
- memusage = strtoul(memusage_str, NULL, 10);
+ if (safe_uint64(memusage_str, &memusage, 10) < 0)
+ lxcfs_error("Failed to convert memusage %s", memusage_str);
- ret = cgroup_ops->get_memory_swap_max(cgroup_ops, cg, &memswlimit_str);
- if (ret >= 0)
- ret = cgroup_ops->get_memory_swap_current(cgroup_ops, cg, &memswusage_str);
- if (ret >= 0) {
- memswlimit = get_min_memlimit(cg, true);
- memswusage = strtoul(memswusage_str, NULL, 10);
- swap_total = (memswlimit - memlimit) / 1024;
- swap_free = (memswusage - memusage) / 1024;
- }
+ if (wants_swap)
+ get_swap_info(cgroup, memlimit, memusage, &swtotal, &swusage, &memswpriority);
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: %8lu kB", &swap_total);
- else if (startswith(line, "SwapFree:"))
- sscanf(line, "SwapFree: %8lu kB", &swap_free);
+ /* When swappiness is 0, pretend we can't swap. */
+ if (memswpriority == 0) {
+ swtotal = swusage;
}
}
- if (swap_total > 0) {
+ if (swtotal > 0) {
l = snprintf(d->buf + total_len, d->size - total_len,
- "none%*svirtual\t\t%lu\t%lu\t0\n", 36, " ",
- swap_total, swap_free);
+ "none%*svirtual\t\t%" PRIu64 "\t%" PRIu64 "\t0\n",
+ 36, " ", swtotal, swusage);
total_len += l;
}
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);
}
static void get_blkio_io_value(char *str, unsigned major, unsigned minor,
- char *iotype, unsigned long *v)
+ char *iotype, uint64_t *v)
{
char *eol;
char key[32];
}
}
+struct lxcfs_diskstats {
+ unsigned int major; /* 1 - major number */
+ unsigned int minor; /* 2 - minor mumber */
+ char dev_name[72]; /* 3 - device name */
+ uint64_t read; /* 4 - reads completed successfully */
+ uint64_t read_merged; /* 5 - reads merged */
+ uint64_t read_sectors; /* 6 - sectors read */
+ uint64_t read_ticks; /* 7 - time spent reading (ms) */
+ uint64_t write; /* 8 - writes completed */
+ uint64_t write_merged; /* 9 - writes merged */
+ uint64_t write_sectors; /* 10 - sectors written */
+ uint64_t write_ticks; /* 11 - time spent writing (ms) */
+ uint64_t ios_pgr; /* 12 - I/Os currently in progress */
+ uint64_t total_ticks; /* 13 - time spent doing I/Os (ms) */
+ uint64_t rq_ticks; /* 14 - weighted time spent doing I/Os (ms) */
+ uint64_t discard; /* 15 - discards completed successfully (4.18+) */
+ uint64_t discard_merged; /* 16 - discards merged (4.18+) */
+ uint64_t discard_sectors; /* 17 - sectors discarded (4.18+) */
+ uint64_t discard_ticks; /* 18 - time spent discarding (4.18+) */
+};
+
static int proc_diskstats_read(char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
__do_fclose FILE *f = NULL;
struct fuse_context *fc = fuse_get_context();
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
- unsigned long read = 0, write = 0;
- unsigned long read_merged = 0, write_merged = 0;
- unsigned long read_sectors = 0, write_sectors = 0;
- unsigned long read_ticks = 0, write_ticks = 0;
- unsigned long ios_pgr = 0, tot_ticks = 0, rq_ticks = 0;
- unsigned long rd_svctm = 0, wr_svctm = 0, rd_wait = 0, wr_wait = 0;
+ struct lxcfs_diskstats stats = {};
+ /* helper fields */
+ uint64_t read_service_time, write_service_time, discard_service_time, read_wait_time,
+ write_wait_time, discard_wait_time;
char *cache = d->buf;
size_t cache_size = d->buflen;
size_t linelen = 0, total_len = 0;
- unsigned int major = 0, minor = 0;
int i = 0;
int ret;
- char dev_name[72];
- if (offset){
- int left;
+ if (offset) {
+ size_t left;
if (offset > d->size)
return -EINVAL;
ssize_t l;
char lbuf[256];
- i = sscanf(line, "%u %u %71s", &major, &minor, dev_name);
+ i = sscanf(line, "%u %u %71s", &stats.major, &stats.minor, stats.dev_name);
if (i != 3)
continue;
- get_blkio_io_value(io_serviced_str, major, minor, "Read", &read);
- get_blkio_io_value(io_serviced_str, major, minor, "Write", &write);
- get_blkio_io_value(io_merged_str, major, minor, "Read", &read_merged);
- get_blkio_io_value(io_merged_str, major, minor, "Write", &write_merged);
- get_blkio_io_value(io_service_bytes_str, major, minor, "Read", &read_sectors);
- read_sectors = read_sectors/512;
- get_blkio_io_value(io_service_bytes_str, major, minor, "Write", &write_sectors);
- write_sectors = write_sectors/512;
-
- get_blkio_io_value(io_service_time_str, major, minor, "Read", &rd_svctm);
- rd_svctm = rd_svctm/1000000;
- get_blkio_io_value(io_wait_time_str, major, minor, "Read", &rd_wait);
- rd_wait = rd_wait/1000000;
- read_ticks = rd_svctm + rd_wait;
-
- get_blkio_io_value(io_service_time_str, major, minor, "Write", &wr_svctm);
- wr_svctm = wr_svctm/1000000;
- get_blkio_io_value(io_wait_time_str, major, minor, "Write", &wr_wait);
- wr_wait = wr_wait/1000000;
- write_ticks = wr_svctm + wr_wait;
-
- get_blkio_io_value(io_service_time_str, major, minor, "Total", &tot_ticks);
- tot_ticks = tot_ticks/1000000;
+ get_blkio_io_value(io_serviced_str, stats.major, stats.minor, "Read", &stats.read);
+ get_blkio_io_value(io_serviced_str, stats.major, stats.minor, "Write", &stats.write);
+ get_blkio_io_value(io_serviced_str, stats.major, stats.minor, "Discard", &stats.discard);
+
+ get_blkio_io_value(io_merged_str, stats.major, stats.minor, "Read", &stats.read_merged);
+ get_blkio_io_value(io_merged_str, stats.major, stats.minor, "Write", &stats.write_merged);
+ get_blkio_io_value(io_merged_str, stats.major, stats.minor, "Discard", &stats.discard_merged);
+
+ get_blkio_io_value(io_service_bytes_str, stats.major, stats.minor, "Read", &stats.read_sectors);
+ stats.read_sectors = stats.read_sectors / 512;
+ get_blkio_io_value(io_service_bytes_str, stats.major, stats.minor, "Write", &stats.write_sectors);
+ stats.write_sectors = stats.write_sectors / 512;
+ get_blkio_io_value(io_service_bytes_str, stats.major, stats.minor, "Discard", &stats.discard_sectors);
+ stats.discard_sectors = stats.discard_sectors / 512;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Read", &read_service_time);
+ read_service_time = read_service_time / 1000000;
+ get_blkio_io_value(io_wait_time_str, stats.major, stats.minor, "Read", &read_wait_time);
+ read_wait_time = read_wait_time / 1000000;
+ stats.read_ticks = read_service_time + read_wait_time;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Write", &write_service_time);
+ write_service_time = write_service_time / 1000000;
+ get_blkio_io_value(io_wait_time_str, stats.major, stats.minor, "Write", &write_wait_time);
+ write_wait_time = write_wait_time / 1000000;
+ stats.write_ticks = write_service_time + write_wait_time;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Discard", &discard_service_time);
+ discard_service_time = discard_service_time / 1000000;
+ get_blkio_io_value(io_wait_time_str, stats.major, stats.minor, "Discard", &discard_wait_time);
+ discard_wait_time = discard_wait_time / 1000000;
+ stats.discard_ticks = discard_service_time + discard_wait_time;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Total", &stats.total_ticks);
+ stats.total_ticks = stats.total_ticks / 1000000;
memset(lbuf, 0, 256);
- if (read || write || read_merged || write_merged || read_sectors || write_sectors || read_ticks || write_ticks)
- snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
- major, minor, dev_name, read, read_merged, read_sectors, read_ticks,
- write, write_merged, write_sectors, write_ticks, ios_pgr, tot_ticks, rq_ticks);
+ if (stats.read || stats.write || stats.read_merged || stats.write_merged ||
+ stats.read_sectors || stats.write_sectors || stats.read_ticks ||
+ stats.write_ticks || stats.ios_pgr || stats.total_ticks || stats.rq_ticks ||
+ stats.discard_merged || stats.discard_sectors || stats.discard_ticks)
+ snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ stats.major,
+ stats.minor,
+ stats.dev_name,
+ stats.read,
+ stats.read_merged,
+ stats.read_sectors,
+ stats.read_ticks,
+ stats.write,
+ stats.write_merged,
+ stats.write_sectors,
+ stats.write_ticks,
+ stats.ios_pgr,
+ stats.total_ticks,
+ stats.rq_ticks,
+ stats.discard_merged,
+ stats.discard_sectors,
+ stats.discard_ticks);
else
continue;
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;
d->cached = 1;
d->size = total_len;
- if (total_len > size ) total_len = size;
+ if (total_len > size)
+ total_len = size;
memcpy(buf, d->buf, total_len);
return total_len;
}
-#if RELOADTEST
+#ifdef RELOADTEST
static inline void iwashere(void)
{
mknod("/tmp/lxcfs-iwashere", S_IFREG, 0644);
}
#endif
-/* This function retrieves the busy time of a group of tasks by looking at
+/*
+ * This function retrieves the busy time of a group of tasks by looking at
* cpuacct.usage. Unfortunately, this only makes sense when the container has
* been given it's own cpuacct cgroup. If not, this function will take the busy
* time of all other taks that do not actually belong to the container into
static double get_reaper_busy(pid_t task)
{
__do_free char *cgroup = NULL, *usage_str = NULL;
- unsigned long usage = 0;
+ uint64_t usage = 0;
pid_t initpid;
initpid = lookup_initpid_in_store(task);
if (!cgroup)
return 0;
prune_init_slice(cgroup);
- if (!cgroup_ops->get(cgroup_ops, "cpuacct", cgroup, "cpuacct.usage",
- &usage_str))
+
+ if (!cgroup_ops->get(cgroup_ops, "cpuacct", cgroup, "cpuacct.usage", &usage_str))
return 0;
- usage = strtoul(usage_str, NULL, 10);
+ if (safe_uint64(usage_str, &usage, 10) < 0)
+ lxcfs_error("Failed to convert usage %s", usage_str);
+
return ((double)usage / 1000000000);
}
__do_fclose FILE *f = NULL;
int ret;
uint64_t starttime;
- /* strlen("/proc/") = 6
- * +
- * LXCFS_NUMSTRLEN64
- * +
- * strlen("/stat") = 5
- * +
- * \0 = 1
- * */
-#define __PROC_PID_STAT_LEN (6 + LXCFS_NUMSTRLEN64 + 5 + 1)
- char path[__PROC_PID_STAT_LEN];
+ char path[STRLITERALLEN("/proc/") + LXCFS_NUMSTRLEN64 +
+ STRLITERALLEN("/stat") + 1];
pid_t qpid;
qpid = lookup_initpid_in_store(pid);
- if (qpid <= 0) {
- /* Caller can check for EINVAL on 0. */
- errno = EINVAL;
- return 0;
- }
+ if (qpid <= 0)
+ return ret_errno(EINVAL);
- ret = snprintf(path, __PROC_PID_STAT_LEN, "/proc/%d/stat", qpid);
- if (ret < 0 || ret >= __PROC_PID_STAT_LEN) {
- /* Caller can check for EINVAL on 0. */
- errno = EINVAL;
- return 0;
- }
+ ret = snprintf(path, sizeof(path), "/proc/%d/stat", qpid);
+ if (ret < 0 || (size_t)ret >= sizeof(path))
+ return ret_errno(EINVAL);
f = fopen_cached(path, "re", &fopen_cache);
- if (!f) {
- /* Caller can check for EINVAL on 0. */
- errno = EINVAL;
- return 0;
- }
+ if (!f)
+ return ret_errno(EINVAL);
/* Note that the *scanf() argument supression requires that length
* modifiers such as "l" are omitted. Otherwise some compilers will yell
"%" PRIu64, /* (22) starttime %llu */
&starttime);
if (ret != 1)
- return ret_set_errno(0, EINVAL);
+ return ret_errno(EINVAL);
return ret_set_errno(starttime, 0);
}
double res = 0;
clockticks = get_reaper_start_time(pid);
- if (clockticks == 0 && errno == EINVAL)
+ if (clockticks <= 0)
return log_debug(0, "Failed to retrieve start time of pid %d", pid);
ret = sysconf(_SC_CLK_TCK);
- if (ret < 0 && errno == EINVAL)
+ if (ret < 0)
return log_debug(0, "Failed to determine number of clock ticks in a second");
ticks_per_sec = (uint64_t)ret;
uint64_t uptime_ms;
double procstart, procage;
- /* We need to substract the time the process has started since system
+ /*
+ * We need to substract the time the process has started since system
* boot minus the time when the system has started to get the actual
* reaper age.
*/
if (ret < 0)
return 0;
- /* We could make this more precise here by using the tv_nsec
- * field in the timespec struct and convert it to milliseconds
- * and then create a double for the seconds and milliseconds but
- * that seems more work than it is worth.
- */
uptime_ms = (spec.tv_sec * 1000) + (spec.tv_nsec * 1e-6);
procage = (uptime_ms - (procstart * 1000)) / 1000;
}
{
struct fuse_context *fc = fuse_get_context();
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
- double busytime = get_reaper_busy(fc->pid);
char *cache = d->buf;
- ssize_t total_len = 0;
- double idletime, reaperage;
+ ssize_t total_len = 0, ret = 0;
+ double busytime, idletime, reaperage;
-#if RELOADTEST
+#ifdef RELOADTEST
iwashere();
#endif
if (offset) {
- int left;
-
- if (!d->cached)
- return 0;
+ 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);
* get_reaper_busy() function.
*/
idletime = reaperage;
+ busytime = get_reaper_busy(fc->pid);
if (reaperage >= busytime)
idletime = reaperage - busytime;
- total_len = snprintf(d->buf, d->buflen, "%.2lf %.2lf\n", reaperage, idletime);
- if (total_len < 0 || total_len >= d->buflen)
- return log_error(0, "Failed to write to cache");
+ ret = snprintf(d->buf, d->buflen, "%.2lf %.2lf\n", reaperage, idletime);
+ if (ret < 0 || ret >= d->buflen)
+ return read_file_fuse("/proc/uptime", buf, size, d);
+ total_len = ret;
- d->size = (int)total_len;
d->cached = 1;
-
- if (total_len > size)
+ d->size = total_len;
+ if ((size_t)total_len > size)
total_len = size;
-
memcpy(buf, d->buf, total_len);
+
return total_len;
}
__do_free struct cpuacct_usage *cg_cpu_usage = NULL;
__do_fclose FILE *f = NULL;
struct fuse_context *fc = fuse_get_context();
+ struct lxcfs_opts *opts = (struct lxcfs_opts *)fc->private_data;
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
size_t linelen = 0, total_len = 0;
int curcpu = -1; /* cpu numbering starts at 0 */
int physcpu = 0;
- unsigned long user = 0, nice = 0, system = 0, idle = 0, iowait = 0,
- irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
- unsigned long user_sum = 0, nice_sum = 0, system_sum = 0, idle_sum = 0,
- iowait_sum = 0, irq_sum = 0, softirq_sum = 0,
- steal_sum = 0, guest_sum = 0, guest_nice_sum = 0;
+ uint64_t user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0,
+ softirq = 0, steal = 0, guest = 0, guest_nice = 0;
+ uint64_t user_sum = 0, nice_sum = 0, system_sum = 0, idle_sum = 0,
+ iowait_sum = 0, irq_sum = 0, softirq_sum = 0, steal_sum = 0,
+ guest_sum = 0, guest_nice_sum = 0;
char cpuall[CPUALL_MAX_SIZE];
/* reserve for cpu all */
char *cache = d->buf + CPUALL_MAX_SIZE;
int cg_cpu_usage_size = 0;
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
* in some case cpuacct_usage.all in "/" will larger then /proc/stat
*/
if (initpid == 1)
- return read_file_fuse("/proc/stat", buf, size, d);
+ return read_file_fuse("/proc/stat", buf, size, d);
cg = get_pid_cgroup(initpid, "cpuset");
if (!cg)
if (!cpuset)
return 0;
- /*
- * Read cpuacct.usage_all for all CPUs.
- * If the cpuacct cgroup is present, it is used to calculate the container's
- * CPU usage. If not, values from the host's /proc/stat are used.
- */
- if (read_cpuacct_usage_all(cg, cpuset, &cg_cpu_usage, &cg_cpu_usage_size) != 0)
- lxcfs_v("%s\n", "proc_stat_read failed to read from cpuacct, falling back to the host's /proc/stat");
-
f = fopen_cached("/proc/stat", "re", &fopen_cache);
if (!f)
return 0;
- //skip first line
+ /* Skip first system cpu line. */
if (getline(&line, &linelen, f) < 0)
return log_error(0, "proc_stat_read read first line failed");
- if (cgroup_ops->can_use_cpuview(cgroup_ops) && cg_cpu_usage) {
- total_len = cpuview_proc_stat(cg, cpuset, cg_cpu_usage, cg_cpu_usage_size,
- f, d->buf, d->buflen);
- goto out;
+ /*
+ * Read cpuacct.usage_all for all CPUs.
+ * If the cpuacct cgroup is present, it is used to calculate the container's
+ * CPU usage. If not, values from the host's /proc/stat are used.
+ */
+ if (read_cpuacct_usage_all(cg, cpuset, &cg_cpu_usage, &cg_cpu_usage_size) == 0) {
+ if (cgroup_ops->can_use_cpuview(cgroup_ops) && opts && opts->use_cfs) {
+ total_len = cpuview_proc_stat(cg, cpuset, cg_cpu_usage,
+ cg_cpu_usage_size, f,
+ d->buf, d->buflen);
+ goto out;
+ }
+ } else {
+ lxcfs_v("proc_stat_read failed to read from cpuacct, falling back to the host's /proc/stat");
}
while (getline(&line, &linelen, f) != -1) {
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;
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;
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;
}
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;
/* Note that "memory.stat" in cgroup2 is hierarchical by default. */
static bool cgroup_parse_memory_stat(const char *cgroup, struct memory_stat *mstat)
{
- __do_close_prot_errno int fd = -EBADF;
+ __do_close int fd = -EBADF;
__do_fclose FILE *f = NULL;
__do_free char *line = NULL;
__do_free void *fdopen_cache = NULL;
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, *memstat_str = NULL,
- *memswlimit_str = NULL, *memswusage_str = NULL;
+ __do_free char *cgroup = NULL, *line = NULL, *memusage_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 = 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;
+ uint64_t memlimit = 0, memusage = 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;
int ret;
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
prune_init_slice(cgroup);
- memlimit = get_min_memlimit(cgroup, false);
-
+ /* memory limits */
ret = cgroup_ops->get_memory_current(cgroup_ops, cgroup, &memusage_str);
if (ret < 0)
- return 0;
+ return read_file_fuse("/proc/meminfo", buf, size, d);
+
+ if (safe_uint64(memusage_str, &memusage, 10) < 0)
+ lxcfs_error("Failed to convert memusage %s", memusage_str);
if (!cgroup_parse_memory_stat(cgroup, &mstat))
- return 0;
+ return read_file_fuse("/proc/meminfo", buf, size, d);
+
+ memlimit = get_min_memlimit(cgroup, false);
/*
* Following values are allowed to fail, because swapaccount might be
* turned off for current kernel.
*/
- ret = cgroup_ops->get_memory_swap_max(cgroup_ops, cgroup, &memswlimit_str);
- if (ret >= 0)
- ret = cgroup_ops->get_memory_swap_current(cgroup_ops, cgroup, &memswusage_str);
- if (ret >= 0) {
- memswlimit = get_min_memlimit(cgroup, true);
- memswusage = strtoul(memswusage_str, NULL, 10);
- memswlimit = memswlimit / 1024;
- memswusage = memswusage / 1024;
- }
-
- memusage = strtoul(memusage_str, NULL, 10);
- memlimit /= 1024;
- memusage /= 1024;
+ if (wants_swap)
+ get_swap_info(cgroup, memlimit, memusage, &swtotal, &swusage, &memswpriority);
f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
if (!f)
- return 0;
+ return read_file_fuse("/proc/meminfo", buf, size, d);
+ memusage /= 1024;
+ memlimit /= 1024;
while (getline(&line, &linelen, f) != -1) {
ssize_t l;
char *printme, lbuf[100];
memset(lbuf, 0, 100);
if (startswith(line, "MemTotal:")) {
sscanf(line+sizeof("MemTotal:")-1, "%" PRIu64, &hosttotal);
+ if (memlimit == 0)
+ memlimit = hosttotal;
+
if (hosttotal < memlimit)
memlimit = hosttotal;
snprintf(lbuf, 100, "MemTotal: %8" PRIu64 " kB\n", memlimit);
} else if (startswith(line, "MemAvailable:")) {
snprintf(lbuf, 100, "MemAvailable: %8" PRIu64 " kB\n", memlimit - memusage + mstat.total_cache / 1024);
printme = lbuf;
- } else if (startswith(line, "SwapTotal:") && memswlimit > 0 &&
- opts && opts->swap_off == false) {
- memswlimit -= memlimit;
- snprintf(lbuf, 100, "SwapTotal: %8" PRIu64 " kB\n", memswlimit);
- printme = lbuf;
- } else if (startswith(line, "SwapTotal:") && opts && opts->swap_off == true) {
- snprintf(lbuf, 100, "SwapTotal: %8" PRIu64 " kB\n", (uint64_t)0);
- printme = lbuf;
- } else if (startswith(line, "SwapFree:") && memswlimit > 0 &&
- memswusage > 0 && opts && opts->swap_off == false) {
- uint64_t swaptotal = memswlimit,
- swapusage = memusage > memswusage
- ? 0
- : memswusage - memusage,
- swapfree = swapusage < swaptotal
- ? swaptotal - swapusage
- : 0;
- snprintf(lbuf, 100, "SwapFree: %8" PRIu64 " kB\n", swapfree);
+ } else if (startswith(line, "SwapTotal:")) {
+ if (wants_swap) {
+ uint64_t hostswtotal = 0;
+
+ sscanf(line + STRLITERALLEN("SwapTotal:"), "%" PRIu64, &hostswtotal);
+
+ /* 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. */
+
+ if (!liblxcfs_memory_is_cgroupv2())
+ swtotal += memlimit;
+
+ if (hostswtotal < swtotal) {
+ swtotal = hostswtotal;
+ }
+
+ /* When swappiness is 0, pretend we can't swap. */
+ if (memswpriority == 0) {
+ swtotal = swusage;
+ }
+ }
+
+ snprintf(lbuf, 100, "SwapTotal: %8" PRIu64 " kB\n", swtotal);
printme = lbuf;
- } else if (startswith(line, "SwapFree:") && opts && opts->swap_off == true) {
- snprintf(lbuf, 100, "SwapFree: %8" PRIu64 " kB\n", (uint64_t)0);
+ } else if (startswith(line, "SwapFree:")) {
+ if (wants_swap) {
+ 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);
mstat.total_inactive_file) /
1024);
printme = lbuf;
- } else if (startswith(line, "Active(anon)")) {
+ } else if (startswith(line, "Active(anon):")) {
snprintf(lbuf, 100, "Active(anon): %8" PRIu64 " kB\n",
mstat.total_active_anon / 1024);
printme = lbuf;
- } else if (startswith(line, "Inactive(anon)")) {
+ } else if (startswith(line, "Inactive(anon):")) {
snprintf(lbuf, 100, "Inactive(anon): %8" PRIu64 " kB\n",
mstat.total_inactive_anon / 1024);
printme = lbuf;
- } else if (startswith(line, "Active(file)")) {
+ } else if (startswith(line, "Active(file):")) {
snprintf(lbuf, 100, "Active(file): %8" PRIu64 " kB\n",
mstat.total_active_file / 1024);
printme = lbuf;
- } else if (startswith(line, "Inactive(file)")) {
+ } else if (startswith(line, "Inactive(file):")) {
snprintf(lbuf, 100, "Inactive(file): %8" PRIu64 " kB\n",
mstat.total_inactive_file / 1024);
printme = lbuf;
- } else if (startswith(line, "Unevictable")) {
+ } else if (startswith(line, "Unevictable:")) {
snprintf(lbuf, 100, "Unevictable: %8" PRIu64 " kB\n",
mstat.total_unevictable / 1024);
printme = lbuf;
- } else if (startswith(line, "Dirty")) {
+ } else if (startswith(line, "Dirty:")) {
snprintf(lbuf, 100, "Dirty: %8" PRIu64 " kB\n",
mstat.total_dirty / 1024);
printme = lbuf;
- } else if (startswith(line, "Writeback")) {
+ } else if (startswith(line, "Writeback:")) {
snprintf(lbuf, 100, "Writeback: %8" PRIu64 " kB\n",
mstat.total_writeback / 1024);
printme = lbuf;
- } else if (startswith(line, "AnonPages")) {
+ } else if (startswith(line, "AnonPages:")) {
snprintf(lbuf, 100, "AnonPages: %8" PRIu64 " kB\n",
(mstat.total_active_anon +
mstat.total_inactive_anon - mstat.total_shmem) /
1024);
printme = lbuf;
- } else if (startswith(line, "Mapped")) {
+ } else if (startswith(line, "Mapped:")) {
snprintf(lbuf, 100, "Mapped: %8" PRIu64 " kB\n",
mstat.total_mapped_file / 1024);
printme = lbuf;
- } else if (startswith(line, "SReclaimable")) {
+ } else if (startswith(line, "SReclaimable:")) {
snprintf(lbuf, 100, "SReclaimable: %8" PRIu64 " kB\n", (uint64_t)0);
printme = lbuf;
- } else if (startswith(line, "SUnreclaim")) {
+ } else if (startswith(line, "SUnreclaim:")) {
snprintf(lbuf, 100, "SUnreclaim: %8" PRIu64 " kB\n", (uint64_t)0);
printme = lbuf;
} else if (startswith(line, "Shmem:")) {
snprintf(lbuf, 100, "Shmem: %8" PRIu64 " kB\n",
mstat.total_shmem / 1024);
printme = lbuf;
- } else if (startswith(line, "ShmemHugePages")) {
+ } else if (startswith(line, "ShmemHugePages:")) {
snprintf(lbuf, 100, "ShmemHugePages: %8" PRIu64 " kB\n", (uint64_t)0);
printme = lbuf;
- } else if (startswith(line, "ShmemPmdMapped")) {
+ } else if (startswith(line, "ShmemPmdMapped:")) {
snprintf(lbuf, 100, "ShmemPmdMapped: %8" PRIu64 " kB\n", (uint64_t)0);
printme = lbuf;
- } else if (startswith(line, "AnonHugePages")) {
+ } else if (startswith(line, "AnonHugePages:")) {
snprintf(lbuf, 100, "AnonHugePages: %8" PRIu64 " kB\n",
mstat.total_rss_huge / 1024);
printme = lbuf;
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;
return total_len;
}
-int proc_read(const char *path, char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
+__lxcfs_fuse_ops int proc_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
{
struct file_info *f = INTTYPE_TO_PTR(fi->fh);
switch (f->type) {
case LXC_TYPE_PROC_MEMINFO:
- return proc_meminfo_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_meminfo_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_MEMINFO_PATH,
+ buf, size, offset, f);
case LXC_TYPE_PROC_CPUINFO:
- return proc_cpuinfo_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_cpuinfo_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_CPUINFO_PATH,
+ buf, size, offset, f);
case LXC_TYPE_PROC_UPTIME:
- return proc_uptime_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_uptime_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_UPTIME_PATH,
+ buf, size, offset, f);
case LXC_TYPE_PROC_STAT:
- return proc_stat_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_stat_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_STAT_PATH, buf,
+ size, offset, f);
case LXC_TYPE_PROC_DISKSTATS:
- return proc_diskstats_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_diskstats_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_DISKSTATS_PATH,
+ buf, size, offset, f);
case LXC_TYPE_PROC_SWAPS:
- return proc_swaps_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_swaps_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_SWAPS_PATH, buf,
+ size, offset, f);
case LXC_TYPE_PROC_LOADAVG:
- return proc_loadavg_read(buf, size, offset, fi);
+ if (liblxcfs_functional())
+ return proc_loadavg_read(buf, size, offset, fi);
+
+ 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;