criu/test/zdtm/static/cgroupns.c
Tycho Andersen fd972a739c tests: fix cgroupns test to not use nested namespaces
Before we were unshare(CLONE_NEWCGROUP)ing in a child task, which meant
that we couldn't c/r this test once we forbid nested cgroup namespaces.

Instead, use a new strategy for testing cgroup namespaces: set up the
namespace before forking the test task so there is no nesting, and then do
a setns back to init's ns to check the cgroup namespace of the test. This
doesn't work in the 'ns' flavor because init in the test's pid ns is the
test itself. There is a bit of a chicken and egg problem here, though,
because if we set it up after test_init(), we can't unshare because that
would be a nested cgroup ns.

Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
2016-03-03 14:00:13 +03:00

214 lines
3.9 KiB
C

#define _GNU_SOURCE
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <limits.h>
#include "zdtmtst.h"
#ifndef CLONE_NEWCGROUP
#define CLONE_NEWCGROUP 0x02000000
#endif
const char *test_doc = "Check that cgroup NS is correctly handled.";
const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
/* we need dirname before test_init() here */
char *dirname = "cgroupns.test";
static const char *cgname = "zdtmtst";
int mount_and_add(const char *controller, const char *path)
{
char aux[1024], paux[1024], subdir[1024];
int cgfd, l;
if (mkdir(dirname, 0700) < 0 && errno != EEXIST) {
pr_perror("Can't make dir");
return -1;
}
sprintf(subdir, "%s/%s", dirname, controller);
if (mkdir(subdir, 0700) < 0) {
pr_perror("Can't make dir");
return -1;
}
sprintf(aux, "none,name=%s", controller);
if (mount("none", subdir, "cgroup", 0, aux)) {
pr_perror("Can't mount cgroups");
goto err_rd;
}
sprintf(paux, "%s/%s", subdir, path);
mkdir(paux, 0600);
l = sprintf(aux, "%d", getpid());
sprintf(paux, "%s/%s/tasks", subdir, path);
cgfd = open(paux, O_WRONLY);
if (cgfd < 0) {
pr_perror("Can't open tasks");
goto err_rs;
}
l = write(cgfd, aux, l);
close(cgfd);
if (l < 0) {
pr_perror("Can't move self to subcg");
goto err_rs;
}
return 0;
err_rs:
umount(dirname);
err_rd:
rmdir(dirname);
return -1;
}
static bool pid_in_cgroup(pid_t pid, const char *controller, const char *path) {
char buf[2048];
FILE *f;
bool ret = false;
sprintf(buf, "/proc/%d/cgroup", pid);
f = fopen(buf, "r");
if (!f) {
pr_perror("fopen");
return false;
}
while (NULL != fgets(buf, sizeof(buf), f)) {
char *pos, *pid_controller, *pid_path;
/* chop off trailing \n */
buf[strlen(buf)-1] = '\0';
/* skip heirarchy no. */
pos = strstr(buf, ":");
if (!pos) {
pr_err("invalid /proc/pid/cgroups file");
goto out;
}
pos++;
pid_controller = pos;
pos = strstr(pos, ":");
if (!pos) {
pr_err("invalid /proc/pid/cgroups file");
goto out;
}
*pos = '\0';
pos++;
pid_path = pos;
if (strcmp(controller, pid_controller))
continue;
if (strcmp(path, pid_path))
pr_err("task not in right cg for controller %s expected %s, got %s\n", controller, path, pid_path);
else
ret = true;
goto out;
}
out:
fclose(f);
return ret;
}
int main(int argc, char **argv)
{
int ret = -1, fd, status;
char path[PATH_MAX];
pid_t pid;
if (!getenv("ZDTM_NEWNS")) {
if (mount_and_add(cgname, "test") < 0)
return -1;
if (unshare(CLONE_NEWCGROUP) < 0) {
pr_perror("unshare");
goto out;
}
}
test_init(argc, argv);
test_daemon();
test_waitsig();
sprintf(path, "name=%s", cgname);
/* first check that the task is in zdtmtst:/ */
if (!pid_in_cgroup(getpid(), path, "/")) {
fail("pid not in cgroup /");
goto out;
}
/* now check that the task is in the right place in a ns by setnsing to
* someone else's ns and looking there.
*/
pid = fork();
if (pid < 0) {
pr_perror("fork");
goto out;
}
if (pid == 0) {
sprintf(path, "/proc/%d/ns/cgroup", 1);
fd = open(path, O_RDONLY);
if (fd < 0) {
pr_perror("open");
exit(1);
}
ret = setns(fd, CLONE_NEWCGROUP);
close(fd);
if (ret < 0) {
pr_perror("setns");
exit(1);
}
sprintf(path, "name=%s", cgname);
if (!pid_in_cgroup(getppid(), path, "/test")) {
fail("pid not in cgroup %s", path);
exit(1);
}
exit(0);
}
if (pid != waitpid(pid, &status, 0)) {
pr_err("wrong pid");
goto out;
}
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
pr_err("got bad exit status %d\n", status);
goto out;
}
ret = 0;
pass();
out:
sprintf(path, "%s/%s/test", dirname, cgname);
rmdir(path);
sprintf(path, "%s/%s", dirname, cgname);
umount(path);
rmdir(path);
rmdir(dirname);
return ret;
}