]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cgroup-show.c
464a3e5c0359637bf8ed74f06d7972fa65ed2253
[thirdparty/systemd.git] / src / shared / cgroup-show.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <dirent.h>
4 #include <errno.h>
5 #include <stddef.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "alloc-util.h"
11 #include "bus-error.h"
12 #include "bus-util.h"
13 #include "cgroup-show.h"
14 #include "cgroup-util.h"
15 #include "env-file.h"
16 #include "fd-util.h"
17 #include "format-util.h"
18 #include "locale-util.h"
19 #include "macro.h"
20 #include "output-mode.h"
21 #include "path-util.h"
22 #include "process-util.h"
23 #include "sort-util.h"
24 #include "string-util.h"
25 #include "terminal-util.h"
26 #include "unit-name.h"
27
28 static void show_pid_array(
29 pid_t pids[],
30 unsigned n_pids,
31 const char *prefix,
32 size_t n_columns,
33 bool extra,
34 bool more,
35 OutputFlags flags) {
36
37 unsigned i, j, pid_width;
38
39 if (n_pids == 0)
40 return;
41
42 typesafe_qsort(pids, n_pids, pid_compare_func);
43
44 /* Filter duplicates */
45 for (j = 0, i = 1; i < n_pids; i++) {
46 if (pids[i] == pids[j])
47 continue;
48 pids[++j] = pids[i];
49 }
50 n_pids = j + 1;
51 pid_width = DECIMAL_STR_WIDTH(pids[j]);
52
53 if (flags & OUTPUT_FULL_WIDTH)
54 n_columns = SIZE_MAX;
55 else {
56 if (n_columns > pid_width + 3) /* something like "├─1114784 " */
57 n_columns -= pid_width + 3;
58 else
59 n_columns = 20;
60 }
61 for (i = 0; i < n_pids; i++) {
62 _cleanup_free_ char *t = NULL;
63
64 (void) get_process_cmdline(pids[i], n_columns, PROCESS_CMDLINE_COMM_FALLBACK, &t);
65
66 if (extra)
67 printf("%s%s ", prefix, special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
68 else
69 printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT)));
70
71 printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t));
72 }
73 }
74
75 static int show_cgroup_one_by_path(
76 const char *path,
77 const char *prefix,
78 size_t n_columns,
79 bool more,
80 OutputFlags flags) {
81
82 char *fn;
83 _cleanup_fclose_ FILE *f = NULL;
84 size_t n = 0, n_allocated = 0;
85 _cleanup_free_ pid_t *pids = NULL;
86 _cleanup_free_ char *p = NULL;
87 pid_t pid;
88 int r;
89
90 r = cg_mangle_path(path, &p);
91 if (r < 0)
92 return r;
93
94 fn = strjoina(p, "/cgroup.procs");
95 f = fopen(fn, "re");
96 if (!f)
97 return -errno;
98
99 while ((r = cg_read_pid(f, &pid)) > 0) {
100
101 if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0)
102 continue;
103
104 if (!GREEDY_REALLOC(pids, n_allocated, n + 1))
105 return -ENOMEM;
106
107 assert(n < n_allocated);
108 pids[n++] = pid;
109 }
110
111 if (r < 0)
112 return r;
113
114 show_pid_array(pids, n, prefix, n_columns, false, more, flags);
115
116 return 0;
117 }
118
119 int show_cgroup_by_path(
120 const char *path,
121 const char *prefix,
122 size_t n_columns,
123 OutputFlags flags) {
124
125 _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
126 _cleanup_closedir_ DIR *d = NULL;
127 char *gn = NULL;
128 bool shown_pids = false;
129 int r;
130
131 assert(path);
132
133 if (n_columns <= 0)
134 n_columns = columns();
135
136 prefix = strempty(prefix);
137
138 r = cg_mangle_path(path, &fn);
139 if (r < 0)
140 return r;
141
142 d = opendir(fn);
143 if (!d)
144 return -errno;
145
146 while ((r = cg_read_subgroup(d, &gn)) > 0) {
147 _cleanup_free_ char *k = NULL;
148
149 k = strjoin(fn, "/", gn);
150 free(gn);
151 if (!k)
152 return -ENOMEM;
153
154 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
155 continue;
156
157 if (!shown_pids) {
158 show_cgroup_one_by_path(path, prefix, n_columns, true, flags);
159 shown_pids = true;
160 }
161
162 if (last) {
163 printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_BRANCH), cg_unescape(basename(last)));
164
165 if (!p1) {
166 p1 = strappend(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
167 if (!p1)
168 return -ENOMEM;
169 }
170
171 show_cgroup_by_path(last, p1, n_columns-2, flags);
172 free(last);
173 }
174
175 last = TAKE_PTR(k);
176 }
177
178 if (r < 0)
179 return r;
180
181 if (!shown_pids)
182 show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
183
184 if (last) {
185 printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), cg_unescape(basename(last)));
186
187 if (!p2) {
188 p2 = strappend(prefix, " ");
189 if (!p2)
190 return -ENOMEM;
191 }
192
193 show_cgroup_by_path(last, p2, n_columns-2, flags);
194 }
195
196 return 0;
197 }
198
199 int show_cgroup(const char *controller,
200 const char *path,
201 const char *prefix,
202 size_t n_columns,
203 OutputFlags flags) {
204 _cleanup_free_ char *p = NULL;
205 int r;
206
207 assert(path);
208
209 r = cg_get_path(controller, path, NULL, &p);
210 if (r < 0)
211 return r;
212
213 return show_cgroup_by_path(p, prefix, n_columns, flags);
214 }
215
216 static int show_extra_pids(
217 const char *controller,
218 const char *path,
219 const char *prefix,
220 size_t n_columns,
221 const pid_t pids[],
222 unsigned n_pids,
223 OutputFlags flags) {
224
225 _cleanup_free_ pid_t *copy = NULL;
226 unsigned i, j;
227 int r;
228
229 assert(path);
230
231 if (n_pids <= 0)
232 return 0;
233
234 if (n_columns <= 0)
235 n_columns = columns();
236
237 prefix = strempty(prefix);
238
239 copy = new(pid_t, n_pids);
240 if (!copy)
241 return -ENOMEM;
242
243 for (i = 0, j = 0; i < n_pids; i++) {
244 _cleanup_free_ char *k = NULL;
245
246 r = cg_pid_get_path(controller, pids[i], &k);
247 if (r < 0)
248 return r;
249
250 if (path_startswith(k, path))
251 continue;
252
253 copy[j++] = pids[i];
254 }
255
256 show_pid_array(copy, j, prefix, n_columns, true, false, flags);
257
258 return 0;
259 }
260
261 int show_cgroup_and_extra(
262 const char *controller,
263 const char *path,
264 const char *prefix,
265 size_t n_columns,
266 const pid_t extra_pids[],
267 unsigned n_extra_pids,
268 OutputFlags flags) {
269
270 int r;
271
272 assert(path);
273
274 r = show_cgroup(controller, path, prefix, n_columns, flags);
275 if (r < 0)
276 return r;
277
278 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
279 }
280
281 int show_cgroup_get_unit_path_and_warn(
282 sd_bus *bus,
283 const char *unit,
284 char **ret) {
285
286 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
287 _cleanup_free_ char *path = NULL;
288 int r;
289
290 path = unit_dbus_path_from_name(unit);
291 if (!path)
292 return log_oom();
293
294 r = sd_bus_get_property_string(
295 bus,
296 "org.freedesktop.systemd1",
297 path,
298 unit_dbus_interface_from_name(unit),
299 "ControlGroup",
300 &error,
301 ret);
302 if (r < 0)
303 return log_error_errno(r, "Failed to query unit control group path: %s",
304 bus_error_message(&error, r));
305
306 return 0;
307 }
308
309 int show_cgroup_get_path_and_warn(
310 const char *machine,
311 const char *prefix,
312 char **ret) {
313
314 int r;
315 _cleanup_free_ char *root = NULL;
316
317 if (machine) {
318 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
319 _cleanup_free_ char *unit = NULL;
320 const char *m;
321
322 m = strjoina("/run/systemd/machines/", machine);
323 r = parse_env_file(NULL, m, "SCOPE", &unit);
324 if (r < 0)
325 return log_error_errno(r, "Failed to load machine data: %m");
326
327 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
328 if (r < 0)
329 return log_error_errno(r, "Failed to create bus connection: %m");
330
331 r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
332 if (r < 0)
333 return r;
334 } else {
335 r = cg_get_root_path(&root);
336 if (r == -ENOMEDIUM)
337 return log_error_errno(r, "Failed to get root control group path.\n"
338 "No cgroup filesystem mounted on /sys/fs/cgroup");
339 else if (r < 0)
340 return log_error_errno(r, "Failed to get root control group path: %m");
341 }
342
343 if (prefix) {
344 char *t;
345
346 t = strjoin(root, prefix);
347 if (!t)
348 return log_oom();
349
350 *ret = t;
351 } else
352 *ret = TAKE_PTR(root);
353
354 return 0;
355 }