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