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