]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-util.c
shared: drop kernel_thread bool from cgroups show code
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
CommitLineData
291d565a
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2016 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 "alloc-util.h"
21#include "bus-internal.h"
22#include "bus-unit-util.h"
23#include "bus-util.h"
24#include "cgroup-util.h"
25#include "env-util.h"
26#include "escape.h"
27#include "hashmap.h"
28#include "list.h"
29#include "locale-util.h"
30#include "parse-util.h"
31#include "path-util.h"
32#include "process-util.h"
33#include "rlimit-util.h"
34#include "signal-util.h"
35#include "string-util.h"
36#include "syslog-util.h"
37#include "terminal-util.h"
38#include "utf8.h"
39#include "util.h"
40
41struct CGroupInfo {
42 char *cgroup_path;
43 bool is_const; /* If false, cgroup_path should be free()'d */
44
45 Hashmap *pids; /* PID → process name */
46 bool done;
47
48 struct CGroupInfo *parent;
49 LIST_FIELDS(struct CGroupInfo, siblings);
50 LIST_HEAD(struct CGroupInfo, children);
51 size_t n_children;
52};
53
54static bool IS_ROOT(const char *p) {
55 return isempty(p) || streq(p, "/");
56}
57
58static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
59 struct CGroupInfo *parent = NULL, *cg;
60 int r;
61
62 assert(cgroups);
63 assert(ret);
64
65 if (IS_ROOT(path))
66 path = "/";
67
68 cg = hashmap_get(cgroups, path);
69 if (cg) {
70 *ret = cg;
71 return 0;
72 }
73
74 if (!IS_ROOT(path)) {
75 const char *e, *pp;
76
77 e = strrchr(path, '/');
78 if (!e)
79 return -EINVAL;
80
81 pp = strndupa(path, e - path);
82 if (!pp)
83 return -ENOMEM;
84
85 r = add_cgroup(cgroups, pp, false, &parent);
86 if (r < 0)
87 return r;
88 }
89
90 cg = new0(struct CGroupInfo, 1);
91 if (!cg)
92 return -ENOMEM;
93
94 if (is_const)
95 cg->cgroup_path = (char*) path;
96 else {
97 cg->cgroup_path = strdup(path);
98 if (!cg->cgroup_path) {
99 free(cg);
100 return -ENOMEM;
101 }
102 }
103
104 cg->is_const = is_const;
105 cg->parent = parent;
106
107 r = hashmap_put(cgroups, cg->cgroup_path, cg);
108 if (r < 0) {
109 if (!is_const)
110 free(cg->cgroup_path);
111 free(cg);
112 return r;
113 }
114
115 if (parent) {
116 LIST_PREPEND(siblings, parent->children, cg);
117 parent->n_children++;
118 }
119
120 *ret = cg;
121 return 1;
122}
123
124static int add_process(
125 Hashmap *cgroups,
126 const char *path,
127 pid_t pid,
128 const char *name) {
129
130 struct CGroupInfo *cg;
131 int r;
132
133 assert(cgroups);
134 assert(name);
135 assert(pid > 0);
136
137 r = add_cgroup(cgroups, path, true, &cg);
138 if (r < 0)
139 return r;
140
141 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
142 if (r < 0)
143 return r;
144
145 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
146}
147
148static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
149 assert(cgroups);
150 assert(cg);
151
152 while (cg->children)
153 remove_cgroup(cgroups, cg->children);
154
155 hashmap_remove(cgroups, cg->cgroup_path);
156
157 if (!cg->is_const)
158 free(cg->cgroup_path);
159
160 hashmap_free(cg->pids);
161
162 if (cg->parent)
163 LIST_REMOVE(siblings, cg->parent->children, cg);
164
165 free(cg);
166}
167
168static int cgroup_info_compare_func(const void *a, const void *b) {
169 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
170
171 assert(x);
172 assert(y);
173
174 return strcmp(x->cgroup_path, y->cgroup_path);
175}
176
177static int dump_processes(
178 Hashmap *cgroups,
179 const char *cgroup_path,
180 const char *prefix,
181 unsigned n_columns,
182 OutputFlags flags) {
183
184 struct CGroupInfo *cg;
185 int r;
186
187 assert(prefix);
188
189 if (IS_ROOT(cgroup_path))
190 cgroup_path = "/";
191
192 cg = hashmap_get(cgroups, cgroup_path);
193 if (!cg)
194 return 0;
195
196 if (!hashmap_isempty(cg->pids)) {
197 const char *name;
198 size_t n = 0, i;
199 pid_t *pids;
200 void *pidp;
201 Iterator j;
202 int width;
203
204 /* Order processes by their PID */
205 pids = newa(pid_t, hashmap_size(cg->pids));
206
207 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
208 pids[n++] = PTR_TO_PID(pidp);
209
210 assert(n == hashmap_size(cg->pids));
211 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
212
213 width = DECIMAL_STR_WIDTH(pids[n-1]);
214
215 for (i = 0; i < n; i++) {
216 _cleanup_free_ char *e = NULL;
217 const char *special;
218 bool more;
219
220 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
221 assert(name);
222
223 if (n_columns != 0) {
224 unsigned k;
225
226 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
227
228 e = ellipsize(name, k, 100);
229 if (e)
230 name = e;
231 }
232
233 more = i+1 < n || cg->children;
234 special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
235
236 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
237 prefix,
238 special,
239 width, pids[i],
240 name);
241 }
242 }
243
244 if (cg->children) {
245 struct CGroupInfo **children, *child;
246 size_t n = 0, i;
247
248 /* Order subcgroups by their name */
249 children = newa(struct CGroupInfo*, cg->n_children);
250 LIST_FOREACH(siblings, child, cg->children)
251 children[n++] = child;
252 assert(n == cg->n_children);
253 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
254
255 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
256
257 for (i = 0; i < n; i++) {
258 _cleanup_free_ char *pp = NULL;
259 const char *name, *special;
260 bool more;
261
262 child = children[i];
263
264 name = strrchr(child->cgroup_path, '/');
265 if (!name)
266 return -EINVAL;
267 name++;
268
269 more = i+1 < n;
270 special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
271
272 fputs(prefix, stdout);
273 fputs(special, stdout);
274 fputs(name, stdout);
275 fputc('\n', stdout);
276
277 special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE);
278
279 pp = strappend(prefix, special);
280 if (!pp)
281 return -ENOMEM;
282
283 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
284 if (r < 0)
285 return r;
286 }
287 }
288
289 cg->done = true;
290 return 0;
291}
292
293static int dump_extra_processes(
294 Hashmap *cgroups,
295 const char *prefix,
296 unsigned n_columns,
297 OutputFlags flags) {
298
299 _cleanup_free_ pid_t *pids = NULL;
300 _cleanup_hashmap_free_ Hashmap *names = NULL;
301 struct CGroupInfo *cg;
302 size_t n_allocated = 0, n = 0, k;
303 Iterator i;
304 int width, r;
305
306 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
307 * combined, sorted, linear list. */
308
309 HASHMAP_FOREACH(cg, cgroups, i) {
310 const char *name;
311 void *pidp;
312 Iterator j;
313
314 if (cg->done)
315 continue;
316
317 if (hashmap_isempty(cg->pids))
318 continue;
319
320 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
321 if (r < 0)
322 return r;
323
324 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
325 return -ENOMEM;
326
327 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
328 pids[n++] = PTR_TO_PID(pidp);
329
330 r = hashmap_put(names, pidp, (void*) name);
331 if (r < 0)
332 return r;
333 }
334 }
335
336 if (n == 0)
337 return 0;
338
339 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
340 width = DECIMAL_STR_WIDTH(pids[n-1]);
341
342 for (k = 0; k < n; k++) {
343 _cleanup_free_ char *e = NULL;
344 const char *name;
345
346 name = hashmap_get(names, PID_TO_PTR(pids[k]));
347 assert(name);
348
349 if (n_columns != 0) {
350 unsigned z;
351
352 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
353
354 e = ellipsize(name, z, 100);
355 if (e)
356 name = e;
357 }
358
359 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
360 prefix,
361 draw_special_char(DRAW_TRIANGULAR_BULLET),
362 width, pids[k],
363 name);
364 }
365
366 return 0;
367}
368
369int unit_show_processes(
370 sd_bus *bus,
371 const char *unit,
372 const char *cgroup_path,
373 const char *prefix,
374 unsigned n_columns,
375 OutputFlags flags,
376 sd_bus_error *error) {
377
378 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
379 Hashmap *cgroups = NULL;
380 struct CGroupInfo *cg;
381 int r;
382
383 assert(bus);
384 assert(unit);
385
386 if (flags & OUTPUT_FULL_WIDTH)
387 n_columns = 0;
388 else if (n_columns <= 0)
389 n_columns = columns();
390
391 prefix = strempty(prefix);
392
393 r = sd_bus_call_method(
394 bus,
395 "org.freedesktop.systemd1",
396 "/org/freedesktop/systemd1",
397 "org.freedesktop.systemd1.Manager",
398 "GetUnitProcesses",
399 error,
400 &reply,
401 "s",
402 unit);
403 if (r < 0)
404 return r;
405
406 cgroups = hashmap_new(&string_hash_ops);
407 if (!cgroups)
408 return -ENOMEM;
409
410 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
411 if (r < 0)
412 goto finish;
413
414 for (;;) {
415 const char *path = NULL, *name = NULL;
416 uint32_t pid;
417
418 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
419 if (r < 0)
420 goto finish;
421 if (r == 0)
422 break;
423
424 r = add_process(cgroups, path, pid, name);
425 if (r < 0)
426 goto finish;
427 }
428
429 r = sd_bus_message_exit_container(reply);
430 if (r < 0)
431 goto finish;
432
433 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
434 if (r < 0)
435 goto finish;
436
437 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
438
439finish:
440 while ((cg = hashmap_first(cgroups)))
441 remove_cgroup(cgroups, cg);
442
443 hashmap_free(cgroups);
444
445 return r;
446}