1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "bus-unit-procs.h"
4 #include "glyph-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_safe(path
, e
- path
);
51 r
= add_cgroup(cgroups
, pp
, false, &parent
);
56 cg
= new0(struct CGroupInfo
, 1);
61 cg
->cgroup_path
= (char*) path
;
63 cg
->cgroup_path
= strdup(path
);
64 if (!cg
->cgroup_path
) {
70 cg
->is_const
= is_const
;
73 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
76 free(cg
->cgroup_path
);
82 LIST_PREPEND(siblings
, parent
->children
, cg
);
90 static int add_process(
96 struct CGroupInfo
*cg
;
103 r
= add_cgroup(cgroups
, path
, true, &cg
);
107 return hashmap_ensure_put(&cg
->pids
, &trivial_hash_ops
, PID_TO_PTR(pid
), (void*) name
);
110 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
115 remove_cgroup(cgroups
, cg
->children
);
117 hashmap_remove(cgroups
, cg
->cgroup_path
);
120 free(cg
->cgroup_path
);
122 hashmap_free(cg
->pids
);
125 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
130 static int cgroup_info_compare_func(struct CGroupInfo
* const *a
, struct CGroupInfo
* const *b
) {
131 return strcmp((*a
)->cgroup_path
, (*b
)->cgroup_path
);
134 static int dump_processes(
136 const char *cgroup_path
,
141 struct CGroupInfo
*cg
;
146 cgroup_path
= empty_to_root(cgroup_path
);
148 cg
= hashmap_get(cgroups
, cgroup_path
);
152 if (!hashmap_isempty(cg
->pids
)) {
159 /* Order processes by their PID */
160 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
162 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
)
163 pids
[n
++] = PTR_TO_PID(pidp
);
165 assert(n
== hashmap_size(cg
->pids
));
166 typesafe_qsort(pids
, n
, pid_compare_func
);
168 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
170 for (i
= 0; i
< n
; i
++) {
171 _cleanup_free_
char *e
= NULL
;
175 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
178 if (n_columns
!= 0) {
181 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
183 e
= ellipsize(name
, k
, 100);
188 more
= i
+1 < n
|| cg
->children
;
189 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
191 fprintf(stdout
, "%s%s%s%*"PID_PRI
" %s%s\n",
202 struct CGroupInfo
**children
;
205 /* Order subcgroups by their name */
206 children
= newa(struct CGroupInfo
*, cg
->n_children
);
207 LIST_FOREACH(siblings
, child
, cg
->children
)
208 children
[n
++] = child
;
209 assert(n
== cg
->n_children
);
210 typesafe_qsort(children
, n
, cgroup_info_compare_func
);
213 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
215 for (i
= 0; i
< n
; i
++) {
216 _cleanup_free_
char *pp
= NULL
;
217 const char *name
, *special
;
220 name
= strrchr(children
[i
]->cgroup_path
, '/');
226 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
228 fputs(prefix
, stdout
);
229 fputs(special
, stdout
);
233 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
);
235 pp
= strjoin(prefix
, special
);
239 r
= dump_processes(cgroups
, children
[i
]->cgroup_path
, pp
, n_columns
, flags
);
249 static int dump_extra_processes(
255 _cleanup_free_ pid_t
*pids
= NULL
;
256 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
257 struct CGroupInfo
*cg
;
261 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
262 * combined, sorted, linear list. */
264 HASHMAP_FOREACH(cg
, cgroups
) {
271 if (hashmap_isempty(cg
->pids
))
274 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
278 if (!GREEDY_REALLOC(pids
, n
+ hashmap_size(cg
->pids
)))
281 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
) {
282 pids
[n
++] = PTR_TO_PID(pidp
);
284 r
= hashmap_put(names
, pidp
, (void*) name
);
293 typesafe_qsort(pids
, n
, pid_compare_func
);
294 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
296 for (k
= 0; k
< n
; k
++) {
297 _cleanup_free_
char *e
= NULL
;
300 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
303 if (n_columns
!= 0) {
306 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
308 e
= ellipsize(name
, z
, 100);
313 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
315 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET
),
323 int unit_show_processes(
326 const char *cgroup_path
,
330 sd_bus_error
*error
) {
332 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
333 Hashmap
*cgroups
= NULL
;
334 struct CGroupInfo
*cg
;
340 if (flags
& OUTPUT_FULL_WIDTH
)
342 else if (n_columns
<= 0)
343 n_columns
= columns();
345 prefix
= strempty(prefix
);
347 r
= sd_bus_call_method(
349 "org.freedesktop.systemd1",
350 "/org/freedesktop/systemd1",
351 "org.freedesktop.systemd1.Manager",
360 cgroups
= hashmap_new(&path_hash_ops
);
364 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
369 const char *path
= NULL
, *name
= NULL
;
372 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
378 r
= add_process(cgroups
, path
, pid
, name
);
382 log_warning_errno(r
, "Invalid process description in GetUnitProcesses reply: cgroup=\"%s\" pid="PID_FMT
" command=\"%s\", ignoring: %m",
386 r
= sd_bus_message_exit_container(reply
);
390 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
394 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
397 while ((cg
= hashmap_first(cgroups
)))
398 remove_cgroup(cgroups
, cg
);
400 hashmap_free(cgroups
);