Line data Source code
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 4 : STATIC_DESTRUCTOR_REGISTER(arg_names, freep); /* don't free the strings */
41 :
42 3 : static int help(void) {
43 3 : _cleanup_free_ char *link = NULL;
44 : int r;
45 :
46 3 : r = terminal_urlify_man("systemd-cgls", "1", &link);
47 3 : if (r < 0)
48 0 : return log_oom();
49 :
50 3 : 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 specified system units\n"
57 : " --user-unit Show the subtrees of specified 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 3 : return 0;
67 : }
68 :
69 4 : 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 4 : assert(argc >= 1);
92 4 : assert(argv);
93 :
94 4 : while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
95 :
96 4 : switch (c) {
97 :
98 3 : case 'h':
99 3 : return help();
100 :
101 0 : case ARG_VERSION:
102 0 : return version();
103 :
104 0 : case ARG_NO_PAGER:
105 0 : arg_pager_flags |= PAGER_DISABLE;
106 0 : break;
107 :
108 0 : case 'a':
109 0 : arg_all = true;
110 0 : break;
111 :
112 0 : case 'u':
113 0 : arg_show_unit = SHOW_UNIT_SYSTEM;
114 0 : if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
115 0 : return log_oom();
116 0 : break;
117 :
118 0 : case ARG_USER_UNIT:
119 0 : arg_show_unit = SHOW_UNIT_USER;
120 0 : if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
121 0 : return log_oom();
122 0 : break;
123 :
124 0 : case 1:
125 : /* positional argument */
126 0 : if (strv_push(&arg_names, optarg) < 0)
127 0 : return log_oom();
128 0 : break;
129 :
130 0 : case 'l':
131 0 : arg_full = true;
132 0 : break;
133 :
134 0 : case 'k':
135 0 : arg_kernel_threads = true;
136 0 : break;
137 :
138 0 : case 'M':
139 0 : arg_machine = optarg;
140 0 : break;
141 :
142 1 : case '?':
143 1 : return -EINVAL;
144 :
145 0 : default:
146 0 : assert_not_reached("Unhandled option");
147 : }
148 :
149 0 : if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
150 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
151 : "Cannot combine --unit or --user-unit with --machine=.");
152 :
153 0 : return 1;
154 : }
155 :
156 0 : static void show_cg_info(const char *controller, const char *path) {
157 :
158 0 : if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
159 0 : printf("Controller %s; ", controller);
160 :
161 0 : printf("Control group %s:\n", empty_to_root(path));
162 0 : fflush(stdout);
163 0 : }
164 :
165 4 : static int run(int argc, char *argv[]) {
166 : int r, output_flags;
167 :
168 4 : log_show_color(true);
169 4 : log_parse_environment();
170 4 : log_open();
171 :
172 4 : r = parse_argv(argc, argv);
173 4 : if (r <= 0)
174 4 : return r;
175 :
176 0 : r = pager_open(arg_pager_flags);
177 0 : if (r > 0 && arg_full < 0)
178 0 : arg_full = true;
179 :
180 0 : output_flags =
181 0 : arg_all * OUTPUT_SHOW_ALL |
182 0 : (arg_full > 0) * OUTPUT_FULL_WIDTH |
183 0 : arg_kernel_threads * OUTPUT_KERNEL_THREADS;
184 :
185 0 : if (arg_names) {
186 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
187 0 : _cleanup_free_ char *root = NULL;
188 : char **name;
189 :
190 0 : STRV_FOREACH(name, arg_names) {
191 : int q;
192 :
193 0 : if (arg_show_unit != SHOW_UNIT_NONE) {
194 : /* Command line arguments are unit names */
195 0 : _cleanup_free_ char *cgroup = NULL;
196 :
197 0 : if (!bus) {
198 : /* Connect to the bus only if necessary */
199 0 : r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
200 : arg_show_unit == SHOW_UNIT_USER,
201 : &bus);
202 0 : if (r < 0)
203 0 : return log_error_errno(r, "Failed to create bus connection: %m");
204 : }
205 :
206 0 : q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
207 0 : if (q < 0)
208 0 : goto failed;
209 :
210 0 : if (isempty(cgroup)) {
211 0 : log_warning("Unit %s not found.", *name);
212 0 : q = -ENOENT;
213 0 : goto failed;
214 : }
215 :
216 0 : printf("Unit %s (%s):\n", *name, cgroup);
217 0 : fflush(stdout);
218 :
219 0 : q = show_cgroup_by_path(cgroup, NULL, 0, output_flags);
220 :
221 0 : } else if (path_startswith(*name, "/sys/fs/cgroup")) {
222 :
223 0 : printf("Directory %s:\n", *name);
224 0 : fflush(stdout);
225 :
226 0 : q = show_cgroup_by_path(*name, NULL, 0, output_flags);
227 : } else {
228 0 : _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
229 : const char *controller, *path;
230 :
231 0 : if (!root) {
232 : /* Query root only if needed, treat error as fatal */
233 0 : r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
234 0 : if (r < 0)
235 0 : return log_error_errno(r, "Failed to list cgroup tree: %m");
236 : }
237 :
238 0 : q = cg_split_spec(*name, &c, &p);
239 0 : if (q < 0) {
240 0 : log_error_errno(q, "Failed to split argument %s: %m", *name);
241 0 : goto failed;
242 : }
243 :
244 0 : controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
245 0 : if (p) {
246 0 : j = path_join(root, p);
247 0 : if (!j)
248 0 : return log_oom();
249 :
250 0 : path_simplify(j, false);
251 0 : path = j;
252 : } else
253 0 : path = root;
254 :
255 0 : show_cg_info(controller, path);
256 :
257 0 : q = show_cgroup(controller, path, NULL, 0, output_flags);
258 : }
259 :
260 0 : failed:
261 0 : if (q < 0 && r >= 0)
262 0 : r = q;
263 : }
264 :
265 : } else {
266 0 : bool done = false;
267 :
268 0 : if (!arg_machine) {
269 0 : _cleanup_free_ char *cwd = NULL;
270 :
271 0 : r = safe_getcwd(&cwd);
272 0 : if (r < 0)
273 0 : return log_error_errno(r, "Cannot determine current working directory: %m");
274 :
275 0 : if (path_startswith(cwd, "/sys/fs/cgroup")) {
276 0 : printf("Working directory %s:\n", cwd);
277 0 : fflush(stdout);
278 :
279 0 : r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
280 0 : done = true;
281 : }
282 : }
283 :
284 0 : if (!done) {
285 0 : _cleanup_free_ char *root = NULL;
286 :
287 0 : r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
288 0 : if (r < 0)
289 0 : return log_error_errno(r, "Failed to list cgroup tree: %m");
290 :
291 0 : show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
292 :
293 0 : printf("-.slice\n");
294 0 : r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
295 : }
296 : }
297 0 : if (r < 0)
298 0 : return log_error_errno(r, "Failed to list cgroup tree: %m");
299 :
300 0 : return 0;
301 : }
302 :
303 4 : DEFINE_MAIN_FUNCTION(run);
|