]>
Commit | Line | Data |
---|---|---|
52ad194e | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
663996b3 MS |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2010 Lennart Poettering | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
663996b3 MS |
21 | #include <errno.h> |
22 | #include <getopt.h> | |
6300502b | 23 | #include <stdio.h> |
663996b3 | 24 | #include <string.h> |
6300502b MP |
25 | #include <unistd.h> |
26 | ||
27 | #include "sd-bus.h" | |
663996b3 | 28 | |
db2df898 | 29 | #include "alloc-util.h" |
6300502b | 30 | #include "bus-util.h" |
663996b3 MS |
31 | #include "cgroup-show.h" |
32 | #include "cgroup-util.h" | |
6300502b | 33 | #include "fileio.h" |
663996b3 | 34 | #include "log.h" |
663996b3 | 35 | #include "output-mode.h" |
6300502b MP |
36 | #include "pager.h" |
37 | #include "path-util.h" | |
2897b343 | 38 | #include "strv.h" |
60f067b4 | 39 | #include "unit-name.h" |
6300502b | 40 | #include "util.h" |
663996b3 MS |
41 | |
42 | static bool arg_no_pager = false; | |
43 | static bool arg_kernel_threads = false; | |
44 | static bool arg_all = false; | |
2897b343 MP |
45 | |
46 | static enum { | |
47 | SHOW_UNIT_NONE, | |
48 | SHOW_UNIT_SYSTEM, | |
49 | SHOW_UNIT_USER, | |
50 | } arg_show_unit = SHOW_UNIT_NONE; | |
51 | static char **arg_names = NULL; | |
52 | ||
663996b3 MS |
53 | static int arg_full = -1; |
54 | static char* arg_machine = NULL; | |
55 | ||
5eef597e | 56 | static void help(void) { |
663996b3 MS |
57 | printf("%s [OPTIONS...] [CGROUP...]\n\n" |
58 | "Recursively show control group contents.\n\n" | |
59 | " -h --help Show this help\n" | |
60 | " --version Show package version\n" | |
61 | " --no-pager Do not pipe output into a pager\n" | |
62 | " -a --all Show all groups, including empty\n" | |
2897b343 MP |
63 | " -u --unit Show the subtrees of specifified system units\n" |
64 | " --user-unit Show the subtrees of specifified user units\n" | |
14228c0d | 65 | " -l --full Do not ellipsize output\n" |
663996b3 | 66 | " -k Include kernel threads in output\n" |
d9dfd233 | 67 | " -M --machine= Show container\n" |
5eef597e | 68 | , program_invocation_short_name); |
663996b3 MS |
69 | } |
70 | ||
71 | static int parse_argv(int argc, char *argv[]) { | |
72 | ||
73 | enum { | |
74 | ARG_NO_PAGER = 0x100, | |
75 | ARG_VERSION, | |
2897b343 | 76 | ARG_USER_UNIT, |
663996b3 MS |
77 | }; |
78 | ||
79 | static const struct option options[] = { | |
2897b343 MP |
80 | { "help", no_argument, NULL, 'h' }, |
81 | { "version", no_argument, NULL, ARG_VERSION }, | |
82 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, | |
83 | { "all", no_argument, NULL, 'a' }, | |
84 | { "full", no_argument, NULL, 'l' }, | |
85 | { "machine", required_argument, NULL, 'M' }, | |
86 | { "unit", optional_argument, NULL, 'u' }, | |
87 | { "user-unit", optional_argument, NULL, ARG_USER_UNIT }, | |
60f067b4 | 88 | {} |
663996b3 MS |
89 | }; |
90 | ||
91 | int c; | |
92 | ||
93 | assert(argc >= 1); | |
94 | assert(argv); | |
95 | ||
2897b343 | 96 | while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0) |
663996b3 MS |
97 | |
98 | switch (c) { | |
99 | ||
100 | case 'h': | |
5eef597e MP |
101 | help(); |
102 | return 0; | |
663996b3 MS |
103 | |
104 | case ARG_VERSION: | |
6300502b | 105 | return version(); |
663996b3 MS |
106 | |
107 | case ARG_NO_PAGER: | |
108 | arg_no_pager = true; | |
109 | break; | |
110 | ||
111 | case 'a': | |
112 | arg_all = true; | |
113 | break; | |
114 | ||
2897b343 MP |
115 | case 'u': |
116 | arg_show_unit = SHOW_UNIT_SYSTEM; | |
117 | if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */ | |
118 | return log_oom(); | |
119 | break; | |
120 | ||
121 | case ARG_USER_UNIT: | |
122 | arg_show_unit = SHOW_UNIT_USER; | |
123 | if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */ | |
124 | return log_oom(); | |
125 | break; | |
126 | ||
127 | case 1: | |
128 | /* positional argument */ | |
129 | if (strv_push(&arg_names, optarg) < 0) | |
130 | return log_oom(); | |
131 | break; | |
132 | ||
14228c0d | 133 | case 'l': |
663996b3 MS |
134 | arg_full = true; |
135 | break; | |
136 | ||
137 | case 'k': | |
138 | arg_kernel_threads = true; | |
139 | break; | |
140 | ||
141 | case 'M': | |
142 | arg_machine = optarg; | |
143 | break; | |
144 | ||
145 | case '?': | |
146 | return -EINVAL; | |
147 | ||
148 | default: | |
60f067b4 | 149 | assert_not_reached("Unhandled option"); |
663996b3 | 150 | } |
663996b3 | 151 | |
2897b343 MP |
152 | if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) { |
153 | log_error("Cannot combine --unit or --user-unit with --machine=."); | |
154 | return -EINVAL; | |
d9dfd233 MP |
155 | } |
156 | ||
2897b343 | 157 | return 1; |
d9dfd233 MP |
158 | } |
159 | ||
6300502b | 160 | static void show_cg_info(const char *controller, const char *path) { |
db2df898 | 161 | |
2897b343 | 162 | if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER)) |
6300502b | 163 | printf("Controller %s; ", controller); |
db2df898 | 164 | |
6300502b MP |
165 | printf("Control group %s:\n", isempty(path) ? "/" : path); |
166 | fflush(stdout); | |
167 | } | |
168 | ||
d9dfd233 MP |
169 | int main(int argc, char *argv[]) { |
170 | int r, output_flags; | |
663996b3 MS |
171 | |
172 | log_parse_environment(); | |
173 | log_open(); | |
174 | ||
175 | r = parse_argv(argc, argv); | |
d9dfd233 | 176 | if (r <= 0) |
663996b3 | 177 | goto finish; |
663996b3 MS |
178 | |
179 | if (!arg_no_pager) { | |
aa27b158 | 180 | r = pager_open(arg_no_pager, false); |
d9dfd233 MP |
181 | if (r > 0 && arg_full < 0) |
182 | arg_full = true; | |
663996b3 MS |
183 | } |
184 | ||
185 | output_flags = | |
186 | arg_all * OUTPUT_SHOW_ALL | | |
aa27b158 MP |
187 | (arg_full > 0) * OUTPUT_FULL_WIDTH | |
188 | arg_kernel_threads * OUTPUT_KERNEL_THREADS; | |
663996b3 | 189 | |
2897b343 MP |
190 | if (arg_names) { |
191 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
d9dfd233 | 192 | _cleanup_free_ char *root = NULL; |
2897b343 | 193 | char **name; |
663996b3 | 194 | |
2897b343 | 195 | STRV_FOREACH(name, arg_names) { |
663996b3 | 196 | int q; |
14228c0d | 197 | |
2897b343 MP |
198 | if (arg_show_unit != SHOW_UNIT_NONE) { |
199 | /* Command line arguments are unit names */ | |
200 | _cleanup_free_ char *cgroup = NULL; | |
201 | ||
202 | if (!bus) { | |
203 | /* Connect to the bus only if necessary */ | |
204 | r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, | |
205 | arg_show_unit == SHOW_UNIT_USER, | |
206 | &bus); | |
207 | if (r < 0) { | |
208 | log_error_errno(r, "Failed to create bus connection: %m"); | |
209 | goto finish; | |
210 | } | |
211 | } | |
212 | ||
213 | q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup); | |
214 | if (q < 0) | |
215 | goto failed; | |
216 | ||
217 | if (isempty(cgroup)) { | |
218 | log_warning("Unit %s not found.", *name); | |
219 | q = -ENOENT; | |
220 | goto failed; | |
221 | } | |
222 | ||
223 | printf("Unit %s (%s):\n", *name, cgroup); | |
224 | fflush(stdout); | |
225 | ||
226 | q = show_cgroup_by_path(cgroup, NULL, 0, output_flags); | |
663996b3 | 227 | |
2897b343 MP |
228 | } else if (path_startswith(*name, "/sys/fs/cgroup")) { |
229 | ||
230 | printf("Directory %s:\n", *name); | |
d9dfd233 MP |
231 | fflush(stdout); |
232 | ||
2897b343 | 233 | q = show_cgroup_by_path(*name, NULL, 0, output_flags); |
d9dfd233 MP |
234 | } else { |
235 | _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; | |
236 | const char *controller, *path; | |
237 | ||
2897b343 MP |
238 | if (!root) { |
239 | /* Query root only if needed, treat error as fatal */ | |
240 | r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root); | |
241 | if (r < 0) | |
242 | goto finish; | |
243 | } | |
244 | ||
245 | q = cg_split_spec(*name, &c, &p); | |
246 | if (q < 0) { | |
247 | log_error_errno(q, "Failed to split argument %s: %m", *name); | |
248 | goto failed; | |
d9dfd233 MP |
249 | } |
250 | ||
251 | controller = c ?: SYSTEMD_CGROUP_CONTROLLER; | |
252 | if (p) { | |
2897b343 | 253 | j = strjoin(root, "/", p); |
d9dfd233 MP |
254 | if (!j) { |
255 | r = log_oom(); | |
256 | goto finish; | |
257 | } | |
258 | ||
259 | path_kill_slashes(j); | |
260 | path = j; | |
261 | } else | |
262 | path = root; | |
263 | ||
6300502b | 264 | show_cg_info(controller, path); |
d9dfd233 | 265 | |
aa27b158 | 266 | q = show_cgroup(controller, path, NULL, 0, output_flags); |
d9dfd233 | 267 | } |
663996b3 | 268 | |
2897b343 MP |
269 | failed: |
270 | if (q < 0 && r >= 0) | |
663996b3 MS |
271 | r = q; |
272 | } | |
273 | ||
274 | } else { | |
d9dfd233 | 275 | bool done = false; |
663996b3 | 276 | |
d9dfd233 MP |
277 | if (!arg_machine) { |
278 | _cleanup_free_ char *cwd = NULL; | |
663996b3 | 279 | |
d9dfd233 MP |
280 | cwd = get_current_dir_name(); |
281 | if (!cwd) { | |
282 | r = log_error_errno(errno, "Cannot determine current working directory: %m"); | |
283 | goto finish; | |
284 | } | |
60f067b4 | 285 | |
d9dfd233 MP |
286 | if (path_startswith(cwd, "/sys/fs/cgroup")) { |
287 | printf("Working directory %s:\n", cwd); | |
288 | fflush(stdout); | |
60f067b4 | 289 | |
aa27b158 | 290 | r = show_cgroup_by_path(cwd, NULL, 0, output_flags); |
d9dfd233 MP |
291 | done = true; |
292 | } | |
293 | } | |
60f067b4 | 294 | |
d9dfd233 MP |
295 | if (!done) { |
296 | _cleanup_free_ char *root = NULL; | |
60f067b4 | 297 | |
2897b343 | 298 | r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root); |
d9dfd233 | 299 | if (r < 0) |
663996b3 | 300 | goto finish; |
663996b3 | 301 | |
6300502b | 302 | show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root); |
d9dfd233 | 303 | |
db2df898 | 304 | printf("-.slice\n"); |
aa27b158 | 305 | r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags); |
663996b3 MS |
306 | } |
307 | } | |
308 | ||
d9dfd233 MP |
309 | if (r < 0) |
310 | log_error_errno(r, "Failed to list cgroup tree: %m"); | |
663996b3 MS |
311 | |
312 | finish: | |
313 | pager_close(); | |
2897b343 | 314 | free(arg_names); /* don't free the strings */ |
663996b3 | 315 | |
d9dfd233 | 316 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
663996b3 | 317 | } |