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