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