]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-show.c
cgroup-show: pass the right error variable
[thirdparty/systemd.git] / src / shared / cgroup-show.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
ab35fb1b 2
fa776d8e
LP
3#include <dirent.h>
4#include <errno.h>
a8fbdf54 5#include <stddef.h>
07630cea 6#include <stdio.h>
a8fbdf54 7#include <stdlib.h>
ab35fb1b 8
b5efdb8a 9#include "alloc-util.h"
049cb73b
ZJS
10#include "bus-error.h"
11#include "bus-util.h"
3ffd4af2 12#include "cgroup-show.h"
07630cea 13#include "cgroup-util.h"
686d13b9 14#include "env-file.h"
5a5a5d29 15#include "escape.h"
3ffd4af2 16#include "fd-util.h"
f97b34a6 17#include "format-util.h"
40d4320d 18#include "hostname-util.h"
8752c575 19#include "locale-util.h"
ab35fb1b 20#include "macro.h"
5a5a5d29 21#include "nulstr-util.h"
a8fbdf54 22#include "output-mode.h"
74d8ccd4 23#include "parse-util.h"
9eb977db 24#include "path-util.h"
07630cea 25#include "process-util.h"
760877e9 26#include "sort-util.h"
07630cea 27#include "string-util.h"
288a74cc 28#include "terminal-util.h"
049cb73b 29#include "unit-name.h"
74d8ccd4 30#include "xattr-util.h"
ab35fb1b 31
0ff308c8
LP
32static void show_pid_array(
33 pid_t pids[],
6f25a110 34 size_t n_pids,
0ff308c8 35 const char *prefix,
bc28751e 36 size_t n_columns,
0ff308c8
LP
37 bool extra,
38 bool more,
39 OutputFlags flags) {
40
6f25a110 41 size_t i, j, pid_width;
b69d29ce 42
da41abc5 43 if (n_pids == 0)
3637713a
LP
44 return;
45
93bab288 46 typesafe_qsort(pids, n_pids, pid_compare_func);
b69d29ce 47
3da7a50f
ZJS
48 /* Filter duplicates */
49 for (j = 0, i = 1; i < n_pids; i++) {
50 if (pids[i] == pids[j])
51 continue;
52 pids[++j] = pids[i];
b69d29ce 53 }
3da7a50f
ZJS
54 n_pids = j + 1;
55 pid_width = DECIMAL_STR_WIDTH(pids[j]);
b69d29ce 56
b47d419c 57 if (flags & OUTPUT_FULL_WIDTH)
bc28751e 58 n_columns = SIZE_MAX;
9bdbc2e2 59 else {
31c294dc
ZJS
60 if (n_columns > pid_width + 3) /* something like "├─1114784 " */
61 n_columns -= pid_width + 3;
9bdbc2e2
LN
62 else
63 n_columns = 20;
64 }
b69d29ce 65 for (i = 0; i < n_pids; i++) {
b47d419c 66 _cleanup_free_ char *t = NULL;
b69d29ce 67
a034620f
LP
68 (void) pid_get_cmdline(pids[i], n_columns,
69 PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_USE_LOCALE,
70 &t);
b69d29ce 71
6b01f1d3 72 if (extra)
9a6f746f 73 printf("%s%s ", prefix, special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
6b01f1d3 74 else
9a6f746f 75 printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT)));
6b01f1d3 76
f996072f 77 printf("%s%*"PID_PRI" %s%s\n", ansi_grey(), (int) pid_width, pids[i], strna(t), ansi_normal());
b69d29ce
LP
78 }
79}
80
291d565a
LP
81static int show_cgroup_one_by_path(
82 const char *path,
83 const char *prefix,
bc28751e 84 size_t n_columns,
291d565a 85 bool more,
291d565a 86 OutputFlags flags) {
b69d29ce 87
7027ff61 88 _cleanup_free_ pid_t *pids = NULL;
319a4f4b 89 _cleanup_fclose_ FILE *f = NULL;
b47d419c 90 _cleanup_free_ char *p = NULL;
319a4f4b 91 size_t n = 0;
319a4f4b 92 char *fn;
fa776d8e 93 int r;
ab35fb1b 94
7027ff61 95 r = cg_mangle_path(path, &p);
b69d29ce 96 if (r < 0)
35d2e7ec 97 return r;
ab35fb1b 98
63c372cb 99 fn = strjoina(p, "/cgroup.procs");
fa776d8e 100 f = fopen(fn, "re");
ab35fb1b 101 if (!f)
fa776d8e 102 return -errno;
ab35fb1b 103
1fb50408
YW
104 for (;;) {
105 pid_t pid;
106
107 /* libvirt / qemu uses threaded mode and cgroup.procs cannot be read at the lower levels.
0e685823 108 * From https://docs.kernel.org/admin-guide/cgroup-v2.html#threads,
1fb50408
YW
109 * “cgroup.procs” in a threaded domain cgroup contains the PIDs of all processes in
110 * the subtree and is not readable in the subtree proper. */
111 r = cg_read_pid(f, &pid);
112 if (IN_SET(r, 0, -EOPNOTSUPP))
113 break;
114 if (r < 0)
115 return r;
ab35fb1b 116
fc87713b 117 if (!(flags & OUTPUT_KERNEL_THREADS) && pid_is_kernel_thread(pid) > 0)
1e5678d0
LP
118 continue;
119
319a4f4b 120 if (!GREEDY_REALLOC(pids, n + 1))
3da7a50f 121 return -ENOMEM;
ab35fb1b 122
c6c18be3 123 pids[n++] = pid;
ab35fb1b
LP
124 }
125
0ff308c8 126 show_pid_array(pids, n, prefix, n_columns, false, more, flags);
ab35fb1b 127
7027ff61 128 return 0;
fa776d8e
LP
129}
130
74d8ccd4
LP
131static int show_cgroup_name(
132 const char *path,
133 const char *prefix,
5a5a5d29
LP
134 SpecialGlyph glyph,
135 OutputFlags flags) {
74d8ccd4 136
5a5a5d29 137 uint64_t cgroupid = UINT64_MAX;
74d8ccd4 138 _cleanup_free_ char *b = NULL;
254d1313 139 _cleanup_close_ int fd = -EBADF;
bde26075 140 bool delegate;
74d8ccd4
LP
141 int r;
142
001feb12
LP
143 fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY, 0);
144 if (fd < 0)
145 return log_debug_errno(errno, "Failed to open cgroup '%s', ignoring: %m", path);
5a5a5d29 146
001feb12 147 r = cg_is_delegated_fd(fd);
b426b4ee
NR
148 if (r < 0)
149 log_debug_errno(r, "Failed to check if cgroup is delegated, ignoring: %m");
150 delegate = r > 0;
74d8ccd4 151
5a5a5d29 152 if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
3b287495
LP
153 r = cg_fd_get_cgroupid(fd, &cgroupid);
154 if (r < 0)
422aa531 155 log_debug_errno(r, "Failed to determine cgroup ID of %s, ignoring: %m", path);
5a5a5d29
LP
156 }
157
158 r = path_extract_filename(path, &b);
159 if (r < 0)
160 return log_error_errno(r, "Failed to extract filename from cgroup path: %m");
74d8ccd4 161
5a5a5d29
LP
162 printf("%s%s%s%s%s",
163 prefix, special_glyph(glyph),
74d8ccd4
LP
164 delegate ? ansi_underline() : "",
165 cg_unescape(b),
74d8ccd4 166 delegate ? ansi_normal() : "");
5a5a5d29
LP
167
168 if (delegate)
169 printf(" %s%s%s",
170 ansi_highlight(),
171 special_glyph(SPECIAL_GLYPH_ELLIPSIS),
172 ansi_normal());
173
174 if (cgroupid != UINT64_MAX)
175 printf(" %s(#%" PRIu64 ")%s", ansi_grey(), cgroupid, ansi_normal());
176
177 printf("\n");
178
001feb12 179 if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS)) {
5a5a5d29 180 _cleanup_free_ char *nl = NULL;
5a5a5d29
LP
181
182 r = flistxattr_malloc(fd, &nl);
183 if (r < 0)
184 log_debug_errno(r, "Failed to enumerate xattrs on '%s', ignoring: %m", path);
185
186 NULSTR_FOREACH(xa, nl) {
187 _cleanup_free_ char *x = NULL, *y = NULL, *buf = NULL;
188 int n;
189
190 if (!STARTSWITH_SET(xa, "user.", "trusted."))
191 continue;
192
193 n = fgetxattr_malloc(fd, xa, &buf);
194 if (n < 0) {
195 log_debug_errno(r, "Failed to read xattr '%s' off '%s', ignoring: %m", xa, path);
196 continue;
197 }
198
199 x = cescape(xa);
200 if (!x)
201 return -ENOMEM;
202
203 y = cescape_length(buf, n);
204 if (!y)
205 return -ENOMEM;
206
207 printf("%s%s%s %s%s%s: %s\n",
208 prefix,
209 glyph == SPECIAL_GLYPH_TREE_BRANCH ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : " ",
fc03e80c 210 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
5a5a5d29
LP
211 ansi_blue(), x, ansi_normal(),
212 y);
213 }
214 }
215
74d8ccd4
LP
216 return 0;
217}
218
291d565a
LP
219int show_cgroup_by_path(
220 const char *path,
221 const char *prefix,
bc28751e 222 size_t n_columns,
291d565a
LP
223 OutputFlags flags) {
224
7027ff61
LP
225 _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
226 _cleanup_closedir_ DIR *d = NULL;
fa776d8e 227 bool shown_pids = false;
74d8ccd4 228 char *gn = NULL;
fa776d8e
LP
229 int r;
230
b69d29ce
LP
231 assert(path);
232
fa776d8e
LP
233 if (n_columns <= 0)
234 n_columns = columns();
235
291d565a 236 prefix = strempty(prefix);
fa776d8e 237
7027ff61 238 r = cg_mangle_path(path, &fn);
c3175a7f 239 if (r < 0)
35d2e7ec 240 return r;
fa776d8e 241
c3175a7f 242 d = opendir(fn);
7027ff61 243 if (!d)
fa776d8e 244 return -errno;
fa776d8e 245
35d2e7ec 246 while ((r = cg_read_subgroup(d, &gn)) > 0) {
7027ff61 247 _cleanup_free_ char *k = NULL;
c3175a7f 248
657ee2d8 249 k = path_join(fn, gn);
c3175a7f 250 free(gn);
7027ff61
LP
251 if (!k)
252 return -ENOMEM;
c3175a7f 253
6f883237 254 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
c3175a7f 255 continue;
fa776d8e
LP
256
257 if (!shown_pids) {
001feb12 258 (void) show_cgroup_one_by_path(path, prefix, n_columns, true, flags);
fa776d8e
LP
259 shown_pids = true;
260 }
261
262 if (last) {
5a5a5d29 263 r = show_cgroup_name(last, prefix, SPECIAL_GLYPH_TREE_BRANCH, flags);
74d8ccd4
LP
264 if (r < 0)
265 return r;
fa776d8e 266
c3175a7f 267 if (!p1) {
b910cc72 268 p1 = strjoin(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
7027ff61
LP
269 if (!p1)
270 return -ENOMEM;
c3175a7f 271 }
fa776d8e 272
0ff308c8 273 show_cgroup_by_path(last, p1, n_columns-2, flags);
fa776d8e 274 free(last);
fa776d8e
LP
275 }
276
ae2a15bc 277 last = TAKE_PTR(k);
fa776d8e
LP
278 }
279
35d2e7ec 280 if (r < 0)
7027ff61 281 return r;
35d2e7ec 282
fa776d8e 283 if (!shown_pids)
001feb12 284 (void) show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
fa776d8e
LP
285
286 if (last) {
5a5a5d29 287 r = show_cgroup_name(last, prefix, SPECIAL_GLYPH_TREE_RIGHT, flags);
74d8ccd4
LP
288 if (r < 0)
289 return r;
fa776d8e 290
c3175a7f 291 if (!p2) {
b910cc72 292 p2 = strjoin(prefix, " ");
7027ff61
LP
293 if (!p2)
294 return -ENOMEM;
c3175a7f 295 }
fa776d8e 296
0ff308c8 297 show_cgroup_by_path(last, p2, n_columns-2, flags);
fa776d8e
LP
298 }
299
7027ff61 300 return 0;
ab35fb1b 301}
35d2e7ec 302
291d565a
LP
303int show_cgroup(const char *controller,
304 const char *path,
305 const char *prefix,
bc28751e 306 size_t n_columns,
291d565a 307 OutputFlags flags) {
7027ff61 308 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
309 int r;
310
35d2e7ec
LP
311 assert(path);
312
1e5678d0
LP
313 r = cg_get_path(controller, path, NULL, &p);
314 if (r < 0)
35d2e7ec
LP
315 return r;
316
0ff308c8 317 return show_cgroup_by_path(p, prefix, n_columns, flags);
35d2e7ec 318}
b69d29ce 319
291d565a
LP
320static int show_extra_pids(
321 const char *controller,
322 const char *path,
323 const char *prefix,
bc28751e 324 size_t n_columns,
291d565a 325 const pid_t pids[],
6f25a110 326 size_t n_pids,
291d565a
LP
327 OutputFlags flags) {
328
7fd1b19b 329 _cleanup_free_ pid_t *copy = NULL;
6f25a110 330 size_t i, j;
b69d29ce
LP
331 int r;
332
b69d29ce
LP
333 assert(path);
334
335 if (n_pids <= 0)
336 return 0;
337
338 if (n_columns <= 0)
339 n_columns = columns();
340
7027ff61 341 prefix = strempty(prefix);
b69d29ce
LP
342
343 copy = new(pid_t, n_pids);
344 if (!copy)
345 return -ENOMEM;
346
347 for (i = 0, j = 0; i < n_pids; i++) {
7fd1b19b 348 _cleanup_free_ char *k = NULL;
b69d29ce 349
7027ff61 350 r = cg_pid_get_path(controller, pids[i], &k);
050fbdb8 351 if (r < 0)
b69d29ce 352 return r;
b69d29ce
LP
353
354 if (path_startswith(k, path))
355 continue;
356
357 copy[j++] = pids[i];
358 }
359
0ff308c8 360 show_pid_array(copy, j, prefix, n_columns, true, false, flags);
b69d29ce 361
b69d29ce
LP
362 return 0;
363}
364
291d565a
LP
365int show_cgroup_and_extra(
366 const char *controller,
367 const char *path,
368 const char *prefix,
bc28751e 369 size_t n_columns,
291d565a 370 const pid_t extra_pids[],
6f25a110 371 size_t n_extra_pids,
291d565a
LP
372 OutputFlags flags) {
373
b69d29ce
LP
374 int r;
375
b69d29ce
LP
376 assert(path);
377
0ff308c8 378 r = show_cgroup(controller, path, prefix, n_columns, flags);
b69d29ce
LP
379 if (r < 0)
380 return r;
381
9bdbc2e2 382 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
b69d29ce
LP
383}
384
bc06be75
ZJS
385int show_cgroup_get_unit_path_and_warn(
386 sd_bus *bus,
387 const char *unit,
388 char **ret) {
389
390 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
391 _cleanup_free_ char *path = NULL;
392 int r;
393
394 path = unit_dbus_path_from_name(unit);
395 if (!path)
396 return log_oom();
397
398 r = sd_bus_get_property_string(
399 bus,
400 "org.freedesktop.systemd1",
401 path,
402 unit_dbus_interface_from_name(unit),
403 "ControlGroup",
404 &error,
405 ret);
406 if (r < 0)
407 return log_error_errno(r, "Failed to query unit control group path: %s",
408 bus_error_message(&error, r));
409
410 return 0;
411}
412
d3e8277d 413int show_cgroup_get_path_and_warn(
049cb73b 414 const char *machine,
d3e8277d 415 const char *prefix,
049cb73b
ZJS
416 char **ret) {
417
d3e8277d 418 _cleanup_free_ char *root = NULL;
40d4320d 419 int r;
d3e8277d
ZJS
420
421 if (machine) {
d3e8277d 422 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
bc06be75 423 _cleanup_free_ char *unit = NULL;
d3e8277d
ZJS
424 const char *m;
425
40d4320d
LP
426 if (!hostname_is_valid(machine, 0))
427 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Machine name is not valid: %s", machine);
428
d3e8277d 429 m = strjoina("/run/systemd/machines/", machine);
13df9c39 430 r = parse_env_file(NULL, m, "SCOPE", &unit);
d3e8277d
ZJS
431 if (r < 0)
432 return log_error_errno(r, "Failed to load machine data: %m");
049cb73b 433
4870133b 434 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, &bus);
d3e8277d 435 if (r < 0)
10a7340a 436 return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL);
d3e8277d 437
bc06be75 438 r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
d3e8277d 439 if (r < 0)
bc06be75 440 return r;
d3e8277d
ZJS
441 } else {
442 r = cg_get_root_path(&root);
049cb73b
ZJS
443 if (r == -ENOMEDIUM)
444 return log_error_errno(r, "Failed to get root control group path.\n"
445 "No cgroup filesystem mounted on /sys/fs/cgroup");
aa13d384 446 if (r < 0)
049cb73b 447 return log_error_errno(r, "Failed to get root control group path: %m");
049cb73b
ZJS
448 }
449
d3e8277d
ZJS
450 if (prefix) {
451 char *t;
049cb73b 452
aa13d384 453 t = path_join(root, prefix);
d3e8277d
ZJS
454 if (!t)
455 return log_oom();
049cb73b 456
d3e8277d 457 *ret = t;
1cc6c93a
YW
458 } else
459 *ret = TAKE_PTR(root);
049cb73b
ZJS
460
461 return 0;
462}