Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <getopt.h>
5 : : #include <stdio.h>
6 : : #include <string.h>
7 : : #include <unistd.h>
8 : :
9 : : #include "sd-bus.h"
10 : :
11 : : #include "alloc-util.h"
12 : : #include "bus-util.h"
13 : : #include "cgroup-show.h"
14 : : #include "cgroup-util.h"
15 : : #include "fileio.h"
16 : : #include "log.h"
17 : : #include "main-func.h"
18 : : #include "output-mode.h"
19 : : #include "pager.h"
20 : : #include "path-util.h"
21 : : #include "pretty-print.h"
22 : : #include "strv.h"
23 : : #include "unit-name.h"
24 : : #include "util.h"
25 : :
26 : : static PagerFlags arg_pager_flags = 0;
27 : : static bool arg_kernel_threads = false;
28 : : static bool arg_all = false;
29 : :
30 : : static enum {
31 : : SHOW_UNIT_NONE,
32 : : SHOW_UNIT_SYSTEM,
33 : : SHOW_UNIT_USER,
34 : : } arg_show_unit = SHOW_UNIT_NONE;
35 : : static char **arg_names = NULL;
36 : :
37 : : static int arg_full = -1;
38 : : static const char* arg_machine = NULL;
39 : :
40 : 16 : STATIC_DESTRUCTOR_REGISTER(arg_names, freep); /* don't free the strings */
41 : :
42 : 12 : static int help(void) {
43 : 12 : _cleanup_free_ char *link = NULL;
44 : : int r;
45 : :
46 : 12 : r = terminal_urlify_man("systemd-cgls", "1", &link);
47 [ - + ]: 12 : if (r < 0)
48 : 0 : return log_oom();
49 : :
50 : 12 : printf("%s [OPTIONS...] [CGROUP...]\n\n"
51 : : "Recursively show control group contents.\n\n"
52 : : " -h --help Show this help\n"
53 : : " --version Show package version\n"
54 : : " --no-pager Do not pipe output into a pager\n"
55 : : " -a --all Show all groups, including empty\n"
56 : : " -u --unit Show the subtrees of specified system units\n"
57 : : " --user-unit Show the subtrees of specified user units\n"
58 : : " -l --full Do not ellipsize output\n"
59 : : " -k Include kernel threads in output\n"
60 : : " -M --machine= Show container\n"
61 : : "\nSee the %s for details.\n"
62 : : , program_invocation_short_name
63 : : , link
64 : : );
65 : :
66 : 12 : return 0;
67 : : }
68 : :
69 : 16 : static int parse_argv(int argc, char *argv[]) {
70 : :
71 : : enum {
72 : : ARG_NO_PAGER = 0x100,
73 : : ARG_VERSION,
74 : : ARG_USER_UNIT,
75 : : };
76 : :
77 : : static const struct option options[] = {
78 : : { "help", no_argument, NULL, 'h' },
79 : : { "version", no_argument, NULL, ARG_VERSION },
80 : : { "no-pager", no_argument, NULL, ARG_NO_PAGER },
81 : : { "all", no_argument, NULL, 'a' },
82 : : { "full", no_argument, NULL, 'l' },
83 : : { "machine", required_argument, NULL, 'M' },
84 : : { "unit", optional_argument, NULL, 'u' },
85 : : { "user-unit", optional_argument, NULL, ARG_USER_UNIT },
86 : : {}
87 : : };
88 : :
89 : : int c;
90 : :
91 [ - + ]: 16 : assert(argc >= 1);
92 [ - + ]: 16 : assert(argv);
93 : :
94 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
95 : :
96 [ + - - - : 16 : switch (c) {
- - - - -
- + - ]
97 : :
98 : 12 : case 'h':
99 : 12 : return help();
100 : :
101 : 0 : case ARG_VERSION:
102 : 0 : return version();
103 : :
104 : 0 : case ARG_NO_PAGER:
105 : 0 : arg_pager_flags |= PAGER_DISABLE;
106 : 0 : break;
107 : :
108 : 0 : case 'a':
109 : 0 : arg_all = true;
110 : 0 : break;
111 : :
112 : 0 : case 'u':
113 : 0 : arg_show_unit = SHOW_UNIT_SYSTEM;
114 [ # # ]: 0 : if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
115 : 0 : return log_oom();
116 : 0 : break;
117 : :
118 : 0 : case ARG_USER_UNIT:
119 : 0 : arg_show_unit = SHOW_UNIT_USER;
120 [ # # ]: 0 : if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
121 : 0 : return log_oom();
122 : 0 : break;
123 : :
124 : 0 : case 1:
125 : : /* positional argument */
126 [ # # ]: 0 : if (strv_push(&arg_names, optarg) < 0)
127 : 0 : return log_oom();
128 : 0 : break;
129 : :
130 : 0 : case 'l':
131 : 0 : arg_full = true;
132 : 0 : break;
133 : :
134 : 0 : case 'k':
135 : 0 : arg_kernel_threads = true;
136 : 0 : break;
137 : :
138 : 0 : case 'M':
139 : 0 : arg_machine = optarg;
140 : 0 : break;
141 : :
142 : 4 : case '?':
143 : 4 : return -EINVAL;
144 : :
145 : 0 : default:
146 : 0 : assert_not_reached("Unhandled option");
147 : : }
148 : :
149 [ # # # # ]: 0 : if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
150 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
151 : : "Cannot combine --unit or --user-unit with --machine=.");
152 : :
153 : 0 : return 1;
154 : : }
155 : :
156 : 0 : static void show_cg_info(const char *controller, const char *path) {
157 : :
158 [ # # # # : 0 : if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
# # ]
159 : 0 : printf("Controller %s; ", controller);
160 : :
161 : 0 : printf("Control group %s:\n", empty_to_root(path));
162 : 0 : fflush(stdout);
163 : 0 : }
164 : :
165 : 16 : static int run(int argc, char *argv[]) {
166 : : int r, output_flags;
167 : :
168 : 16 : log_show_color(true);
169 : 16 : log_parse_environment();
170 : 16 : log_open();
171 : :
172 : 16 : r = parse_argv(argc, argv);
173 [ + - ]: 16 : if (r <= 0)
174 : 16 : return r;
175 : :
176 : 0 : r = pager_open(arg_pager_flags);
177 [ # # # # ]: 0 : if (r > 0 && arg_full < 0)
178 : 0 : arg_full = true;
179 : :
180 : 0 : output_flags =
181 : 0 : arg_all * OUTPUT_SHOW_ALL |
182 [ # # ]: 0 : (arg_full > 0) * OUTPUT_FULL_WIDTH |
183 : 0 : arg_kernel_threads * OUTPUT_KERNEL_THREADS;
184 : :
185 [ # # ]: 0 : if (arg_names) {
186 [ # # ]: 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
187 [ # # ]: 0 : _cleanup_free_ char *root = NULL;
188 : : char **name;
189 : :
190 [ # # # # ]: 0 : STRV_FOREACH(name, arg_names) {
191 : : int q;
192 : :
193 [ # # ]: 0 : if (arg_show_unit != SHOW_UNIT_NONE) {
194 : : /* Command line arguments are unit names */
195 [ # # # ]: 0 : _cleanup_free_ char *cgroup = NULL;
196 : :
197 [ # # ]: 0 : if (!bus) {
198 : : /* Connect to the bus only if necessary */
199 : 0 : r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
200 : : arg_show_unit == SHOW_UNIT_USER,
201 : : &bus);
202 [ # # ]: 0 : if (r < 0)
203 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
204 : : }
205 : :
206 : 0 : q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
207 [ # # ]: 0 : if (q < 0)
208 : 0 : goto failed;
209 : :
210 [ # # ]: 0 : if (isempty(cgroup)) {
211 [ # # ]: 0 : log_warning("Unit %s not found.", *name);
212 : 0 : q = -ENOENT;
213 : 0 : goto failed;
214 : : }
215 : :
216 : 0 : printf("Unit %s (%s):\n", *name, cgroup);
217 : 0 : fflush(stdout);
218 : :
219 : 0 : q = show_cgroup_by_path(cgroup, NULL, 0, output_flags);
220 : :
221 [ # # ]: 0 : } else if (path_startswith(*name, "/sys/fs/cgroup")) {
222 : :
223 : 0 : printf("Directory %s:\n", *name);
224 : 0 : fflush(stdout);
225 : :
226 : 0 : q = show_cgroup_by_path(*name, NULL, 0, output_flags);
227 : : } else {
228 [ # # # # : 0 : _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
# # # #
# ]
229 : : const char *controller, *path;
230 : :
231 [ # # ]: 0 : if (!root) {
232 : : /* Query root only if needed, treat error as fatal */
233 : 0 : r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
234 [ # # ]: 0 : if (r < 0)
235 [ # # ]: 0 : return log_error_errno(r, "Failed to list cgroup tree: %m");
236 : : }
237 : :
238 : 0 : q = cg_split_spec(*name, &c, &p);
239 [ # # ]: 0 : if (q < 0) {
240 [ # # ]: 0 : log_error_errno(q, "Failed to split argument %s: %m", *name);
241 : 0 : goto failed;
242 : : }
243 : :
244 [ # # ]: 0 : controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
245 [ # # ]: 0 : if (p) {
246 : 0 : j = path_join(root, p);
247 [ # # ]: 0 : if (!j)
248 : 0 : return log_oom();
249 : :
250 : 0 : path_simplify(j, false);
251 : 0 : path = j;
252 : : } else
253 : 0 : path = root;
254 : :
255 : 0 : show_cg_info(controller, path);
256 : :
257 : 0 : q = show_cgroup(controller, path, NULL, 0, output_flags);
258 : : }
259 : :
260 : 0 : failed:
261 [ # # # # ]: 0 : if (q < 0 && r >= 0)
262 : 0 : r = q;
263 : : }
264 : :
265 : : } else {
266 : 0 : bool done = false;
267 : :
268 [ # # ]: 0 : if (!arg_machine) {
269 [ # # ]: 0 : _cleanup_free_ char *cwd = NULL;
270 : :
271 : 0 : r = safe_getcwd(&cwd);
272 [ # # ]: 0 : if (r < 0)
273 [ # # ]: 0 : return log_error_errno(r, "Cannot determine current working directory: %m");
274 : :
275 [ # # ]: 0 : if (path_startswith(cwd, "/sys/fs/cgroup")) {
276 : 0 : printf("Working directory %s:\n", cwd);
277 : 0 : fflush(stdout);
278 : :
279 : 0 : r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
280 : 0 : done = true;
281 : : }
282 : : }
283 : :
284 [ # # ]: 0 : if (!done) {
285 [ # # ]: 0 : _cleanup_free_ char *root = NULL;
286 : :
287 : 0 : r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
288 [ # # ]: 0 : if (r < 0)
289 [ # # ]: 0 : return log_error_errno(r, "Failed to list cgroup tree: %m");
290 : :
291 : 0 : show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
292 : :
293 : 0 : printf("-.slice\n");
294 : 0 : r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
295 : : }
296 : : }
297 [ # # ]: 0 : if (r < 0)
298 [ # # ]: 0 : return log_error_errno(r, "Failed to list cgroup tree: %m");
299 : :
300 : 0 : return 0;
301 : : }
302 : :
303 : 16 : DEFINE_MAIN_FUNCTION(run);
|