]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cgls/cgls.c
cgls: mangle user-provided unit names
[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 "parse-util.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 OutputFlags arg_output_flags = OUTPUT_CGROUP_XATTRS | OUTPUT_CGROUP_ID;
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 " --xattr=BOOL Show cgroup extended attributes\n"
58 " --cgroup-id=BOOL Show cgroup ID\n"
59 " -l --full Do not ellipsize output\n"
60 " -k Include kernel threads in output\n"
61 " -M --machine= Show container\n"
62 "\nSee the %s for details.\n",
63 program_invocation_short_name,
64 link);
65
66 return 0;
67 }
68
69 static int parse_argv(int argc, char *argv[]) {
70
71 enum {
72 ARG_NO_PAGER = 0x100,
73 ARG_VERSION,
74 ARG_USER_UNIT,
75 ARG_XATTR,
76 ARG_CGROUP_ID,
77 };
78
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 },
88 { "xattr", required_argument, NULL, ARG_XATTR },
89 { "cgroup-id", required_argument, NULL, ARG_CGROUP_ID },
90 {}
91 };
92
93 int c, r;
94
95 assert(argc >= 1);
96 assert(argv);
97
98 while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
99
100 switch (c) {
101
102 case 'h':
103 return help();
104
105 case ARG_VERSION:
106 return version();
107
108 case ARG_NO_PAGER:
109 arg_pager_flags |= PAGER_DISABLE;
110 break;
111
112 case 'a':
113 arg_output_flags |= OUTPUT_SHOW_ALL;
114 break;
115
116 case 'u':
117 arg_show_unit = SHOW_UNIT_SYSTEM;
118 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
119 return log_oom();
120 break;
121
122 case ARG_USER_UNIT:
123 arg_show_unit = SHOW_UNIT_USER;
124 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
125 return log_oom();
126 break;
127
128 case 1:
129 /* positional argument */
130 if (strv_push(&arg_names, optarg) < 0)
131 return log_oom();
132 break;
133
134 case 'l':
135 arg_full = true;
136 break;
137
138 case 'k':
139 arg_output_flags |= OUTPUT_KERNEL_THREADS;
140 break;
141
142 case 'M':
143 arg_machine = optarg;
144 break;
145
146 case ARG_XATTR:
147 r = parse_boolean(optarg);
148 if (r < 0)
149 return log_error_errno(r, "Failed to parse --xattr= value: %s", optarg);
150
151 SET_FLAG(arg_output_flags, OUTPUT_CGROUP_XATTRS, r);
152 break;
153
154 case ARG_CGROUP_ID:
155 r = parse_boolean(optarg);
156 if (r < 0)
157 return log_error_errno(r, "Failed to parse --cgroup-id= value: %s", optarg);
158
159 SET_FLAG(arg_output_flags, OUTPUT_CGROUP_ID, r);
160 break;
161
162 case '?':
163 return -EINVAL;
164
165 default:
166 assert_not_reached();
167 }
168
169 if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
170 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
171 "Cannot combine --unit or --user-unit with --machine=.");
172
173 return 1;
174 }
175
176 static void show_cg_info(const char *controller, const char *path) {
177
178 if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
179 printf("Controller %s; ", controller);
180
181 printf("Control group %s:\n", empty_to_root(path));
182 fflush(stdout);
183 }
184
185 static int run(int argc, char *argv[]) {
186 int r;
187
188 log_setup();
189
190 r = parse_argv(argc, argv);
191 if (r <= 0)
192 return r;
193
194 pager_open(arg_pager_flags);
195 if (arg_full < 0 && pager_have())
196 arg_full = true;
197
198 if (arg_full > 0)
199 arg_output_flags |= OUTPUT_FULL_WIDTH;
200
201 if (arg_names) {
202 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
203 _cleanup_free_ char *root = NULL;
204 char **name;
205
206 STRV_FOREACH(name, arg_names) {
207 int q;
208
209 if (arg_show_unit != SHOW_UNIT_NONE) {
210 /* Command line arguments are unit names */
211 _cleanup_free_ char *cgroup = NULL, *unit_name = NULL;
212
213 r = unit_name_mangle(*name, UNIT_NAME_MANGLE_WARN, &unit_name);
214 if (r < 0)
215 return log_error_errno(r, "Failed to mangle unit name: %m");
216
217 if (!bus) {
218 /* Connect to the bus only if necessary */
219 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
220 arg_show_unit == SHOW_UNIT_USER,
221 &bus);
222 if (r < 0)
223 return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL);
224 }
225
226 q = show_cgroup_get_unit_path_and_warn(bus, unit_name, &cgroup);
227 if (q < 0)
228 goto failed;
229
230 if (isempty(cgroup)) {
231 q = log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Unit %s not found.", unit_name);
232 goto failed;
233 }
234
235 printf("Unit %s (%s):\n", unit_name, cgroup);
236 fflush(stdout);
237
238 q = show_cgroup_by_path(cgroup, NULL, 0, arg_output_flags);
239
240 } else if (path_startswith(*name, "/sys/fs/cgroup")) {
241
242 printf("Directory %s:\n", *name);
243 fflush(stdout);
244
245 q = show_cgroup_by_path(*name, NULL, 0, arg_output_flags);
246 } else {
247 _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
248 const char *controller, *path;
249
250 if (!root) {
251 /* Query root only if needed, treat error as fatal */
252 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
253 if (r < 0)
254 return log_error_errno(r, "Failed to list cgroup tree: %m");
255 }
256
257 q = cg_split_spec(*name, &c, &p);
258 if (q < 0) {
259 log_error_errno(q, "Failed to split argument %s: %m", *name);
260 goto failed;
261 }
262
263 controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
264 if (p) {
265 j = path_join(root, p);
266 if (!j)
267 return log_oom();
268
269 path_simplify(j);
270 path = j;
271 } else
272 path = root;
273
274 show_cg_info(controller, path);
275
276 q = show_cgroup(controller, path, NULL, 0, arg_output_flags);
277 }
278
279 failed:
280 if (q < 0 && r >= 0)
281 r = q;
282 }
283
284 } else {
285 bool done = false;
286
287 if (!arg_machine) {
288 _cleanup_free_ char *cwd = NULL;
289
290 r = safe_getcwd(&cwd);
291 if (r < 0)
292 return log_error_errno(r, "Cannot determine current working directory: %m");
293
294 if (path_startswith(cwd, "/sys/fs/cgroup")) {
295 printf("Working directory %s:\n", cwd);
296 fflush(stdout);
297
298 r = show_cgroup_by_path(cwd, NULL, 0, arg_output_flags);
299 done = true;
300 }
301 }
302
303 if (!done) {
304 _cleanup_free_ char *root = NULL;
305
306 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
307 if (r < 0)
308 return log_error_errno(r, "Failed to list cgroup tree: %m");
309
310 show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
311
312 printf("-.slice\n");
313 r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_output_flags);
314 }
315 }
316 if (r < 0)
317 return log_error_errno(r, "Failed to list cgroup tree: %m");
318
319 return 0;
320 }
321
322 DEFINE_MAIN_FUNCTION(run);