1:255.16-alt1
[systemd_ALT.git] / src / cgls / cgls.c
blob70fa260246929ea1e66b6a0b8b9869c63ca25b40
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <errno.h>
4 #include <getopt.h>
5 #include <stdio.h>
6 #include <unistd.h>
8 #include "sd-bus.h"
10 #include "alloc-util.h"
11 #include "build.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 "parse-util.h"
21 #include "path-util.h"
22 #include "pretty-print.h"
23 #include "strv.h"
24 #include "unit-name.h"
26 static PagerFlags arg_pager_flags = 0;
27 static OutputFlags arg_output_flags = 0;
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;
36 static int arg_full = -1;
37 static const char* arg_machine = NULL;
39 STATIC_DESTRUCTOR_REGISTER(arg_names, freep); /* don't free the strings */
41 static int help(void) {
42 _cleanup_free_ char *link = NULL;
43 int r;
45 r = terminal_urlify_man("systemd-cgls", "1", &link);
46 if (r < 0)
47 return log_oom();
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 " -x --xattr=BOOL Show cgroup extended attributes\n"
58 " -c --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=NAME Show container NAME\n"
62 "\nSee the %s for details.\n",
63 program_invocation_short_name,
64 link);
66 return 0;
69 static int parse_argv(int argc, char *argv[]) {
71 enum {
72 ARG_NO_PAGER = 0x100,
73 ARG_VERSION,
74 ARG_USER_UNIT,
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 { "xattr", required_argument, NULL, 'x' },
87 { "cgroup-id", required_argument, NULL, 'c' },
91 int c, r;
93 assert(argc >= 1);
94 assert(argv);
96 /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
97 * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
98 optind = 0;
99 while ((c = getopt_long(argc, argv, "-hkalM:u::xc", options, NULL)) >= 0)
101 switch (c) {
103 case 'h':
104 return help();
106 case ARG_VERSION:
107 return version();
109 case ARG_NO_PAGER:
110 arg_pager_flags |= PAGER_DISABLE;
111 break;
113 case 'a':
114 arg_output_flags |= OUTPUT_SHOW_ALL;
115 break;
117 case 'u':
118 arg_show_unit = SHOW_UNIT_SYSTEM;
119 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
120 return log_oom();
121 break;
123 case ARG_USER_UNIT:
124 arg_show_unit = SHOW_UNIT_USER;
125 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
126 return log_oom();
127 break;
129 case 1:
130 /* positional argument */
131 if (strv_push(&arg_names, optarg) < 0)
132 return log_oom();
133 break;
135 case 'l':
136 arg_full = true;
137 break;
139 case 'k':
140 arg_output_flags |= OUTPUT_KERNEL_THREADS;
141 break;
143 case 'M':
144 arg_machine = optarg;
145 break;
147 case 'x':
148 if (optarg) {
149 r = parse_boolean(optarg);
150 if (r < 0)
151 return log_error_errno(r, "Failed to parse --xattr= value: %s", optarg);
152 } else
153 r = true;
155 SET_FLAG(arg_output_flags, OUTPUT_CGROUP_XATTRS, r);
156 break;
158 case 'c':
159 if (optarg) {
160 r = parse_boolean(optarg);
161 if (r < 0)
162 return log_error_errno(r, "Failed to parse --cgroup-id= value: %s", optarg);
163 } else
164 r = true;
166 SET_FLAG(arg_output_flags, OUTPUT_CGROUP_ID, r);
167 break;
169 case '?':
170 return -EINVAL;
172 default:
173 assert_not_reached();
176 if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
177 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
178 "Cannot combine --unit or --user-unit with --machine=.");
180 return 1;
183 static void show_cg_info(const char *controller, const char *path) {
185 if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
186 printf("Controller %s; ", controller);
188 printf("CGroup %s:\n", empty_to_root(path));
189 fflush(stdout);
192 static int run(int argc, char *argv[]) {
193 int r;
195 log_setup();
197 r = parse_argv(argc, argv);
198 if (r <= 0)
199 return r;
201 pager_open(arg_pager_flags);
202 if (arg_full < 0 && pager_have())
203 arg_full = true;
205 if (arg_full > 0)
206 arg_output_flags |= OUTPUT_FULL_WIDTH;
208 if (arg_names) {
209 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
210 _cleanup_free_ char *root = NULL;
212 STRV_FOREACH(name, arg_names) {
213 int q;
215 if (arg_show_unit != SHOW_UNIT_NONE) {
216 /* Command line arguments are unit names */
217 _cleanup_free_ char *cgroup = NULL, *unit_name = NULL;
219 r = unit_name_mangle(*name, UNIT_NAME_MANGLE_WARN, &unit_name);
220 if (r < 0)
221 return log_error_errno(r, "Failed to mangle unit name: %m");
223 if (!bus) {
224 /* Connect to the bus only if necessary */
225 r = bus_connect_transport_systemd(
226 BUS_TRANSPORT_LOCAL, NULL,
227 arg_show_unit == SHOW_UNIT_USER ? RUNTIME_SCOPE_USER : RUNTIME_SCOPE_SYSTEM,
228 &bus);
229 if (r < 0)
230 return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL);
233 q = show_cgroup_get_unit_path_and_warn(bus, unit_name, &cgroup);
234 if (q < 0)
235 goto failed;
237 if (isempty(cgroup)) {
238 q = log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Unit %s not found.", unit_name);
239 goto failed;
242 printf("Unit %s (%s):\n", unit_name, cgroup);
243 fflush(stdout);
245 q = show_cgroup_by_path(cgroup, NULL, 0, arg_output_flags);
247 } else if (path_startswith(*name, "/sys/fs/cgroup")) {
249 printf("Directory %s:\n", *name);
250 fflush(stdout);
252 q = show_cgroup_by_path(*name, NULL, 0, arg_output_flags);
253 } else {
254 _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
255 const char *controller, *path;
257 if (!root) {
258 /* Query root only if needed, treat error as fatal */
259 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
260 if (r < 0)
261 return log_error_errno(r, "Failed to list cgroup tree: %m");
264 q = cg_split_spec(*name, &c, &p);
265 if (q < 0) {
266 log_error_errno(q, "Failed to split argument %s: %m", *name);
267 goto failed;
270 controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
271 if (p) {
272 j = path_join(root, p);
273 if (!j)
274 return log_oom();
276 path_simplify(j);
277 path = j;
278 } else
279 path = root;
281 show_cg_info(controller, path);
283 q = show_cgroup(controller, path, NULL, 0, arg_output_flags);
286 failed:
287 if (q < 0 && r >= 0)
288 r = q;
291 } else {
292 bool done = false;
294 if (!arg_machine) {
295 _cleanup_free_ char *cwd = NULL;
297 r = safe_getcwd(&cwd);
298 if (r < 0)
299 return log_error_errno(r, "Cannot determine current working directory: %m");
301 if (path_startswith(cwd, "/sys/fs/cgroup")) {
302 printf("Working directory %s:\n", cwd);
303 fflush(stdout);
305 r = show_cgroup_by_path(cwd, NULL, 0, arg_output_flags);
306 done = true;
310 if (!done) {
311 _cleanup_free_ char *root = NULL;
313 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
314 if (r < 0)
315 return log_error_errno(r, "Failed to list cgroup tree: %m");
317 show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
319 printf("-.slice\n");
320 r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_output_flags);
323 if (r < 0)
324 return log_error_errno(r, "Failed to list cgroup tree: %m");
326 return 0;
329 DEFINE_MAIN_FUNCTION(run);