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 if (empty_or_root(path
))
37 cg
= hashmap_get(cgroups
, path
);
43 if (!empty_or_root(path
)) {
46 e
= strrchr(path
, '/');
50 pp
= strndupa(path
, e
- path
);
54 r
= add_cgroup(cgroups
, pp
, false, &parent
);
59 cg
= new0(struct CGroupInfo
, 1);
64 cg
->cgroup_path
= (char*) path
;
66 cg
->cgroup_path
= strdup(path
);
67 if (!cg
->cgroup_path
) {
73 cg
->is_const
= is_const
;
76 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
79 free(cg
->cgroup_path
);
85 LIST_PREPEND(siblings
, parent
->children
, cg
);
93 static int add_process(
99 struct CGroupInfo
*cg
;
106 r
= add_cgroup(cgroups
, path
, true, &cg
);
110 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
114 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
117 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
122 remove_cgroup(cgroups
, cg
->children
);
124 hashmap_remove(cgroups
, cg
->cgroup_path
);
127 free(cg
->cgroup_path
);
129 hashmap_free(cg
->pids
);
132 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
137 static int cgroup_info_compare_func(struct CGroupInfo
* const *a
, struct CGroupInfo
* const *b
) {
138 return strcmp((*a
)->cgroup_path
, (*b
)->cgroup_path
);
141 static int dump_processes(
143 const char *cgroup_path
,
148 struct CGroupInfo
*cg
;
153 if (empty_or_root(cgroup_path
))
156 cg
= hashmap_get(cgroups
, cgroup_path
);
160 if (!hashmap_isempty(cg
->pids
)) {
168 /* Order processes by their PID */
169 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
171 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
172 pids
[n
++] = PTR_TO_PID(pidp
);
174 assert(n
== hashmap_size(cg
->pids
));
175 typesafe_qsort(pids
, n
, pid_compare_func
);
177 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
179 for (i
= 0; i
< n
; i
++) {
180 _cleanup_free_
char *e
= NULL
;
184 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
187 if (n_columns
!= 0) {
190 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
192 e
= ellipsize(name
, k
, 100);
197 more
= i
+1 < n
|| cg
->children
;
198 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
200 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
209 struct CGroupInfo
**children
, *child
;
212 /* Order subcgroups by their name */
213 children
= newa(struct CGroupInfo
*, cg
->n_children
);
214 LIST_FOREACH(siblings
, child
, cg
->children
)
215 children
[n
++] = child
;
216 assert(n
== cg
->n_children
);
217 typesafe_qsort(children
, n
, cgroup_info_compare_func
);
220 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
222 for (i
= 0; i
< n
; i
++) {
223 _cleanup_free_
char *pp
= NULL
;
224 const char *name
, *special
;
229 name
= strrchr(child
->cgroup_path
, '/');
235 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
237 fputs(prefix
, stdout
);
238 fputs(special
, stdout
);
242 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
);
244 pp
= strappend(prefix
, special
);
248 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
258 static int dump_extra_processes(
264 _cleanup_free_ pid_t
*pids
= NULL
;
265 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
266 struct CGroupInfo
*cg
;
267 size_t n_allocated
= 0, n
= 0, k
;
271 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
272 * combined, sorted, linear list. */
274 HASHMAP_FOREACH(cg
, cgroups
, i
) {
282 if (hashmap_isempty(cg
->pids
))
285 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
289 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
292 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
293 pids
[n
++] = PTR_TO_PID(pidp
);
295 r
= hashmap_put(names
, pidp
, (void*) name
);
304 typesafe_qsort(pids
, n
, pid_compare_func
);
305 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
307 for (k
= 0; k
< n
; k
++) {
308 _cleanup_free_
char *e
= NULL
;
311 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
314 if (n_columns
!= 0) {
317 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
319 e
= ellipsize(name
, z
, 100);
324 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
326 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET
),
334 int unit_show_processes(
337 const char *cgroup_path
,
341 sd_bus_error
*error
) {
343 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
344 Hashmap
*cgroups
= NULL
;
345 struct CGroupInfo
*cg
;
351 if (flags
& OUTPUT_FULL_WIDTH
)
353 else if (n_columns
<= 0)
354 n_columns
= columns();
356 prefix
= strempty(prefix
);
358 r
= sd_bus_call_method(
360 "org.freedesktop.systemd1",
361 "/org/freedesktop/systemd1",
362 "org.freedesktop.systemd1.Manager",
371 cgroups
= hashmap_new(&path_hash_ops
);
375 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
380 const char *path
= NULL
, *name
= NULL
;
383 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
389 r
= add_process(cgroups
, path
, pid
, name
);
394 r
= sd_bus_message_exit_container(reply
);
398 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
402 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
405 while ((cg
= hashmap_first(cgroups
)))
406 remove_cgroup(cgroups
, cg
);
408 hashmap_free(cgroups
);