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