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