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/>.
29 #include "alloc-util.h"
31 #include "cgroup-show.h"
32 #include "cgroup-util.h"
35 #include "output-mode.h"
37 #include "path-util.h"
39 #include "unit-name.h"
42 static bool arg_no_pager
= false;
43 static bool arg_kernel_threads
= false;
44 static bool arg_all
= false;
50 } arg_show_unit
= SHOW_UNIT_NONE
;
51 static char **arg_names
= NULL
;
53 static int arg_full
= -1;
54 static char* arg_machine
= NULL
;
56 static void help(void) {
57 printf("%s [OPTIONS...] [CGROUP...]\n\n"
58 "Recursively show control group contents.\n\n"
59 " -h --help Show this help\n"
60 " --version Show package version\n"
61 " --no-pager Do not pipe output into a pager\n"
62 " -a --all Show all groups, including empty\n"
63 " -u --unit Show the subtrees of specifified system units\n"
64 " --user-unit Show the subtrees of specifified user units\n"
65 " -l --full Do not ellipsize output\n"
66 " -k Include kernel threads in output\n"
67 " -M --machine= Show container\n"
68 , program_invocation_short_name
);
71 static int parse_argv(int argc
, char *argv
[]) {
79 static const struct option options
[] = {
80 { "help", no_argument
, NULL
, 'h' },
81 { "version", no_argument
, NULL
, ARG_VERSION
},
82 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
83 { "all", no_argument
, NULL
, 'a' },
84 { "full", no_argument
, NULL
, 'l' },
85 { "machine", required_argument
, NULL
, 'M' },
86 { "unit", optional_argument
, NULL
, 'u' },
87 { "user-unit", optional_argument
, NULL
, ARG_USER_UNIT
},
96 while ((c
= getopt_long(argc
, argv
, "-hkalM:u::", options
, NULL
)) >= 0)
116 arg_show_unit
= SHOW_UNIT_SYSTEM
;
117 if (strv_push(&arg_names
, optarg
) < 0) /* push optarg if not empty */
122 arg_show_unit
= SHOW_UNIT_USER
;
123 if (strv_push(&arg_names
, optarg
) < 0) /* push optarg if not empty */
128 /* positional argument */
129 if (strv_push(&arg_names
, optarg
) < 0)
138 arg_kernel_threads
= true;
142 arg_machine
= optarg
;
149 assert_not_reached("Unhandled option");
152 if (arg_machine
&& arg_show_unit
!= SHOW_UNIT_NONE
) {
153 log_error("Cannot combine --unit or --user-unit with --machine=.");
160 static void show_cg_info(const char *controller
, const char *path
) {
162 if (cg_all_unified() == 0 && controller
&& !streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
163 printf("Controller %s; ", controller
);
165 printf("Control group %s:\n", isempty(path
) ? "/" : path
);
169 int main(int argc
, char *argv
[]) {
172 log_parse_environment();
175 r
= parse_argv(argc
, argv
);
180 r
= pager_open(arg_no_pager
, false);
181 if (r
> 0 && arg_full
< 0)
186 arg_all
* OUTPUT_SHOW_ALL
|
187 (arg_full
> 0) * OUTPUT_FULL_WIDTH
|
188 arg_kernel_threads
* OUTPUT_KERNEL_THREADS
;
191 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
192 _cleanup_free_
char *root
= NULL
;
195 STRV_FOREACH(name
, arg_names
) {
198 if (arg_show_unit
!= SHOW_UNIT_NONE
) {
199 /* Command line arguments are unit names */
200 _cleanup_free_
char *cgroup
= NULL
;
203 /* Connect to the bus only if necessary */
204 r
= bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL
, NULL
,
205 arg_show_unit
== SHOW_UNIT_USER
,
208 log_error_errno(r
, "Failed to create bus connection: %m");
213 q
= show_cgroup_get_unit_path_and_warn(bus
, *name
, &cgroup
);
217 if (isempty(cgroup
)) {
218 log_warning("Unit %s not found.", *name
);
223 printf("Unit %s (%s):\n", *name
, cgroup
);
226 q
= show_cgroup_by_path(cgroup
, NULL
, 0, output_flags
);
228 } else if (path_startswith(*name
, "/sys/fs/cgroup")) {
230 printf("Directory %s:\n", *name
);
233 q
= show_cgroup_by_path(*name
, NULL
, 0, output_flags
);
235 _cleanup_free_
char *c
= NULL
, *p
= NULL
, *j
= NULL
;
236 const char *controller
, *path
;
239 /* Query root only if needed, treat error as fatal */
240 r
= show_cgroup_get_path_and_warn(arg_machine
, NULL
, &root
);
245 q
= cg_split_spec(*name
, &c
, &p
);
247 log_error_errno(q
, "Failed to split argument %s: %m", *name
);
251 controller
= c
?: SYSTEMD_CGROUP_CONTROLLER
;
253 j
= strjoin(root
, "/", p
);
259 path_kill_slashes(j
);
264 show_cg_info(controller
, path
);
266 q
= show_cgroup(controller
, path
, NULL
, 0, output_flags
);
278 _cleanup_free_
char *cwd
= NULL
;
280 cwd
= get_current_dir_name();
282 r
= log_error_errno(errno
, "Cannot determine current working directory: %m");
286 if (path_startswith(cwd
, "/sys/fs/cgroup")) {
287 printf("Working directory %s:\n", cwd
);
290 r
= show_cgroup_by_path(cwd
, NULL
, 0, output_flags
);
296 _cleanup_free_
char *root
= NULL
;
298 r
= show_cgroup_get_path_and_warn(arg_machine
, NULL
, &root
);
302 show_cg_info(SYSTEMD_CGROUP_CONTROLLER
, root
);
305 r
= show_cgroup(SYSTEMD_CGROUP_CONTROLLER
, root
, NULL
, 0, output_flags
);
310 log_error_errno(r
, "Failed to list cgroup tree: %m");
314 free(arg_names
); /* don't free the strings */
316 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;