1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "bus-unit-procs.h"
6 #include "locale-util.h"
9 #include "process-util.h"
10 #include "sort-util.h"
11 #include "string-util.h"
12 #include "terminal-util.h"
16 bool is_const
; /* If false, cgroup_path should be free()'d */
18 Hashmap
*pids
; /* PID → process name */
21 struct CGroupInfo
*parent
;
22 LIST_FIELDS(struct CGroupInfo
, siblings
);
23 LIST_HEAD(struct CGroupInfo
, children
);
27 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
28 struct CGroupInfo
*parent
= NULL
, *cg
;
34 path
= empty_to_root(path
);
36 cg
= hashmap_get(cgroups
, path
);
42 if (!empty_or_root(path
)) {
45 e
= strrchr(path
, '/');
49 pp
= strndupa(path
, e
- path
);
53 r
= add_cgroup(cgroups
, pp
, false, &parent
);
58 cg
= new0(struct CGroupInfo
, 1);
63 cg
->cgroup_path
= (char*) path
;
65 cg
->cgroup_path
= strdup(path
);
66 if (!cg
->cgroup_path
) {
72 cg
->is_const
= is_const
;
75 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
78 free(cg
->cgroup_path
);
84 LIST_PREPEND(siblings
, parent
->children
, cg
);
92 static int add_process(
98 struct CGroupInfo
*cg
;
105 r
= add_cgroup(cgroups
, path
, true, &cg
);
109 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
113 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
116 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
121 remove_cgroup(cgroups
, cg
->children
);
123 hashmap_remove(cgroups
, cg
->cgroup_path
);
126 free(cg
->cgroup_path
);
128 hashmap_free(cg
->pids
);
131 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
136 static int cgroup_info_compare_func(struct CGroupInfo
* const *a
, struct CGroupInfo
* const *b
) {
137 return strcmp((*a
)->cgroup_path
, (*b
)->cgroup_path
);
140 static int dump_processes(
142 const char *cgroup_path
,
147 struct CGroupInfo
*cg
;
152 cgroup_path
= empty_to_root(cgroup_path
);
154 cg
= hashmap_get(cgroups
, cgroup_path
);
158 if (!hashmap_isempty(cg
->pids
)) {
166 /* Order processes by their PID */
167 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
169 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
170 pids
[n
++] = PTR_TO_PID(pidp
);
172 assert(n
== hashmap_size(cg
->pids
));
173 typesafe_qsort(pids
, n
, pid_compare_func
);
175 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
177 for (i
= 0; i
< n
; i
++) {
178 _cleanup_free_
char *e
= NULL
;
182 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
185 if (n_columns
!= 0) {
188 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
190 e
= ellipsize(name
, k
, 100);
195 more
= i
+1 < n
|| cg
->children
;
196 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
198 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
207 struct CGroupInfo
**children
, *child
;
210 /* Order subcgroups by their name */
211 children
= newa(struct CGroupInfo
*, cg
->n_children
);
212 LIST_FOREACH(siblings
, child
, cg
->children
)
213 children
[n
++] = child
;
214 assert(n
== cg
->n_children
);
215 typesafe_qsort(children
, n
, cgroup_info_compare_func
);
218 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
220 for (i
= 0; i
< n
; i
++) {
221 _cleanup_free_
char *pp
= NULL
;
222 const char *name
, *special
;
227 name
= strrchr(child
->cgroup_path
, '/');
233 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
235 fputs(prefix
, stdout
);
236 fputs(special
, stdout
);
240 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
);
242 pp
= strjoin(prefix
, special
);
246 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
256 static int dump_extra_processes(
262 _cleanup_free_ pid_t
*pids
= NULL
;
263 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
264 struct CGroupInfo
*cg
;
265 size_t n_allocated
= 0, n
= 0, k
;
269 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
270 * combined, sorted, linear list. */
272 HASHMAP_FOREACH(cg
, cgroups
, i
) {
280 if (hashmap_isempty(cg
->pids
))
283 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
287 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
290 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
291 pids
[n
++] = PTR_TO_PID(pidp
);
293 r
= hashmap_put(names
, pidp
, (void*) name
);
302 typesafe_qsort(pids
, n
, pid_compare_func
);
303 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
305 for (k
= 0; k
< n
; k
++) {
306 _cleanup_free_
char *e
= NULL
;
309 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
312 if (n_columns
!= 0) {
315 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
317 e
= ellipsize(name
, z
, 100);
322 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
324 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET
),
332 int unit_show_processes(
335 const char *cgroup_path
,
339 sd_bus_error
*error
) {
341 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
342 Hashmap
*cgroups
= NULL
;
343 struct CGroupInfo
*cg
;
349 if (flags
& OUTPUT_FULL_WIDTH
)
351 else if (n_columns
<= 0)
352 n_columns
= columns();
354 prefix
= strempty(prefix
);
356 r
= sd_bus_call_method(
358 "org.freedesktop.systemd1",
359 "/org/freedesktop/systemd1",
360 "org.freedesktop.systemd1.Manager",
369 cgroups
= hashmap_new(&path_hash_ops
);
373 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
378 const char *path
= NULL
, *name
= NULL
;
381 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
387 r
= add_process(cgroups
, path
, pid
, name
);
391 log_warning_errno(r
, "Invalid process description in GetUnitProcesses reply: cgroup=\"%s\" pid="PID_FMT
" command=\"%s\", ignoring: %m",
395 r
= sd_bus_message_exit_container(reply
);
399 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
403 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
406 while ((cg
= hashmap_first(cgroups
)))
407 remove_cgroup(cgroups
, cg
);
409 hashmap_free(cgroups
);