]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cgls/cgls.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[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 "output-mode.h"
18 #include "pager.h"
19 #include "path-util.h"
20 #include "strv.h"
21 #include "terminal-util.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 char* arg_machine = NULL;
38
39 static int help(void) {
40 _cleanup_free_ char *link = NULL;
41 int r;
42
43 r = terminal_urlify_man("systemd-cgls", "1", &link);
44 if (r < 0)
45 return log_oom();
46
47 printf("%s [OPTIONS...] [CGROUP...]\n\n"
48 "Recursively show control group contents.\n\n"
49 " -h --help Show this help\n"
50 " --version Show package version\n"
51 " --no-pager Do not pipe output into a pager\n"
52 " -a --all Show all groups, including empty\n"
53 " -u --unit Show the subtrees of specifified system units\n"
54 " --user-unit Show the subtrees of specifified user units\n"
55 " -l --full Do not ellipsize output\n"
56 " -k Include kernel threads in output\n"
57 " -M --machine= Show container\n"
58 "\nSee the %s for details.\n"
59 , program_invocation_short_name
60 , link
61 );
62
63 return 0;
64 }
65
66 static int parse_argv(int argc, char *argv[]) {
67
68 enum {
69 ARG_NO_PAGER = 0x100,
70 ARG_VERSION,
71 ARG_USER_UNIT,
72 };
73
74 static const struct option options[] = {
75 { "help", no_argument, NULL, 'h' },
76 { "version", no_argument, NULL, ARG_VERSION },
77 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
78 { "all", no_argument, NULL, 'a' },
79 { "full", no_argument, NULL, 'l' },
80 { "machine", required_argument, NULL, 'M' },
81 { "unit", optional_argument, NULL, 'u' },
82 { "user-unit", optional_argument, NULL, ARG_USER_UNIT },
83 {}
84 };
85
86 int c;
87
88 assert(argc >= 1);
89 assert(argv);
90
91 while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
92
93 switch (c) {
94
95 case 'h':
96 return help();
97
98 case ARG_VERSION:
99 return version();
100
101 case ARG_NO_PAGER:
102 arg_pager_flags |= PAGER_DISABLE;
103 break;
104
105 case 'a':
106 arg_all = true;
107 break;
108
109 case 'u':
110 arg_show_unit = SHOW_UNIT_SYSTEM;
111 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
112 return log_oom();
113 break;
114
115 case ARG_USER_UNIT:
116 arg_show_unit = SHOW_UNIT_USER;
117 if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
118 return log_oom();
119 break;
120
121 case 1:
122 /* positional argument */
123 if (strv_push(&arg_names, optarg) < 0)
124 return log_oom();
125 break;
126
127 case 'l':
128 arg_full = true;
129 break;
130
131 case 'k':
132 arg_kernel_threads = true;
133 break;
134
135 case 'M':
136 arg_machine = optarg;
137 break;
138
139 case '?':
140 return -EINVAL;
141
142 default:
143 assert_not_reached("Unhandled option");
144 }
145
146 if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) {
147 log_error("Cannot combine --unit or --user-unit with --machine=.");
148 return -EINVAL;
149 }
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 int main(int argc, char *argv[]) {
164 int r, output_flags;
165
166 log_parse_environment();
167 log_open();
168
169 r = parse_argv(argc, argv);
170 if (r <= 0)
171 goto finish;
172
173 r = pager_open(arg_pager_flags);
174 if (r > 0 && arg_full < 0)
175 arg_full = true;
176
177 output_flags =
178 arg_all * OUTPUT_SHOW_ALL |
179 (arg_full > 0) * OUTPUT_FULL_WIDTH |
180 arg_kernel_threads * OUTPUT_KERNEL_THREADS;
181
182 if (arg_names) {
183 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
184 _cleanup_free_ char *root = NULL;
185 char **name;
186
187 STRV_FOREACH(name, arg_names) {
188 int q;
189
190 if (arg_show_unit != SHOW_UNIT_NONE) {
191 /* Command line arguments are unit names */
192 _cleanup_free_ char *cgroup = NULL;
193
194 if (!bus) {
195 /* Connect to the bus only if necessary */
196 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
197 arg_show_unit == SHOW_UNIT_USER,
198 &bus);
199 if (r < 0) {
200 log_error_errno(r, "Failed to create bus connection: %m");
201 goto finish;
202 }
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 goto finish;
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 r = log_oom();
248 goto finish;
249 }
250
251 path_simplify(j, false);
252 path = j;
253 } else
254 path = root;
255
256 show_cg_info(controller, path);
257
258 q = show_cgroup(controller, path, NULL, 0, output_flags);
259 }
260
261 failed:
262 if (q < 0 && r >= 0)
263 r = q;
264 }
265
266 } else {
267 bool done = false;
268
269 if (!arg_machine) {
270 _cleanup_free_ char *cwd = NULL;
271
272 r = safe_getcwd(&cwd);
273 if (r < 0) {
274 log_error_errno(r, "Cannot determine current working directory: %m");
275 goto finish;
276 }
277
278 if (path_startswith(cwd, "/sys/fs/cgroup")) {
279 printf("Working directory %s:\n", cwd);
280 fflush(stdout);
281
282 r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
283 done = true;
284 }
285 }
286
287 if (!done) {
288 _cleanup_free_ char *root = NULL;
289
290 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
291 if (r < 0)
292 goto finish;
293
294 show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
295
296 printf("-.slice\n");
297 r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
298 }
299 }
300
301 if (r < 0)
302 log_error_errno(r, "Failed to list cgroup tree: %m");
303
304 finish:
305 pager_close();
306 free(arg_names); /* don't free the strings */
307
308 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
309 }