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