2 This file is part of elogind.
4 Copyright 2016 Lennart Poettering
6 elogind 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.
11 elogind 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.
16 You should have received a copy of the GNU Lesser General Public License
17 along with elogind; If not, see <http://www.gnu.org/licenses/>.
20 #include "alloc-util.h"
21 #include "bus-internal.h"
22 #include "bus-unit-util.h"
24 #include "cgroup-util.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"
43 bool is_const; /* If false, cgroup_path should be free()'d */
45 Hashmap *pids; /* PID → process name */
48 struct CGroupInfo *parent;
49 LIST_FIELDS(struct CGroupInfo, siblings);
50 LIST_HEAD(struct CGroupInfo, children);
54 static bool IS_ROOT(const char *p) {
55 return isempty(p) || streq(p, "/");
58 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
59 struct CGroupInfo *parent = NULL, *cg;
68 cg = hashmap_get(cgroups, path);
77 e = strrchr(path, '/');
81 pp = strndupa(path, e - path);
85 r = add_cgroup(cgroups, pp, false, &parent);
90 cg = new0(struct CGroupInfo, 1);
95 cg->cgroup_path = (char*) path;
97 cg->cgroup_path = strdup(path);
98 if (!cg->cgroup_path) {
104 cg->is_const = is_const;
107 r = hashmap_put(cgroups, cg->cgroup_path, cg);
110 free(cg->cgroup_path);
116 LIST_PREPEND(siblings, parent->children, cg);
117 parent->n_children++;
124 static int add_process(
130 struct CGroupInfo *cg;
137 r = add_cgroup(cgroups, path, true, &cg);
141 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
145 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
148 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
153 remove_cgroup(cgroups, cg->children);
155 hashmap_remove(cgroups, cg->cgroup_path);
158 free(cg->cgroup_path);
160 hashmap_free(cg->pids);
163 LIST_REMOVE(siblings, cg->parent->children, cg);
168 static 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;
174 return strcmp(x->cgroup_path, y->cgroup_path);
177 static int dump_processes(
179 const char *cgroup_path,
184 struct CGroupInfo *cg;
189 if (IS_ROOT(cgroup_path))
192 cg = hashmap_get(cgroups, cgroup_path);
196 if (!hashmap_isempty(cg->pids)) {
204 /* Order processes by their PID */
205 pids = newa(pid_t, hashmap_size(cg->pids));
207 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
208 pids[n++] = PTR_TO_PID(pidp);
210 assert(n == hashmap_size(cg->pids));
211 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
213 width = DECIMAL_STR_WIDTH(pids[n-1]);
215 for (i = 0; i < n; i++) {
216 _cleanup_free_ char *e = NULL;
220 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
223 if (n_columns != 0) {
226 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
228 e = ellipsize(name, k, 100);
233 more = i+1 < n || cg->children;
234 special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
236 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
245 struct CGroupInfo **children, *child;
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);
255 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
257 for (i = 0; i < n; i++) {
258 _cleanup_free_ char *pp = NULL;
259 const char *name, *special;
264 name = strrchr(child->cgroup_path, '/');
270 special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
272 fputs(prefix, stdout);
273 fputs(special, stdout);
277 special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE);
279 pp = strappend(prefix, special);
283 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
293 static int dump_extra_processes(
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;
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. */
309 HASHMAP_FOREACH(cg, cgroups, i) {
317 if (hashmap_isempty(cg->pids))
320 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
324 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
327 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
328 pids[n++] = PTR_TO_PID(pidp);
330 r = hashmap_put(names, pidp, (void*) name);
339 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
340 width = DECIMAL_STR_WIDTH(pids[n-1]);
342 for (k = 0; k < n; k++) {
343 _cleanup_free_ char *e = NULL;
346 name = hashmap_get(names, PID_TO_PTR(pids[k]));
349 if (n_columns != 0) {
352 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
354 e = ellipsize(name, z, 100);
359 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
361 draw_special_char(DRAW_TRIANGULAR_BULLET),
369 int unit_show_processes(
372 const char *cgroup_path,
376 sd_bus_error *error) {
378 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
379 Hashmap *cgroups = NULL;
380 struct CGroupInfo *cg;
386 if (flags & OUTPUT_FULL_WIDTH)
388 else if (n_columns <= 0)
389 n_columns = columns();
391 prefix = strempty(prefix);
393 r = sd_bus_call_method(
395 "org.freedesktop.elogind1",
396 "/org/freedesktop/elogind1",
397 "org.freedesktop.elogind1.Manager",
406 cgroups = hashmap_new(&string_hash_ops);
410 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
415 const char *path = NULL, *name = NULL;
418 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
424 r = add_process(cgroups, path, pid, name);
429 r = sd_bus_message_exit_container(reply);
433 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
437 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
440 while ((cg = hashmap_first(cgroups)))
441 remove_cgroup(cgroups, cg);
443 hashmap_free(cgroups);