]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cgls/cgls.c
tree-wide: port all code to use safe_getcwd()
[thirdparty/systemd.git] / src / cgls / cgls.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
21 #include <errno.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "sd-bus.h"
28
29 #include "alloc-util.h"
30 #include "bus-util.h"
31 #include "cgroup-show.h"
32 #include "cgroup-util.h"
33 #include "fileio.h"
34 #include "log.h"
35 #include "output-mode.h"
36 #include "pager.h"
37 #include "path-util.h"
38 #include "strv.h"
39 #include "unit-name.h"
40 #include "util.h"
41
42 static bool arg_no_pager = false;
43 static bool arg_kernel_threads = false;
44 static bool arg_all = false;
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
53 static int arg_full = -1;
54 static char* arg_machine = NULL;
55
56 static void help(void) {
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"
63 " -u --unit Show the subtrees of specifified system units\n"
64 " --user-unit Show the subtrees of specifified user units\n"
65 " -l --full Do not ellipsize output\n"
66 " -k Include kernel threads in output\n"
67 " -M --machine= Show container\n"
68 , program_invocation_short_name);
69 }
70
71 static int parse_argv(int argc, char *argv[]) {
72
73 enum {
74 ARG_NO_PAGER = 0x100,
75 ARG_VERSION,
76 ARG_USER_UNIT,
77 };
78
79 static const struct option options[] = {
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 },
88 {}
89 };
90
91 int c;
92
93 assert(argc >= 1);
94 assert(argv);
95
96 while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
97
98 switch (c) {
99
100 case 'h':
101 help();
102 return 0;
103
104 case ARG_VERSION:
105 return version();
106
107 case ARG_NO_PAGER:
108 arg_no_pager = true;
109 break;
110
111 case 'a':
112 arg_all = true;
113 break;
114
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
133 case 'l':
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:
149 assert_not_reached("Unhandled option");
150 }
151
152 if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) {
153 log_error("Cannot combine --unit or --user-unit with --machine=.");
154 return -EINVAL;
155 }
156
157 return 1;
158 }
159
160 static void show_cg_info(const char *controller, const char *path) {
161
162 if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
163 printf("Controller %s; ", controller);
164
165 printf("Control group %s:\n", isempty(path) ? "/" : path);
166 fflush(stdout);
167 }
168
169 int main(int argc, char *argv[]) {
170 int r, output_flags;
171
172 log_parse_environment();
173 log_open();
174
175 r = parse_argv(argc, argv);
176 if (r <= 0)
177 goto finish;
178
179 if (!arg_no_pager) {
180 r = pager_open(arg_no_pager, false);
181 if (r > 0 && arg_full < 0)
182 arg_full = true;
183 }
184
185 output_flags =
186 arg_all * OUTPUT_SHOW_ALL |
187 (arg_full > 0) * OUTPUT_FULL_WIDTH |
188 arg_kernel_threads * OUTPUT_KERNEL_THREADS;
189
190 if (arg_names) {
191 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
192 _cleanup_free_ char *root = NULL;
193 char **name;
194
195 STRV_FOREACH(name, arg_names) {
196 int q;
197
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);
227
228 } else if (path_startswith(*name, "/sys/fs/cgroup")) {
229
230 printf("Directory %s:\n", *name);
231 fflush(stdout);
232
233 q = show_cgroup_by_path(*name, NULL, 0, output_flags);
234 } else {
235 _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
236 const char *controller, *path;
237
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;
249 }
250
251 controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
252 if (p) {
253 j = strjoin(root, "/", p);
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
264 show_cg_info(controller, path);
265
266 q = show_cgroup(controller, path, NULL, 0, output_flags);
267 }
268
269 failed:
270 if (q < 0 && r >= 0)
271 r = q;
272 }
273
274 } else {
275 bool done = false;
276
277 if (!arg_machine) {
278 _cleanup_free_ char *cwd = NULL;
279
280 r = safe_getcwd(&cwd);
281 if (r < 0) {
282 log_error_errno(r, "Cannot determine current working directory: %m");
283 goto finish;
284 }
285
286 if (path_startswith(cwd, "/sys/fs/cgroup")) {
287 printf("Working directory %s:\n", cwd);
288 fflush(stdout);
289
290 r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
291 done = true;
292 }
293 }
294
295 if (!done) {
296 _cleanup_free_ char *root = NULL;
297
298 r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
299 if (r < 0)
300 goto finish;
301
302 show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
303
304 printf("-.slice\n");
305 r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
306 }
307 }
308
309 if (r < 0)
310 log_error_errno(r, "Failed to list cgroup tree: %m");
311
312 finish:
313 pager_close();
314 free(arg_names); /* don't free the strings */
315
316 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
317 }