Coverage Report

Created: 2019-06-19 13:33

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