]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cgls/cgls.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[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 specifified system units\n"
57 " --user-unit Show the subtrees of specifified 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_parse_environment();
169 log_open();
170
171 r = parse_argv(argc, argv);
172 if (r <= 0)
173 return r;
174
175 r = pager_open(arg_pager_flags);
176 if (r > 0 && arg_full < 0)
177 arg_full = true;
178
179 output_flags =
180 arg_all * OUTPUT_SHOW_ALL |
181 (arg_full > 0) * OUTPUT_FULL_WIDTH |
182 arg_kernel_threads * OUTPUT_KERNEL_THREADS;
183
184 if (arg_names) {
185 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
186 _cleanup_free_ char *root = NULL;
187 char **name;
188
189 STRV_FOREACH(name, arg_names) {
190 int q;
191
192 if (arg_show_unit != SHOW_UNIT_NONE) {
193 /* Command line arguments are unit names */
194 _cleanup_free_ char *cgroup = NULL;
195
196 if (!bus) {
197 /* Connect to the bus only if necessary */
198 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
199 arg_show_unit == SHOW_UNIT_USER,
200 &bus);
201 if (r < 0)
202 return log_error_errno(r, "Failed to create bus connection: %m");
203 }
204
205 q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
206 if (q < 0)
207 goto failed;
208
209 if (isempty(cgroup)) {
210 log_warning("Unit %s not found.", *name);
211 q = -ENOENT;
212 goto failed;
213 }
214
215 printf("Unit %s (%s):\n", *name, cgroup);
216 fflush(stdout);
217
218 q = show_cgroup_by_path(cgroup, NULL, 0, output_flags);
219
220 } else if (path_startswith(*name, "/sys/fs/cgroup")) {
221
222 printf("Directory %s:\n", *name);
223 fflush(stdout);
224
225 q = show_cgroup_by_path(*name, NULL, 0, output_flags);
226 } else {
227 _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
228 const char *controller, *path;
229
230 if (!root) {
231 /* Query root only if needed, treat error as fatal */
232 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
233 if (r < 0)
234 return log_error_errno(r, "Failed to list cgroup tree: %m");
235 }
236
237 q = cg_split_spec(*name, &c, &p);
238 if (q < 0) {
239 log_error_errno(q, "Failed to split argument %s: %m", *name);
240 goto failed;
241 }
242
243 controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
244 if (p) {
245 j = strjoin(root, "/", p);
246 if (!j)
247 return log_oom();
248
249 path_simplify(j, false);
250 path = j;
251 } else
252 path = root;
253
254 show_cg_info(controller, path);
255
256 q = show_cgroup(controller, path, NULL, 0, output_flags);
257 }
258
259 failed:
260 if (q < 0 && r >= 0)
261 r = q;
262 }
263
264 } else {
265 bool done = false;
266
267 if (!arg_machine) {
268 _cleanup_free_ char *cwd = NULL;
269
270 r = safe_getcwd(&cwd);
271 if (r < 0)
272 return log_error_errno(r, "Cannot determine current working directory: %m");
273
274 if (path_startswith(cwd, "/sys/fs/cgroup")) {
275 printf("Working directory %s:\n", cwd);
276 fflush(stdout);
277
278 r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
279 done = true;
280 }
281 }
282
283 if (!done) {
284 _cleanup_free_ char *root = NULL;
285
286 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
287 if (r < 0)
288 return log_error_errno(r, "Failed to list cgroup tree: %m");
289
290 show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
291
292 printf("-.slice\n");
293 r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
294 }
295 }
296 if (r < 0)
297 return log_error_errno(r, "Failed to list cgroup tree: %m");
298
299 return 0;
300 }
301
302 DEFINE_MAIN_FUNCTION(run);