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