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/>.
25 # include <sys/stat.h>
27 # ifdef MAJOR_IN_MKDEV
28 # include <sys/mkdev.h>
29 # elif MAJOR_IN_SYSMACROS
30 # include <sys/sysmacros.h>
34 # include "testutilslxc.h"
35 # include "virstring.h"
37 # include "viralloc.h"
39 static int (*real_open
)(const char *path
, int flags
, ...);
40 static FILE *(*real_fopen
)(const char *path
, const char *mode
);
41 static int (*real_access
)(const char *path
, int mode
);
42 static int (*real_mkdir
)(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 char *fakesysfscgroupdir
;
50 const char *fakedevicedir0
= FAKEDEVDIR0
;
51 const char *fakedevicedir1
= FAKEDEVDIR1
;
54 # define SYSFS_CGROUP_PREFIX "/not/really/sys/fs/cgroup"
55 # define SYSFS_CPU_PRESENT "/sys/devices/system/cpu/present"
56 # define SYSFS_CPU_PRESENT_MOCKED "devices_system_cpu_present"
61 * We fake out /proc/mounts, so make it look as is cgroups
62 * are mounted on /not/really/sys/fs/cgroup. We don't
63 * use /sys/fs/cgroup, because we want to make it easy to
64 * detect places where we've not mocked enough syscalls.
66 * In any open/acces/mkdir calls we look at path and if
67 * it starts with /not/really/sys/fs/cgroup, we rewrite
68 * the path to point at a subdirectory of the temporary
69 * directory referred to by LIBVIRT_FAKE_ROOT_DIR env
70 * variable that is set by the main test suite
72 * In mkdir() calls, we simulate the cgroups behaviour
73 * whereby creating the directory auto-creates a bunch
77 static int make_file(const char *path
,
83 char *filepath
= NULL
;
85 if (asprintf(&filepath
, "%s/%s", path
, name
) < 0)
88 if ((fd
= real_open(filepath
, O_CREAT
|O_WRONLY
, 0600)) < 0)
91 if (write(fd
, value
, strlen(value
)) != strlen(value
))
96 if (fd
!= -1 && close(fd
) < 0)
104 static int make_controller_v1(const char *path
, mode_t mode
)
107 const char *controller
;
109 if (!STRPREFIX(path
, fakesysfscgroupdir
)) {
113 controller
= path
+ strlen(fakesysfscgroupdir
) + 1;
115 if (STREQ(controller
, "cpu"))
116 return symlink("cpu,cpuacct", path
);
117 if (STREQ(controller
, "cpuacct"))
118 return symlink("cpu,cpuacct", path
);
120 if (real_mkdir(path
, mode
) < 0)
123 # define MAKE_FILE(name, value) \
125 if (make_file(path, name, value) < 0) \
129 if (STRPREFIX(controller
, "cpu,cpuacct")) {
130 MAKE_FILE("cpu.cfs_period_us", "100000\n");
131 MAKE_FILE("cpu.cfs_quota_us", "-1\n");
132 MAKE_FILE("cpu.shares", "1024\n");
133 MAKE_FILE("cpuacct.stat",
135 "system 43421396\n");
136 MAKE_FILE("cpuacct.usage", "2787788855799582\n");
137 MAKE_FILE("cpuacct.usage_percpu",
138 "7059492996 0 0 0 0 0 0 0 4180532496 0 0 0 0 0 0 0 "
139 "1957541268 0 0 0 0 0 0 0 2065932204 0 0 0 0 0 0 0 "
140 "18228689414 0 0 0 0 0 0 0 4245525148 0 0 0 0 0 0 0 "
141 "2911161568 0 0 0 0 0 0 0 1407758136 0 0 0 0 0 0 0 "
142 "1836807700 0 0 0 0 0 0 0 1065296618 0 0 0 0 0 0 0 "
143 "2046213266 0 0 0 0 0 0 0 747889778 0 0 0 0 0 0 0 "
144 "709566900 0 0 0 0 0 0 0 444777342 0 0 0 0 0 0 0 "
145 "5683512916 0 0 0 0 0 0 0 635751356 0 0 0 0 0 0 0\n");
146 } else if (STRPREFIX(controller
, "cpuset")) {
147 if (STREQ(controller
, "cpuset"))
148 MAKE_FILE("cpuset.cpus", "0-1");
150 MAKE_FILE("cpuset.cpus", ""); /* Values don't inherit */
151 MAKE_FILE("cpuset.memory_migrate", "0\n");
152 if (STREQ(controller
, "cpuset"))
153 MAKE_FILE("cpuset.mems", "0");
155 MAKE_FILE("cpuset.mems", ""); /* Values don't inherit */
156 } else if (STRPREFIX(controller
, "memory")) {
157 MAKE_FILE("memory.limit_in_bytes", "9223372036854775807\n");
158 MAKE_FILE("memory.memsw.limit_in_bytes", ""); /* Not supported */
159 MAKE_FILE("memory.memsw.usage_in_bytes", ""); /* Not supported */
160 MAKE_FILE("memory.soft_limit_in_bytes", "9223372036854775807\n");
161 MAKE_FILE("memory.stat",
164 "mapped_file 42090496\n"
165 "pgpgin 13022605027\n"
166 "pgpgout 13023820533\n"
167 "pgfault 54429417056\n"
168 "pgmajfault 315715\n"
169 "inactive_anon 145887232\n"
170 "active_anon 67100672\n"
171 "inactive_file 627400704\n"
172 "active_file 661872640\n"
173 "unevictable 3690496\n"
174 "hierarchical_memory_limit 9223372036854775807\n"
175 "total_cache 1336635392\n"
176 "total_rss 118689792\n"
177 "total_mapped_file 42106880\n"
178 "total_pgpgin 13022606816\n"
179 "total_pgpgout 13023820793\n"
180 "total_pgfault 54429422313\n"
181 "total_pgmajfault 315715\n"
182 "total_inactive_anon 145891328\n"
183 "total_active_anon 88010752\n"
184 "total_inactive_file 627400704\n"
185 "total_active_file 661872640\n"
186 "total_unevictable 3690496\n"
187 "recent_rotated_anon 112807028\n"
188 "recent_rotated_file 2547948\n"
189 "recent_scanned_anon 113796164\n"
190 "recent_scanned_file 8199863\n");
191 MAKE_FILE("memory.usage_in_bytes", "1455321088\n");
192 MAKE_FILE("memory.use_hierarchy", "0\n");
193 } else if (STRPREFIX(controller
, "freezer")) {
194 MAKE_FILE("freezer.state", "THAWED");
195 } else if (STRPREFIX(controller
, "blkio")) {
196 MAKE_FILE("blkio.throttle.io_service_bytes",
197 "8:0 Read 59542107136\n"
198 "8:0 Write 411440480256\n"
199 "8:0 Sync 248486822912\n"
200 "8:0 Async 222495764480\n"
201 "8:0 Total 470982587392\n"
202 "9:0 Read 59542107137\n"
203 "9:0 Write 411440480257\n"
204 "9:0 Sync 248486822912\n"
205 "9:0 Async 222495764480\n"
206 "9:0 Total 470982587392\n");
207 MAKE_FILE("blkio.throttle.io_serviced",
209 "8:0 Write 36641903\n"
210 "8:0 Sync 30723171\n"
211 "8:0 Async 10751315\n"
212 "8:0 Total 41474486\n"
214 "9:0 Write 36641904\n"
215 "9:0 Sync 30723171\n"
216 "9:0 Async 10751315\n"
217 "9:0 Total 41474486\n");
218 MAKE_FILE("blkio.throttle.read_bps_device", "");
219 MAKE_FILE("blkio.throttle.read_iops_device", "");
220 MAKE_FILE("blkio.throttle.write_bps_device", "");
221 MAKE_FILE("blkio.throttle.write_iops_device", "");
222 MAKE_FILE("blkio.weight", "1000\n");
223 MAKE_FILE("blkio.weight_device", "");
238 static int make_controller_v2(const char *path
, mode_t mode
)
240 if (!STRPREFIX(path
, fakesysfscgroupdir
)) {
245 if (real_mkdir(path
, mode
) < 0 && errno
!= EEXIST
)
248 # define MAKE_FILE(name, value) \
250 if (make_file(path, name, value) < 0) \
254 MAKE_FILE("cgroup.controllers", "cpu io memory\n");
255 MAKE_FILE("cgroup.subtree_control", "");
256 MAKE_FILE("cgroup.type", "domain\n");
257 MAKE_FILE("cpu.max", "max 100000\n");
258 MAKE_FILE("cpu.stat",
264 "throttled_usec 0\n");
265 MAKE_FILE("cpu.weight", "100\n");
266 MAKE_FILE("memory.current", "1455321088\n");
267 MAKE_FILE("memory.high", "max\n");
268 MAKE_FILE("memory.max", "max\n");
269 MAKE_FILE("memory.stat",
284 "slab_reclaimable 0\n"
285 "slab_unreclaimable 0\n"
295 "workingset_refault 0\n"
296 "workingset_activate 0\n"
297 "workingset_nodereclaim 0\n");
298 MAKE_FILE("memory.swap.current", "0\n");
299 MAKE_FILE("memory.swap.max", "max\n");
300 MAKE_FILE("io.stat", "8:0 rbytes=26828800 wbytes=77062144 rios=2256 wios=7849 dbytes=0 dios=0\n");
301 MAKE_FILE("io.max", "");
302 MAKE_FILE("io.weight", "default 100\n");
310 static void init_syms(void)
315 VIR_MOCK_REAL_INIT(fopen
);
316 VIR_MOCK_REAL_INIT(access
);
317 VIR_MOCK_REAL_INIT(mkdir
);
318 VIR_MOCK_REAL_INIT(open
);
322 static int make_controller(const char *path
, mode_t mode
)
325 bool unified
= false;
328 mock
= getenv("VIR_CGROUP_MOCK_MODE");
330 if (STREQ(mock
, "unified")) {
332 } else if (STREQ(mock
, "hybrid")) {
335 fprintf(stderr
, "invalid mode '%s'\n", mock
);
340 if (unified
|| (hybrid
&& strstr(path
, "unified"))) {
341 return make_controller_v2(path
, mode
);
343 return make_controller_v1(path
, mode
);
348 static void init_sysfs(void)
351 char *newfakerootdir
;
352 bool unified
= false;
355 if (!(newfakerootdir
= getenv("LIBVIRT_FAKE_ROOT_DIR"))) {
356 fprintf(stderr
, "Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
360 if (fakerootdir
&& STREQ(fakerootdir
, newfakerootdir
))
363 fakerootdir
= newfakerootdir
;
365 mock
= getenv("VIR_CGROUP_MOCK_MODE");
367 if (STREQ(mock
, "unified")) {
369 } else if (STREQ(mock
, "hybrid")) {
372 fprintf(stderr
, "invalid mode '%s'\n", mock
);
377 VIR_FREE(fakesysfscgroupdir
);
379 if (virAsprintfQuiet(&fakesysfscgroupdir
, "%s%s",
380 fakerootdir
, SYSFS_CGROUP_PREFIX
) < 0)
383 if (virFileMakePath(fakesysfscgroupdir
) < 0) {
384 fprintf(stderr
, "Cannot create %s\n", fakesysfscgroupdir
);
388 # define MAKE_CONTROLLER(subpath) \
391 if (asprintf(&path, "%s/%s", fakesysfscgroupdir, subpath) < 0) \
393 if (make_controller(path, 0755) < 0) { \
394 fprintf(stderr, "Cannot initialize %s\n", path); \
404 MAKE_CONTROLLER("unified");
405 MAKE_CONTROLLER("cpuset");
406 MAKE_CONTROLLER("freezer");
408 MAKE_CONTROLLER("cpu");
409 MAKE_CONTROLLER("cpuacct");
410 MAKE_CONTROLLER("cpu,cpuacct");
411 MAKE_CONTROLLER("cpuset");
412 MAKE_CONTROLLER("blkio");
413 MAKE_CONTROLLER("memory");
414 MAKE_CONTROLLER("freezer");
416 if (make_file(fakesysfscgroupdir
,
417 SYSFS_CPU_PRESENT_MOCKED
, "8-23,48-159\n") < 0)
423 FILE *fopen(const char *path
, const char *mode
)
425 char *filepath
= NULL
;
426 const char *type
= NULL
;
428 const char *filename
= getenv("VIR_CGROUP_MOCK_FILENAME");
432 if (STREQ(path
, "/proc/mounts")) {
433 if (STREQ(mode
, "r")) {
440 if (STREQ(path
, "/proc/cgroups")) {
441 if (STREQ(mode
, "r")) {
448 if (STREQ(path
, "/proc/self/cgroup")) {
449 if (STREQ(mode
, "r")) {
450 type
= "self.cgroup";
462 if (virAsprintfQuiet(&filepath
, "%s/vircgroupdata/%s.%s",
463 abs_srcdir
, filename
, type
) < 0) {
466 rc
= real_fopen(filepath
, mode
);
471 return real_fopen(path
, mode
);
474 int access(const char *path
, int mode
)
480 if (STRPREFIX(path
, SYSFS_CGROUP_PREFIX
)) {
483 if (asprintf(&newpath
, "%s%s",
485 path
+ strlen(SYSFS_CGROUP_PREFIX
)) < 0) {
489 ret
= real_access(newpath
, mode
);
491 } else if (STREQ(path
, "/proc/cgroups") ||
492 STREQ(path
, "/proc/self/cgroup") ||
493 STREQ(path
, SYSFS_CPU_PRESENT
)) {
494 /* These files are readable for all. */
495 ret
= (mode
== F_OK
|| mode
== R_OK
) ? 0 : -1;
496 } else if (STREQ(path
, "/proc/mounts")) {
497 /* This one is accessible anytime for anybody. In fact, it's just
498 * a symlink to /proc/self/mounts. */
501 ret
= real_access(path
, mode
);
506 # define VIR_MOCK_STAT_HOOK \
508 if (STRPREFIX(path, fakedevicedir0)) { \
509 sb->st_mode = S_IFBLK; \
510 sb->st_rdev = makedev(8, 0); \
512 } else if (STRPREFIX(path, fakedevicedir1)) { \
513 sb->st_mode = S_IFBLK; \
514 sb->st_rdev = makedev(9, 0); \
519 # include "virmockstathelpers.c"
522 virMockStatRedirect(const char *path
, char **newpath
)
524 if (STREQ(path
, SYSFS_CPU_PRESENT
)) {
526 if (asprintf(newpath
, "%s/%s",
528 SYSFS_CPU_PRESENT_MOCKED
) < 0)
530 } else if (STRPREFIX(path
, SYSFS_CGROUP_PREFIX
)) {
532 if (asprintf(newpath
, "%s%s",
534 path
+ strlen(SYSFS_CGROUP_PREFIX
)) < 0)
541 int mkdir(const char *path
, mode_t mode
)
547 if (STRPREFIX(path
, SYSFS_CGROUP_PREFIX
)) {
550 if (asprintf(&newpath
, "%s%s",
552 path
+ strlen(SYSFS_CGROUP_PREFIX
)) < 0) {
556 ret
= make_controller(newpath
, mode
);
559 ret
= real_mkdir(path
, mode
);
564 int open(const char *path
, int flags
, ...)
567 char *newpath
= NULL
;
571 if (STREQ(path
, SYSFS_CPU_PRESENT
)) {
573 if (asprintf(&newpath
, "%s/%s",
575 SYSFS_CPU_PRESENT_MOCKED
) < 0) {
581 if (STRPREFIX(path
, SYSFS_CGROUP_PREFIX
)) {
583 if (asprintf(&newpath
, "%s%s",
585 path
+ strlen(SYSFS_CGROUP_PREFIX
)) < 0) {
590 if (flags
& O_CREAT
) {
594 mode
= (mode_t
) va_arg(ap
, int);
596 ret
= real_open(newpath
? newpath
: path
, flags
, mode
);
598 ret
= real_open(newpath
? newpath
: path
, flags
);
604 /* Nothing to override on non-__linux__ platforms */