| /* |
| |
| nsjail - cgroup namespacing |
| ----------------------------------------- |
| |
| Copyright 2014 Google Inc. All Rights Reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| #include "cgroup.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <sstream> |
| |
| #include "logs.h" |
| #include "util.h" |
| |
| namespace cgroup { |
| |
| static bool createCgroup(const std::string& cgroup_path, pid_t pid) { |
| LOG_D("Create '%s' for PID=%d", cgroup_path.c_str(), (int)pid); |
| if (mkdir(cgroup_path.c_str(), 0700) == -1 && errno != EEXIST) { |
| PLOG_W("mkdir('%s', 0700) failed", cgroup_path.c_str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool writeToCgroup( |
| const std::string& cgroup_path, const std::string& value, const std::string& what) { |
| LOG_D("Setting '%s' to '%s'", cgroup_path.c_str(), value.c_str()); |
| if (!util::writeBufToFile( |
| cgroup_path.c_str(), value.c_str(), value.length(), O_WRONLY | O_CLOEXEC)) { |
| LOG_W("Could not update %s", what.c_str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool addPidToTaskList(const std::string& cgroup_path, pid_t pid) { |
| std::string pid_str = std::to_string(pid); |
| std::string tasks_path = cgroup_path + "/tasks"; |
| LOG_D("Adding PID='%s' to '%s'", pid_str.c_str(), tasks_path.c_str()); |
| return writeToCgroup(tasks_path, pid_str, "'" + tasks_path + "' task list"); |
| } |
| |
| static bool initNsFromParentMem(nsjconf_t* nsjconf, pid_t pid) { |
| if (nsjconf->cgroup_mem_max == (size_t)0) { |
| return true; |
| } |
| |
| std::string mem_cgroup_path = nsjconf->cgroup_mem_mount + '/' + nsjconf->cgroup_mem_parent + |
| "/NSJAIL." + std::to_string(pid); |
| RETURN_ON_FAILURE(createCgroup(mem_cgroup_path, pid)); |
| |
| std::string mem_max_str = std::to_string(nsjconf->cgroup_mem_max); |
| RETURN_ON_FAILURE(writeToCgroup( |
| mem_cgroup_path + "/memory.limit_in_bytes", mem_max_str, "memory cgroup max limit")); |
| |
| /* |
| * Use OOM-killer instead of making processes hang/sleep |
| */ |
| RETURN_ON_FAILURE(writeToCgroup( |
| mem_cgroup_path + "/memory.oom_control", "0", "memory cgroup oom control")); |
| |
| return addPidToTaskList(mem_cgroup_path, pid); |
| } |
| |
| static bool initNsFromParentPids(nsjconf_t* nsjconf, pid_t pid) { |
| if (nsjconf->cgroup_pids_max == 0U) { |
| return true; |
| } |
| |
| std::string pids_cgroup_path = nsjconf->cgroup_pids_mount + '/' + |
| nsjconf->cgroup_pids_parent + "/NSJAIL." + |
| std::to_string(pid); |
| RETURN_ON_FAILURE(createCgroup(pids_cgroup_path, pid)); |
| |
| std::string pids_max_str = std::to_string(nsjconf->cgroup_pids_max); |
| RETURN_ON_FAILURE( |
| writeToCgroup(pids_cgroup_path + "/pids.max", pids_max_str, "pids cgroup max limit")); |
| |
| return addPidToTaskList(pids_cgroup_path, pid); |
| } |
| |
| static bool initNsFromParentNetCls(nsjconf_t* nsjconf, pid_t pid) { |
| if (nsjconf->cgroup_net_cls_classid == 0U) { |
| return true; |
| } |
| |
| std::string net_cls_cgroup_path = nsjconf->cgroup_net_cls_mount + '/' + |
| nsjconf->cgroup_net_cls_parent + "/NSJAIL." + |
| std::to_string(pid); |
| RETURN_ON_FAILURE(createCgroup(net_cls_cgroup_path, pid)); |
| |
| std::string net_cls_classid_str; |
| { |
| std::stringstream ss; |
| ss << "0x" << std::hex << nsjconf->cgroup_net_cls_classid; |
| net_cls_classid_str = ss.str(); |
| } |
| RETURN_ON_FAILURE(writeToCgroup(net_cls_cgroup_path + "/net_cls.classid", |
| net_cls_classid_str, "net_cls cgroup classid")); |
| |
| return addPidToTaskList(net_cls_cgroup_path, pid); |
| } |
| |
| static bool initNsFromParentCpu(nsjconf_t* nsjconf, pid_t pid) { |
| if (nsjconf->cgroup_cpu_ms_per_sec == 0U) { |
| return true; |
| } |
| |
| std::string cpu_cgroup_path = nsjconf->cgroup_cpu_mount + '/' + nsjconf->cgroup_cpu_parent + |
| "/NSJAIL." + std::to_string(pid); |
| RETURN_ON_FAILURE(createCgroup(cpu_cgroup_path, pid)); |
| |
| std::string cpu_ms_per_sec_str = std::to_string(nsjconf->cgroup_cpu_ms_per_sec * 1000U); |
| RETURN_ON_FAILURE( |
| writeToCgroup(cpu_cgroup_path + "/cpu.cfs_quota_us", cpu_ms_per_sec_str, "cpu quota")); |
| |
| RETURN_ON_FAILURE( |
| writeToCgroup(cpu_cgroup_path + "/cpu.cfs_period_us", "1000000", "cpu period")); |
| |
| return addPidToTaskList(cpu_cgroup_path, pid); |
| } |
| |
| bool initNsFromParent(nsjconf_t* nsjconf, pid_t pid) { |
| RETURN_ON_FAILURE(initNsFromParentMem(nsjconf, pid)); |
| RETURN_ON_FAILURE(initNsFromParentPids(nsjconf, pid)); |
| RETURN_ON_FAILURE(initNsFromParentNetCls(nsjconf, pid)); |
| return initNsFromParentCpu(nsjconf, pid); |
| } |
| |
| static void removeCgroup(const std::string& cgroup_path) { |
| LOG_D("Remove '%s'", cgroup_path.c_str()); |
| if (rmdir(cgroup_path.c_str()) == -1) { |
| PLOG_W("rmdir('%s') failed", cgroup_path.c_str()); |
| } |
| } |
| |
| void finishFromParent(nsjconf_t* nsjconf, pid_t pid) { |
| if (nsjconf->cgroup_mem_max != (size_t)0) { |
| std::string mem_cgroup_path = nsjconf->cgroup_mem_mount + '/' + |
| nsjconf->cgroup_mem_parent + "/NSJAIL." + |
| std::to_string(pid); |
| removeCgroup(mem_cgroup_path); |
| } |
| if (nsjconf->cgroup_pids_max != 0U) { |
| std::string pids_cgroup_path = nsjconf->cgroup_pids_mount + '/' + |
| nsjconf->cgroup_pids_parent + "/NSJAIL." + |
| std::to_string(pid); |
| removeCgroup(pids_cgroup_path); |
| } |
| if (nsjconf->cgroup_net_cls_classid != 0U) { |
| std::string net_cls_cgroup_path = nsjconf->cgroup_net_cls_mount + '/' + |
| nsjconf->cgroup_net_cls_parent + "/NSJAIL." + |
| std::to_string(pid); |
| removeCgroup(net_cls_cgroup_path); |
| } |
| if (nsjconf->cgroup_cpu_ms_per_sec != 0U) { |
| std::string cpu_cgroup_path = nsjconf->cgroup_cpu_mount + '/' + |
| nsjconf->cgroup_cpu_parent + "/NSJAIL." + |
| std::to_string(pid); |
| removeCgroup(cpu_cgroup_path); |
| } |
| } |
| |
| bool initNs(void) { |
| return true; |
| } |
| |
| } // namespace cgroup |