blob: b9e8e6eeed82db89340ca404a567f496a195d3ed [file] [log] [blame]
/* Copyright 2016 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include <gtest/gtest.h>
#include "libcontainer/cgroup.h"
namespace libcontainer {
namespace {
constexpr char kCgroupName[] = "testcg";
constexpr char kCgroupParentName[] = "testparentcg";
bool CreateFile(const base::FilePath& path) {
return base::WriteFile(path, "", 0) == 0;
}
bool FileHasString(const base::FilePath& path, const std::string& expected) {
std::string contents;
if (!base::ReadFileToString(path, &contents))
return false;
return contents.find(expected) != std::string::npos;
}
bool FileHasLine(const base::FilePath& path, const std::string& expected) {
std::string contents;
if (!base::ReadFileToString(path, &contents))
return false;
for (const auto& line : base::SplitString(
contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
if (line == expected)
return true;
}
return false;
}
} // namespace
TEST(CgroupTest, CgroupNewWithParent) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath cgroup_root = temp_dir.GetPath();
for (const char* subsystem :
{"cpu", "cpuacct", "cpuset", "devices", "freezer", "schedtune"}) {
base::FilePath path = cgroup_root.Append(subsystem);
EXPECT_EQ(0, mkdir(path.value().c_str(), S_IRWXU | S_IRWXG));
path = path.Append(kCgroupParentName);
EXPECT_EQ(0, mkdir(path.value().c_str(), S_IRWXU | S_IRWXG));
}
ASSERT_TRUE(base::WriteFile(
cgroup_root.Append("cpuset").Append(kCgroupParentName).Append("cpus"),
"0-3", 3));
ASSERT_TRUE(base::WriteFile(
cgroup_root.Append("cpuset").Append(kCgroupParentName).Append("mems"),
"0", 1));
std::unique_ptr<libcontainer::Cgroup> ccg = libcontainer::Cgroup::Create(
kCgroupName, cgroup_root, base::FilePath(kCgroupParentName), getuid(),
getgid());
ASSERT_NE(nullptr, ccg.get());
EXPECT_TRUE(base::DirectoryExists(
cgroup_root.Append("cpu").Append(kCgroupParentName).Append(kCgroupName)));
EXPECT_TRUE(base::DirectoryExists(cgroup_root.Append("cpuacct")
.Append(kCgroupParentName)
.Append(kCgroupName)));
EXPECT_TRUE(base::DirectoryExists(cgroup_root.Append("cpuset")
.Append(kCgroupParentName)
.Append(kCgroupName)));
EXPECT_TRUE(base::DirectoryExists(cgroup_root.Append("devices")
.Append(kCgroupParentName)
.Append(kCgroupName)));
EXPECT_TRUE(base::DirectoryExists(cgroup_root.Append("freezer")
.Append(kCgroupParentName)
.Append(kCgroupName)));
EXPECT_TRUE(base::DirectoryExists(cgroup_root.Append("schedtune")
.Append(kCgroupParentName)
.Append(kCgroupName)));
EXPECT_TRUE(temp_dir.Delete());
}
class BasicCgroupManipulationTest : public ::testing::Test {
public:
BasicCgroupManipulationTest() = default;
~BasicCgroupManipulationTest() override = default;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath cgroup_root;
ASSERT_TRUE(base::CreateTemporaryDirInDir(temp_dir_.GetPath(), "cgtest",
&cgroup_root));
for (const char* subsystem :
{"cpu", "cpuacct", "cpuset", "devices", "freezer", "schedtune"}) {
base::FilePath path = cgroup_root.Append(subsystem);
ASSERT_EQ(0, mkdir(path.value().c_str(), S_IRWXU | S_IRWXG));
}
ASSERT_TRUE(base::WriteFile(cgroup_root.Append("cpuset/cpus"), "0-3", 3));
ASSERT_TRUE(base::WriteFile(cgroup_root.Append("cpuset/mems"), "0", 1));
ccg_ = libcontainer::Cgroup::Create(kCgroupName, cgroup_root,
base::FilePath(), 0, 0);
ASSERT_NE(nullptr, ccg_.get());
cpu_cg_ = cgroup_root.Append("cpu").Append(kCgroupName);
cpuacct_cg_ = cgroup_root.Append("cpuacct").Append(kCgroupName);
cpuset_cg_ = cgroup_root.Append("cpuset").Append(kCgroupName);
devices_cg_ = cgroup_root.Append("devices").Append(kCgroupName);
freezer_cg_ = cgroup_root.Append("freezer").Append(kCgroupName);
schedtune_cg_ = cgroup_root.Append("schedtune").Append(kCgroupName);
ASSERT_TRUE(base::DirectoryExists(cpu_cg_));
ASSERT_TRUE(base::DirectoryExists(cpuacct_cg_));
ASSERT_TRUE(base::DirectoryExists(cpuset_cg_));
ASSERT_TRUE(base::DirectoryExists(devices_cg_));
ASSERT_TRUE(base::DirectoryExists(freezer_cg_));
ASSERT_TRUE(base::DirectoryExists(schedtune_cg_));
ASSERT_TRUE(CreateFile(cpu_cg_.Append("tasks")));
ASSERT_TRUE(CreateFile(cpu_cg_.Append("cpu.shares")));
ASSERT_TRUE(CreateFile(cpu_cg_.Append("cpu.cfs_quota_us")));
ASSERT_TRUE(CreateFile(cpu_cg_.Append("cpu.cfs_period_us")));
ASSERT_TRUE(CreateFile(cpu_cg_.Append("cpu.rt_runtime_us")));
ASSERT_TRUE(CreateFile(cpu_cg_.Append("cpu.rt_period_us")));
ASSERT_TRUE(CreateFile(cpuacct_cg_.Append("tasks")));
ASSERT_TRUE(CreateFile(cpuset_cg_.Append("tasks")));
ASSERT_TRUE(CreateFile(devices_cg_.Append("tasks")));
ASSERT_TRUE(CreateFile(devices_cg_.Append("devices.allow")));
ASSERT_TRUE(CreateFile(devices_cg_.Append("devices.deny")));
ASSERT_TRUE(CreateFile(freezer_cg_.Append("tasks")));
ASSERT_TRUE(CreateFile(freezer_cg_.Append("freezer.state")));
ASSERT_TRUE(CreateFile(schedtune_cg_.Append("tasks")));
}
void TearDown() override {
ccg_.reset();
ASSERT_TRUE(temp_dir_.Delete());
}
protected:
std::unique_ptr<libcontainer::Cgroup> ccg_;
base::FilePath cpu_cg_;
base::FilePath cpuacct_cg_;
base::FilePath cpuset_cg_;
base::FilePath devices_cg_;
base::FilePath freezer_cg_;
base::FilePath schedtune_cg_;
base::ScopedTempDir temp_dir_;
};
TEST_F(BasicCgroupManipulationTest, freeze) {
EXPECT_TRUE(ccg_->Freeze());
EXPECT_TRUE(FileHasString(freezer_cg_.Append("freezer.state"), "FROZEN"));
}
TEST_F(BasicCgroupManipulationTest, thaw) {
EXPECT_TRUE(ccg_->Thaw());
EXPECT_TRUE(FileHasString(freezer_cg_.Append("freezer.state"), "THAWED"));
}
TEST_F(BasicCgroupManipulationTest, default_all_devs_disallow) {
ASSERT_TRUE(ccg_->DenyAllDevices());
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.deny"), "a"));
}
TEST_F(BasicCgroupManipulationTest, add_device_invalid_type) {
EXPECT_FALSE(ccg_->AddDevice(1, 14, 3, 1, 1, 0, 'x'));
}
TEST_F(BasicCgroupManipulationTest, add_device_no_perms) {
EXPECT_FALSE(ccg_->AddDevice(1, 14, 3, 0, 0, 0, 'c'));
}
TEST_F(BasicCgroupManipulationTest, add_device_rw) {
EXPECT_TRUE(ccg_->AddDevice(1, 14, 3, 1, 1, 0, 'c'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "c 14:3 rw"));
}
TEST_F(BasicCgroupManipulationTest, add_device_rwm) {
EXPECT_TRUE(ccg_->AddDevice(1, 14, 3, 1, 1, 1, 'c'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "c 14:3 rwm"));
}
TEST_F(BasicCgroupManipulationTest, add_device_ro) {
EXPECT_TRUE(ccg_->AddDevice(1, 14, 3, 1, 0, 0, 'c'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "c 14:3 r"));
}
TEST_F(BasicCgroupManipulationTest, add_device_wo) {
EXPECT_TRUE(ccg_->AddDevice(1, 14, 3, 0, 1, 0, 'c'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "c 14:3 w"));
}
TEST_F(BasicCgroupManipulationTest, add_device_major_wide) {
EXPECT_TRUE(ccg_->AddDevice(1, 14, -1, 0, 1, 0, 'c'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "c 14:* w"));
}
TEST_F(BasicCgroupManipulationTest, add_device_major_minor_wildcard) {
EXPECT_TRUE(ccg_->AddDevice(1, -1, -1, 0, 1, 0, 'c'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "c *:* w"));
}
TEST_F(BasicCgroupManipulationTest, add_device_deny_all) {
EXPECT_TRUE(ccg_->AddDevice(0, -1, -1, 1, 1, 1, 'a'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.deny"), "a *:* rwm"));
}
TEST_F(BasicCgroupManipulationTest, add_device_block) {
EXPECT_TRUE(ccg_->AddDevice(1, 14, 3, 1, 1, 0, 'b'));
EXPECT_TRUE(FileHasLine(devices_cg_.Append("devices.allow"), "b 14:3 rw"));
}
TEST_F(BasicCgroupManipulationTest, set_cpu_shares) {
EXPECT_TRUE(ccg_->SetCpuShares(500));
EXPECT_TRUE(FileHasString(cpu_cg_.Append("cpu.shares"), "500"));
}
TEST_F(BasicCgroupManipulationTest, set_cpu_quota) {
EXPECT_TRUE(ccg_->SetCpuQuota(200000));
EXPECT_TRUE(FileHasString(cpu_cg_.Append("cpu.cfs_quota_us"), "200000"));
}
TEST_F(BasicCgroupManipulationTest, set_cpu_period) {
EXPECT_TRUE(ccg_->SetCpuPeriod(800000));
EXPECT_TRUE(FileHasString(cpu_cg_.Append("cpu.cfs_period_us"), "800000"));
}
TEST_F(BasicCgroupManipulationTest, set_cpu_rt_runtime) {
EXPECT_TRUE(ccg_->SetCpuRtRuntime(100000));
EXPECT_TRUE(FileHasString(cpu_cg_.Append("cpu.rt_runtime_us"), "100000"));
}
TEST_F(BasicCgroupManipulationTest, set_cpu_rt_period) {
EXPECT_TRUE(ccg_->SetCpuRtPeriod(500000));
EXPECT_TRUE(FileHasString(cpu_cg_.Append("cpu.rt_period_us"), "500000"));
}
TEST_F(BasicCgroupManipulationTest, OpenCgroupFileRefusesToWriteToSymlink) {
base::FilePath cpu_rt_period_us_path = cpu_cg_.Append("cpu.rt_period_us");
base::FilePath target_path = cpu_cg_.Append("symlink_target");
ASSERT_TRUE(base::DeleteFile(cpu_rt_period_us_path));
ASSERT_TRUE(base::CreateSymbolicLink(target_path, cpu_rt_period_us_path));
// This should fail since we are trying to write to a symlink.
EXPECT_FALSE(ccg_->SetCpuRtPeriod(500000));
}
TEST_F(BasicCgroupManipulationTest, OpenCgroupFileRefusesToWriteToNonOpenFIFO) {
base::FilePath cpu_rt_period_us_path = cpu_cg_.Append("cpu.rt_period_us");
ASSERT_TRUE(base::DeleteFile(cpu_rt_period_us_path));
ASSERT_NE(mkfifo(cpu_rt_period_us_path.value().c_str(), 0664), -1);
// This should fail since we are trying to write to a FIFO.
EXPECT_FALSE(ccg_->SetCpuRtPeriod(500000));
}
TEST_F(BasicCgroupManipulationTest, OpenCgroupFileRefusesToWriteToOpenFIFO) {
base::FilePath cpu_rt_period_us_path = cpu_cg_.Append("cpu.rt_period_us");
ASSERT_TRUE(base::DeleteFile(cpu_rt_period_us_path));
ASSERT_NE(mkfifo(cpu_rt_period_us_path.value().c_str(), 0664), -1);
base::ScopedFD fd(
open(cpu_rt_period_us_path.value().c_str(), O_RDONLY | O_NONBLOCK));
ASSERT_TRUE(fd.is_valid());
// This should fail since we are trying to write to a FIFO.
EXPECT_FALSE(ccg_->SetCpuRtPeriod(500000));
}
} // namespace libcontainer