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