]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-procs.c
catalog: update Polish translation
[thirdparty/systemd.git] / src / shared / bus-unit-procs.c
CommitLineData
25b1d72d
LP
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include "bus-unit-procs.h"
4#include "hashmap.h"
5#include "list.h"
6#include "locale-util.h"
7#include "macro.h"
1b63cc6e 8#include "path-util.h"
25b1d72d
LP
9#include "process-util.h"
10#include "sort-util.h"
11#include "string-util.h"
12#include "terminal-util.h"
13
14struct CGroupInfo {
15 char *cgroup_path;
16 bool is_const; /* If false, cgroup_path should be free()'d */
17
18 Hashmap *pids; /* PID → process name */
19 bool done;
20
21 struct CGroupInfo *parent;
22 LIST_FIELDS(struct CGroupInfo, siblings);
23 LIST_HEAD(struct CGroupInfo, children);
24 size_t n_children;
25};
26
25b1d72d
LP
27static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
28 struct CGroupInfo *parent = NULL, *cg;
29 int r;
30
31 assert(cgroups);
32 assert(ret);
33
9228fef0 34 path = empty_to_root(path);
25b1d72d
LP
35
36 cg = hashmap_get(cgroups, path);
37 if (cg) {
38 *ret = cg;
39 return 0;
40 }
41
1b63cc6e 42 if (!empty_or_root(path)) {
25b1d72d
LP
43 const char *e, *pp;
44
45 e = strrchr(path, '/');
46 if (!e)
47 return -EINVAL;
48
49 pp = strndupa(path, e - path);
25b1d72d
LP
50
51 r = add_cgroup(cgroups, pp, false, &parent);
52 if (r < 0)
53 return r;
54 }
55
56 cg = new0(struct CGroupInfo, 1);
57 if (!cg)
58 return -ENOMEM;
59
60 if (is_const)
61 cg->cgroup_path = (char*) path;
62 else {
63 cg->cgroup_path = strdup(path);
64 if (!cg->cgroup_path) {
65 free(cg);
66 return -ENOMEM;
67 }
68 }
69
70 cg->is_const = is_const;
71 cg->parent = parent;
72
73 r = hashmap_put(cgroups, cg->cgroup_path, cg);
74 if (r < 0) {
75 if (!is_const)
76 free(cg->cgroup_path);
77 free(cg);
78 return r;
79 }
80
81 if (parent) {
82 LIST_PREPEND(siblings, parent->children, cg);
83 parent->n_children++;
84 }
85
86 *ret = cg;
87 return 1;
88}
89
90static int add_process(
91 Hashmap *cgroups,
92 const char *path,
93 pid_t pid,
94 const char *name) {
95
96 struct CGroupInfo *cg;
97 int r;
98
99 assert(cgroups);
100 assert(name);
101 assert(pid > 0);
102
103 r = add_cgroup(cgroups, path, true, &cg);
104 if (r < 0)
105 return r;
106
107 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
108 if (r < 0)
109 return r;
110
111 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
112}
113
114static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
115 assert(cgroups);
116 assert(cg);
117
118 while (cg->children)
119 remove_cgroup(cgroups, cg->children);
120
121 hashmap_remove(cgroups, cg->cgroup_path);
122
123 if (!cg->is_const)
124 free(cg->cgroup_path);
125
126 hashmap_free(cg->pids);
127
128 if (cg->parent)
129 LIST_REMOVE(siblings, cg->parent->children, cg);
130
131 free(cg);
132}
133
134static int cgroup_info_compare_func(struct CGroupInfo * const *a, struct CGroupInfo * const *b) {
135 return strcmp((*a)->cgroup_path, (*b)->cgroup_path);
136}
137
138static int dump_processes(
139 Hashmap *cgroups,
140 const char *cgroup_path,
141 const char *prefix,
142 unsigned n_columns,
143 OutputFlags flags) {
144
145 struct CGroupInfo *cg;
146 int r;
147
148 assert(prefix);
149
9228fef0 150 cgroup_path = empty_to_root(cgroup_path);
25b1d72d
LP
151
152 cg = hashmap_get(cgroups, cgroup_path);
153 if (!cg)
154 return 0;
155
156 if (!hashmap_isempty(cg->pids)) {
157 const char *name;
158 size_t n = 0, i;
159 pid_t *pids;
160 void *pidp;
161 Iterator j;
162 int width;
163
164 /* Order processes by their PID */
165 pids = newa(pid_t, hashmap_size(cg->pids));
166
167 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
168 pids[n++] = PTR_TO_PID(pidp);
169
170 assert(n == hashmap_size(cg->pids));
171 typesafe_qsort(pids, n, pid_compare_func);
172
173 width = DECIMAL_STR_WIDTH(pids[n-1]);
174
175 for (i = 0; i < n; i++) {
176 _cleanup_free_ char *e = NULL;
177 const char *special;
178 bool more;
179
180 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
181 assert(name);
182
183 if (n_columns != 0) {
184 unsigned k;
185
186 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
187
188 e = ellipsize(name, k, 100);
189 if (e)
190 name = e;
191 }
192
193 more = i+1 < n || cg->children;
194 special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT);
195
196 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
197 prefix,
198 special,
199 width, pids[i],
200 name);
201 }
202 }
203
204 if (cg->children) {
205 struct CGroupInfo **children, *child;
206 size_t n = 0, i;
207
208 /* Order subcgroups by their name */
209 children = newa(struct CGroupInfo*, cg->n_children);
210 LIST_FOREACH(siblings, child, cg->children)
211 children[n++] = child;
212 assert(n == cg->n_children);
213 typesafe_qsort(children, n, cgroup_info_compare_func);
214
215 if (n_columns != 0)
216 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
217
218 for (i = 0; i < n; i++) {
219 _cleanup_free_ char *pp = NULL;
220 const char *name, *special;
221 bool more;
222
223 child = children[i];
224
225 name = strrchr(child->cgroup_path, '/');
226 if (!name)
227 return -EINVAL;
228 name++;
229
230 more = i+1 < n;
231 special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT);
232
233 fputs(prefix, stdout);
234 fputs(special, stdout);
235 fputs(name, stdout);
236 fputc('\n', stdout);
237
238 special = special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE);
239
b910cc72 240 pp = strjoin(prefix, special);
25b1d72d
LP
241 if (!pp)
242 return -ENOMEM;
243
244 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
245 if (r < 0)
246 return r;
247 }
248 }
249
250 cg->done = true;
251 return 0;
252}
253
254static int dump_extra_processes(
255 Hashmap *cgroups,
256 const char *prefix,
257 unsigned n_columns,
258 OutputFlags flags) {
259
260 _cleanup_free_ pid_t *pids = NULL;
261 _cleanup_hashmap_free_ Hashmap *names = NULL;
262 struct CGroupInfo *cg;
263 size_t n_allocated = 0, n = 0, k;
264 Iterator i;
265 int width, r;
266
267 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
268 * combined, sorted, linear list. */
269
270 HASHMAP_FOREACH(cg, cgroups, i) {
271 const char *name;
272 void *pidp;
273 Iterator j;
274
275 if (cg->done)
276 continue;
277
278 if (hashmap_isempty(cg->pids))
279 continue;
280
281 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
282 if (r < 0)
283 return r;
284
285 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
286 return -ENOMEM;
287
288 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
289 pids[n++] = PTR_TO_PID(pidp);
290
291 r = hashmap_put(names, pidp, (void*) name);
292 if (r < 0)
293 return r;
294 }
295 }
296
297 if (n == 0)
298 return 0;
299
300 typesafe_qsort(pids, n, pid_compare_func);
301 width = DECIMAL_STR_WIDTH(pids[n-1]);
302
303 for (k = 0; k < n; k++) {
304 _cleanup_free_ char *e = NULL;
305 const char *name;
306
307 name = hashmap_get(names, PID_TO_PTR(pids[k]));
308 assert(name);
309
310 if (n_columns != 0) {
311 unsigned z;
312
313 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
314
315 e = ellipsize(name, z, 100);
316 if (e)
317 name = e;
318 }
319
320 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
321 prefix,
322 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET),
323 width, pids[k],
324 name);
325 }
326
327 return 0;
328}
329
330int unit_show_processes(
331 sd_bus *bus,
332 const char *unit,
333 const char *cgroup_path,
334 const char *prefix,
335 unsigned n_columns,
336 OutputFlags flags,
337 sd_bus_error *error) {
338
339 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
340 Hashmap *cgroups = NULL;
341 struct CGroupInfo *cg;
342 int r;
343
344 assert(bus);
345 assert(unit);
346
347 if (flags & OUTPUT_FULL_WIDTH)
348 n_columns = 0;
349 else if (n_columns <= 0)
350 n_columns = columns();
351
352 prefix = strempty(prefix);
353
354 r = sd_bus_call_method(
355 bus,
356 "org.freedesktop.systemd1",
357 "/org/freedesktop/systemd1",
358 "org.freedesktop.systemd1.Manager",
359 "GetUnitProcesses",
360 error,
361 &reply,
362 "s",
363 unit);
364 if (r < 0)
365 return r;
366
367 cgroups = hashmap_new(&path_hash_ops);
368 if (!cgroups)
369 return -ENOMEM;
370
371 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
372 if (r < 0)
373 goto finish;
374
375 for (;;) {
376 const char *path = NULL, *name = NULL;
377 uint32_t pid;
378
379 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
380 if (r < 0)
381 goto finish;
382 if (r == 0)
383 break;
384
385 r = add_process(cgroups, path, pid, name);
107c2051 386 if (r == -ENOMEM)
25b1d72d 387 goto finish;
107c2051
ZJS
388 if (r < 0)
389 log_warning_errno(r, "Invalid process description in GetUnitProcesses reply: cgroup=\"%s\" pid="PID_FMT" command=\"%s\", ignoring: %m",
390 path, pid, name);
25b1d72d
LP
391 }
392
393 r = sd_bus_message_exit_container(reply);
394 if (r < 0)
395 goto finish;
396
397 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
398 if (r < 0)
399 goto finish;
400
401 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
402
403finish:
404 while ((cg = hashmap_first(cgroups)))
405 remove_cgroup(cgroups, cg);
406
407 hashmap_free(cgroups);
408
409 return r;
410}