namespace utils {
+std::string readFileStream(const std::string& path)
+{
+ std::ifstream is(path);
+ return std::string(std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>());
+}
std::string readFileContent(const std::string& path)
{
namespace utils {
/**
- * Reads the content of a file; Throws exception on error
+ * Reads the content of file stream (no seek); Throws exception on error
+ */
+std::string readFileStream(const std::string& path);
+
+/**
+ * Reads the content of a file (performs seek); Throws exception on error
*/
std::string readFileContent(const std::string& path);
*/
#include "utils/text.hpp"
-#include <sstream>
namespace utils {
namespace {
return s;
}
-std::string join(const std::vector<std::string>& vec, const char *delim)
-{
- std::stringstream res;
- for (const auto& s : vec) {
- if (res.tellp() > 0) {
- res << delim;
- }
- res << s;
- }
- return res.str();
-}
-
std::vector<std::string> split(const std::string& str, const std::string& delim)
{
std::vector<std::string> tokens;
#include <string>
#include <vector>
+#include <sstream>
namespace utils {
*/
std::string toHexString(const void *data, unsigned len);
-std::string join(const std::vector<std::string>& vec, const char *delim);
+template<typename T>
+std::string join(const std::vector<T>& vec, const char *delim)
+{
+ std::stringstream res;
+ for (const auto& s : vec) {
+ if (res.tellp()>0) {
+ res << delim;
+ }
+ res << s;
+ }
+ return res.str();
+}
+
std::vector<std::string> split(const std::string& str, const std::string& delim);
} // namespace utils
#include "lxcpp/cgroups/cgroup.hpp"
#include "lxcpp/exception.hpp"
#include "utils/fs.hpp"
+#include "utils/text.hpp"
#include "logger/logger.hpp"
namespace fs = boost::filesystem;
namespace lxcpp {
namespace {
-std::string getSubsysName(const std::string& s) {
+std::string getSubsysName(const std::string& s)
+{
auto p = s.find(':');
if (p == std::string::npos) {
- const std::string msg = "wgrong cgroup format";
+ const std::string msg = "wrong subsys format " + s;
LOGE(msg);
throw CGroupException(msg);
}
return s.substr(0, p);
}
-std::string getCGroupName(const std::string& s) {
+
+std::string getCGroupName(const std::string& s)
+{
auto p = s.find(':');
if (p == std::string::npos) {
- const std::string msg = "wgrong cgroup format";
+ const std::string msg = "wrong cgroup format " + s;
LOGE(msg);
throw CGroupException(msg);
}
return s.substr(p + 1);
}
+
} // namespace
CGroup::CGroup(const std::string& subsysAndCgroup) :
void CGroup::destroy()
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName;
- fs::remove_all(path);
+ //remove_all is not good for cgroup filesystem
+ fs::remove(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);
+ throw CGroupException(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());
}
void CGroup::setValue(const std::string& param, const std::string& value)
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / (mSubsys.getName() + "." + param);
- utils::saveFileContent(path.string(), value);
+ if (!utils::saveFileContent(path.string(), value)) {
+ const std::string msg = "Invalid param " + param;
+ LOGE(msg);
+ throw CGroupException(msg);
+ }
}
std::string CGroup::getValue(const std::string& param) const
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / (mSubsys.getName() + "." + param);
- return utils::readFileContent(path.string());
+ return utils::readFileStream(path.string());
+}
+
+void CGroup::assignGroup(pid_t pid)
+{
+ setCommonValue("procs", std::to_string(pid));
}
-void CGroup::assignProcess(pid_t pid)
+void CGroup::assignPid(pid_t pid)
{
const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / "tasks";
utils::saveFileContent(path.string(), std::to_string(pid));
}
+std::vector<pid_t> CGroup::getPids() const
+{
+ 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);
+ throw CGroupException(msg);
+ }
+
+ std::vector<pid_t> pids;
+ while (fileStream.good()) {
+ int pid;
+ fileStream >> pid;
+ pids.push_back(pid);
+ }
+
+ return pids;
+}
+
+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);
+ }
+ }
+
+ const std::string msg = "cgroup not found for pid " + std::to_string(pid);
+ LOGE(msg);
+ throw CGroupException(msg);
+}
+
} //namespace lxcpp
void destroy();
/**
+ * Set common 'cgroup' paramter
+ * Equivalent of: echo value > mSubsys_path/mName/cgroup.param
+ */
+ void setCommonValue(const std::string& param, const std::string& value);
+
+ /**
+ * 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)
* Equivalent of: echo value > mSubsys_path/mName/mSubsys_name.param
*/
std::string getValue(const std::string& param) const;
/**
- * Assign process to this cgroup (will be removed from previous cgroup automatically)
+ * 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)
* Equivalent of: echo pid > mSubsys_path/mName/tasks
*/
- void assignProcess(pid_t pid);
+ void assignPid(pid_t pid);
+
+ /**
+ * 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
+ */
+ static CGroup getCGroup(const std::string& subsys, pid_t pid);
private:
const Subsystem mSubsys; // referred subsystem
--- /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 management, devices
+ */
+
+#include "lxcpp/cgroups/devices.hpp"
+#include "lxcpp/exception.hpp"
+#include "utils/text.hpp"
+#include "logger/logger.hpp"
+
+#include <regex>
+
+namespace lxcpp {
+
+namespace {
+std::string devString(int n)
+{
+ return n >= -1 ? std::to_string(n) : "*";
+}
+
+DevicePermission& parsePerms(DevicePermission& p,const std::string& line)
+{
+ std::string re = "^([a-z]) ([0-9]+|\\*):([0-9]+|\\*) ([a-z]+)$";
+ std::smatch match;
+ try {
+ std::regex rgx(re);
+ if (!std::regex_search(line, match, rgx)) {
+ throw CGroupException("wrong input: " + line);
+ }
+ } catch (CGroupException) {
+ throw;
+ } catch (std::runtime_error e) {
+ throw std::runtime_error(e.what() + std::string(" update your c++ libs"));
+ }
+
+ p.type = match.str(1).at(0);
+ p.major = match.str(2) == "*" ? -1 : std::stoi(match.str(2));
+ p.minor = match.str(3) == "*" ? -1 : std::stoi(match.str(3));
+ p.permission = match.str(4);
+ return p;
+}
+
+} //namespace
+
+void DevicesCGroup::allow(DevicePermission p)
+{
+ allow(p.type, p.major, p.minor, p.permission);
+}
+
+void DevicesCGroup::deny(DevicePermission p)
+{
+ deny(p.type, p.major, p.minor, p.permission);
+}
+
+void DevicesCGroup::allow(char type, int major, int minor, const std::string& perm)
+{
+ setValue("allow", type + std::string(" ") + devString(major) + ":" + devString(minor) + " " + perm);
+}
+
+void DevicesCGroup::deny(char type, int major, int minor, const std::string& perm)
+{
+ setValue("deny", type + std::string(" ") + devString(major) + ":" + devString(minor) + " " + perm);
+}
+
+std::vector<DevicePermission> DevicesCGroup::list()
+{
+ std::vector<DevicePermission> list;
+ DevicePermission p;
+ for (const auto& ln : utils::split(getValue("list"), "\n")) {
+ if (!ln.empty()) {
+ list.push_back(parsePerms(p, ln));
+ }
+ }
+ return list;
+}
+
+} //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 Control-groups management, devices
+ */
+
+#ifndef LXCPP_CGROUPS_DEVICES_HPP
+#define LXCPP_CGROUPS_DEVICES_HPP
+
+#include "lxcpp/cgroups/cgroup.hpp"
+
+namespace lxcpp {
+
+struct DevicePermission {
+ char type; // 'a'==any, 'b'==block, 'c'==character
+ int major,minor; // -1==any
+ std::string permission; // combination of "rwm" (r==read,w==write,m==create)
+};
+
+class DevicesCGroup : public CGroup {
+public:
+ DevicesCGroup(const std::string& name) :
+ CGroup("devices", name)
+ {
+ }
+
+ void allow(DevicePermission p);
+ void deny(DevicePermission p);
+ void allow(char type, int major, int minor, const std::string& perm);
+ void deny(char type, int major, int minor, const std::string& perm);
+ std::vector<DevicePermission> list();
+};
+
+} //namespace lxcpp
+
+#endif //LXCPP_CGROUPS_DEVICE_HPP
throw CGroupException(msg);
}
- std::vector<std::string> av = availableSubsystems();
//find mount point for a name
std::ifstream fileStream("/proc/mounts");
if (!fileStream.good()) {
#include "ut.hpp"
#include "logger/logger.hpp"
-#include "lxcpp/cgroups/subsystem.hpp"
+#include "lxcpp/cgroups/devices.hpp"
+#include "lxcpp/exception.hpp"
+#include "utils/text.hpp"
namespace {
{
std::vector<std::string> subs;
BOOST_CHECK_NO_THROW(subs = Subsystem::availableSubsystems());
- BOOST_CHECK(subs.size() > 0);
+ BOOST_CHECK_MESSAGE(subs.size() > 0, "Control groups not supported");
for (auto n : subs){
Subsystem s(n);
- LOGD(s.getName() << ": " << (s.isAttached()?s.getMountPoint():"[not attached]"));
+ LOGD(s.getName() << ": " << (s.isAttached() ? s.getMountPoint() : "[not attached]"));
}
}
BOOST_CHECK(cg.size() > 0);
}
+BOOST_AUTO_TEST_CASE(GetPidsByCGroup)
+{
+ 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.getName() == "freezer", "freezer not equal");
BOOST_CHECK_MESSAGE(sub.isAvailable(), "freezer not found");
if (!sub.isAvailable()) return ;
-
if (sub.isAttached()) {
std::string mp = sub.getMountPoint();
BOOST_CHECK_NO_THROW(Subsystem::detach(mp));
+ BOOST_CHECK(Subsystem(sub.getName()).isAttached()==false);
BOOST_CHECK_NO_THROW(Subsystem::attach(mp, {sub.getName()}));
+ BOOST_CHECK(Subsystem(sub.getName()).isAttached()==true);
}
else {
std::string mp = "/sys/fs/cgroup/" + sub.getName();
BOOST_CHECK_NO_THROW(Subsystem::attach(mp, {sub.getName()}));
+ BOOST_CHECK(Subsystem(sub.getName()).isAttached()==true);
BOOST_CHECK_NO_THROW(Subsystem::detach(mp));
+ BOOST_CHECK(Subsystem(sub.getName()).isAttached()==false);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(ControlGroupParams)
+{
+ CGroup memg("memory:/ut-params");
+ BOOST_CHECK(memg.exists() == false);
+ BOOST_CHECK_NO_THROW(memg.create());
+ BOOST_CHECK(memg.exists() == true);
+
+ if (!memg.exists()) return ;
+
+ memg.assignPid(::getpid());
+ memg.setValue("limit_in_bytes", "10k");
+ memg.setValue("soft_limit_in_bytes", "10k");
+ 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());
+ BOOST_CHECK_NO_THROW(memg.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(DevicesParams)
+{
+ DevicesCGroup devcg("/");
+ std::vector<DevicePermission> list = devcg.list();
+ for (const auto& i : list) {
+ LOGD(std::string("perm = ") + i.type + " " +
+ std::to_string(i.major) + ":" + std::to_string(i.minor) + " " + i.permission);
}
}