master
c 185 lines 6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "cgroup-internals.h"
4 #include "libnetdata/netipc/netipc_netdata.h"
5
6 #ifdef OS_LINUX
7
8 #define CGROUP_NETIPC_SERVICE_NAME "cgroups-snapshot"
9 #define CGROUP_NETIPC_WORKER_COUNT 2
10
11 static nipc_managed_server_t cgroup_netipc_server;
12 static ND_THREAD *cgroup_netipc_thread = NULL;
13
14 // find the cgroup.procs path for a given cgroup (v1 hierarchy)
15 // writes into path_buf, returns true if found
16 static bool cgroup_find_procs_path_v1(char *path_buf, size_t path_buf_size, const char *cg_id) {
17 struct stat buf;
18
19 snprintfz(path_buf, path_buf_size - 1, "%s%s/cgroup.procs", cgroup_cpuset_base, cg_id);
20 if (stat(path_buf, &buf) == 0)
21 return true;
22
23 snprintfz(path_buf, path_buf_size - 1, "%s%s/cgroup.procs", cgroup_blkio_base, cg_id);
24 if (stat(path_buf, &buf) == 0)
25 return true;
26
27 snprintfz(path_buf, path_buf_size - 1, "%s%s/cgroup.procs", cgroup_memory_base, cg_id);
28 if (stat(path_buf, &buf) == 0)
29 return true;
30
31 path_buf[0] = '\0';
32 return false;
33 }
34
35 // handler callback invoked by netipc worker threads when a client requests a snapshot
36 static bool cgroups_snapshot_handler(void *user __maybe_unused,
37 const nipc_cgroups_req_t *request __maybe_unused,
38 nipc_cgroups_builder_t *builder) {
39 static uint64_t generation = 0;
40 static uint64_t last_logged_zero_generation = 0;
41 static uint64_t last_logged_truncated_generation = 0;
42 uint64_t snapshot_generation;
43 char name_buf[256];
44 char path_buf[FILENAME_MAX + 1];
45
46 netdata_mutex_lock(&cgroup_root_mutex);
47
48 // set snapshot header — systemd is always enabled in this codebase
49 snapshot_generation = ++generation;
50 nipc_cgroups_builder_set_header(builder, CONFIG_BOOLEAN_YES, snapshot_generation);
51
52 struct cgroup *cg;
53 int count;
54 int enabled_count = 0;
55 bool truncated = false;
56 for (cg = cgroup_root, count = 0; cg && count < cgroup_root_max; cg = cg->next, count++) {
57 const char *prefix = is_cgroup_systemd_service(cg)
58 ? services_chart_id_prefix
59 : cgroup_chart_id_prefix;
60
61 snprintfz(name_buf, sizeof(name_buf) - 1, "%s%s", prefix, cg->chart_id);
62 uint32_t hash = simple_hash(name_buf);
63 uint32_t options = cg->options;
64 uint32_t enabled = cg->enabled;
65
66 // find the cgroup.procs path
67 if (cgroup_use_unified_cgroups) {
68 struct stat buf;
69 snprintfz(path_buf, FILENAME_MAX, "%s%s/cgroup.procs", cgroup_unified_base, cg->id);
70 if (stat(path_buf, &buf) == -1) {
71 path_buf[0] = '\0';
72 enabled = 0;
73 }
74 } else {
75 if (!cgroup_find_procs_path_v1(path_buf, sizeof(path_buf), cg->id))
76 enabled = 0;
77 }
78
79 if (enabled)
80 enabled_count++;
81
82 nipc_error_t err = nipc_cgroups_builder_add(
83 builder, hash, options, enabled,
84 name_buf, (uint32_t)strlen(name_buf),
85 path_buf, (uint32_t)strlen(path_buf));
86
87 if (err == NIPC_ERR_OVERFLOW) {
88 truncated = true;
89 break; // buffer full — send what we have
90 }
91 }
92
93 bool log_zero_generation = false;
94 bool log_truncated_generation = false;
95
96 if (count == 0 && last_logged_zero_generation != snapshot_generation) {
97 last_logged_zero_generation = snapshot_generation;
98 log_zero_generation = true;
99 }
100
101 if (truncated && last_logged_truncated_generation != snapshot_generation) {
102 last_logged_truncated_generation = snapshot_generation;
103 log_truncated_generation = true;
104 }
105
106 netdata_mutex_unlock(&cgroup_root_mutex);
107
108 if (log_zero_generation) {
109 collector_info("CGROUP: netipc snapshot generation=%llu returned zero items",
110 (unsigned long long)snapshot_generation);
111 }
112
113 if (log_truncated_generation) {
114 collector_error(
115 "CGROUP: netipc snapshot generation=%llu truncated after %d items (%d enabled) due to response size limits",
116 (unsigned long long)snapshot_generation,
117 count,
118 enabled_count);
119 }
120
121 return true;
122 }
123
124 // thread entry point for the netipc accept loop
125 static void cgroup_netipc_server_thread(void *arg) {
126 nipc_server_run((nipc_managed_server_t *)arg);
127 }
128
129 void cgroup_netipc_init(void) {
130 uint64_t auth = netipc_auth_token();
131
132 nipc_server_config_t config = {
133 .supported_profiles = NIPC_PROFILE_BASELINE | NIPC_PROFILE_SHM_HYBRID | NIPC_PROFILE_SHM_FUTEX,
134 .preferred_profiles = NIPC_PROFILE_SHM_FUTEX,
135 .auth_token = auth,
136 };
137
138 nipc_cgroups_service_handler_t handler = {
139 .handle = cgroups_snapshot_handler,
140 .snapshot_max_items = 0, // auto-estimate from negotiated limits
141 .user = NULL,
142 };
143
144 nipc_error_t err = nipc_server_init_typed(
145 &cgroup_netipc_server,
146 os_run_dir(true),
147 CGROUP_NETIPC_SERVICE_NAME,
148 &config,
149 CGROUP_NETIPC_WORKER_COUNT,
150 &handler);
151
152 if (err != NIPC_OK) {
153 collector_error("CGROUP: netipc server init failed (error %u), IPC sharing disabled", (unsigned int)err);
154 return;
155 }
156
157 cgroup_netipc_thread = nd_thread_create(
158 "P[cgroupsipc]", NETDATA_THREAD_OPTION_DONT_LOG_STARTUP,
159 cgroup_netipc_server_thread, &cgroup_netipc_server);
160
161 if (!cgroup_netipc_thread) {
162 collector_error("CGROUP: failed to create netipc server thread");
163 nipc_server_destroy(&cgroup_netipc_server);
164 return;
165 }
166
167 collector_info("CGROUP: netipc server started on '%s/%s.sock'",
168 os_run_dir(true), CGROUP_NETIPC_SERVICE_NAME);
169 }
170
171 void cgroup_netipc_cleanup(void) {
172 if (!cgroup_netipc_thread)
173 return;
174
175 nipc_server_stop(&cgroup_netipc_server);
176 nd_thread_join(cgroup_netipc_thread);
177 cgroup_netipc_thread = NULL;
178
179 nipc_server_drain(&cgroup_netipc_server, 5000);
180 nipc_server_destroy(&cgroup_netipc_server);
181
182 collector_info("CGROUP: netipc server stopped");
183 }
184
185 #endif // OS_LINUX