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