2 * Copyright (C) 2013 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
18 * Author: Daniel P. Berrange <berrange@redhat.com>
24 # include "internal.h"
31 # include <sys/stat.h>
33 # include "testutilslxc.h"
35 static int (*realopen)(const char *path, int flags, ...);
36 static FILE *(*realfopen)(const char *path, const char *mode);
37 static int (*realaccess)(const char *path, int mode);
38 static int (*realstat)(const char *path, struct stat *sb);
39 static int (*real__xstat)(int ver, const char *path, struct stat *sb);
40 static int (*reallstat)(const char *path, struct stat *sb);
41 static int (*real__lxstat)(int ver, const char *path, struct stat *sb);
42 static int (*realmkdir)(const char *path, mode_t mode);
44 /* Don't make static, since it causes problems with clang
45 * when passed as an arg to asprintf()
46 * vircgroupmock.c:462:22: error: static variable 'fakesysfsdir' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
49 const char *fakedevicedir0 = FAKEDEVDIR0;
50 const char *fakedevicedir1 = FAKEDEVDIR1;
53 # define SYSFS_PREFIX "/not/really/sys/fs/cgroup/"
58 * We fake out /proc/mounts, so make it look as is cgroups
59 * are mounted on /not/really/sys/fs/cgroup. We don't
60 * use /sys/fs/cgroup, because we want to make it easy to
61 * detect places where we've not mocked enough syscalls.
63 * In any open/acces/mkdir calls we look at path and if
64 * it starts with /not/really/sys/fs/cgroup, we rewrite
65 * the path to point at a temporary directory referred
66 * to by LIBVIRT_FAKE_SYSFS_DIR env variable that is
67 * set by the main test suite
69 * In mkdir() calls, we simulate the cgroups behaviour
70 * whereby creating the directory auto-creates a bunch
75 * Intentionally missing the 'devices' mount.
76 * Co-mounting cpu & cpuacct controllers
77 * An anonymous controller for systemd
79 const char *procmounts =
80 "rootfs / rootfs rw 0 0\n"
81 "tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n"
82 "tmpfs /not/really/sys/fs/cgroup tmpfs rw,seclabel,nosuid,nodev,noexec,mode=755 0 0\n"
83 "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"
84 "cgroup /not/really/sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n"
85 "cgroup /not/really/sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpuacct,cpu 0 0\n"
86 "cgroup /not/really/sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n"
87 "cgroup /not/really/sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n"
88 "cgroup /not/really/sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n"
89 "/dev/sda1 /boot ext4 rw,seclabel,relatime,data=ordered 0 0\n"
90 "tmpfs /tmp tmpfs rw,seclabel,relatime,size=1024000k 0 0\n";
92 const char *procselfcgroups =
96 "3:cpuacct,cpu:/system\n"
98 "1:name=systemd:/user/berrange/123\n";
100 const char *proccgroups =
101 "#subsys_name hierarchy num_cgroups enabled\n"
111 const char *procmountsallinone =
112 "rootfs / rootfs rw 0 0\n"
113 "sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\n"
114 "proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n"
115 "udev /dev devtmpfs rw,relatime,size=16458560k,nr_inodes=4114640,mode=755 0 0\n"
116 "devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n"
117 "nfsd /proc/fs/nfsd nfsd rw,relatime 0 0\n"
118 "cgroup /not/really/sys/fs/cgroup cgroup rw,relatime,blkio,devices,memory,cpuacct,cpu,cpuset 0 0\n";
120 const char *procselfcgroupsallinone =
121 "6:blkio,devices,memory,cpuacct,cpu,cpuset:/";
123 const char *proccgroupsallinone =
124 "#subsys_name hierarchy num_cgroups enabled\n"
132 const char *procmountslogind =
133 "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"
134 "systemd /not/really/sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,name=systemd 0 0\n";
136 const char *procselfcgroupslogind =
137 "1:name=systemd:/\n";
139 const char *proccgroupslogind =
140 "#subsys_name hierarchy num_cgroups enabled\n"
149 "perf_event 0 1 1\n";
153 static int make_file(const char *path,
159 char *filepath = NULL;
161 if (asprintf(&filepath, "%s/%s", path, name) < 0)
164 if ((fd = realopen(filepath, O_CREAT|O_WRONLY, 0600)) < 0)
167 if (write(fd, value, strlen(value)) != strlen(value))
172 if (fd != -1 && close(fd) < 0)
179 static int make_controller(const char *path, mode_t mode)
182 const char *controller;
184 if (!STRPREFIX(path, fakesysfsdir)) {
188 controller = path + strlen(fakesysfsdir) + 1;
190 if (STREQ(controller, "cpu"))
191 return symlink("cpu,cpuacct", path);
192 if (STREQ(controller, "cpuacct"))
193 return symlink("cpu,cpuacct", path);
195 if (realmkdir(path, mode) < 0)
198 # define MAKE_FILE(name, value) \
200 if (make_file(path, name, value) < 0) \
204 if (STRPREFIX(controller, "cpu,cpuacct")) {
205 MAKE_FILE("cpu.cfs_period_us", "100000\n");
206 MAKE_FILE("cpu.cfs_quota_us", "-1\n");
207 MAKE_FILE("cpu.rt_period_us", "1000000\n");
208 MAKE_FILE("cpu.rt_runtime_us", "950000\n");
209 MAKE_FILE("cpu.shares", "1024\n");
210 MAKE_FILE("cpu.stat",
213 "throttled_time 0\n");
214 MAKE_FILE("cpuacct.stat",
216 "system 43421396\n");
217 MAKE_FILE("cpuacct.usage", "2787788855799582\n");
218 MAKE_FILE("cpuacct.usage_percpu", "1413142688153030 1374646168910542\n");
219 } else if (STRPREFIX(controller, "cpuset")) {
220 MAKE_FILE("cpuset.cpu_exclusive", "1\n");
221 if (STREQ(controller, "cpuset"))
222 MAKE_FILE("cpuset.cpus", "0-1");
224 MAKE_FILE("cpuset.cpus", ""); /* Values don't inherit */
225 MAKE_FILE("cpuset.mem_exclusive", "1\n");
226 MAKE_FILE("cpuset.mem_hardwall", "0\n");
227 MAKE_FILE("cpuset.memory_migrate", "0\n");
228 MAKE_FILE("cpuset.memory_pressure", "0\n");
229 MAKE_FILE("cpuset.memory_pressure_enabled", "0\n");
230 MAKE_FILE("cpuset.memory_spread_page", "0\n");
231 MAKE_FILE("cpuset.memory_spread_slab", "0\n");
232 if (STREQ(controller, "cpuset"))
233 MAKE_FILE("cpuset.mems", "0");
235 MAKE_FILE("cpuset.mems", ""); /* Values don't inherit */
236 MAKE_FILE("cpuset.sched_load_balance", "1\n");
237 MAKE_FILE("cpuset.sched_relax_domain_level", "-1\n");
238 } else if (STRPREFIX(controller, "memory")) {
239 MAKE_FILE("memory.failcnt", "0\n");
240 MAKE_FILE("memory.force_empty", ""); /* Write only */
241 MAKE_FILE("memory.kmem.tcp.failcnt", "0\n");
242 MAKE_FILE("memory.kmem.tcp.limit_in_bytes", "9223372036854775807\n");
243 MAKE_FILE("memory.kmem.tcp.max_usage_in_bytes", "0\n");
244 MAKE_FILE("memory.kmem.tcp.usage_in_bytes", "16384\n");
245 MAKE_FILE("memory.limit_in_bytes", "9223372036854775807\n");
246 MAKE_FILE("memory.max_usage_in_bytes", "0\n");
247 MAKE_FILE("memory.memsw.failcnt", ""); /* Not supported */
248 MAKE_FILE("memory.memsw.limit_in_bytes", ""); /* Not supported */
249 MAKE_FILE("memory.memsw.max_usage_in_bytes", ""); /* Not supported */
250 MAKE_FILE("memory.memsw.usage_in_bytes", ""); /* Not supported */
251 MAKE_FILE("memory.move_charge_at_immigrate", "0\n");
252 MAKE_FILE("memory.numa_stat",
253 "total=367664 N0=367664\n"
254 "file=314764 N0=314764\n"
255 "anon=51999 N0=51999\n"
256 "unevictable=901 N0=901\n");
257 MAKE_FILE("memory.oom_control",
258 "oom_kill_disable 0\n"
260 MAKE_FILE("memory.soft_limit_in_bytes", "9223372036854775807\n");
261 MAKE_FILE("memory.stat",
264 "mapped_file 42090496\n"
265 "pgpgin 13022605027\n"
266 "pgpgout 13023820533\n"
267 "pgfault 54429417056\n"
268 "pgmajfault 315715\n"
269 "inactive_anon 145887232\n"
270 "active_anon 67100672\n"
271 "inactive_file 627400704\n"
272 "active_file 661872640\n"
273 "unevictable 3690496\n"
274 "hierarchical_memory_limit 9223372036854775807\n"
275 "total_cache 1336635392\n"
276 "total_rss 118689792\n"
277 "total_mapped_file 42106880\n"
278 "total_pgpgin 13022606816\n"
279 "total_pgpgout 13023820793\n"
280 "total_pgfault 54429422313\n"
281 "total_pgmajfault 315715\n"
282 "total_inactive_anon 145891328\n"
283 "total_active_anon 88010752\n"
284 "total_inactive_file 627400704\n"
285 "total_active_file 661872640\n"
286 "total_unevictable 3690496\n"
287 "recent_rotated_anon 112807028\n"
288 "recent_rotated_file 2547948\n"
289 "recent_scanned_anon 113796164\n"
290 "recent_scanned_file 8199863\n");
291 MAKE_FILE("memory.swappiness", "60\n");
292 MAKE_FILE("memory.usage_in_bytes", "1455321088\n");
293 MAKE_FILE("memory.use_hierarchy", "0\n");
294 } else if (STRPREFIX(controller, "freezer")) {
295 MAKE_FILE("freezer.state", "THAWED");
296 } else if (STRPREFIX(controller, "blkio")) {
297 MAKE_FILE("blkio.io_merged",
299 "8:0 Write 2248076\n"
301 "8:0 Async 3285962\n"
302 "8:0 Total 3349025\n");
303 MAKE_FILE("blkio.io_queued",
309 MAKE_FILE("blkio.io_service_bytes",
310 "8:0 Read 59542078464\n"
311 "8:0 Write 397369182208\n"
312 "8:0 Sync 234080922624\n"
313 "8:0 Async 222830338048\n"
314 "8:0 Total 456911260672\n");
315 MAKE_FILE("blkio.io_serviced",
317 "8:0 Write 14966516\n"
318 "8:0 Sync 12064031\n"
319 "8:0 Async 6304989\n"
320 "8:0 Total 18369020\n");
321 MAKE_FILE("blkio.io_service_time",
322 "8:0 Read 10747537542349\n"
323 "8:0 Write 9200028590575\n"
324 "8:0 Sync 6449319855381\n"
325 "8:0 Async 13498246277543\n"
326 "8:0 Total 19947566132924\n");
327 MAKE_FILE("blkio.io_wait_time",
328 "8:0 Read 14687514824889\n"
329 "8:0 Write 357748452187691\n"
330 "8:0 Sync 55296974349413\n"
331 "8:0 Async 317138992663167\n"
332 "8:0 Total 372435967012580\n");
333 MAKE_FILE("blkio.reset_stats", ""); /* Write only */
334 MAKE_FILE("blkio.sectors", "8:0 892404806\n");
335 MAKE_FILE("blkio.throttle.io_service_bytes",
336 "8:0 Read 59542107136\n"
337 "8:0 Write 411440480256\n"
338 "8:0 Sync 248486822912\n"
339 "8:0 Async 222495764480\n"
340 "8:0 Total 470982587392\n"
341 "9:0 Read 59542107137\n"
342 "9:0 Write 411440480257\n"
343 "9:0 Sync 248486822912\n"
344 "9:0 Async 222495764480\n"
345 "9:0 Total 470982587392\n");
346 MAKE_FILE("blkio.throttle.io_serviced",
348 "8:0 Write 36641903\n"
349 "8:0 Sync 30723171\n"
350 "8:0 Async 10751315\n"
351 "8:0 Total 41474486\n"
353 "9:0 Write 36641904\n"
354 "9:0 Sync 30723171\n"
355 "9:0 Async 10751315\n"
356 "9:0 Total 41474486\n");
357 MAKE_FILE("blkio.throttle.read_bps_device", "");
358 MAKE_FILE("blkio.throttle.read_iops_device", "");
359 MAKE_FILE("blkio.throttle.write_bps_device", "");
360 MAKE_FILE("blkio.throttle.write_iops_device", "");
361 MAKE_FILE("blkio.time", "8:0 61019089\n");
362 MAKE_FILE("blkio.weight", "1000\n");
363 MAKE_FILE("blkio.weight_device", "");
375 static void init_syms(void)
380 # define LOAD_SYM(name) \
382 if (!(real ## name = dlsym(RTLD_NEXT, #name))) { \
383 fprintf(stderr, "Cannot find real '%s' symbol\n", #name); \
388 # define LOAD_SYM_ALT(name1, name2) \
390 if (!(real ## name1 = dlsym(RTLD_NEXT, #name1)) && \
391 !(real ## name2 = dlsym(RTLD_NEXT, #name2))) { \
392 fprintf(stderr, "Cannot find real '%s' or '%s' symbol\n", #name1, #name2); \
399 LOAD_SYM_ALT(lstat, __lxstat);
400 LOAD_SYM_ALT(stat, __xstat);
405 static void init_sysfs(void)
410 if (!(fakesysfsdir = getenv("LIBVIRT_FAKE_SYSFS_DIR"))) {
411 fprintf(stderr, "Missing LIBVIRT_FAKE_SYSFS_DIR env variable\n");
415 # define MAKE_CONTROLLER(subpath) \
418 if (asprintf(&path, "%s/%s", fakesysfsdir, subpath) < 0)\
420 if (make_controller(path, 0755) < 0) { \
421 fprintf(stderr, "Cannot initialize %s\n", path); \
426 MAKE_CONTROLLER("cpu");
427 MAKE_CONTROLLER("cpuacct");
428 MAKE_CONTROLLER("cpu,cpuacct");
429 MAKE_CONTROLLER("cpu,cpuacct/system");
430 MAKE_CONTROLLER("cpuset");
431 MAKE_CONTROLLER("blkio");
432 MAKE_CONTROLLER("memory");
433 MAKE_CONTROLLER("freezer");
437 FILE *fopen(const char *path, const char *mode)
440 bool allinone = false, logind = false;
443 mock = getenv("VIR_CGROUP_MOCK_MODE");
445 if (STREQ(mock, "allinone"))
447 else if (STREQ(mock, "logind"))
451 if (STREQ(path, "/proc/mounts")) {
452 if (STREQ(mode, "r")) {
454 return fmemopen((void *)procmountsallinone,
455 strlen(procmountsallinone), mode);
457 return fmemopen((void *)procmountslogind,
458 strlen(procmountslogind), mode);
460 return fmemopen((void *)procmounts, strlen(procmounts), mode);
466 if (STREQ(path, "/proc/cgroups")) {
467 if (STREQ(mode, "r")) {
469 return fmemopen((void *)proccgroupsallinone,
470 strlen(proccgroupsallinone), mode);
472 return fmemopen((void *)proccgroupslogind,
473 strlen(proccgroupslogind), mode);
475 return fmemopen((void *)proccgroups, strlen(proccgroups), mode);
481 if (STREQ(path, "/proc/self/cgroup")) {
482 if (STREQ(mode, "r")) {
484 return fmemopen((void *)procselfcgroupsallinone,
485 strlen(procselfcgroupsallinone), mode);
487 return fmemopen((void *)procselfcgroupslogind,
488 strlen(procselfcgroupslogind), mode);
490 return fmemopen((void *)procselfcgroups, strlen(procselfcgroups), mode);
497 return realfopen(path, mode);
500 int access(const char *path, int mode)
506 if (STRPREFIX(path, SYSFS_PREFIX)) {
509 if (asprintf(&newpath, "%s/%s",
511 path + strlen(SYSFS_PREFIX)) < 0) {
515 ret = realaccess(newpath, mode);
517 } else if (STREQ(path, "/proc/cgroups") ||
518 STREQ(path, "/proc/self/cgroup")) {
519 /* These files are readable for all. */
520 ret = (mode == F_OK || mode == R_OK) ? 0 : -1;
521 } else if (STREQ(path, "/proc/mounts")) {
522 /* This one is accessible anytime for anybody. In fact, it's just
523 * a symlink to /proc/self/mounts. */
526 ret = realaccess(path, mode);
531 int __lxstat(int ver, const char *path, struct stat *sb)
537 if (STRPREFIX(path, SYSFS_PREFIX)) {
540 if (asprintf(&newpath, "%s/%s",
542 path + strlen(SYSFS_PREFIX)) < 0) {
546 ret = real__lxstat(ver, newpath, sb);
548 } else if (STRPREFIX(path, fakedevicedir0)) {
549 sb->st_mode = S_IFBLK;
550 sb->st_rdev = makedev(8, 0);
552 } else if (STRPREFIX(path, fakedevicedir1)) {
553 sb->st_mode = S_IFBLK;
554 sb->st_rdev = makedev(9, 0);
557 ret = real__lxstat(ver, path, sb);
562 int lstat(const char *path, struct stat *sb)
568 if (STRPREFIX(path, SYSFS_PREFIX)) {
571 if (asprintf(&newpath, "%s/%s",
573 path + strlen(SYSFS_PREFIX)) < 0) {
577 ret = reallstat(newpath, sb);
579 } else if (STRPREFIX(path, fakedevicedir0)) {
580 sb->st_mode = S_IFBLK;
581 sb->st_rdev = makedev(8, 0);
583 } else if (STRPREFIX(path, fakedevicedir1)) {
584 sb->st_mode = S_IFBLK;
585 sb->st_rdev = makedev(9, 0);
588 ret = reallstat(path, sb);
593 int __xstat(int ver, const char *path, struct stat *sb)
599 if (STRPREFIX(path, SYSFS_PREFIX)) {
602 if (asprintf(&newpath, "%s/%s",
604 path + strlen(SYSFS_PREFIX)) < 0) {
608 ret = real__xstat(ver, newpath, sb);
610 } else if (STRPREFIX(path, fakedevicedir0)) {
611 sb->st_mode = S_IFBLK;
612 sb->st_rdev = makedev(8, 0);
614 } else if (STRPREFIX(path, fakedevicedir1)) {
615 sb->st_mode = S_IFBLK;
616 sb->st_rdev = makedev(9, 0);
619 ret = real__xstat(ver, path, sb);
624 int stat(const char *path, struct stat *sb)
630 if (STRPREFIX(path, SYSFS_PREFIX)) {
633 if (asprintf(&newpath, "%s/%s",
635 path + strlen(SYSFS_PREFIX)) < 0) {
639 ret = realstat(newpath, sb);
641 } else if (STRPREFIX(path, fakedevicedir0)) {
642 sb->st_mode = S_IFBLK;
643 sb->st_rdev = makedev(8, 0);
645 } else if (STRPREFIX(path, fakedevicedir1)) {
646 sb->st_mode = S_IFBLK;
647 sb->st_rdev = makedev(9, 0);
650 ret = realstat(path, sb);
655 int mkdir(const char *path, mode_t mode)
661 if (STRPREFIX(path, SYSFS_PREFIX)) {
664 if (asprintf(&newpath, "%s/%s",
666 path + strlen(SYSFS_PREFIX)) < 0) {
670 ret = make_controller(newpath, mode);
673 ret = realmkdir(path, mode);
678 int open(const char *path, int flags, ...)
681 char *newpath = NULL;
685 if (STRPREFIX(path, SYSFS_PREFIX)) {
687 if (asprintf(&newpath, "%s/%s",
689 path + strlen(SYSFS_PREFIX)) < 0) {
694 if (flags & O_CREAT) {
698 mode = va_arg(ap, mode_t);
700 ret = realopen(newpath ? newpath : path, flags, mode);
702 ret = realopen(newpath ? newpath : path, flags);
708 /* Nothing to override on non-__linux__ platforms */