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