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