]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
fa776d8e | 2 | |
fa776d8e LP |
3 | #include <errno.h> |
4 | #include <getopt.h> | |
3f6fd1ba | 5 | #include <stdio.h> |
fa776d8e | 6 | #include <string.h> |
3f6fd1ba LP |
7 | #include <unistd.h> |
8 | ||
9 | #include "sd-bus.h" | |
fa776d8e | 10 | |
b5efdb8a | 11 | #include "alloc-util.h" |
3f6fd1ba | 12 | #include "bus-util.h" |
fa776d8e | 13 | #include "cgroup-show.h" |
c6c18be3 | 14 | #include "cgroup-util.h" |
3f6fd1ba | 15 | #include "fileio.h" |
fa776d8e | 16 | #include "log.h" |
9bdbc2e2 | 17 | #include "output-mode.h" |
3f6fd1ba LP |
18 | #include "pager.h" |
19 | #include "path-util.h" | |
256c1eba | 20 | #include "strv.h" |
37ec0fdd | 21 | #include "terminal-util.h" |
13be4979 | 22 | #include "unit-name.h" |
3f6fd1ba | 23 | #include "util.h" |
1968a360 LP |
24 | |
25 | static bool arg_no_pager = false; | |
1e5678d0 | 26 | static bool arg_kernel_threads = false; |
c3175a7f | 27 | static bool arg_all = false; |
e7aa3f50 ZJS |
28 | |
29 | static enum { | |
30 | SHOW_UNIT_NONE, | |
31 | SHOW_UNIT_SYSTEM, | |
32 | SHOW_UNIT_USER, | |
33 | } arg_show_unit = SHOW_UNIT_NONE; | |
256c1eba | 34 | static char **arg_names = NULL; |
e7aa3f50 | 35 | |
9bdbc2e2 | 36 | static int arg_full = -1; |
38158b92 | 37 | static char* arg_machine = NULL; |
fa776d8e | 38 | |
37ec0fdd LP |
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 | ||
fa776d8e LP |
47 | printf("%s [OPTIONS...] [CGROUP...]\n\n" |
48 | "Recursively show control group contents.\n\n" | |
69fc152f | 49 | " -h --help Show this help\n" |
c3175a7f | 50 | " --version Show package version\n" |
1e5678d0 | 51 | " --no-pager Do not pipe output into a pager\n" |
c3175a7f | 52 | " -a --all Show all groups, including empty\n" |
e7aa3f50 ZJS |
53 | " -u --unit Show the subtrees of specifified system units\n" |
54 | " --user-unit Show the subtrees of specifified user units\n" | |
98a6e132 | 55 | " -l --full Do not ellipsize output\n" |
38158b92 | 56 | " -k Include kernel threads in output\n" |
e049fa16 | 57 | " -M --machine= Show container\n" |
37ec0fdd LP |
58 | "\nSee the %s for details.\n" |
59 | , program_invocation_short_name | |
60 | , link | |
61 | ); | |
62 | ||
63 | return 0; | |
fa776d8e LP |
64 | } |
65 | ||
66 | static int parse_argv(int argc, char *argv[]) { | |
67 | ||
1968a360 | 68 | enum { |
c3175a7f | 69 | ARG_NO_PAGER = 0x100, |
9bdbc2e2 | 70 | ARG_VERSION, |
e7aa3f50 | 71 | ARG_USER_UNIT, |
1968a360 LP |
72 | }; |
73 | ||
fa776d8e | 74 | static const struct option options[] = { |
e7aa3f50 ZJS |
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' }, | |
256c1eba ZJS |
81 | { "unit", optional_argument, NULL, 'u' }, |
82 | { "user-unit", optional_argument, NULL, ARG_USER_UNIT }, | |
eb9da376 | 83 | {} |
fa776d8e LP |
84 | }; |
85 | ||
86 | int c; | |
87 | ||
88 | assert(argc >= 1); | |
89 | assert(argv); | |
90 | ||
256c1eba | 91 | while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0) |
fa776d8e LP |
92 | |
93 | switch (c) { | |
94 | ||
95 | case 'h': | |
37ec0fdd | 96 | return help(); |
fa776d8e | 97 | |
c3175a7f | 98 | case ARG_VERSION: |
3f6fd1ba | 99 | return version(); |
c3175a7f | 100 | |
1968a360 LP |
101 | case ARG_NO_PAGER: |
102 | arg_no_pager = true; | |
103 | break; | |
104 | ||
c3175a7f LP |
105 | case 'a': |
106 | arg_all = true; | |
107 | break; | |
108 | ||
d9855d87 | 109 | case 'u': |
e7aa3f50 | 110 | arg_show_unit = SHOW_UNIT_SYSTEM; |
256c1eba ZJS |
111 | if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */ |
112 | return log_oom(); | |
e7aa3f50 ZJS |
113 | break; |
114 | ||
115 | case ARG_USER_UNIT: | |
116 | arg_show_unit = SHOW_UNIT_USER; | |
256c1eba ZJS |
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(); | |
d9855d87 ZJS |
125 | break; |
126 | ||
98a6e132 | 127 | case 'l': |
9bdbc2e2 LN |
128 | arg_full = true; |
129 | break; | |
130 | ||
1e5678d0 LP |
131 | case 'k': |
132 | arg_kernel_threads = true; | |
133 | break; | |
134 | ||
38158b92 ZJS |
135 | case 'M': |
136 | arg_machine = optarg; | |
137 | break; | |
138 | ||
fa776d8e LP |
139 | case '?': |
140 | return -EINVAL; | |
141 | ||
142 | default: | |
eb9da376 | 143 | assert_not_reached("Unhandled option"); |
fa776d8e | 144 | } |
fa776d8e | 145 | |
e7aa3f50 | 146 | if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) { |
ed737400 | 147 | log_error("Cannot combine --unit or --user-unit with --machine=."); |
d9855d87 ZJS |
148 | return -EINVAL; |
149 | } | |
150 | ||
fa776d8e LP |
151 | return 1; |
152 | } | |
153 | ||
58033c91 | 154 | static void show_cg_info(const char *controller, const char *path) { |
a6a4d3c4 | 155 | |
b4cccbc1 | 156 | if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER)) |
58033c91 | 157 | printf("Controller %s; ", controller); |
a6a4d3c4 | 158 | |
945403e6 | 159 | printf("Control group %s:\n", empty_to_root(path)); |
58033c91 EV |
160 | fflush(stdout); |
161 | } | |
162 | ||
e049fa16 LP |
163 | int main(int argc, char *argv[]) { |
164 | int r, output_flags; | |
fa776d8e LP |
165 | |
166 | log_parse_environment(); | |
2396fb04 | 167 | log_open(); |
fa776d8e | 168 | |
1e5678d0 | 169 | r = parse_argv(argc, argv); |
e049fa16 | 170 | if (r <= 0) |
fa776d8e | 171 | goto finish; |
fa776d8e | 172 | |
ee5324aa YW |
173 | r = pager_open(arg_no_pager, false); |
174 | if (r > 0 && arg_full < 0) | |
175 | arg_full = true; | |
9bdbc2e2 LN |
176 | |
177 | output_flags = | |
178 | arg_all * OUTPUT_SHOW_ALL | | |
0ff308c8 LP |
179 | (arg_full > 0) * OUTPUT_FULL_WIDTH | |
180 | arg_kernel_threads * OUTPUT_KERNEL_THREADS; | |
1968a360 | 181 | |
256c1eba | 182 | if (arg_names) { |
d9855d87 | 183 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; |
e049fa16 | 184 | _cleanup_free_ char *root = NULL; |
256c1eba | 185 | char **name; |
fa776d8e | 186 | |
256c1eba | 187 | STRV_FOREACH(name, arg_names) { |
fa776d8e | 188 | int q; |
baa89da4 | 189 | |
e7aa3f50 | 190 | if (arg_show_unit != SHOW_UNIT_NONE) { |
d9855d87 ZJS |
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 */ | |
e7aa3f50 ZJS |
196 | r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, |
197 | arg_show_unit == SHOW_UNIT_USER, | |
198 | &bus); | |
d9855d87 ZJS |
199 | if (r < 0) { |
200 | log_error_errno(r, "Failed to create bus connection: %m"); | |
201 | goto finish; | |
202 | } | |
203 | } | |
204 | ||
256c1eba | 205 | q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup); |
d9855d87 ZJS |
206 | if (q < 0) |
207 | goto failed; | |
208 | ||
209 | if (isempty(cgroup)) { | |
256c1eba | 210 | log_warning("Unit %s not found.", *name); |
d9855d87 ZJS |
211 | q = -ENOENT; |
212 | goto failed; | |
213 | } | |
214 | ||
256c1eba | 215 | printf("Unit %s (%s):\n", *name, cgroup); |
d9855d87 ZJS |
216 | fflush(stdout); |
217 | ||
218 | q = show_cgroup_by_path(cgroup, NULL, 0, output_flags); | |
219 | ||
256c1eba | 220 | } else if (path_startswith(*name, "/sys/fs/cgroup")) { |
fa776d8e | 221 | |
256c1eba | 222 | printf("Directory %s:\n", *name); |
e049fa16 | 223 | fflush(stdout); |
fa776d8e | 224 | |
256c1eba | 225 | q = show_cgroup_by_path(*name, NULL, 0, output_flags); |
e049fa16 LP |
226 | } else { |
227 | _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; | |
228 | const char *controller, *path; | |
fa776d8e | 229 | |
d9855d87 ZJS |
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 | ||
65c88349 ZJS |
237 | q = cg_split_spec(*name, &c, &p); |
238 | if (q < 0) { | |
239 | log_error_errno(q, "Failed to split argument %s: %m", *name); | |
d9855d87 | 240 | goto failed; |
13be4979 LN |
241 | } |
242 | ||
e049fa16 LP |
243 | controller = c ?: SYSTEMD_CGROUP_CONTROLLER; |
244 | if (p) { | |
605405c6 | 245 | j = strjoin(root, "/", p); |
e049fa16 LP |
246 | if (!j) { |
247 | r = log_oom(); | |
248 | goto finish; | |
249 | } | |
13be4979 | 250 | |
858d36c1 | 251 | path_simplify(j, false); |
e049fa16 LP |
252 | path = j; |
253 | } else | |
254 | path = root; | |
13be4979 | 255 | |
58033c91 | 256 | show_cg_info(controller, path); |
13be4979 | 257 | |
0ff308c8 | 258 | q = show_cgroup(controller, path, NULL, 0, output_flags); |
e049fa16 | 259 | } |
13be4979 | 260 | |
d9855d87 ZJS |
261 | failed: |
262 | if (q < 0 && r >= 0) | |
e049fa16 LP |
263 | r = q; |
264 | } | |
265 | ||
266 | } else { | |
267 | bool done = false; | |
13be4979 | 268 | |
e049fa16 LP |
269 | if (!arg_machine) { |
270 | _cleanup_free_ char *cwd = NULL; | |
271 | ||
d7249575 LP |
272 | r = safe_getcwd(&cwd); |
273 | if (r < 0) { | |
274 | log_error_errno(r, "Cannot determine current working directory: %m"); | |
7027ff61 | 275 | goto finish; |
b9a8e638 | 276 | } |
1f16b4a6 | 277 | |
e049fa16 LP |
278 | if (path_startswith(cwd, "/sys/fs/cgroup")) { |
279 | printf("Working directory %s:\n", cwd); | |
280 | fflush(stdout); | |
281 | ||
0ff308c8 | 282 | r = show_cgroup_by_path(cwd, NULL, 0, output_flags); |
e049fa16 LP |
283 | done = true; |
284 | } | |
285 | } | |
286 | ||
287 | if (!done) { | |
288 | _cleanup_free_ char *root = NULL; | |
289 | ||
d3e8277d | 290 | r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root); |
e049fa16 LP |
291 | if (r < 0) |
292 | goto finish; | |
293 | ||
58033c91 | 294 | show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root); |
e049fa16 | 295 | |
b1f044bb | 296 | printf("-.slice\n"); |
0ff308c8 | 297 | r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags); |
1f16b4a6 | 298 | } |
fa776d8e LP |
299 | } |
300 | ||
e049fa16 LP |
301 | if (r < 0) |
302 | log_error_errno(r, "Failed to list cgroup tree: %m"); | |
fa776d8e LP |
303 | |
304 | finish: | |
1968a360 | 305 | pager_close(); |
256c1eba | 306 | free(arg_names); /* don't free the strings */ |
fa776d8e | 307 | |
e049fa16 | 308 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
fa776d8e | 309 | } |