]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-show.c
tree-wide: use -EBADF for fd initialization
[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[],
34 unsigned n_pids,
35 const char *prefix,
bc28751e 36 size_t n_columns,
0ff308c8
LP
37 bool extra,
38 bool more,
39 OutputFlags flags) {
40
3da7a50f 41 unsigned 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
e3b4efd2
ZJS
68 (void) get_process_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
0ff308c8 117 if (!(flags & OUTPUT_KERNEL_THREADS) && 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
bde26075
LP
131static int is_delegated(int cgfd, const char *path) {
132 _cleanup_free_ char *b = NULL;
133 int r;
134
135 assert(cgfd >= 0 || path);
136
137 r = getxattr_malloc(cgfd < 0 ? path : FORMAT_PROC_FD_PATH(cgfd), "trusted.delegate", &b);
00675c36 138 if (r < 0 && ERRNO_IS_XATTR_ABSENT(r)) {
d9bc1c36
LP
139 /* If the trusted xattr isn't set (preferred), then check the untrusted one. Under the
140 * assumption that whoever is trusted enough to own the cgroup, is also trusted enough to
141 * decide if it is delegated or not this should be safe. */
142 r = getxattr_malloc(cgfd < 0 ? path : FORMAT_PROC_FD_PATH(cgfd), "user.delegate", &b);
00675c36 143 if (r < 0 && ERRNO_IS_XATTR_ABSENT(r))
bde26075 144 return false;
bde26075 145 }
d9bc1c36
LP
146 if (r < 0)
147 return log_debug_errno(r, "Failed to read delegate xattr, ignoring: %m");
bde26075
LP
148
149 r = parse_boolean(b);
150 if (r < 0)
d9bc1c36 151 return log_debug_errno(r, "Failed to parse delegate xattr boolean value, ignoring: %m");
bde26075
LP
152
153 return r;
154}
155
74d8ccd4
LP
156static int show_cgroup_name(
157 const char *path,
158 const char *prefix,
5a5a5d29
LP
159 SpecialGlyph glyph,
160 OutputFlags flags) {
74d8ccd4 161
5a5a5d29 162 uint64_t cgroupid = UINT64_MAX;
74d8ccd4 163 _cleanup_free_ char *b = NULL;
254d1313 164 _cleanup_close_ int fd = -EBADF;
bde26075 165 bool delegate;
74d8ccd4
LP
166 int r;
167
5a5a5d29
LP
168 if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) || FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
169 fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY, 0);
170 if (fd < 0)
171 log_debug_errno(errno, "Failed to open cgroup '%s', ignoring: %m", path);
172 }
173
bde26075 174 delegate = is_delegated(fd, path) > 0;
74d8ccd4 175
5a5a5d29
LP
176 if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
177 cg_file_handle fh = CG_FILE_HANDLE_INIT;
178 int mnt_id = -1;
179
180 if (name_to_handle_at(
181 fd < 0 ? AT_FDCWD : fd,
182 fd < 0 ? path : "",
183 &fh.file_handle,
184 &mnt_id,
185 fd < 0 ? 0 : AT_EMPTY_PATH) < 0)
186 log_debug_errno(errno, "Failed to determine cgroup ID of %s, ignoring: %m", path);
187 else
188 cgroupid = CG_FILE_HANDLE_CGROUPID(fh);
189 }
190
191 r = path_extract_filename(path, &b);
192 if (r < 0)
193 return log_error_errno(r, "Failed to extract filename from cgroup path: %m");
74d8ccd4 194
5a5a5d29
LP
195 printf("%s%s%s%s%s",
196 prefix, special_glyph(glyph),
74d8ccd4
LP
197 delegate ? ansi_underline() : "",
198 cg_unescape(b),
74d8ccd4 199 delegate ? ansi_normal() : "");
5a5a5d29
LP
200
201 if (delegate)
202 printf(" %s%s%s",
203 ansi_highlight(),
204 special_glyph(SPECIAL_GLYPH_ELLIPSIS),
205 ansi_normal());
206
207 if (cgroupid != UINT64_MAX)
208 printf(" %s(#%" PRIu64 ")%s", ansi_grey(), cgroupid, ansi_normal());
209
210 printf("\n");
211
212 if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) && fd >= 0) {
213 _cleanup_free_ char *nl = NULL;
5a5a5d29
LP
214
215 r = flistxattr_malloc(fd, &nl);
216 if (r < 0)
217 log_debug_errno(r, "Failed to enumerate xattrs on '%s', ignoring: %m", path);
218
219 NULSTR_FOREACH(xa, nl) {
220 _cleanup_free_ char *x = NULL, *y = NULL, *buf = NULL;
221 int n;
222
223 if (!STARTSWITH_SET(xa, "user.", "trusted."))
224 continue;
225
226 n = fgetxattr_malloc(fd, xa, &buf);
227 if (n < 0) {
228 log_debug_errno(r, "Failed to read xattr '%s' off '%s', ignoring: %m", xa, path);
229 continue;
230 }
231
232 x = cescape(xa);
233 if (!x)
234 return -ENOMEM;
235
236 y = cescape_length(buf, n);
237 if (!y)
238 return -ENOMEM;
239
240 printf("%s%s%s %s%s%s: %s\n",
241 prefix,
242 glyph == SPECIAL_GLYPH_TREE_BRANCH ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : " ",
fc03e80c 243 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
5a5a5d29
LP
244 ansi_blue(), x, ansi_normal(),
245 y);
246 }
247 }
248
74d8ccd4
LP
249 return 0;
250}
251
291d565a
LP
252int show_cgroup_by_path(
253 const char *path,
254 const char *prefix,
bc28751e 255 size_t n_columns,
291d565a
LP
256 OutputFlags flags) {
257
7027ff61
LP
258 _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
259 _cleanup_closedir_ DIR *d = NULL;
fa776d8e 260 bool shown_pids = false;
74d8ccd4 261 char *gn = NULL;
fa776d8e
LP
262 int r;
263
b69d29ce
LP
264 assert(path);
265
fa776d8e
LP
266 if (n_columns <= 0)
267 n_columns = columns();
268
291d565a 269 prefix = strempty(prefix);
fa776d8e 270
7027ff61 271 r = cg_mangle_path(path, &fn);
c3175a7f 272 if (r < 0)
35d2e7ec 273 return r;
fa776d8e 274
c3175a7f 275 d = opendir(fn);
7027ff61 276 if (!d)
fa776d8e 277 return -errno;
fa776d8e 278
35d2e7ec 279 while ((r = cg_read_subgroup(d, &gn)) > 0) {
7027ff61 280 _cleanup_free_ char *k = NULL;
c3175a7f 281
657ee2d8 282 k = path_join(fn, gn);
c3175a7f 283 free(gn);
7027ff61
LP
284 if (!k)
285 return -ENOMEM;
c3175a7f 286
6f883237 287 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
c3175a7f 288 continue;
fa776d8e
LP
289
290 if (!shown_pids) {
0ff308c8 291 show_cgroup_one_by_path(path, prefix, n_columns, true, flags);
fa776d8e
LP
292 shown_pids = true;
293 }
294
295 if (last) {
5a5a5d29 296 r = show_cgroup_name(last, prefix, SPECIAL_GLYPH_TREE_BRANCH, flags);
74d8ccd4
LP
297 if (r < 0)
298 return r;
fa776d8e 299
c3175a7f 300 if (!p1) {
b910cc72 301 p1 = strjoin(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
7027ff61
LP
302 if (!p1)
303 return -ENOMEM;
c3175a7f 304 }
fa776d8e 305
0ff308c8 306 show_cgroup_by_path(last, p1, n_columns-2, flags);
fa776d8e 307 free(last);
fa776d8e
LP
308 }
309
ae2a15bc 310 last = TAKE_PTR(k);
fa776d8e
LP
311 }
312
35d2e7ec 313 if (r < 0)
7027ff61 314 return r;
35d2e7ec 315
fa776d8e 316 if (!shown_pids)
0ff308c8 317 show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
fa776d8e
LP
318
319 if (last) {
5a5a5d29 320 r = show_cgroup_name(last, prefix, SPECIAL_GLYPH_TREE_RIGHT, flags);
74d8ccd4
LP
321 if (r < 0)
322 return r;
fa776d8e 323
c3175a7f 324 if (!p2) {
b910cc72 325 p2 = strjoin(prefix, " ");
7027ff61
LP
326 if (!p2)
327 return -ENOMEM;
c3175a7f 328 }
fa776d8e 329
0ff308c8 330 show_cgroup_by_path(last, p2, n_columns-2, flags);
fa776d8e
LP
331 }
332
7027ff61 333 return 0;
ab35fb1b 334}
35d2e7ec 335
291d565a
LP
336int show_cgroup(const char *controller,
337 const char *path,
338 const char *prefix,
bc28751e 339 size_t n_columns,
291d565a 340 OutputFlags flags) {
7027ff61 341 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
342 int r;
343
35d2e7ec
LP
344 assert(path);
345
1e5678d0
LP
346 r = cg_get_path(controller, path, NULL, &p);
347 if (r < 0)
35d2e7ec
LP
348 return r;
349
0ff308c8 350 return show_cgroup_by_path(p, prefix, n_columns, flags);
35d2e7ec 351}
b69d29ce 352
291d565a
LP
353static int show_extra_pids(
354 const char *controller,
355 const char *path,
356 const char *prefix,
bc28751e 357 size_t n_columns,
291d565a
LP
358 const pid_t pids[],
359 unsigned n_pids,
360 OutputFlags flags) {
361
7fd1b19b 362 _cleanup_free_ pid_t *copy = NULL;
b69d29ce
LP
363 unsigned i, j;
364 int r;
365
b69d29ce
LP
366 assert(path);
367
368 if (n_pids <= 0)
369 return 0;
370
371 if (n_columns <= 0)
372 n_columns = columns();
373
7027ff61 374 prefix = strempty(prefix);
b69d29ce
LP
375
376 copy = new(pid_t, n_pids);
377 if (!copy)
378 return -ENOMEM;
379
380 for (i = 0, j = 0; i < n_pids; i++) {
7fd1b19b 381 _cleanup_free_ char *k = NULL;
b69d29ce 382
7027ff61 383 r = cg_pid_get_path(controller, pids[i], &k);
050fbdb8 384 if (r < 0)
b69d29ce 385 return r;
b69d29ce
LP
386
387 if (path_startswith(k, path))
388 continue;
389
390 copy[j++] = pids[i];
391 }
392
0ff308c8 393 show_pid_array(copy, j, prefix, n_columns, true, false, flags);
b69d29ce 394
b69d29ce
LP
395 return 0;
396}
397
291d565a
LP
398int show_cgroup_and_extra(
399 const char *controller,
400 const char *path,
401 const char *prefix,
bc28751e 402 size_t n_columns,
291d565a
LP
403 const pid_t extra_pids[],
404 unsigned n_extra_pids,
405 OutputFlags flags) {
406
b69d29ce
LP
407 int r;
408
b69d29ce
LP
409 assert(path);
410
0ff308c8 411 r = show_cgroup(controller, path, prefix, n_columns, flags);
b69d29ce
LP
412 if (r < 0)
413 return r;
414
9bdbc2e2 415 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
b69d29ce
LP
416}
417
bc06be75
ZJS
418int show_cgroup_get_unit_path_and_warn(
419 sd_bus *bus,
420 const char *unit,
421 char **ret) {
422
423 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
424 _cleanup_free_ char *path = NULL;
425 int r;
426
427 path = unit_dbus_path_from_name(unit);
428 if (!path)
429 return log_oom();
430
431 r = sd_bus_get_property_string(
432 bus,
433 "org.freedesktop.systemd1",
434 path,
435 unit_dbus_interface_from_name(unit),
436 "ControlGroup",
437 &error,
438 ret);
439 if (r < 0)
440 return log_error_errno(r, "Failed to query unit control group path: %s",
441 bus_error_message(&error, r));
442
443 return 0;
444}
445
d3e8277d 446int show_cgroup_get_path_and_warn(
049cb73b 447 const char *machine,
d3e8277d 448 const char *prefix,
049cb73b
ZJS
449 char **ret) {
450
d3e8277d 451 _cleanup_free_ char *root = NULL;
40d4320d 452 int r;
d3e8277d
ZJS
453
454 if (machine) {
d3e8277d 455 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
bc06be75 456 _cleanup_free_ char *unit = NULL;
d3e8277d
ZJS
457 const char *m;
458
40d4320d
LP
459 if (!hostname_is_valid(machine, 0))
460 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Machine name is not valid: %s", machine);
461
d3e8277d 462 m = strjoina("/run/systemd/machines/", machine);
13df9c39 463 r = parse_env_file(NULL, m, "SCOPE", &unit);
d3e8277d
ZJS
464 if (r < 0)
465 return log_error_errno(r, "Failed to load machine data: %m");
049cb73b 466
d3e8277d
ZJS
467 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
468 if (r < 0)
10a7340a 469 return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL);
d3e8277d 470
bc06be75 471 r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
d3e8277d 472 if (r < 0)
bc06be75 473 return r;
d3e8277d
ZJS
474 } else {
475 r = cg_get_root_path(&root);
049cb73b
ZJS
476 if (r == -ENOMEDIUM)
477 return log_error_errno(r, "Failed to get root control group path.\n"
478 "No cgroup filesystem mounted on /sys/fs/cgroup");
aa13d384 479 if (r < 0)
049cb73b 480 return log_error_errno(r, "Failed to get root control group path: %m");
049cb73b
ZJS
481 }
482
d3e8277d
ZJS
483 if (prefix) {
484 char *t;
049cb73b 485
aa13d384 486 t = path_join(root, prefix);
d3e8277d
ZJS
487 if (!t)
488 return log_oom();
049cb73b 489
d3e8277d 490 *ret = t;
1cc6c93a
YW
491 } else
492 *ret = TAKE_PTR(root);
049cb73b
ZJS
493
494 return 0;
495}