]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cgls/cgls.c
Merge pull request #12392 from poettering/firstboot-salt
[thirdparty/systemd.git] / src / cgls / cgls.c
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 STATIC_DESTRUCTOR_REGISTER(arg_names, freep); /* don't free the strings */
41
42 static int help(void) {
43 _cleanup_free_ char *link = NULL;
44 int r;
45
46 r = terminal_urlify_man("systemd-cgls", "1", &link);
47 if (r < 0)
48 return log_oom();
49
50 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 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 };
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 assert(argc >= 1);
92 assert(argv);
93
94 while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
95
96 switch (c) {
97
98 case 'h':
99 return help();
100
101 case ARG_VERSION:
102 return version();
103
104 case ARG_NO_PAGER:
105 arg_pager_flags |= PAGER_DISABLE;
106 break;
107
108 case 'a':
109 arg_all = true;
110 break;
111
112 case 'u':
113 arg_show_unit = SHOW_UNIT_SYSTEM;
114 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
115 return log_oom();
116 break;
117
118 case ARG_USER_UNIT:
119 arg_show_unit = SHOW_UNIT_USER;
120 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
121 return log_oom();
122 break;
123
124 case 1:
125 /* positional argument */
126 if (strv_push(&arg_names, optarg) < 0)
127 return log_oom();
128 break;
129
130 case 'l':
131 arg_full = true;
132 break;
133
134 case 'k':
135 arg_kernel_threads = true;
136 break;
137
138 case 'M':
139 arg_machine = optarg;
140 break;
141
142 case '?':
143 return -EINVAL;
144
145 default:
146 assert_not_reached("Unhandled option");
147 }
148
149 if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
150 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
151 "Cannot combine --unit or --user-unit with --machine=.");
152
153 return 1;
154 }
155
156 static void show_cg_info(const char *controller, const char *path) {
157
158 if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
159 printf("Controller %s; ", controller);
160
161 printf("Control group %s:\n", empty_to_root(path));
162 fflush(stdout);
163 }
164
165 static int run(int argc, char *argv[]) {
166 int r, output_flags;
167
168 log_show_color(true);
169 log_parse_environment();
170 log_open();
171
172 r = parse_argv(argc, argv);
173 if (r <= 0)
174 return r;
175
176 r = pager_open(arg_pager_flags);
177 if (r > 0 && arg_full < 0)
178 arg_full = true;
179
180 output_flags =
181 arg_all * OUTPUT_SHOW_ALL |
182 (arg_full > 0) * OUTPUT_FULL_WIDTH |
183 arg_kernel_threads * OUTPUT_KERNEL_THREADS;
184
185 if (arg_names) {
186 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
187 _cleanup_free_ char *root = NULL;
188 char **name;
189
190 STRV_FOREACH(name, arg_names) {
191 int q;
192
193 if (arg_show_unit != SHOW_UNIT_NONE) {
194 /* Command line arguments are unit names */
195 _cleanup_free_ char *cgroup = NULL;
196
197 if (!bus) {
198 /* Connect to the bus only if necessary */
199 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
200 arg_show_unit == SHOW_UNIT_USER,
201 &bus);
202 if (r < 0)
203 return log_error_errno(r, "Failed to create bus connection: %m");
204 }
205
206 q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
207 if (q < 0)
208 goto failed;
209
210 if (isempty(cgroup)) {
211 log_warning("Unit %s not found.", *name);
212 q = -ENOENT;
213 goto failed;
214 }
215
216 printf("Unit %s (%s):\n", *name, cgroup);
217 fflush(stdout);
218
219 q = show_cgroup_by_path(cgroup, NULL, 0, output_flags);
220
221 } else if (path_startswith(*name, "/sys/fs/cgroup")) {
222
223 printf("Directory %s:\n", *name);
224 fflush(stdout);
225
226 q = show_cgroup_by_path(*name, NULL, 0, output_flags);
227 } else {
228 _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
229 const char *controller, *path;
230
231 if (!root) {
232 /* Query root only if needed, treat error as fatal */
233 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
234 if (r < 0)
235 return log_error_errno(r, "Failed to list cgroup tree: %m");
236 }
237
238 q = cg_split_spec(*name, &c, &p);
239 if (q < 0) {
240 log_error_errno(q, "Failed to split argument %s: %m", *name);
241 goto failed;
242 }
243
244 controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
245 if (p) {
246 j = strjoin(root, "/", p);
247 if (!j)
248 return log_oom();
249
250 path_simplify(j, false);
251 path = j;
252 } else
253 path = root;
254
255 show_cg_info(controller, path);
256
257 q = show_cgroup(controller, path, NULL, 0, output_flags);
258 }
259
260 failed:
261 if (q < 0 && r >= 0)
262 r = q;
263 }
264
265 } else {
266 bool done = false;
267
268 if (!arg_machine) {
269 _cleanup_free_ char *cwd = NULL;
270
271 r = safe_getcwd(&cwd);
272 if (r < 0)
273 return log_error_errno(r, "Cannot determine current working directory: %m");
274
275 if (path_startswith(cwd, "/sys/fs/cgroup")) {
276 printf("Working directory %s:\n", cwd);
277 fflush(stdout);
278
279 r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
280 done = true;
281 }
282 }
283
284 if (!done) {
285 _cleanup_free_ char *root = NULL;
286
287 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
288 if (r < 0)
289 return log_error_errno(r, "Failed to list cgroup tree: %m");
290
291 show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
292
293 printf("-.slice\n");
294 r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
295 }
296 }
297 if (r < 0)
298 return log_error_errno(r, "Failed to list cgroup tree: %m");
299
300 return 0;
301 }
302
303 DEFINE_MAIN_FUNCTION(run);