2 This file is part of systemd.
4 Copyright 2016 Lennart Poettering
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.
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.
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/>.
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.systemd1",
396 "/org/freedesktop/systemd1",
397 "org.freedesktop.systemd1.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
);