std::string readFileStream(const std::string& path)
{
- std::ifstream is(path);
- return std::string(std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>());
+ std::ifstream file(path);
+
+ if (!file) {
+ throw UtilsException("Read failed");
+ }
+ // 2 x faster then std::istreambuf_iterator
+ std::stringstream content;
+ content << file.rdbuf();
+ return content.str();
+}
+
+bool readFileStream(const std::string& path, std::string& result)
+{
+ std::ifstream file(path);
+
+ if (!file) {
+ return false;
+ }
+ std::stringstream content;
+ content << file.rdbuf();
+ result = content.str();
+ return true;
}
std::string readFileContent(const std::string& path)
{
std::string result;
+
if (!readFileContent(path, result)) {
throw UtilsException("Read failed");
}
std::string readFileStream(const std::string& path);
/**
+ * Reads the content of file stream (no seek)
+ */
+bool readFileStream(const std::string& path, std::string& result);
+
+/**
* Reads the content of a file (performs seek); Throws exception on error
*/
std::string readFileContent(const std::string& path);
--- /dev/null
+/*
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * @author Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief Control-groups configuration
+ */
+
+#ifndef LXCPP_CGROUPS_CGROUP_CONFIG_HPP
+#define LXCPP_CGROUPS_CGROUP_CONFIG_HPP
+
+#include "config/config.hpp"
+#include "config/fields.hpp"
+
+#include <string>
+#include <vector>
+
+namespace lxcpp {
+
+struct SubsystemConfig {
+ std::string name;
+ std::string path;
+
+ CONFIG_REGISTER
+ (
+ name,
+ path
+ )
+};
+
+struct CGroupParam {
+ std::string name;
+ std::string value;
+
+ CONFIG_REGISTER
+ (
+ name,
+ value
+ )
+};
+
+struct CGroupConfig {
+ std::string subsystem;
+ std::string name;
+ std::vector<CGroupParam> common; // cgroup.*
+ std::vector<CGroupParam> params; // {name}.*
+
+ CONFIG_REGISTER
+ (
+ subsystem,
+ name,
+ common,
+ params
+ )
+};
+
+struct CGroupsConfig {
+ std::vector<SubsystemConfig> subsystems;
+ std::vector<CGroupConfig> cgroups;
+
+ CONFIG_REGISTER
+ (
+ subsystems,
+ cgroups
+ )
+};
+
+} // namespace lxcpp
+
+#endif //LXCPP_CGROUPS_CGROUP_CONFIG_HPP
#include "lxcpp/cgroups/cgroup.hpp"
#include "lxcpp/exception.hpp"
+#include "utils/exception.hpp"
#include "utils/fs.hpp"
#include "utils/text.hpp"
#include "logger/logger.hpp"
std::string getSubsysName(const std::string& s)
{
auto p = s.find(':');
+
if (p == std::string::npos) {
const std::string msg = "wrong subsys format " + s;
LOGE(msg);
std::string getCGroupName(const std::string& s)
{
auto p = s.find(':');
+
if (p == std::string::npos) {
const std::string msg = "wrong cgroup format " + s;
LOGE(msg);
bool CGroup::exists() const
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName;
+
return fs::is_directory(path);
}
void CGroup::setCommonValue(const std::string& param, const std::string& value)
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / ("cgroup." + param);
+
if (!utils::saveFileContent(path.string(), value)) {
const std::string msg = "Invalid param " + param;
LOGE(msg);
std::string CGroup::getCommonValue(const std::string& param) const
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / ("cgroup." + param);
- return utils::readFileStream(path.string());
+
+ try {
+ return utils::readFileStream(path.string());
+ } catch (const utils::UtilsException& e) {
+ const std::string msg = "Invalid param " + param;
+ LOGE(msg);
+ throw CGroupException(msg);
+ }
}
void CGroup::setValue(const std::string& param, const std::string& value)
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / (mSubsys.getName() + "." + param);
+
if (!utils::saveFileContent(path.string(), value)) {
const std::string msg = "Invalid param " + param;
LOGE(msg);
std::string CGroup::getValue(const std::string& param) const
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / (mSubsys.getName() + "." + param);
- return utils::readFileStream(path.string());
+
+ try {
+ return utils::readFileStream(path.string());
+ } catch (const utils::UtilsException& e) {
+ const std::string msg = "Invalid param " + param;
+ LOGE(msg);
+ throw CGroupException(msg);
+ }
}
void CGroup::assignGroup(pid_t pid)
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / "tasks";
std::ifstream fileStream(path.string());
+
if (!fileStream.good()) {
const std::string msg = "Failed to open " + path.string();
LOGE(msg);
CGroup CGroup::getCGroup(const std::string& subsys, pid_t pid)
{
std::vector<std::string> cgroups = Subsystem::getCGroups(pid);
+
for (const auto& i : cgroups) {
if (utils::beginsWith(i, subsys + ":")) {
return CGroup(i);
public:
/**
- * Define control-group object
+ * Define control-group object.
+ * Name can be also considered as path relative to root of subsystem.
*/
CGroup(const std::string& subsys, const std::string& name) :
mSubsys(subsys),
}
/**
- * Define control-group object (format "subsys:cgroup_path")
+ * Define control-group object (format "subsys:cgroup_path").
*/
CGroup(const std::string& subsysAndCgroup);
/**
- * Check if cgroup exists
+ * Check if cgroup exists.
* @return true if cgroup path (subsys.path / mName) exists
*/
bool exists() const;
/**
- * Create cgroup directory
+ * Create cgroup directory.
* Equivalent of: mkdir subsys.path / mName
*/
void create();
/**
* Destroy cgroup directory
* Equivalent of: rmdir subsys.path / mName
- * Note: set memory.force_empty before removing a cgroup to avoid moving out-of-use page caches to parent
+ * Note: set memory.force_empty before removing a cgroup to avoid moving out-of-use page caches to parent.
*/
void destroy();
/**
- * Set common 'cgroup' paramter
+ * Set common 'cgroup' parameter.
* Equivalent of: echo value > mSubsys_path/mName/cgroup.param
*/
void setCommonValue(const std::string& param, const std::string& value);
/**
- * Get common 'cgroup' paramter
+ * Get common 'cgroup' paramter.
* Equivalent of: cat mSubsys_path/mName/cgroup.param
*/
std::string getCommonValue(const std::string& param) const;
/**
- * Set cgroup parameter to value (name validity depends on subsystem)
+ * Set cgroup parameter to value (name validity depends on subsystem).
* Equivalent of: echo value > mSubsys_path/mName/mSubsys_name.param
*/
void setValue(const std::string& param, const std::string& value);
/**
- * Get cgroup parameter
+ * Get cgroup parameter.
* Equivalent of: cat mSubsys_path/mName/mSubsys_name.param
*/
std::string getValue(const std::string& param) const;
/**
- * Assign all processes in threadgroup of pid to this cgroup
+ * Assign all processes in threadgroup of pid to this cgroup.
* Equivalent of: echo pid > mSubsys_path/mName/cgroup.procs
*/
void assignGroup(pid_t pid);
/**
- * Assign single process to this cgroup (will be removed from previous cgroup automatically)
+ * Assign single process to this cgroup (will be removed from previous cgroup automatically).
* Equivalent of: echo pid > mSubsys_path/mName/tasks
*/
void assignPid(pid_t pid);
/**
- * Get list of pid assigned to this group
+ * Get list of pid assigned to this group.
* Equivalent of: cat mSubsys_path/mName/tasks
*/
std::vector<pid_t> getPids() const;
/**
- * Get cgroup of process pid in given subsystem
+ * Get cgroup of process pid in given subsystem.
*/
static CGroup getCGroup(const std::string& subsys, pid_t pid);
--- /dev/null
+/*
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * @author Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief CGroups configuration command
+ */
+
+#include "lxcpp/commands/cgroups.hpp"
+#include "lxcpp/cgroups/cgroup.hpp"
+#include "lxcpp/exception.hpp"
+#include "logger/logger.hpp"
+
+namespace lxcpp {
+
+void CGroupMakeAll::execute()
+{
+ for (const auto i : mCgroups.subsystems) {
+ SubsystemMake(i).execute();
+ }
+
+ for (const auto i : mCgroups.cgroups) {
+ CGroupMake(i).execute();
+ }
+}
+
+void SubsystemMake::execute()
+{
+ Subsystem sub(mSubsys.name);
+
+ if (!sub.isAttached()) {
+ sub.attach(mSubsys.path, {mSubsys.name});
+ } else if (sub.getMountPoint() != mSubsys.path) {
+ std::string msg = "Subsys " + mSubsys.name + " already mounted elsewhere";
+ LOGW(msg);
+ // no exception, usually subsystems are mounted
+ }
+}
+
+void CGroupMake::execute()
+{
+ CGroup cgroup(mCgroup.subsystem, mCgroup.name);
+
+ if (!cgroup.exists()) {
+ cgroup.create();
+ }
+
+ for (const auto p : mCgroup.common) {
+ cgroup.setCommonValue(p.name, p.value);
+ }
+
+ for (const auto p : mCgroup.params) {
+ cgroup.setValue(p.name, p.value);
+ }
+}
+
+void CGroupAssignPid::execute()
+{
+ CGroup cgroup(mSubsysName, mCgroupName);
+
+ cgroup.assignPid(mPid);
+}
+
+} // namespace lxcpp
--- /dev/null
+/*
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * @author Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief CGroups configuration command
+ */
+
+#ifndef LXCPP_COMMANDS_CGROUPS_HPP
+#define LXCPP_COMMANDS_CGROUPS_HPP
+
+#include "lxcpp/commands/command.hpp"
+#include "lxcpp/cgroups/cgroup-config.hpp"
+
+
+namespace lxcpp {
+
+class CGroupMakeAll final: Command {
+public:
+ /**
+ * Creates and configures cgroups.
+ */
+ CGroupMakeAll(const CGroupsConfig& cfg) :
+ mCgroups(cfg)
+ {
+ }
+
+ void execute();
+
+private:
+ const CGroupsConfig& mCgroups;
+};
+
+class SubsystemMake final: Command {
+public:
+ /**
+ * Creates and configures cgroup subsystem.
+ */
+ SubsystemMake(const SubsystemConfig& cfg) :
+ mSubsys(cfg)
+ {
+ }
+
+ void execute();
+
+private:
+ const SubsystemConfig& mSubsys;
+};
+
+class CGroupMake final: Command {
+public:
+ /**
+ * Creates and configures cgroup.
+ */
+ CGroupMake(const CGroupConfig& cfg) :
+ mCgroup(cfg)
+ {
+ }
+
+ void execute();
+
+private:
+ const CGroupConfig& mCgroup;
+};
+
+class CGroupAssignPid final: Command {
+public:
+ /**
+ * Add pid to existng group.
+ */
+ CGroupAssignPid(const std::string& subsys, const std::string& cgroup, pid_t pid) :
+ mSubsysName(subsys),
+ mCgroupName(cgroup),
+ mPid(pid)
+ {
+ }
+
+ void execute();
+
+private:
+ std::string mSubsysName;
+ std::string mCgroupName;
+ pid_t mPid;
+};
+
+} // namespace lxcpp
+
+#endif //LXCPP_COMMANDS_CGROUPS_HPP
#include "lxcpp/terminal-config.hpp"
#include "lxcpp/provision-config.hpp"
#include "lxcpp/userns-config.hpp"
+#include "lxcpp/cgroups/cgroup-config.hpp"
#include <config/config.hpp>
#include <config/fields.hpp>
*/
UserNSConfig mUserNSConfig;
+ /*
+ * CGropus configuration
+ */
+ CGroupsConfig mCgroups;
+
ContainerConfig() : mGuardPid(-1), mInitPid(-1), mNamespaces(0) {}
CONFIG_REGISTER
}
}
+void ContainerImpl::addSubsystem(const std::string& name, const std::string& path)
+{
+ mConfig.mCgroups.subsystems.push_back(SubsystemConfig{name, path});
+}
+
+void ContainerImpl::addCGroup(const std::string& subsys,
+ const std::string& grpname,
+ const std::vector<CGroupParam>& comm,
+ const std::vector<CGroupParam>& params)
+{
+ mConfig.mCgroups.cgroups.push_back(CGroupConfig{subsys, grpname, comm, params});
+}
+
} // namespace lxcpp
const LinkVector& getLinks() const;
void removeLink(const provision::Link& item);
+ // CGroups
+ void addSubsystem(const std::string& name, const std::string& path);
+ void addCGroup(const std::string& subsys,
+ const std::string& grpname,
+ const std::vector<CGroupParam>& comm,
+ const std::vector<CGroupParam>& params);
+
private:
ContainerConfig mConfig;
};
#include "lxcpp/network-config.hpp"
#include "lxcpp/provision-config.hpp"
+#include "lxcpp/cgroups/cgroup-config.hpp"
#include "lxcpp/logger-config.hpp"
#include <sys/types.h>
const std::string& target) = 0;
virtual const LinkVector& getLinks() const = 0;
virtual void removeLink(const provision::Link& item) = 0;
+
+ // CGroups
+ virtual void addSubsystem(const std::string& name, const std::string& path) = 0;
+ virtual void addCGroup(const std::string& subsys,
+ const std::string& grpname,
+ const std::vector<CGroupParam>& comm,
+ const std::vector<CGroupParam>& params) = 0;
};
} // namespace lxcpp
*/
#include "config.hpp"
+#include "config/manager.hpp"
#include "ut.hpp"
#include "logger/logger.hpp"
#include "lxcpp/cgroups/devices.hpp"
+#include "lxcpp/cgroups/cgroup-config.hpp"
+
+#include "lxcpp/commands/cgroups.hpp"
+
#include "lxcpp/exception.hpp"
#include "utils/text.hpp"
BOOST_AUTO_TEST_CASE(GetAvailable)
{
std::vector<std::string> subs;
+
BOOST_CHECK_NO_THROW(subs = Subsystem::availableSubsystems());
BOOST_CHECK_MESSAGE(subs.size() > 0, "Control groups not supported");
for (auto n : subs){
BOOST_AUTO_TEST_CASE(GetCGroupsByPid)
{
std::vector<std::string> cg;
+
BOOST_CHECK_NO_THROW(cg=Subsystem::getCGroups(::getpid()));
BOOST_CHECK(cg.size() > 0);
}
{
CGroup cg = CGroup::getCGroup("memory", ::getpid());
std::vector<pid_t> pids;
+
BOOST_CHECK_NO_THROW(pids = cg.getPids());
BOOST_CHECK(pids.size() > 0);
}
BOOST_AUTO_TEST_CASE(SubsysAttach)
{
Subsystem sub("freezer");
+
BOOST_CHECK_MESSAGE(sub.isAvailable(), "freezer not found");
if (!sub.isAvailable()) return ;
BOOST_AUTO_TEST_CASE(ControlGroupParams)
{
CGroup memg("memory:/ut-params");
- BOOST_CHECK(memg.exists() == false);
+
+ // this test can fail if prev. test round left unexpected
+ BOOST_CHECK_MESSAGE(memg.exists() == false, "Cgroup alredy exists");
BOOST_CHECK_NO_THROW(memg.create());
BOOST_CHECK(memg.exists() == true);
- if (!memg.exists()) return ;
+ if (!memg.exists()) {
+ return ;
+ }
+
+ BOOST_CHECK_NO_THROW(memg.assignPid(::getpid()));
+ BOOST_CHECK_NO_THROW(memg.assignGroup(::getpid()));
- memg.assignPid(::getpid());
- memg.setValue("limit_in_bytes", "10k");
- memg.setValue("soft_limit_in_bytes", "10k");
+ BOOST_CHECK_NO_THROW(memg.setValue("limit_in_bytes", "10k"));
+ BOOST_CHECK_NO_THROW(memg.setValue("soft_limit_in_bytes", "10k"));
+ BOOST_CHECK_THROW(memg.getValue("non-existing-name"), CGroupException);
BOOST_CHECK_THROW(memg.setValue("non-existing-name", "xxx"), CGroupException);
LOGD("limit_in_bytes: " << memg.getValue("limit_in_bytes"));
LOGD("soft_limit_in_bytes: " << memg.getValue("soft_limit_in_bytes"));
LOGD("max_usage_in_bytes: " << memg.getValue("max_usage_in_bytes"));
- CGroup("memory:/").assignPid(::getpid());
+ CGroup memtop("memory:/");
+ memtop.assignPid(::getpid());
+ memtop.setCommonValue("procs", std::to_string(::getpid()));
+
BOOST_CHECK_NO_THROW(memg.destroy());
}
BOOST_AUTO_TEST_CASE(DevicesParams)
{
- DevicesCGroup devcg("/");
- std::vector<DevicePermission> list = devcg.list();
+ DevicesCGroup devgrp("/tmp");
+
+ BOOST_CHECK_NO_THROW(devgrp.create());
+
+ std::vector<DevicePermission> list;
+ BOOST_CHECK_NO_THROW(list = devgrp.list());
for (__attribute__((unused)) const auto& i : list) {
LOGD(std::string("perm = ") + i.type + " " +
std::to_string(i.major) + ":" + std::to_string(i.minor) + " " + i.permission);
}
+
+ BOOST_CHECK_NO_THROW(devgrp.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(CGroupConfigSerialization)
+{
+ CGroupsConfig cfg;
+ std::string tmpConfigFile = "/tmp/cgconfig.conf";
+
+ BOOST_CHECK_NO_THROW(config::saveToJsonString(cfg));
+
+ cfg.subsystems.push_back(SubsystemConfig{"cpu", "/tmp/cgroup/cpu"});
+
+ CGroupConfig cpucfg = {"cpu", "/testcpu", {}, {}};
+ cfg.cgroups.push_back(cpucfg);
+ config::saveToJsonFile(tmpConfigFile, cfg);
+
+ CGroupsConfig cfg2;
+ BOOST_CHECK_NO_THROW(config::loadFromJsonFile(tmpConfigFile, cfg2));
+ BOOST_CHECK(cfg2.subsystems.size()==cfg.subsystems.size());
+}
+
+BOOST_AUTO_TEST_CASE(CGroupCommands)
+{
+ CGroupsConfig cfg;
+ Subsystem sub("cpu");
+ std::string mp = sub.isAttached() ? sub.getMountPoint() : "/tmp/cgroup/cpu";
+
+ cfg.subsystems.push_back(SubsystemConfig{"cpu", mp});
+ CGroupConfig cpucfg = {"cpu", "/testcpu", {}, {}};
+ cfg.cgroups.push_back(cpucfg);
+
+ CGroupMakeAll cmd(cfg);
+ BOOST_CHECK_NO_THROW(cmd.execute());
+
+ CGroup cpugrp("cpu", "/testcpu");
+ BOOST_CHECK_NO_THROW(cpugrp.destroy());
}
BOOST_AUTO_TEST_SUITE_END()
config::saveToJsonFile(tmpConfigFile, cfg);
NetworkConfig cfg2;
- config::loadFromJsonFile(tmpConfigFile, cfg2);
+ BOOST_CHECK_NO_THROW(config::loadFromJsonFile(tmpConfigFile, cfg2));
int ifnum = cfg.getInterfaces().size();
for (int i = 0; i < ifnum; ++i) {