]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cgroup-show.c
Add SPDX license identifiers to source files under the LGPL
[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 = k;
193 k = NULL;
194 }
195
196 if (r < 0)
197 return r;
198
199 if (!shown_pids)
200 show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
201
202 if (last) {
203 printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last)));
204
205 if (!p2) {
206 p2 = strappend(prefix, " ");
207 if (!p2)
208 return -ENOMEM;
209 }
210
211 show_cgroup_by_path(last, p2, n_columns-2, flags);
212 }
213
214 return 0;
215 }
216
217 int show_cgroup(const char *controller,
218 const char *path,
219 const char *prefix,
220 unsigned n_columns,
221 OutputFlags flags) {
222 _cleanup_free_ char *p = NULL;
223 int r;
224
225 assert(path);
226
227 r = cg_get_path(controller, path, NULL, &p);
228 if (r < 0)
229 return r;
230
231 return show_cgroup_by_path(p, prefix, n_columns, flags);
232 }
233
234 static int show_extra_pids(
235 const char *controller,
236 const char *path,
237 const char *prefix,
238 unsigned n_columns,
239 const pid_t pids[],
240 unsigned n_pids,
241 OutputFlags flags) {
242
243 _cleanup_free_ pid_t *copy = NULL;
244 unsigned i, j;
245 int r;
246
247 assert(path);
248
249 if (n_pids <= 0)
250 return 0;
251
252 if (n_columns <= 0)
253 n_columns = columns();
254
255 prefix = strempty(prefix);
256
257 copy = new(pid_t, n_pids);
258 if (!copy)
259 return -ENOMEM;
260
261 for (i = 0, j = 0; i < n_pids; i++) {
262 _cleanup_free_ char *k = NULL;
263
264 r = cg_pid_get_path(controller, pids[i], &k);
265 if (r < 0)
266 return r;
267
268 if (path_startswith(k, path))
269 continue;
270
271 copy[j++] = pids[i];
272 }
273
274 show_pid_array(copy, j, prefix, n_columns, true, false, flags);
275
276 return 0;
277 }
278
279 int show_cgroup_and_extra(
280 const char *controller,
281 const char *path,
282 const char *prefix,
283 unsigned n_columns,
284 const pid_t extra_pids[],
285 unsigned n_extra_pids,
286 OutputFlags flags) {
287
288 int r;
289
290 assert(path);
291
292 r = show_cgroup(controller, path, prefix, n_columns, flags);
293 if (r < 0)
294 return r;
295
296 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
297 }
298
299 int show_cgroup_and_extra_by_spec(
300 const char *spec,
301 const char *prefix,
302 unsigned n_columns,
303 const pid_t extra_pids[],
304 unsigned n_extra_pids,
305 OutputFlags flags) {
306
307 _cleanup_free_ char *controller = NULL, *path = NULL;
308 int r;
309
310 assert(spec);
311
312 r = cg_split_spec(spec, &controller, &path);
313 if (r < 0)
314 return r;
315
316 return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
317 }
318
319 int show_cgroup_get_unit_path_and_warn(
320 sd_bus *bus,
321 const char *unit,
322 char **ret) {
323
324 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
325 _cleanup_free_ char *path = NULL;
326 int r;
327
328 path = unit_dbus_path_from_name(unit);
329 if (!path)
330 return log_oom();
331
332 r = sd_bus_get_property_string(
333 bus,
334 "org.freedesktop.systemd1",
335 path,
336 unit_dbus_interface_from_name(unit),
337 "ControlGroup",
338 &error,
339 ret);
340 if (r < 0)
341 return log_error_errno(r, "Failed to query unit control group path: %s",
342 bus_error_message(&error, r));
343
344 return 0;
345 }
346
347 int show_cgroup_get_path_and_warn(
348 const char *machine,
349 const char *prefix,
350 char **ret) {
351
352 int r;
353 _cleanup_free_ char *root = NULL;
354
355 if (machine) {
356 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
357 _cleanup_free_ char *unit = NULL;
358 const char *m;
359
360 m = strjoina("/run/systemd/machines/", machine);
361 r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
362 if (r < 0)
363 return log_error_errno(r, "Failed to load machine data: %m");
364
365 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
366 if (r < 0)
367 return log_error_errno(r, "Failed to create bus connection: %m");
368
369 r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
370 if (r < 0)
371 return r;
372 } else {
373 r = cg_get_root_path(&root);
374 if (r == -ENOMEDIUM)
375 return log_error_errno(r, "Failed to get root control group path.\n"
376 "No cgroup filesystem mounted on /sys/fs/cgroup");
377 else if (r < 0)
378 return log_error_errno(r, "Failed to get root control group path: %m");
379 }
380
381 if (prefix) {
382 char *t;
383
384 t = strjoin(root, prefix);
385 if (!t)
386 return log_oom();
387
388 *ret = t;
389 } else {
390 *ret = root;
391 root = NULL;
392 }
393
394 return 0;
395 }