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