]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cgroup-show.c
systemctl,loginctl,cgls: do not ellipsize cgroup members when --full is specified
[thirdparty/systemd.git] / src / shared / cgroup-show.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <dirent.h>
25 #include <errno.h>
26
27 #include "util.h"
28 #include "macro.h"
29 #include "path-util.h"
30 #include "cgroup-util.h"
31 #include "cgroup-show.h"
32
33 static int compare(const void *a, const void *b) {
34 const pid_t *p = a, *q = b;
35
36 if (*p < *q)
37 return -1;
38 if (*p > *q)
39 return 1;
40 return 0;
41 }
42
43 static unsigned ilog10(unsigned long ul) {
44 int n = 0;
45
46 while (ul > 0) {
47 n++;
48 ul /= 10;
49 }
50
51 return n;
52 }
53
54 static void show_pid_array(int pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
55 unsigned i, m, pid_width;
56 pid_t biggest = 0;
57
58 /* Filter duplicates */
59 m = 0;
60 for (i = 0; i < n_pids; i++) {
61 unsigned j;
62
63 if (pids[i] > biggest)
64 biggest = pids[i];
65
66 for (j = i+1; j < n_pids; j++)
67 if (pids[i] == pids[j])
68 break;
69
70 if (j >= n_pids)
71 pids[m++] = pids[i];
72 }
73 n_pids = m;
74 pid_width = ilog10(biggest);
75
76 /* And sort */
77 qsort(pids, n_pids, sizeof(pid_t), compare);
78
79 if(flags & OUTPUT_FULL_WIDTH)
80 n_columns = 0;
81 else {
82 if (n_columns > pid_width+2)
83 n_columns -= pid_width+2;
84 else
85 n_columns = 20;
86 }
87 for (i = 0; i < n_pids; i++) {
88 char *t = NULL;
89
90 get_process_cmdline(pids[i], n_columns, true, &t);
91
92 printf("%s%s%*lu %s\n",
93 prefix,
94 draw_special_char(extra ? DRAW_TRIANGULAR_BULLET :
95 ((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT)),
96 pid_width,
97 (unsigned long) pids[i],
98 strna(t));
99
100 free(t);
101 }
102 }
103
104
105 static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
106 char *fn;
107 FILE *f;
108 size_t n = 0, n_allocated = 0;
109 pid_t *pids = NULL;
110 char *p;
111 pid_t pid;
112 int r;
113
114 r = cg_fix_path(path, &p);
115 if (r < 0)
116 return r;
117
118 r = asprintf(&fn, "%s/cgroup.procs", p);
119 free(p);
120 if (r < 0)
121 return -ENOMEM;
122
123 f = fopen(fn, "re");
124 free(fn);
125 if (!f)
126 return -errno;
127
128 while ((r = cg_read_pid(f, &pid)) > 0) {
129
130 if (!kernel_threads && is_kernel_thread(pid) > 0)
131 continue;
132
133 if (n >= n_allocated) {
134 pid_t *npids;
135
136 n_allocated = MAX(16U, n*2U);
137
138 npids = realloc(pids, sizeof(pid_t) * n_allocated);
139 if (!npids) {
140 r = -ENOMEM;
141 goto finish;
142 }
143
144 pids = npids;
145 }
146
147 assert(n < n_allocated);
148 pids[n++] = pid;
149 }
150
151 if (r < 0)
152 goto finish;
153
154 if (n > 0)
155 show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
156
157 r = 0;
158
159 finish:
160 free(pids);
161
162 if (f)
163 fclose(f);
164
165 return r;
166 }
167
168 int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
169 DIR *d;
170 char *last = NULL;
171 char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL;
172 bool shown_pids = false;
173 int r;
174
175 assert(path);
176
177 if (n_columns <= 0)
178 n_columns = columns();
179
180 if (!prefix)
181 prefix = "";
182
183 r = cg_fix_path(path, &fn);
184 if (r < 0)
185 return r;
186
187 d = opendir(fn);
188 if (!d) {
189 free(fn);
190 return -errno;
191 }
192
193 while ((r = cg_read_subgroup(d, &gn)) > 0) {
194 char *k;
195
196 r = asprintf(&k, "%s/%s", fn, gn);
197 free(gn);
198 if (r < 0) {
199 r = -ENOMEM;
200 goto finish;
201 }
202
203 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k, false) > 0) {
204 free(k);
205 continue;
206 }
207
208 if (!shown_pids) {
209 show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
210 shown_pids = true;
211 }
212
213 if (last) {
214 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH),
215 path_get_file_name(last));
216
217 if (!p1) {
218 p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERT));
219 if (!p1) {
220 free(k);
221 r = -ENOMEM;
222 goto finish;
223 }
224 }
225
226 show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
227 free(last);
228 }
229
230 last = k;
231 }
232
233 if (r < 0)
234 goto finish;
235
236 if (!shown_pids)
237 show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
238
239 if (last) {
240 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT),
241 path_get_file_name(last));
242
243 if (!p2) {
244 p2 = strappend(prefix, " ");
245 if (!p2) {
246 r = -ENOMEM;
247 goto finish;
248 }
249 }
250
251 show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
252 }
253
254 r = 0;
255
256 finish:
257 free(p1);
258 free(p2);
259 free(last);
260 free(fn);
261
262 closedir(d);
263
264 return r;
265 }
266
267 int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
268 char *p;
269 int r;
270
271 assert(controller);
272 assert(path);
273
274 r = cg_get_path(controller, path, NULL, &p);
275 if (r < 0)
276 return r;
277
278 r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
279 free(p);
280
281 return r;
282 }
283
284 static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) {
285 pid_t *copy;
286 unsigned i, j;
287 int r;
288
289 assert(controller);
290 assert(path);
291
292 if (n_pids <= 0)
293 return 0;
294
295 if (n_columns <= 0)
296 n_columns = columns();
297
298 if (!prefix)
299 prefix = "";
300
301 copy = new(pid_t, n_pids);
302 if (!copy)
303 return -ENOMEM;
304
305 for (i = 0, j = 0; i < n_pids; i++) {
306 char *k;
307
308 r = cg_get_by_pid(controller, pids[i], &k);
309 if (r < 0) {
310 free(copy);
311 return r;
312 }
313
314 if (path_startswith(k, path))
315 continue;
316
317 copy[j++] = pids[i];
318 }
319
320 show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
321
322 free(copy);
323 return 0;
324 }
325
326 int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
327 int r;
328
329 assert(controller);
330 assert(path);
331
332 r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
333 if (r < 0)
334 return r;
335
336 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
337 }
338
339 int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
340 int r;
341 _cleanup_free_ char *controller = NULL, *path = NULL;
342
343 assert(spec);
344
345 r = cg_split_spec(spec, &controller, &path);
346 if (r < 0)
347 return r;
348
349 return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
350 }