• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 16280725298

14 Jul 2025 08:16PM UTC coverage: 72.166% (-0.006%) from 72.172%
16280725298

push

github

web-flow
Two fixlets for coverage test (#38183)

302135 of 418667 relevant lines covered (72.17%)

773261.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.08
/src/shared/bus-unit-procs.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-bus.h"
4

5
#include "ansi-color.h"
6
#include "bus-locator.h"
7
#include "bus-unit-procs.h"
8
#include "format-util.h"
9
#include "glyph-util.h"
10
#include "hashmap.h"
11
#include "list.h"
12
#include "log.h"
13
#include "output-mode.h"
14
#include "path-util.h"
15
#include "process-util.h"
16
#include "sort-util.h"
17
#include "string-util.h"
18
#include "terminal-util.h"
19

20
struct CGroupInfo {
21
        char *cgroup_path;
22
        bool is_const; /* If false, cgroup_path should be free()'d */
23

24
        Hashmap *pids; /* PID → process name */
25
        bool done;
26

27
        struct CGroupInfo *parent;
28
        LIST_FIELDS(struct CGroupInfo, siblings);
29
        LIST_HEAD(struct CGroupInfo, children);
30
        size_t n_children;
31
};
32

33
static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1,568✔
34
        struct CGroupInfo *parent = NULL, *cg;
1,568✔
35
        int r;
1,568✔
36

37
        assert(cgroups);
1,568✔
38
        assert(ret);
1,568✔
39

40
        path = empty_to_root(path);
1,568✔
41

42
        cg = hashmap_get(cgroups, path);
1,568✔
43
        if (cg) {
1,568✔
44
                *ret = cg;
708✔
45
                return 0;
708✔
46
        }
47

48
        if (!empty_or_root(path)) {
860✔
49
                const char *e, *pp;
711✔
50

51
                e = strrchr(path, '/');
711✔
52
                if (!e)
711✔
53
                        return -EINVAL;
54

55
                pp = strndupa_safe(path, e - path);
711✔
56

57
                r = add_cgroup(cgroups, pp, false, &parent);
711✔
58
                if (r < 0)
711✔
59
                        return r;
60
        }
61

62
        cg = new0(struct CGroupInfo, 1);
860✔
63
        if (!cg)
860✔
64
                return -ENOMEM;
65

66
        if (is_const)
860✔
67
                cg->cgroup_path = (char*) path;
311✔
68
        else {
69
                cg->cgroup_path = strdup(path);
549✔
70
                if (!cg->cgroup_path) {
549✔
71
                        free(cg);
×
72
                        return -ENOMEM;
×
73
                }
74
        }
75

76
        cg->is_const = is_const;
860✔
77
        cg->parent = parent;
860✔
78

79
        r = hashmap_put(cgroups, cg->cgroup_path, cg);
860✔
80
        if (r < 0) {
860✔
81
                if (!is_const)
×
82
                        free(cg->cgroup_path);
×
83
                free(cg);
×
84
                return r;
×
85
        }
86

87
        if (parent) {
860✔
88
                LIST_PREPEND(siblings, parent->children, cg);
711✔
89
                parent->n_children++;
711✔
90
        }
91

92
        *ret = cg;
860✔
93
        return 1;
860✔
94
}
95

96
static int add_process(
857✔
97
                Hashmap *cgroups,
98
                const char *path,
99
                pid_t pid,
100
                const char *name) {
101

102
        struct CGroupInfo *cg;
857✔
103
        int r;
857✔
104

105
        assert(cgroups);
857✔
106
        assert(name);
857✔
107
        assert(pid > 0);
857✔
108

109
        r = add_cgroup(cgroups, path, true, &cg);
857✔
110
        if (r < 0)
857✔
111
                return r;
857✔
112

113
        return hashmap_ensure_put(&cg->pids, &trivial_hash_ops, PID_TO_PTR(pid), (void*) name);
857✔
114
}
115

116
static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
860✔
117
        assert(cgroups);
860✔
118
        assert(cg);
860✔
119

120
        while (cg->children)
1,356✔
121
                remove_cgroup(cgroups, cg->children);
496✔
122

123
        hashmap_remove(cgroups, cg->cgroup_path);
860✔
124

125
        if (!cg->is_const)
860✔
126
                free(cg->cgroup_path);
549✔
127

128
        hashmap_free(cg->pids);
860✔
129

130
        if (cg->parent)
860✔
131
                LIST_REMOVE(siblings, cg->parent->children, cg);
711✔
132

133
        free(cg);
860✔
134
}
860✔
135

136
static int cgroup_info_compare_func(struct CGroupInfo * const *a, struct CGroupInfo * const *b) {
197✔
137
        return strcmp((*a)->cgroup_path, (*b)->cgroup_path);
197✔
138
}
139

140
static int dump_processes(
392✔
141
                Hashmap *cgroups,
142
                const char *cgroup_path,
143
                const char *prefix,
144
                unsigned n_columns,
145
                OutputFlags flags) {
146

147
        struct CGroupInfo *cg;
392✔
148
        int r;
392✔
149

150
        assert(prefix);
392✔
151

152
        cgroup_path = empty_to_root(cgroup_path);
392✔
153

154
        cg = hashmap_get(cgroups, cgroup_path);
392✔
155
        if (!cg)
392✔
156
                return 0;
157

158
        if (!hashmap_isempty(cg->pids)) {
364✔
159
                const char *name;
265✔
160
                void *pidp;
265✔
161
                size_t n = 0;
265✔
162

163
                /* Order processes by their PID */
164
                pid_t *pids = newa(pid_t, hashmap_size(cg->pids));
265✔
165

166
                HASHMAP_FOREACH_KEY(name, pidp, cg->pids)
1,068✔
167
                        pids[n++] = PTR_TO_PID(pidp);
803✔
168

169
                assert(n == hashmap_size(cg->pids));
265✔
170
                assert(n > 0);
265✔
171
                typesafe_qsort(pids, n, pid_compare_func);
265✔
172

173
                int width = DECIMAL_STR_WIDTH(pids[n-1]);
1,024✔
174

175
                for (size_t i = 0; i < n; i++) {
1,068✔
176
                        _cleanup_free_ char *e = NULL;
803✔
177
                        const char *special;
803✔
178
                        bool more;
803✔
179

180
                        name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
803✔
181
                        assert(name);
803✔
182

183
                        if (n_columns != 0) {
803✔
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;
1,068✔
194
                        special = glyph(more ? GLYPH_TREE_BRANCH : GLYPH_TREE_RIGHT);
265✔
195

196
                        fprintf(stdout, "%s%s%s%*"PID_PRI" %s%s\n",
1,606✔
197
                                prefix,
198
                                special,
199
                                ansi_grey(),
200
                                width, pids[i],
201
                                name,
202
                                ansi_normal());
203
                }
204
        }
205

206
        if (cg->children) {
364✔
207
                struct CGroupInfo **children;
99✔
208
                size_t n = 0, i;
99✔
209

210
                /* Order subcgroups by their name */
211
                children = newa(struct CGroupInfo*, cg->n_children);
99✔
212
                LIST_FOREACH(siblings, child, cg->children)
316✔
213
                        children[n++] = child;
217✔
214
                assert(n == cg->n_children);
99✔
215
                typesafe_qsort(children, n, cgroup_info_compare_func);
99✔
216

217
                if (n_columns != 0)
99✔
218
                        n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
×
219

220
                for (i = 0; i < n; i++) {
316✔
221
                        _cleanup_free_ char *pp = NULL;
217✔
222
                        const char *name, *special;
217✔
223
                        bool more;
217✔
224

225
                        name = strrchr(children[i]->cgroup_path, '/');
217✔
226
                        if (!name)
217✔
227
                                return -EINVAL;
228
                        name++;
217✔
229

230
                        more = i+1 < n;
217✔
231
                        special = glyph(more ? GLYPH_TREE_BRANCH : GLYPH_TREE_RIGHT);
217✔
232

233
                        fputs(prefix, stdout);
217✔
234
                        fputs(special, stdout);
217✔
235
                        fputs(name, stdout);
217✔
236
                        fputc('\n', stdout);
217✔
237

238
                        special = glyph(more ? GLYPH_TREE_VERTICAL : GLYPH_TREE_SPACE);
217✔
239

240
                        pp = strjoin(prefix, special);
217✔
241
                        if (!pp)
217✔
242
                                return -ENOMEM;
243

244
                        r = dump_processes(cgroups, children[i]->cgroup_path, pp, n_columns, flags);
217✔
245
                        if (r < 0)
217✔
246
                                return r;
247
                }
248
        }
249

250
        cg->done = true;
364✔
251
        return 0;
364✔
252
}
253

254
static int dump_extra_processes(
147✔
255
                Hashmap *cgroups,
256
                const char *prefix,
257
                unsigned n_columns,
258
                OutputFlags flags) {
259

260
        _cleanup_free_ pid_t *pids = NULL;
147✔
261
        _cleanup_hashmap_free_ Hashmap *names = NULL;
147✔
262
        struct CGroupInfo *cg;
147✔
263
        size_t n = 0, k;
147✔
264
        int width, r;
147✔
265

266
        /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
267
         * combined, sorted, linear list. */
268

269
        HASHMAP_FOREACH(cg, cgroups) {
831✔
270
                const char *name;
684✔
271
                void *pidp;
684✔
272

273
                if (cg->done)
684✔
274
                        continue;
682✔
275

276
                if (hashmap_isempty(cg->pids))
348✔
277
                        continue;
346✔
278

279
                r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
2✔
280
                if (r < 0)
2✔
281
                        return r;
×
282

283
                if (!GREEDY_REALLOC(pids, n + hashmap_size(cg->pids)))
2✔
284
                        return -ENOMEM;
285

286
                HASHMAP_FOREACH_KEY(name, pidp, cg->pids) {
4✔
287
                        pids[n++] = PTR_TO_PID(pidp);
2✔
288

289
                        r = hashmap_put(names, pidp, (void*) name);
2✔
290
                        if (r < 0)
2✔
291
                                return r;
×
292
                }
293
        }
294

295
        if (n == 0)
147✔
296
                return 0;
297

298
        typesafe_qsort(pids, n, pid_compare_func);
2✔
299
        width = DECIMAL_STR_WIDTH(pids[n-1]);
6✔
300

301
        for (k = 0; k < n; k++) {
4✔
302
                _cleanup_free_ char *e = NULL;
2✔
303
                const char *name;
2✔
304

305
                name = hashmap_get(names, PID_TO_PTR(pids[k]));
2✔
306
                assert(name);
2✔
307

308
                if (n_columns != 0) {
2✔
309
                        unsigned z;
×
310

311
                        z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
×
312

313
                        e = ellipsize(name, z, 100);
×
314
                        if (e)
×
315
                                name = e;
×
316
                }
317

318
                fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
2✔
319
                        prefix,
320
                        glyph(GLYPH_TRIANGULAR_BULLET),
321
                        width, pids[k],
2✔
322
                        name);
323
        }
324

325
        return 0;
326
}
327

328
int unit_show_processes(
175✔
329
                sd_bus *bus,
330
                const char *unit,
331
                const char *cgroup_path,
332
                const char *prefix,
333
                unsigned n_columns,
334
                OutputFlags flags,
335
                sd_bus_error *reterr_error) {
336

337
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
175✔
338
        Hashmap *cgroups = NULL;
175✔
339
        struct CGroupInfo *cg;
175✔
340
        int r;
175✔
341

342
        assert(bus);
175✔
343
        assert(unit);
175✔
344

345
        if (flags & OUTPUT_FULL_WIDTH)
175✔
346
                n_columns = 0;
347
        else if (n_columns <= 0)
×
348
                n_columns = columns();
×
349

350
        prefix = strempty(prefix);
175✔
351

352
        r = bus_call_method(
175✔
353
                        bus,
354
                        bus_systemd_mgr,
355
                        "GetUnitProcesses",
356
                        reterr_error,
357
                        &reply,
358
                        "s",
359
                        unit);
360
        if (r < 0)
175✔
361
                return r;
362

363
        cgroups = hashmap_new(&path_hash_ops);
175✔
364
        if (!cgroups)
175✔
365
                return -ENOMEM;
366

367
        r = sd_bus_message_enter_container(reply, 'a', "(sus)");
175✔
368
        if (r < 0)
175✔
369
                goto finish;
×
370

371
        for (;;) {
1,889✔
372
                const char *path = NULL, *name = NULL;
1,032✔
373
                uint32_t pid;
1,032✔
374

375
                r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1,032✔
376
                if (r < 0)
1,032✔
377
                        goto finish;
×
378
                if (r == 0)
1,032✔
379
                        break;
380

381
                r = add_process(cgroups, path, pid, name);
857✔
382
                if (r == -ENOMEM)
857✔
383
                        goto finish;
×
384
                if (r < 0)
857✔
385
                        log_warning_errno(r, "Invalid process description in GetUnitProcesses reply: cgroup=\"%s\" pid=%u command=\"%s\", ignoring: %m",
857✔
386
                                          path, pid, name);
387
        }
388

389
        r = sd_bus_message_exit_container(reply);
175✔
390
        if (r < 0)
175✔
391
                goto finish;
×
392

393
        r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
175✔
394
        if (r < 0)
175✔
395
                goto finish;
×
396

397
        if (!FLAGS_SET(flags, OUTPUT_HIDE_EXTRA)) {
175✔
398
                r = dump_extra_processes(cgroups, prefix, n_columns, flags);
147✔
399
                if (r < 0)
147✔
400
                        goto finish;
×
401
        }
402

403
        r = 0;
175✔
404

405
finish:
175✔
406
        while ((cg = hashmap_first(cgroups)))
539✔
407
               remove_cgroup(cgroups, cg);
364✔
408

409
        hashmap_free(cgroups);
175✔
410

411
        return r;
412
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc