libvirt/tests/vircgroupmock.c
Pavel Hrdina f9085cf702 vircgroupmock: cleanup unused cgroup files
Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
2018-09-25 09:59:23 +02:00

674 lines
21 KiB
C

/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#ifdef __linux__
# include "virmock.h"
# include <unistd.h>
# include <fcntl.h>
# include <sys/stat.h>
# ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>
# elif MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
# endif
# include <stdarg.h>
# include "testutilslxc.h"
# include "virstring.h"
# include "virfile.h"
static int (*real_open)(const char *path, int flags, ...);
static FILE *(*real_fopen)(const char *path, const char *mode);
static int (*real_access)(const char *path, int mode);
static int (*real_stat)(const char *path, struct stat *sb);
static int (*real___xstat)(int ver, const char *path, struct stat *sb);
static int (*real_lstat)(const char *path, struct stat *sb);
static int (*real___lxstat)(int ver, const char *path, struct stat *sb);
static int (*real_mkdir)(const char *path, mode_t mode);
/* Don't make static, since it causes problems with clang
* when passed as an arg to asprintf()
* vircgroupmock.c:462:22: error: static variable 'fakesysfsdir' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
*/
char *fakerootdir;
char *fakesysfscgroupdir;
const char *fakedevicedir0 = FAKEDEVDIR0;
const char *fakedevicedir1 = FAKEDEVDIR1;
# define SYSFS_CGROUP_PREFIX "/not/really/sys/fs/cgroup/"
# define SYSFS_CPU_PRESENT "/sys/devices/system/cpu/present"
# define SYSFS_CPU_PRESENT_MOCKED "devices_system_cpu_present"
/*
* The plan:
*
* We fake out /proc/mounts, so make it look as is cgroups
* are mounted on /not/really/sys/fs/cgroup. We don't
* use /sys/fs/cgroup, because we want to make it easy to
* detect places where we've not mocked enough syscalls.
*
* In any open/acces/mkdir calls we look at path and if
* it starts with /not/really/sys/fs/cgroup, we rewrite
* the path to point at a subdirectory of the temporary
* directory referred to by LIBVIRT_FAKE_ROOT_DIR env
* variable that is set by the main test suite
*
* In mkdir() calls, we simulate the cgroups behaviour
* whereby creating the directory auto-creates a bunch
* of files beneath it
*/
/*
* Intentionally missing the 'devices' mount.
* Co-mounting cpu & cpuacct controllers
* An anonymous controller for systemd
*/
const char *procmounts =
"rootfs / rootfs rw 0 0\n"
"tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n"
"tmpfs /not/really/sys/fs/cgroup tmpfs rw,seclabel,nosuid,nodev,noexec,mode=755 0 0\n"
"cgroup /not/really/sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd 0 0\n"
"cgroup /not/really/sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n"
"cgroup /not/really/sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpuacct,cpu 0 0\n"
"cgroup /not/really/sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n"
"cgroup /not/really/sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n"
"cgroup /not/really/sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n"
"/dev/sda1 /boot ext4 rw,seclabel,relatime,data=ordered 0 0\n"
"tmpfs /tmp tmpfs rw,seclabel,relatime,size=1024000k 0 0\n";
const char *procselfcgroups =
"115:memory:/\n"
"8:blkio:/\n"
"6:freezer:/\n"
"3:cpuacct,cpu:/system\n"
"2:cpuset:/\n"
"1:name=systemd:/user/berrange/123\n";
const char *proccgroups =
"#subsys_name hierarchy num_cgroups enabled\n"
"cpuset 2 4 1\n"
"cpu 3 48 1\n"
"cpuacct 3 48 1\n"
"memory 4 4 1\n"
"devices 5 4 1\n"
"freezer 6 4 1\n"
"blkio 8 4 1\n";
const char *procmountsallinone =
"rootfs / rootfs rw 0 0\n"
"sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\n"
"proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n"
"udev /dev devtmpfs rw,relatime,size=16458560k,nr_inodes=4114640,mode=755 0 0\n"
"devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n"
"nfsd /proc/fs/nfsd nfsd rw,relatime 0 0\n"
"cgroup /not/really/sys/fs/cgroup cgroup rw,relatime,blkio,devices,memory,cpuacct,cpu,cpuset 0 0\n";
const char *procselfcgroupsallinone =
"6:blkio,devices,memory,cpuacct,cpu,cpuset:/";
const char *proccgroupsallinone =
"#subsys_name hierarchy num_cgroups enabled\n"
"cpuset 6 1 1\n"
"cpu 6 1 1\n"
"cpuacct 6 1 1\n"
"memory 6 1 1\n"
"devices 6 1 1\n"
"blkio 6 1 1\n";
const char *procmountslogind =
"none /not/really/sys/fs/cgroup tmpfs rw,rootcontext=system_u:object_r:sysfs_t:s0,seclabel,relatime,size=4k,mode=755 0 0\n"
"systemd /not/really/sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,name=systemd 0 0\n";
const char *procselfcgroupslogind =
"1:name=systemd:/\n";
const char *proccgroupslogind =
"#subsys_name hierarchy num_cgroups enabled\n"
"cpuset 0 1 1\n"
"cpu 0 1 1\n"
"cpuacct 0 1 1\n"
"memory 0 1 0\n"
"devices 0 1 1\n"
"freezer 0 1 1\n"
"net_cls 0 1 1\n"
"blkio 0 1 1\n"
"perf_event 0 1 1\n";
static int make_file(const char *path,
const char *name,
const char *value)
{
int fd = -1;
int ret = -1;
char *filepath = NULL;
if (asprintf(&filepath, "%s/%s", path, name) < 0)
return -1;
if ((fd = real_open(filepath, O_CREAT|O_WRONLY, 0600)) < 0)
goto cleanup;
if (write(fd, value, strlen(value)) != strlen(value))
goto cleanup;
ret = 0;
cleanup:
if (fd != -1 && close(fd) < 0)
ret = -1;
free(filepath);
return ret;
}
static int make_controller(const char *path, mode_t mode)
{
int ret = -1;
const char *controller;
if (!STRPREFIX(path, fakesysfscgroupdir)) {
errno = EINVAL;
return -1;
}
controller = path + strlen(fakesysfscgroupdir) + 1;
if (STREQ(controller, "cpu"))
return symlink("cpu,cpuacct", path);
if (STREQ(controller, "cpuacct"))
return symlink("cpu,cpuacct", path);
if (real_mkdir(path, mode) < 0)
goto cleanup;
# define MAKE_FILE(name, value) \
do { \
if (make_file(path, name, value) < 0) \
goto cleanup; \
} while (0)
if (STRPREFIX(controller, "cpu,cpuacct")) {
MAKE_FILE("cpu.cfs_period_us", "100000\n");
MAKE_FILE("cpu.cfs_quota_us", "-1\n");
MAKE_FILE("cpu.shares", "1024\n");
MAKE_FILE("cpuacct.stat",
"user 216687025\n"
"system 43421396\n");
MAKE_FILE("cpuacct.usage", "2787788855799582\n");
MAKE_FILE("cpuacct.usage_percpu",
"7059492996 0 0 0 0 0 0 0 4180532496 0 0 0 0 0 0 0 "
"1957541268 0 0 0 0 0 0 0 2065932204 0 0 0 0 0 0 0 "
"18228689414 0 0 0 0 0 0 0 4245525148 0 0 0 0 0 0 0 "
"2911161568 0 0 0 0 0 0 0 1407758136 0 0 0 0 0 0 0 "
"1836807700 0 0 0 0 0 0 0 1065296618 0 0 0 0 0 0 0 "
"2046213266 0 0 0 0 0 0 0 747889778 0 0 0 0 0 0 0 "
"709566900 0 0 0 0 0 0 0 444777342 0 0 0 0 0 0 0 "
"5683512916 0 0 0 0 0 0 0 635751356 0 0 0 0 0 0 0\n");
} else if (STRPREFIX(controller, "cpuset")) {
if (STREQ(controller, "cpuset"))
MAKE_FILE("cpuset.cpus", "0-1");
else
MAKE_FILE("cpuset.cpus", ""); /* Values don't inherit */
MAKE_FILE("cpuset.memory_migrate", "0\n");
if (STREQ(controller, "cpuset"))
MAKE_FILE("cpuset.mems", "0");
else
MAKE_FILE("cpuset.mems", ""); /* Values don't inherit */
} else if (STRPREFIX(controller, "memory")) {
MAKE_FILE("memory.limit_in_bytes", "9223372036854775807\n");
MAKE_FILE("memory.memsw.limit_in_bytes", ""); /* Not supported */
MAKE_FILE("memory.memsw.usage_in_bytes", ""); /* Not supported */
MAKE_FILE("memory.soft_limit_in_bytes", "9223372036854775807\n");
MAKE_FILE("memory.stat",
"cache 1336619008\n"
"rss 97792000\n"
"mapped_file 42090496\n"
"pgpgin 13022605027\n"
"pgpgout 13023820533\n"
"pgfault 54429417056\n"
"pgmajfault 315715\n"
"inactive_anon 145887232\n"
"active_anon 67100672\n"
"inactive_file 627400704\n"
"active_file 661872640\n"
"unevictable 3690496\n"
"hierarchical_memory_limit 9223372036854775807\n"
"total_cache 1336635392\n"
"total_rss 118689792\n"
"total_mapped_file 42106880\n"
"total_pgpgin 13022606816\n"
"total_pgpgout 13023820793\n"
"total_pgfault 54429422313\n"
"total_pgmajfault 315715\n"
"total_inactive_anon 145891328\n"
"total_active_anon 88010752\n"
"total_inactive_file 627400704\n"
"total_active_file 661872640\n"
"total_unevictable 3690496\n"
"recent_rotated_anon 112807028\n"
"recent_rotated_file 2547948\n"
"recent_scanned_anon 113796164\n"
"recent_scanned_file 8199863\n");
MAKE_FILE("memory.usage_in_bytes", "1455321088\n");
MAKE_FILE("memory.use_hierarchy", "0\n");
} else if (STRPREFIX(controller, "freezer")) {
MAKE_FILE("freezer.state", "THAWED");
} else if (STRPREFIX(controller, "blkio")) {
MAKE_FILE("blkio.throttle.io_service_bytes",
"8:0 Read 59542107136\n"
"8:0 Write 411440480256\n"
"8:0 Sync 248486822912\n"
"8:0 Async 222495764480\n"
"8:0 Total 470982587392\n"
"9:0 Read 59542107137\n"
"9:0 Write 411440480257\n"
"9:0 Sync 248486822912\n"
"9:0 Async 222495764480\n"
"9:0 Total 470982587392\n");
MAKE_FILE("blkio.throttle.io_serviced",
"8:0 Read 4832583\n"
"8:0 Write 36641903\n"
"8:0 Sync 30723171\n"
"8:0 Async 10751315\n"
"8:0 Total 41474486\n"
"9:0 Read 4832584\n"
"9:0 Write 36641904\n"
"9:0 Sync 30723171\n"
"9:0 Async 10751315\n"
"9:0 Total 41474486\n");
MAKE_FILE("blkio.throttle.read_bps_device", "");
MAKE_FILE("blkio.throttle.read_iops_device", "");
MAKE_FILE("blkio.throttle.write_bps_device", "");
MAKE_FILE("blkio.throttle.write_iops_device", "");
MAKE_FILE("blkio.weight", "1000\n");
MAKE_FILE("blkio.weight_device", "");
} else {
errno = EINVAL;
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static void init_syms(void)
{
if (real_fopen)
return;
VIR_MOCK_REAL_INIT(fopen);
VIR_MOCK_REAL_INIT(access);
VIR_MOCK_REAL_INIT_ALT(lstat, __lxstat);
VIR_MOCK_REAL_INIT_ALT(stat, __xstat);
VIR_MOCK_REAL_INIT(mkdir);
VIR_MOCK_REAL_INIT(open);
}
static void init_sysfs(void)
{
if (fakerootdir && fakesysfscgroupdir)
return;
if (!(fakerootdir = getenv("LIBVIRT_FAKE_ROOT_DIR"))) {
fprintf(stderr, "Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
abort();
}
if (virAsprintfQuiet(&fakesysfscgroupdir, "%s%s",
fakerootdir, SYSFS_CGROUP_PREFIX) < 0)
abort();
if (virFileMakePath(fakesysfscgroupdir) < 0) {
fprintf(stderr, "Cannot create %s\n", fakesysfscgroupdir);
abort();
}
# define MAKE_CONTROLLER(subpath) \
do { \
char *path; \
if (asprintf(&path, "%s/%s", fakesysfscgroupdir, subpath) < 0) \
abort(); \
if (make_controller(path, 0755) < 0) { \
fprintf(stderr, "Cannot initialize %s\n", path); \
free(path); \
abort(); \
} \
free(path); \
} while (0)
MAKE_CONTROLLER("cpu");
MAKE_CONTROLLER("cpuacct");
MAKE_CONTROLLER("cpu,cpuacct");
MAKE_CONTROLLER("cpu,cpuacct/system");
MAKE_CONTROLLER("cpuset");
MAKE_CONTROLLER("blkio");
MAKE_CONTROLLER("memory");
MAKE_CONTROLLER("freezer");
if (make_file(fakesysfscgroupdir,
SYSFS_CPU_PRESENT_MOCKED, "8-23,48-159\n") < 0)
abort();
}
FILE *fopen(const char *path, const char *mode)
{
const char *mock;
bool allinone = false, logind = false;
init_syms();
mock = getenv("VIR_CGROUP_MOCK_MODE");
if (mock) {
if (STREQ(mock, "allinone"))
allinone = true;
else if (STREQ(mock, "logind"))
logind = true;
}
if (STREQ(path, "/proc/mounts")) {
if (STREQ(mode, "r")) {
if (allinone)
return fmemopen((void *)procmountsallinone,
strlen(procmountsallinone) + 1, mode);
else if (logind)
return fmemopen((void *)procmountslogind,
strlen(procmountslogind) + 1, mode);
else
return fmemopen((void *)procmounts,
strlen(procmounts) + 1, mode);
} else {
errno = EACCES;
return NULL;
}
}
if (STREQ(path, "/proc/cgroups")) {
if (STREQ(mode, "r")) {
if (allinone)
return fmemopen((void *)proccgroupsallinone,
strlen(proccgroupsallinone) + 1, mode);
else if (logind)
return fmemopen((void *)proccgroupslogind,
strlen(proccgroupslogind) + 1, mode);
else
return fmemopen((void *)proccgroups,
strlen(proccgroups) + 1, mode);
} else {
errno = EACCES;
return NULL;
}
}
if (STREQ(path, "/proc/self/cgroup")) {
if (STREQ(mode, "r")) {
if (allinone)
return fmemopen((void *)procselfcgroupsallinone,
strlen(procselfcgroupsallinone) + 1, mode);
else if (logind)
return fmemopen((void *)procselfcgroupslogind,
strlen(procselfcgroupslogind) + 1, mode);
else
return fmemopen((void *)procselfcgroups,
strlen(procselfcgroups) + 1, mode);
} else {
errno = EACCES;
return NULL;
}
}
return real_fopen(path, mode);
}
int access(const char *path, int mode)
{
int ret;
init_syms();
if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
char *newpath;
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
ret = real_access(newpath, mode);
free(newpath);
} else if (STREQ(path, "/proc/cgroups") ||
STREQ(path, "/proc/self/cgroup") ||
STREQ(path, SYSFS_CPU_PRESENT)) {
/* These files are readable for all. */
ret = (mode == F_OK || mode == R_OK) ? 0 : -1;
} else if (STREQ(path, "/proc/mounts")) {
/* This one is accessible anytime for anybody. In fact, it's just
* a symlink to /proc/self/mounts. */
ret = 0;
} else {
ret = real_access(path, mode);
}
return ret;
}
int __lxstat(int ver, const char *path, struct stat *sb)
{
int ret;
init_syms();
if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
char *newpath;
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
ret = real___lxstat(ver, newpath, sb);
free(newpath);
} else if (STRPREFIX(path, fakedevicedir0)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(8, 0);
return 0;
} else if (STRPREFIX(path, fakedevicedir1)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(9, 0);
return 0;
} else {
ret = real___lxstat(ver, path, sb);
}
return ret;
}
int lstat(const char *path, struct stat *sb)
{
int ret;
init_syms();
if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
char *newpath;
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
ret = real_lstat(newpath, sb);
free(newpath);
} else if (STRPREFIX(path, fakedevicedir0)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(8, 0);
return 0;
} else if (STRPREFIX(path, fakedevicedir1)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(9, 0);
return 0;
} else {
ret = real_lstat(path, sb);
}
return ret;
}
int __xstat(int ver, const char *path, struct stat *sb)
{
int ret;
init_syms();
if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
char *newpath;
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
ret = real___xstat(ver, newpath, sb);
free(newpath);
} else if (STRPREFIX(path, fakedevicedir0)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(8, 0);
return 0;
} else if (STRPREFIX(path, fakedevicedir1)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(9, 0);
return 0;
} else {
ret = real___xstat(ver, path, sb);
}
return ret;
}
int stat(const char *path, struct stat *sb)
{
char *newpath = NULL;
int ret;
init_syms();
if (STREQ(path, SYSFS_CPU_PRESENT)) {
init_sysfs();
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
SYSFS_CPU_PRESENT_MOCKED) < 0) {
errno = ENOMEM;
return -1;
}
} else if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
} else if (STRPREFIX(path, fakedevicedir0)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(8, 0);
return 0;
} else if (STRPREFIX(path, fakedevicedir1)) {
sb->st_mode = S_IFBLK;
sb->st_rdev = makedev(9, 0);
return 0;
} else {
if (!(newpath = strdup(path)))
return -1;
}
ret = real_stat(newpath, sb);
free(newpath);
return ret;
}
int mkdir(const char *path, mode_t mode)
{
int ret;
init_syms();
if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
char *newpath;
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
ret = make_controller(newpath, mode);
free(newpath);
} else {
ret = real_mkdir(path, mode);
}
return ret;
}
int open(const char *path, int flags, ...)
{
int ret;
char *newpath = NULL;
init_syms();
if (STREQ(path, SYSFS_CPU_PRESENT)) {
init_sysfs();
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
SYSFS_CPU_PRESENT_MOCKED) < 0) {
errno = ENOMEM;
return -1;
}
}
if (STRPREFIX(path, SYSFS_CGROUP_PREFIX)) {
init_sysfs();
if (asprintf(&newpath, "%s/%s",
fakesysfscgroupdir,
path + strlen(SYSFS_CGROUP_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
}
if (flags & O_CREAT) {
va_list ap;
mode_t mode;
va_start(ap, flags);
mode = (mode_t) va_arg(ap, int);
va_end(ap);
ret = real_open(newpath ? newpath : path, flags, mode);
} else {
ret = real_open(newpath ? newpath : path, flags);
}
free(newpath);
return ret;
}
#else
/* Nothing to override on non-__linux__ platforms */
#endif