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