chiark / gitweb /
core: general cgroup rework
[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 void show_pid_array(int pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
44         unsigned i, m, pid_width;
45         pid_t biggest = 0;
46
47         /* Filter duplicates */
48         m = 0;
49         for (i = 0; i < n_pids; i++) {
50                 unsigned j;
51
52                 if (pids[i] > biggest)
53                         biggest = pids[i];
54
55                 for (j = i+1; j < n_pids; j++)
56                         if (pids[i] == pids[j])
57                                 break;
58
59                 if (j >= n_pids)
60                         pids[m++] = pids[i];
61         }
62         n_pids = m;
63         pid_width = DECIMAL_STR_WIDTH(biggest);
64
65         /* And sort */
66         qsort(pids, n_pids, sizeof(pid_t), compare);
67
68         if(flags & OUTPUT_FULL_WIDTH)
69                 n_columns = 0;
70         else {
71                 if (n_columns > pid_width+2)
72                         n_columns -= pid_width+2;
73                 else
74                         n_columns = 20;
75         }
76         for (i = 0; i < n_pids; i++) {
77                 char *t = NULL;
78
79                 get_process_cmdline(pids[i], n_columns, true, &t);
80
81                 printf("%s%s%*lu %s\n",
82                        prefix,
83                        draw_special_char(extra ? DRAW_TRIANGULAR_BULLET :
84                                          ((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT)),
85                        pid_width,
86                        (unsigned long) pids[i],
87                        strna(t));
88
89                 free(t);
90         }
91 }
92
93
94 static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
95         char *fn;
96         _cleanup_fclose_ FILE *f = NULL;
97         size_t n = 0, n_allocated = 0;
98         _cleanup_free_ pid_t *pids = NULL;
99         char *p = NULL;
100         pid_t pid;
101         int r;
102
103         r = cg_mangle_path(path, &p);
104         if (r < 0)
105                 return r;
106
107         fn = strappend(p, "/cgroup.procs");
108         free(p);
109         if (!fn)
110                 return -ENOMEM;
111
112         f = fopen(fn, "re");
113         free(fn);
114         if (!f)
115                 return -errno;
116
117         while ((r = cg_read_pid(f, &pid)) > 0) {
118
119                 if (!kernel_threads && is_kernel_thread(pid) > 0)
120                         continue;
121
122                 if (n >= n_allocated) {
123                         pid_t *npids;
124
125                         n_allocated = MAX(16U, n*2U);
126
127                         npids = realloc(pids, sizeof(pid_t) * n_allocated);
128                         if (!npids)
129                                 return -ENOMEM;
130
131                         pids = npids;
132                 }
133
134                 assert(n < n_allocated);
135                 pids[n++] = pid;
136         }
137
138         if (r < 0)
139                 return r;
140
141         if (n > 0)
142                 show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
143
144         return 0;
145 }
146
147 int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
148         _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
149         _cleanup_closedir_ DIR *d = NULL;
150         char *gn = NULL;
151         bool shown_pids = false;
152         int r;
153
154         assert(path);
155
156         if (n_columns <= 0)
157                 n_columns = columns();
158
159         if (!prefix)
160                 prefix = "";
161
162         r = cg_mangle_path(path, &fn);
163         if (r < 0)
164                 return r;
165
166         d = opendir(fn);
167         if (!d)
168                 return -errno;
169
170         while ((r = cg_read_subgroup(d, &gn)) > 0) {
171                 _cleanup_free_ char *k = NULL;
172
173                 k = strjoin(fn, "/", gn, NULL);
174                 free(gn);
175                 if (!k)
176                         return -ENOMEM;
177
178                 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k, false) > 0)
179                         continue;
180
181                 if (!shown_pids) {
182                         show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
183                         shown_pids = true;
184                 }
185
186                 if (last) {
187                         printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH),
188                                            path_get_file_name(last));
189
190                         if (!p1) {
191                                 p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERT));
192                                 if (!p1)
193                                         return -ENOMEM;
194                         }
195
196                         show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
197                         free(last);
198                 }
199
200                 last = k;
201                 k = NULL;
202         }
203
204         if (r < 0)
205                 return r;
206
207         if (!shown_pids)
208                 show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
209
210         if (last) {
211                 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT),
212                                    path_get_file_name(last));
213
214                 if (!p2) {
215                         p2 = strappend(prefix, "  ");
216                         if (!p2)
217                                 return -ENOMEM;
218                 }
219
220                 show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
221         }
222
223         return 0;
224 }
225
226 int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
227         _cleanup_free_ char *p = NULL;
228         int r;
229
230         assert(path);
231
232         r = cg_get_path(controller, path, NULL, &p);
233         if (r < 0)
234                 return r;
235
236         return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
237 }
238
239 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) {
240         _cleanup_free_ pid_t *copy = NULL;
241         unsigned i, j;
242         int r;
243
244         assert(path);
245
246         if (n_pids <= 0)
247                 return 0;
248
249         if (n_columns <= 0)
250                 n_columns = columns();
251
252         prefix = strempty(prefix);
253
254         copy = new(pid_t, n_pids);
255         if (!copy)
256                 return -ENOMEM;
257
258         for (i = 0, j = 0; i < n_pids; i++) {
259                 _cleanup_free_ char *k = NULL;
260
261                 r = cg_pid_get_path(controller, pids[i], &k);
262                 if (r < 0)
263                         return r;
264
265                 if (path_startswith(k, path))
266                         continue;
267
268                 copy[j++] = pids[i];
269         }
270
271         show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
272
273         return 0;
274 }
275
276 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) {
277         int r;
278
279         assert(path);
280
281         r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
282         if (r < 0)
283                 return r;
284
285         return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
286 }
287
288 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) {
289         _cleanup_free_ char *controller = NULL, *path = NULL;
290         int r;
291
292         assert(spec);
293
294         r = cg_split_spec(spec, &controller, &path);
295         if (r < 0)
296                 return r;
297
298         return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
299 }