]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cgroup-show.c
core,systemctl: add bus API to retrieve processes of a unit
[thirdparty/systemd.git] / src / shared / cgroup-show.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <dirent.h>
21 #include <errno.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "alloc-util.h"
28 #include "cgroup-show.h"
29 #include "cgroup-util.h"
30 #include "fd-util.h"
31 #include "formats-util.h"
32 #include "locale-util.h"
33 #include "macro.h"
34 #include "output-mode.h"
35 #include "path-util.h"
36 #include "process-util.h"
37 #include "string-util.h"
38 #include "terminal-util.h"
39
40 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) {
41 unsigned i, j, pid_width;
42
43 if (n_pids == 0)
44 return;
45
46 qsort(pids, n_pids, sizeof(pid_t), pid_compare_func);
47
48 /* Filter duplicates */
49 for (j = 0, i = 1; i < n_pids; i++) {
50 if (pids[i] == pids[j])
51 continue;
52 pids[++j] = pids[i];
53 }
54 n_pids = j + 1;
55 pid_width = DECIMAL_STR_WIDTH(pids[j]);
56
57 if (flags & OUTPUT_FULL_WIDTH)
58 n_columns = 0;
59 else {
60 if (n_columns > pid_width+2)
61 n_columns -= pid_width+2;
62 else
63 n_columns = 20;
64 }
65 for (i = 0; i < n_pids; i++) {
66 _cleanup_free_ char *t = NULL;
67
68 get_process_cmdline(pids[i], n_columns, true, &t);
69
70 if (extra)
71 printf("%s%s ", prefix, draw_special_char(DRAW_TRIANGULAR_BULLET));
72 else
73 printf("%s%s", prefix, draw_special_char(((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT)));
74
75 printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t));
76 }
77 }
78
79 static int show_cgroup_one_by_path(
80 const char *path,
81 const char *prefix,
82 unsigned n_columns,
83 bool more,
84 bool kernel_threads,
85 OutputFlags flags) {
86
87 char *fn;
88 _cleanup_fclose_ FILE *f = NULL;
89 size_t n = 0, n_allocated = 0;
90 _cleanup_free_ pid_t *pids = NULL;
91 _cleanup_free_ char *p = NULL;
92 pid_t pid;
93 int r;
94
95 r = cg_mangle_path(path, &p);
96 if (r < 0)
97 return r;
98
99 fn = strjoina(p, "/cgroup.procs");
100 f = fopen(fn, "re");
101 if (!f)
102 return -errno;
103
104 while ((r = cg_read_pid(f, &pid)) > 0) {
105
106 if (!kernel_threads && is_kernel_thread(pid) > 0)
107 continue;
108
109 if (!GREEDY_REALLOC(pids, n_allocated, n + 1))
110 return -ENOMEM;
111
112 assert(n < n_allocated);
113 pids[n++] = pid;
114 }
115
116 if (r < 0)
117 return r;
118
119 show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
120
121 return 0;
122 }
123
124 int show_cgroup_by_path(
125 const char *path,
126 const char *prefix,
127 unsigned n_columns,
128 bool kernel_threads,
129 OutputFlags flags) {
130
131 _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
132 _cleanup_closedir_ DIR *d = NULL;
133 char *gn = NULL;
134 bool shown_pids = false;
135 int r;
136
137 assert(path);
138
139 if (n_columns <= 0)
140 n_columns = columns();
141
142 prefix = strempty(prefix);
143
144 r = cg_mangle_path(path, &fn);
145 if (r < 0)
146 return r;
147
148 d = opendir(fn);
149 if (!d)
150 return -errno;
151
152 while ((r = cg_read_subgroup(d, &gn)) > 0) {
153 _cleanup_free_ char *k = NULL;
154
155 k = strjoin(fn, "/", gn, NULL);
156 free(gn);
157 if (!k)
158 return -ENOMEM;
159
160 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
161 continue;
162
163 if (!shown_pids) {
164 show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
165 shown_pids = true;
166 }
167
168 if (last) {
169 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last)));
170
171 if (!p1) {
172 p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL));
173 if (!p1)
174 return -ENOMEM;
175 }
176
177 show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
178 free(last);
179 }
180
181 last = k;
182 k = NULL;
183 }
184
185 if (r < 0)
186 return r;
187
188 if (!shown_pids)
189 show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
190
191 if (last) {
192 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last)));
193
194 if (!p2) {
195 p2 = strappend(prefix, " ");
196 if (!p2)
197 return -ENOMEM;
198 }
199
200 show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
201 }
202
203 return 0;
204 }
205
206 int show_cgroup(const char *controller,
207 const char *path,
208 const char *prefix,
209 unsigned n_columns,
210 bool kernel_threads,
211
212 OutputFlags flags) {
213 _cleanup_free_ char *p = NULL;
214 int r;
215
216 assert(path);
217
218 r = cg_get_path(controller, path, NULL, &p);
219 if (r < 0)
220 return r;
221
222 return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
223 }
224
225 static int show_extra_pids(
226 const char *controller,
227 const char *path,
228 const char *prefix,
229 unsigned n_columns,
230 const pid_t pids[],
231 unsigned n_pids,
232 OutputFlags flags) {
233
234 _cleanup_free_ pid_t *copy = NULL;
235 unsigned i, j;
236 int r;
237
238 assert(path);
239
240 if (n_pids <= 0)
241 return 0;
242
243 if (n_columns <= 0)
244 n_columns = columns();
245
246 prefix = strempty(prefix);
247
248 copy = new(pid_t, n_pids);
249 if (!copy)
250 return -ENOMEM;
251
252 for (i = 0, j = 0; i < n_pids; i++) {
253 _cleanup_free_ char *k = NULL;
254
255 r = cg_pid_get_path(controller, pids[i], &k);
256 if (r < 0)
257 return r;
258
259 if (path_startswith(k, path))
260 continue;
261
262 copy[j++] = pids[i];
263 }
264
265 show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
266
267 return 0;
268 }
269
270 int show_cgroup_and_extra(
271 const char *controller,
272 const char *path,
273 const char *prefix,
274 unsigned n_columns,
275 bool kernel_threads,
276 const pid_t extra_pids[],
277 unsigned n_extra_pids,
278 OutputFlags flags) {
279
280 int r;
281
282 assert(path);
283
284 r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
285 if (r < 0)
286 return r;
287
288 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
289 }
290
291 int show_cgroup_and_extra_by_spec(
292 const char *spec,
293 const char *prefix,
294 unsigned n_columns,
295 bool kernel_threads,
296 const pid_t extra_pids[],
297 unsigned n_extra_pids,
298 OutputFlags flags) {
299
300 _cleanup_free_ char *controller = NULL, *path = NULL;
301 int r;
302
303 assert(spec);
304
305 r = cg_split_spec(spec, &controller, &path);
306 if (r < 0)
307 return r;
308
309 return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
310 }