1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
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.
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.
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/>.
28 #include "alloc-util.h"
29 #include "bus-error.h"
31 #include "cgroup-show.h"
32 #include "cgroup-util.h"
35 #include "format-util.h"
36 #include "locale-util.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"
45 static void show_pid_array(
54 unsigned i
, j
, pid_width
;
59 qsort(pids
, n_pids
, sizeof(pid_t
), pid_compare_func
);
61 /* Filter duplicates */
62 for (j
= 0, i
= 1; i
< n_pids
; i
++) {
63 if (pids
[i
] == pids
[j
])
68 pid_width
= DECIMAL_STR_WIDTH(pids
[j
]);
70 if (flags
& OUTPUT_FULL_WIDTH
)
73 if (n_columns
> pid_width
+2)
74 n_columns
-= pid_width
+2;
78 for (i
= 0; i
< n_pids
; i
++) {
79 _cleanup_free_
char *t
= NULL
;
81 (void) get_process_cmdline(pids
[i
], n_columns
, true, &t
);
84 printf("%s%s ", prefix
, special_glyph(TRIANGULAR_BULLET
));
86 printf("%s%s", prefix
, special_glyph(((more
|| i
< n_pids
-1) ? TREE_BRANCH
: TREE_RIGHT
)));
88 printf("%*"PID_PRI
" %s\n", pid_width
, pids
[i
], strna(t
));
92 static int show_cgroup_one_by_path(
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
;
107 r
= cg_mangle_path(path
, &p
);
111 fn
= strjoina(p
, "/cgroup.procs");
116 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
118 if (!(flags
& OUTPUT_KERNEL_THREADS
) && is_kernel_thread(pid
) > 0)
121 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ 1))
124 assert(n
< n_allocated
);
131 show_pid_array(pids
, n
, prefix
, n_columns
, false, more
, flags
);
136 int show_cgroup_by_path(
142 _cleanup_free_
char *fn
= NULL
, *p1
= NULL
, *last
= NULL
, *p2
= NULL
;
143 _cleanup_closedir_
DIR *d
= NULL
;
145 bool shown_pids
= false;
151 n_columns
= columns();
153 prefix
= strempty(prefix
);
155 r
= cg_mangle_path(path
, &fn
);
163 while ((r
= cg_read_subgroup(d
, &gn
)) > 0) {
164 _cleanup_free_
char *k
= NULL
;
166 k
= strjoin(fn
, "/", gn
);
171 if (!(flags
& OUTPUT_SHOW_ALL
) && cg_is_empty_recursive(NULL
, k
) > 0)
175 show_cgroup_one_by_path(path
, prefix
, n_columns
, true, flags
);
180 printf("%s%s%s\n", prefix
, special_glyph(TREE_BRANCH
), cg_unescape(basename(last
)));
183 p1
= strappend(prefix
, special_glyph(TREE_VERTICAL
));
188 show_cgroup_by_path(last
, p1
, n_columns
-2, flags
);
200 show_cgroup_one_by_path(path
, prefix
, n_columns
, !!last
, flags
);
203 printf("%s%s%s\n", prefix
, special_glyph(TREE_RIGHT
), cg_unescape(basename(last
)));
206 p2
= strappend(prefix
, " ");
211 show_cgroup_by_path(last
, p2
, n_columns
-2, flags
);
217 int show_cgroup(const char *controller
,
222 _cleanup_free_
char *p
= NULL
;
227 r
= cg_get_path(controller
, path
, NULL
, &p
);
231 return show_cgroup_by_path(p
, prefix
, n_columns
, flags
);
234 static int show_extra_pids(
235 const char *controller
,
243 _cleanup_free_ pid_t
*copy
= NULL
;
253 n_columns
= columns();
255 prefix
= strempty(prefix
);
257 copy
= new(pid_t
, n_pids
);
261 for (i
= 0, j
= 0; i
< n_pids
; i
++) {
262 _cleanup_free_
char *k
= NULL
;
264 r
= cg_pid_get_path(controller
, pids
[i
], &k
);
268 if (path_startswith(k
, path
))
274 show_pid_array(copy
, j
, prefix
, n_columns
, true, false, flags
);
279 int show_cgroup_and_extra(
280 const char *controller
,
284 const pid_t extra_pids
[],
285 unsigned n_extra_pids
,
292 r
= show_cgroup(controller
, path
, prefix
, n_columns
, flags
);
296 return show_extra_pids(controller
, path
, prefix
, n_columns
, extra_pids
, n_extra_pids
, flags
);
299 int show_cgroup_and_extra_by_spec(
303 const pid_t extra_pids
[],
304 unsigned n_extra_pids
,
307 _cleanup_free_
char *controller
= NULL
, *path
= NULL
;
312 r
= cg_split_spec(spec
, &controller
, &path
);
316 return show_cgroup_and_extra(controller
, path
, prefix
, n_columns
, extra_pids
, n_extra_pids
, flags
);
319 int show_cgroup_get_unit_path_and_warn(
324 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
325 _cleanup_free_
char *path
= NULL
;
328 path
= unit_dbus_path_from_name(unit
);
332 r
= sd_bus_get_property_string(
334 "org.freedesktop.systemd1",
336 unit_dbus_interface_from_name(unit
),
341 return log_error_errno(r
, "Failed to query unit control group path: %s",
342 bus_error_message(&error
, r
));
347 int show_cgroup_get_path_and_warn(
353 _cleanup_free_
char *root
= NULL
;
356 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
357 _cleanup_free_
char *unit
= NULL
;
360 m
= strjoina("/run/systemd/machines/", machine
);
361 r
= parse_env_file(m
, NEWLINE
, "SCOPE", &unit
, NULL
);
363 return log_error_errno(r
, "Failed to load machine data: %m");
365 r
= bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL
, NULL
, false, &bus
);
367 return log_error_errno(r
, "Failed to create bus connection: %m");
369 r
= show_cgroup_get_unit_path_and_warn(bus
, unit
, &root
);
373 r
= cg_get_root_path(&root
);
375 return log_error_errno(r
, "Failed to get root control group path.\n"
376 "No cgroup filesystem mounted on /sys/fs/cgroup");
378 return log_error_errno(r
, "Failed to get root control group path: %m");
384 t
= strjoin(root
, prefix
);