File: cgroup_limits.cpp

package info (click to toggle)
falcosecurity-libs 0.1.1dev%2Bgit20220316.e5c53d64-5.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,732 kB
  • sloc: cpp: 55,770; ansic: 37,330; makefile: 74; sh: 13
file content (141 lines) | stat: -rw-r--r-- 5,160 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "cgroup_limits.h"

#include <fstream>
#include "cgroup_list_counter.h"
#include "sinsp.h"

namespace {
// to prevent 32-bit number of kilobytes from overflowing, ignore values larger than 4 TiB.
// This reports extremely large values (e.g. almost-but-not-quite 9EiB as set by k8s) as unlimited.
// Note: we use the same maximum value for cpu shares/quotas as well; the typical values are much lower
// and so should never exceed CGROUP_VAL_MAX either
constexpr const int64_t CGROUP_VAL_MAX = (1ULL << 42u) - 1;

/**
 * \brief Read a single int64_t value from cgroupfs
 * @param subsys path to the specific cgroup subsystem, e.g. /sys/fs/cgroup/cpu
 * @param cgroup cgroup path within the cgroup mountpoint (like in /proc/pid/cgroup)
 * @param filename the filename within the cgroup directory, e.g. cpu.shares
 * @param out reference to the output value
 * @return true if we successfully read the value and it's within reasonable range,
 *          reasonable being [0; CGROUP_VAL_MAX)
 */
bool read_cgroup_val(std::shared_ptr<std::string>& subsys,
		     const std::string& cgroup,
		     const std::string& filename,
		     int64_t& out)
{
	std::string path = *subsys.get() + "/" + cgroup + "/" + filename;
	std::ifstream cg_val(path);

	int64_t val = -1;
	cg_val >> val;

	if(val <= 0 || val > CGROUP_VAL_MAX)
	{
		g_logger.format(sinsp_logger::SEV_DEBUG, "(cgroup-limits) value of %s (%lld) out of range, ignoring",
			path.c_str(), val);
		return false;
	}
	out = val;
	return true;
}

/**
 * Read from a cpuset file to get the number of cpus in the cpuset
 */
bool read_cgroup_list_count(const std::string& subsys,
			    const std::string& cgroup,
			    const std::string& filename,
			    int32_t& out)
{
	std::string path = subsys + "/" + cgroup + "/" + filename;
	std::ifstream cg_val(path);

	if(!cg_val)
	{
		return false;
	}

 	std::string cpuset_cpus((std::istreambuf_iterator<char>(cg_val)),
			    std::istreambuf_iterator<char>());

	if(cpuset_cpus.empty())
	{
		return false;
	}

 	libsinsp::cgroup_list_counter counter;
	out = counter(cpuset_cpus.c_str());

	g_logger.format(sinsp_logger::SEV_DEBUG,
			"(cgroup-limits) Pulling cpu set from %s: %s = %d",
			path.c_str(),
			cpuset_cpus.c_str(),
			out);

 	return (out > 0);
}

}

namespace libsinsp {
namespace cgroup_limits {

bool get_cgroup_resource_limits(const cgroup_limits_key& key, cgroup_limits_value& value, bool name_check)
{
	bool found_all = true;
	std::shared_ptr<std::string> memcg_root = sinsp::lookup_cgroup_dir("memory");
	if(name_check && key.m_mem_cgroup.find(key.m_container_id) == std::string::npos)
	{
		g_logger.format(sinsp_logger::SEV_INFO, "(cgroup-limits) mem cgroup for container [%s]: %s/%s -- no per-container memory cgroup, ignoring",
			key.m_container_id.c_str(), memcg_root->c_str(), key.m_mem_cgroup.c_str());
	}
	else
	{
		g_logger.format(sinsp_logger::SEV_DEBUG, "(cgroup-limits) mem cgroup for container [%s]: %s/%s",
			key.m_container_id.c_str(), memcg_root->c_str(), key.m_mem_cgroup.c_str());
		found_all = read_cgroup_val(memcg_root, key.m_mem_cgroup, "memory.limit_in_bytes", value.m_memory_limit) && found_all;
	}

	std::shared_ptr<std::string> cpucg_root = sinsp::lookup_cgroup_dir("cpu");
	if(name_check && key.m_cpu_cgroup.find(key.m_container_id) == std::string::npos)
	{
		g_logger.format(sinsp_logger::SEV_INFO, "(cgroup-limits) cpu cgroup for container [%s]: %s/%s -- no per-container CPU cgroup, ignoring",
				key.m_container_id.c_str(), cpucg_root->c_str(), key.m_cpu_cgroup.c_str());
	}
	else
	{
		g_logger.format(sinsp_logger::SEV_DEBUG, "(cgroup-limits) cpu cgroup for container [%s]: %s/%s",
				key.m_container_id.c_str(), cpucg_root->c_str(), key.m_cpu_cgroup.c_str());
		found_all = read_cgroup_val(cpucg_root, key.m_cpu_cgroup, "cpu.shares", value.m_cpu_shares) && found_all;
		found_all = read_cgroup_val(cpucg_root, key.m_cpu_cgroup, "cpu.cfs_quota_us", value.m_cpu_quota) && found_all;
		found_all = read_cgroup_val(cpucg_root, key.m_cpu_cgroup, "cpu.cfs_period_us", value.m_cpu_period) && found_all;
	}

	std::shared_ptr<std::string> cpuset_root = sinsp::lookup_cgroup_dir("cpuset");
	if (name_check && key.m_cpuset_cgroup.find(key.m_container_id) == std::string::npos)
	{
		g_logger.format(sinsp_logger::SEV_DEBUG, "(cgroup-limits) cpuset cgroup for container [%s]: %s/%s -- no per-container cpuset cgroup, ignoring",
				key.m_container_id.c_str(), cpuset_root->c_str(), key.m_cpuset_cgroup.c_str());
	}
	else
	{
		g_logger.format(sinsp_logger::SEV_DEBUG, "(cgroup-limits) cpuset cgroup for container [%s]: %s/%s",
				key.m_container_id.c_str(), cpuset_root->c_str(), key.m_cpuset_cgroup.c_str());
		found_all = read_cgroup_list_count(*cpuset_root,
						   key.m_cpuset_cgroup,
						   "cpuset.cpus",
						   value.m_cpuset_cpu_count) && found_all;
	}

	g_logger.format(sinsp_logger::SEV_DEBUG,
		"(cgroup-limits) Got cgroup limits for container [%s]: "
		"mem_limit=%ld, cpu_shares=%ld cpu_quota=%ld cpu_period=%ld cpuset_cpu_count=%d",
		key.m_container_id.c_str(),
		value.m_memory_limit, value.m_cpu_shares, value.m_cpu_quota, value.m_cpu_period, value.m_cpuset_cpu_count);

	return found_all;
}
}
}