| /* 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, false)); |
| 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, false)); |
| 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, false)); |
| 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 |