chiark / gitweb /
437d09711b947ff92219b84e50f7658310379e4d
[elogind.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) {
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 (n_columns > pid_width+2)
80                 n_columns -= pid_width+2;
81         else
82                 n_columns = 20;
83
84         for (i = 0; i < n_pids; i++) {
85                 char *t = NULL;
86
87                 get_process_cmdline(pids[i], n_columns, true, &t);
88
89                 printf("%s%s%*lu %s\n",
90                        prefix,
91                        draw_special_char(extra ? DRAW_TRIANGULAR_BULLET :
92                                          ((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT)),
93                        pid_width,
94                        (unsigned long) pids[i],
95                        strna(t));
96
97                 free(t);
98         }
99 }
100
101
102 static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads) {
103         char *fn;
104         FILE *f;
105         size_t n = 0, n_allocated = 0;
106         pid_t *pids = NULL;
107         char *p;
108         pid_t pid;
109         int r;
110
111         r = cg_fix_path(path, &p);
112         if (r < 0)
113                 return r;
114
115         r = asprintf(&fn, "%s/cgroup.procs", p);
116         free(p);
117         if (r < 0)
118                 return -ENOMEM;
119
120         f = fopen(fn, "re");
121         free(fn);
122         if (!f)
123                 return -errno;
124
125         while ((r = cg_read_pid(f, &pid)) > 0) {
126
127                 if (!kernel_threads && is_kernel_thread(pid) > 0)
128                         continue;
129
130                 if (n >= n_allocated) {
131                         pid_t *npids;
132
133                         n_allocated = MAX(16U, n*2U);
134
135                         npids = realloc(pids, sizeof(pid_t) * n_allocated);
136                         if (!npids) {
137                                 r = -ENOMEM;
138                                 goto finish;
139                         }
140
141                         pids = npids;
142                 }
143
144                 assert(n < n_allocated);
145                 pids[n++] = pid;
146         }
147
148         if (r < 0)
149                 goto finish;
150
151         if (n > 0)
152                 show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads);
153
154         r = 0;
155
156 finish:
157         free(pids);
158
159         if (f)
160                 fclose(f);
161
162         return r;
163 }
164
165 int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all) {
166         DIR *d;
167         char *last = NULL;
168         char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL;
169         bool shown_pids = false;
170         int r;
171
172         assert(path);
173
174         if (n_columns <= 0)
175                 n_columns = columns();
176
177         if (!prefix)
178                 prefix = "";
179
180         r = cg_fix_path(path, &fn);
181         if (r < 0)
182                 return r;
183
184         d = opendir(fn);
185         if (!d) {
186                 free(fn);
187                 return -errno;
188         }
189
190         while ((r = cg_read_subgroup(d, &gn)) > 0) {
191                 char *k;
192
193                 r = asprintf(&k, "%s/%s", fn, gn);
194                 free(gn);
195                 if (r < 0) {
196                         r = -ENOMEM;
197                         goto finish;
198                 }
199
200                 if (!all && cg_is_empty_recursive(NULL, k, false) > 0) {
201                         free(k);
202                         continue;
203                 }
204
205                 if (!shown_pids) {
206                         show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads);
207                         shown_pids = true;
208                 }
209
210                 if (last) {
211                         printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH),
212                                            path_get_file_name(last));
213
214                         if (!p1) {
215                                 p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERT));
216                                 if (!p1) {
217                                         free(k);
218                                         r = -ENOMEM;
219                                         goto finish;
220                                 }
221                         }
222
223                         show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, all);
224                         free(last);
225                 }
226
227                 last = k;
228         }
229
230         if (r < 0)
231                 goto finish;
232
233         if (!shown_pids)
234                 show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads);
235
236         if (last) {
237                 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT),
238                                    path_get_file_name(last));
239
240                 if (!p2) {
241                         p2 = strappend(prefix, "  ");
242                         if (!p2) {
243                                 r = -ENOMEM;
244                                 goto finish;
245                         }
246                 }
247
248                 show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, all);
249         }
250
251         r = 0;
252
253 finish:
254         free(p1);
255         free(p2);
256         free(last);
257         free(fn);
258
259         closedir(d);
260
261         return r;
262 }
263
264 int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all) {
265         char *p;
266         int r;
267
268         assert(controller);
269         assert(path);
270
271         r = cg_get_path(controller, path, NULL, &p);
272         if (r < 0)
273                 return r;
274
275         r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads, all);
276         free(p);
277
278         return r;
279 }
280
281 static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids) {
282         pid_t *copy;
283         unsigned i, j;
284         int r;
285
286         assert(controller);
287         assert(path);
288
289         if (n_pids <= 0)
290                 return 0;
291
292         if (n_columns <= 0)
293                 n_columns = columns();
294
295         if (!prefix)
296                 prefix = "";
297
298         copy = new(pid_t, n_pids);
299         if (!copy)
300                 return -ENOMEM;
301
302         for (i = 0, j = 0; i < n_pids; i++) {
303                 char *k;
304
305                 r = cg_get_by_pid(controller, pids[i], &k);
306                 if (r < 0) {
307                         free(copy);
308                         return r;
309                 }
310
311                 if (path_startswith(k, path))
312                         continue;
313
314                 copy[j++] = pids[i];
315         }
316
317         show_pid_array(copy, j, prefix, n_columns, true, false, false);
318
319         free(copy);
320         return 0;
321 }
322
323 int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids) {
324         int r;
325
326         assert(controller);
327         assert(path);
328
329         r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, all);
330         if (r < 0)
331                 return r;
332
333         return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids);
334 }
335
336 int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids) {
337         int r;
338         char *controller, *path;
339
340         assert(spec);
341
342         r = cg_split_spec(spec, &controller, &path);
343         if (r < 0)
344                 return r;
345
346         r = show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, all, extra_pids, n_extra_pids);
347         free(controller);
348         free(path);
349
350         return r;
351 }