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

systemd / systemd / 13380515387

17 Feb 2025 09:20PM UTC coverage: 71.822% (+0.1%) from 71.714%
13380515387

push

github

DaanDeMeyer
ukify: print all remaining log-like output to stderr

We want to be able to capture stdout for json and such, so convert
all remaining logging to stderr.

293883 of 409184 relevant lines covered (71.82%)

716959.33 hits per line

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

79.57
/src/basic/cgroup-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <limits.h>
5
#include <signal.h>
6
#include <stddef.h>
7
#include <stdlib.h>
8
#include <sys/types.h>
9
#include <sys/utsname.h>
10
#include <sys/xattr.h>
11
#include <unistd.h>
12

13
#include "alloc-util.h"
14
#include "cgroup-util.h"
15
#include "constants.h"
16
#include "dirent-util.h"
17
#include "extract-word.h"
18
#include "fd-util.h"
19
#include "fileio.h"
20
#include "format-util.h"
21
#include "fs-util.h"
22
#include "log.h"
23
#include "login-util.h"
24
#include "macro.h"
25
#include "missing_fs.h"
26
#include "missing_magic.h"
27
#include "missing_threads.h"
28
#include "mkdir.h"
29
#include "parse-util.h"
30
#include "path-util.h"
31
#include "process-util.h"
32
#include "set.h"
33
#include "special.h"
34
#include "stat-util.h"
35
#include "stdio-util.h"
36
#include "string-table.h"
37
#include "string-util.h"
38
#include "strv.h"
39
#include "unit-name.h"
40
#include "user-util.h"
41
#include "xattr-util.h"
42

43
int cg_path_open(const char *controller, const char *path) {
778✔
44
        _cleanup_free_ char *fs = NULL;
778✔
45
        int r;
778✔
46

47
        r = cg_get_path(controller, path, /* item=*/ NULL, &fs);
778✔
48
        if (r < 0)
778✔
49
                return r;
50

51
        return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC));
778✔
52
}
53

54
int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
1✔
55
        _cleanup_close_ int fsfd = -EBADF;
1✔
56

57
        if (cgroupfs_fd < 0) {
1✔
58
                fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY);
×
59
                if (fsfd < 0)
×
60
                        return -errno;
×
61

62
                cgroupfs_fd = fsfd;
63
        }
64

65
        cg_file_handle fh = CG_FILE_HANDLE_INIT;
1✔
66
        CG_FILE_HANDLE_CGROUPID(fh) = id;
1✔
67

68
        return RET_NERRNO(open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC));
2✔
69
}
70

71
int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret) {
×
72
        _cleanup_close_ int cgfd = -EBADF;
×
73
        int r;
×
74

75
        cgfd = cg_cgroupid_open(cgroupfs_fd, id);
×
76
        if (cgfd < 0)
×
77
                return cgfd;
78

79
        _cleanup_free_ char *path = NULL;
×
80
        r = fd_get_path(cgfd, &path);
×
81
        if (r < 0)
×
82
                return r;
83

84
        if (!path_startswith(path, "/sys/fs/cgroup/"))
×
85
                return -EXDEV; /* recognizable error */
86

87
        if (ret)
×
88
                *ret = TAKE_PTR(path);
×
89
        return 0;
90
}
91

92
int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret) {
5,749✔
93
        cg_file_handle fh = CG_FILE_HANDLE_INIT;
5,749✔
94
        int mnt_id;
5,749✔
95

96
        assert(dfd >= 0 || (dfd == AT_FDCWD && path_is_absolute(path)));
11,468✔
97
        assert(ret);
5,749✔
98

99
        /* This is cgroupfs so we know the size of the handle, thus no need to loop around like
100
         * name_to_handle_at_loop() does in mountpoint-util.c */
101
        if (name_to_handle_at(dfd, strempty(path), &fh.file_handle, &mnt_id, isempty(path) ? AT_EMPTY_PATH : 0) < 0) {
11,498✔
102
                assert(errno != EOVERFLOW);
×
103
                return -errno;
×
104
        }
105

106
        *ret = CG_FILE_HANDLE_CGROUPID(fh);
5,749✔
107
        return 0;
5,749✔
108
}
109

110
static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
29,518✔
111
        _cleanup_free_ char *fs = NULL;
29,518✔
112
        FILE *f;
29,518✔
113
        int r;
29,518✔
114

115
        assert(ret);
29,518✔
116

117
        r = cg_get_path(controller, path, item, &fs);
29,518✔
118
        if (r < 0)
29,518✔
119
                return r;
120

121
        f = fopen(fs, "re");
29,518✔
122
        if (!f)
29,518✔
123
                return -errno;
18,457✔
124

125
        *ret = f;
11,061✔
126
        return 0;
11,061✔
127
}
128

129
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret) {
412✔
130
        return cg_enumerate_items(controller, path, ret, "cgroup.procs");
412✔
131
}
132

133
int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags) {
22,148✔
134
        unsigned long ul;
22,148✔
135

136
        /* Note that the cgroup.procs might contain duplicates! See cgroups.txt for details. */
137

138
        assert(f);
22,148✔
139
        assert(ret);
22,148✔
140

141
        for (;;) {
22,148✔
142
                errno = 0;
22,148✔
143
                if (fscanf(f, "%lu", &ul) != 1) {
22,148✔
144

145
                        if (feof(f)) {
11,291✔
146
                                *ret = 0;
11,291✔
147
                                return 0;
11,291✔
148
                        }
149

150
                        return errno_or_else(EIO);
×
151
                }
152

153
                if (ul > PID_T_MAX)
10,857✔
154
                        return -EIO;
155

156
                /* In some circumstances (e.g. WSL), cgroups might contain unmappable PIDs from other
157
                 * contexts. These show up as zeros, and depending on the caller, can either be plain
158
                 * skipped over, or returned as-is. */
159
                if (ul == 0 && !FLAGS_SET(flags, CGROUP_DONT_SKIP_UNMAPPED))
10,857✔
160
                        continue;
×
161

162
                *ret = (pid_t) ul;
10,857✔
163
                return 1;
10,857✔
164
        }
165
}
166

167
int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) {
15,119✔
168
        int r;
15,119✔
169

170
        assert(f);
15,119✔
171
        assert(ret);
15,119✔
172

173
        for (;;) {
×
174
                pid_t pid;
15,119✔
175

176
                r = cg_read_pid(f, &pid, flags);
15,119✔
177
                if (r < 0)
15,119✔
178
                        return log_debug_errno(r, "Failed to read pid from cgroup item: %m");
×
179
                if (r == 0) {
15,119✔
180
                        *ret = PIDREF_NULL;
10,667✔
181
                        return 0;
10,667✔
182
                }
183

184
                if (pid == 0)
4,452✔
185
                        return -EREMOTE;
186

187
                if (FLAGS_SET(flags, CGROUP_NO_PIDFD)) {
4,452✔
188
                        *ret = PIDREF_MAKE_FROM_PID(pid);
526✔
189
                        return 1;
526✔
190
                }
191

192
                r = pidref_set_pid(ret, pid);
3,926✔
193
                if (r >= 0)
3,926✔
194
                        return 1;
195
                if (r != -ESRCH)
×
196
                        return r;
197

198
                /* ESRCH → gone by now? just skip over it, read the next */
199
        }
200
}
201

202
int cg_read_event(
12,457✔
203
                const char *controller,
204
                const char *path,
205
                const char *event,
206
                char **ret) {
207

208
        _cleanup_free_ char *events = NULL, *content = NULL;
12,457✔
209
        int r;
12,457✔
210

211
        r = cg_get_path(controller, path, "cgroup.events", &events);
12,457✔
212
        if (r < 0)
12,457✔
213
                return r;
214

215
        r = read_full_virtual_file(events, &content, NULL);
12,457✔
216
        if (r < 0)
12,457✔
217
                return r;
218

219
        for (const char *p = content;;) {
4,784✔
220
                _cleanup_free_ char *line = NULL, *key = NULL;
4,784✔
221
                const char *q;
4,784✔
222

223
                r = extract_first_word(&p, &line, "\n", 0);
4,784✔
224
                if (r < 0)
4,784✔
225
                        return r;
226
                if (r == 0)
4,784✔
227
                        return -ENOENT;
228

229
                q = line;
4,784✔
230
                r = extract_first_word(&q, &key, " ", 0);
4,784✔
231
                if (r < 0)
4,784✔
232
                        return r;
233
                if (r == 0)
4,784✔
234
                        return -EINVAL;
235

236
                if (!streq(key, event))
4,784✔
237
                        continue;
×
238

239
                return strdup_to(ret, q);
4,784✔
240
        }
241
}
242

243
bool cg_ns_supported(void) {
588✔
244
        static thread_local int supported = -1;
588✔
245

246
        if (supported >= 0)
588✔
247
                return supported;
×
248

249
        if (access("/proc/self/ns/cgroup", F_OK) >= 0)
588✔
250
                return (supported = true);
588✔
251
        if (errno != ENOENT)
×
252
                log_debug_errno(errno, "Failed to check whether /proc/self/ns/cgroup is available, assuming not: %m");
×
253
        return (supported = false);
×
254
}
255

256
bool cg_freezer_supported(void) {
×
257
        static thread_local int supported = -1;
×
258

259
        if (supported >= 0)
×
260
                return supported;
×
261

262
        if (cg_all_unified() <= 0)
×
263
                return (supported = false);
×
264

265
        if (access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) >= 0)
×
266
                return (supported = true);
×
267
        if (errno != ENOENT)
×
268
                log_debug_errno(errno, "Failed to check whether cgroup freezer is available, assuming not: %m");
×
269
        return (supported = false);
×
270
}
271

272
bool cg_kill_supported(void) {
×
273
        static thread_local int supported = -1;
×
274

275
        if (supported >= 0)
×
276
                return supported;
×
277

278
        if (cg_all_unified() <= 0)
×
279
                return (supported = false);
×
280

281
        if (access("/sys/fs/cgroup/init.scope/cgroup.kill", F_OK) >= 0)
×
282
                return (supported = true);
×
283
        if (errno != ENOENT)
×
284
                log_debug_errno(errno, "Failed to check whether cgroup.kill is available, assuming not: %m");
×
285
        return (supported = false);
×
286
}
287

288
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret) {
27,314✔
289
        _cleanup_free_ char *fs = NULL;
27,314✔
290
        DIR *d;
27,314✔
291
        int r;
27,314✔
292

293
        assert(ret);
27,314✔
294

295
        /* This is not recursive! */
296

297
        r = cg_get_path(controller, path, NULL, &fs);
27,314✔
298
        if (r < 0)
27,314✔
299
                return r;
300

301
        d = opendir(fs);
27,314✔
302
        if (!d)
27,314✔
303
                return -errno;
15,871✔
304

305
        *ret = d;
11,443✔
306
        return 0;
11,443✔
307
}
308

309
int cg_read_subgroup(DIR *d, char **ret) {
16,955✔
310
        assert(d);
16,955✔
311
        assert(ret);
16,955✔
312

313
        FOREACH_DIRENT_ALL(de, d, return -errno) {
537,800✔
314
                if (de->d_type != DT_DIR)
526,127✔
315
                        continue;
497,499✔
316

317
                if (dot_or_dot_dot(de->d_name))
28,628✔
318
                        continue;
23,346✔
319

320
                return strdup_to_full(ret, de->d_name);
5,282✔
321
        }
322

323
        *ret = NULL;
11,673✔
324
        return 0;
11,673✔
325
}
326

327
static int cg_kill_items(
28,855✔
328
                const char *path,
329
                const char *item,
330
                int sig,
331
                CGroupFlags flags,
332
                Set *s,
333
                cg_kill_log_func_t log_kill,
334
                void *userdata) {
335

336
        _cleanup_set_free_ Set *allocated_set = NULL;
28,855✔
337
        int r, ret = 0;
28,855✔
338

339
        assert(path);
28,855✔
340
        assert(item);
28,855✔
341
        assert(sig >= 0);
28,855✔
342

343
         /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence
344
          * don't send SIGCONT on SIGKILL. */
345
        if (IN_SET(sig, SIGCONT, SIGKILL))
28,855✔
346
                flags &= ~CGROUP_SIGCONT;
10,846✔
347

348
        /* This goes through the tasks list and kills them all. This is repeated until no further processes
349
         * are added to the tasks list, to properly handle forking processes.
350
         *
351
         * When sending SIGKILL, prefer cg_kill_kernel_sigkill(), which is fully atomic. */
352

353
        if (!s) {
28,855✔
354
                s = allocated_set = set_new(NULL);
904✔
355
                if (!s)
904✔
356
                        return -ENOMEM;
357
        }
358

359
        bool done;
29,106✔
360
        do {
29,106✔
361
                _cleanup_fclose_ FILE *f = NULL;
18,457✔
362
                int ret_log_kill;
29,106✔
363

364
                done = true;
29,106✔
365

366
                r = cg_enumerate_items(SYSTEMD_CGROUP_CONTROLLER, path, &f, item);
29,106✔
367
                if (r == -ENOENT)
29,106✔
368
                        break;
369
                if (r < 0)
10,649✔
370
                        return RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup items: %m"));
×
371

372
                for (;;) {
15,028✔
373
                        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
15,028✔
374

375
                        r = cg_read_pidref(f, &pidref, flags);
15,028✔
376
                        if (r < 0)
15,028✔
377
                                return RET_GATHER(ret, log_debug_errno(r, "Failed to read pidref from cgroup '%s': %m", path));
×
378
                        if (r == 0)
15,028✔
379
                                break;
380

381
                        if ((flags & CGROUP_IGNORE_SELF) && pidref_is_self(&pidref))
4,379✔
382
                                continue;
904✔
383

384
                        if (set_contains(s, PID_TO_PTR(pidref.pid)))
3,475✔
385
                                continue;
2,130✔
386

387
                        /* Ignore kernel threads to mimic the behavior of cgroup.kill. */
388
                        if (pidref_is_kernel_thread(&pidref) > 0) {
1,345✔
389
                                log_debug("Ignoring kernel thread with pid " PID_FMT " in cgroup '%s'", pidref.pid, path);
×
390
                                continue;
×
391
                        }
392

393
                        if (log_kill)
1,345✔
394
                                ret_log_kill = log_kill(&pidref, sig, userdata);
91✔
395

396
                        /* If we haven't killed this process yet, kill it */
397
                        r = pidref_kill(&pidref, sig);
1,345✔
398
                        if (r < 0 && r != -ESRCH)
1,345✔
399
                                RET_GATHER(ret, log_debug_errno(r, "Failed to kill process with pid " PID_FMT " from cgroup '%s': %m", pidref.pid, path));
×
400
                        if (r >= 0) {
1,345✔
401
                                if (flags & CGROUP_SIGCONT)
1,345✔
402
                                        (void) pidref_kill(&pidref, SIGCONT);
1,252✔
403

404
                                if (ret == 0) {
1,345✔
405
                                        if (log_kill)
321✔
406
                                                ret = ret_log_kill;
407
                                        else
408
                                                ret = 1;
230✔
409
                                }
410
                        }
411

412
                        done = false;
1,345✔
413

414
                        r = set_put(s, PID_TO_PTR(pidref.pid));
1,345✔
415
                        if (r < 0)
1,345✔
416
                                return RET_GATHER(ret, r);
×
417
                }
418

419
                /* To avoid racing against processes which fork quicker than we can kill them, we repeat this
420
                 * until no new pids need to be killed. */
421

422
        } while (!done);
10,649✔
423

424
        return ret;
425
}
426

427
int cg_kill(
23,432✔
428
                const char *path,
429
                int sig,
430
                CGroupFlags flags,
431
                Set *s,
432
                cg_kill_log_func_t log_kill,
433
                void *userdata) {
434

435
        int r, ret;
23,432✔
436

437
        assert(path);
23,432✔
438

439
        ret = cg_kill_items(path, "cgroup.procs", sig, flags, s, log_kill, userdata);
23,432✔
440
        if (ret < 0)
23,432✔
441
                return log_debug_errno(ret, "Failed to kill processes in cgroup '%s' item cgroup.procs: %m", path);
×
442
        if (sig != SIGKILL)
23,432✔
443
                return ret;
444

445
        /* Only in case of killing with SIGKILL and when using cgroupsv2, kill remaining threads manually as
446
           a workaround for kernel bug. It was fixed in 5.2-rc5 (c03cd7738a83), backported to 4.19.66
447
           (4340d175b898) and 4.14.138 (feb6b123b7dd). */
448
        r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
5,423✔
449
        if (r < 0)
5,423✔
450
                return r;
451
        if (r == 0)
5,423✔
452
                return ret;
453

454
        /* Opening pidfds for non thread group leaders only works from 6.9 onwards with PIDFD_THREAD. On
455
         * older kernels or without PIDFD_THREAD pidfd_open() fails with EINVAL. Since we might read non
456
         * thread group leader IDs from cgroup.threads, we set CGROUP_NO_PIDFD to avoid trying open pidfd's
457
         * for them and instead use the regular pid. */
458
        r = cg_kill_items(path, "cgroup.threads", sig, flags|CGROUP_NO_PIDFD, s, log_kill, userdata);
5,423✔
459
        if (r < 0)
5,423✔
460
                return log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.threads: %m", path);
×
461

462
        return r > 0 || ret > 0;
5,423✔
463
}
464

465
int cg_kill_recursive(
22,978✔
466
                const char *path,
467
                int sig,
468
                CGroupFlags flags,
469
                Set *s,
470
                cg_kill_log_func_t log_kill,
471
                void *userdata) {
472

473
        _cleanup_set_free_ Set *allocated_set = NULL;
×
474
        _cleanup_closedir_ DIR *d = NULL;
22,978✔
475
        int r, ret;
22,978✔
476

477
        assert(path);
22,978✔
478
        assert(sig >= 0);
22,978✔
479

480
        if (!s) {
22,978✔
481
                s = allocated_set = set_new(NULL);
22,066✔
482
                if (!s)
22,066✔
483
                        return -ENOMEM;
484
        }
485

486
        ret = cg_kill(path, sig, flags, s, log_kill, userdata);
22,978✔
487

488
        r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
22,978✔
489
        if (r < 0) {
22,978✔
490
                if (r != -ENOENT)
15,871✔
491
                        RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup '%s' subgroups: %m", path));
×
492

493
                return ret;
15,871✔
494
        }
495

496
        for (;;) {
7,471✔
497
                _cleanup_free_ char *fn = NULL, *p = NULL;
7,289✔
498

499
                r = cg_read_subgroup(d, &fn);
7,289✔
500
                if (r < 0) {
7,289✔
501
                        RET_GATHER(ret, log_debug_errno(r, "Failed to read subgroup from cgroup '%s': %m", path));
×
502
                        break;
503
                }
504
                if (r == 0)
7,289✔
505
                        break;
506

507
                p = path_join(empty_to_root(path), fn);
364✔
508
                if (!p)
182✔
509
                        return -ENOMEM;
×
510

511
                r = cg_kill_recursive(p, sig, flags, s, log_kill, userdata);
182✔
512
                if (r < 0)
182✔
513
                        log_debug_errno(r, "Failed to recursively kill processes in cgroup '%s': %m", p);
×
514
                if (r != 0 && ret >= 0)
182✔
515
                        ret = r;
15✔
516
        }
517

518
        return ret;
7,107✔
519
}
520

521
int cg_kill_kernel_sigkill(const char *path) {
×
522
        _cleanup_free_ char *killfile = NULL;
×
523
        int r;
×
524

525
        /* Kills the cgroup at `path` directly by writing to its cgroup.kill file.  This sends SIGKILL to all
526
         * processes in the cgroup and has the advantage of being completely atomic, unlike cg_kill_items(). */
527

528
        assert(path);
×
529

530
        if (!cg_kill_supported())
×
531
                return -EOPNOTSUPP;
532

533
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, "cgroup.kill", &killfile);
×
534
        if (r < 0)
×
535
                return r;
536

537
        r = write_string_file(killfile, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
×
538
        if (r < 0)
×
539
                return log_debug_errno(r, "Failed to write to cgroup.kill for cgroup '%s': %m", path);
×
540

541
        return 0;
542
}
543

544
static const char *controller_to_dirname(const char *controller) {
×
545
        assert(controller);
×
546

547
        /* Converts a controller name to the directory name below /sys/fs/cgroup/ we want to mount it
548
         * to. Effectively, this just cuts off the name= prefixed used for named hierarchies, if it is
549
         * specified. */
550

551
        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
×
552
                if (cg_hybrid_unified() > 0)
×
553
                        controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
554
                else
555
                        controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
×
556
        }
557

558
        return startswith(controller, "name=") ?: controller;
×
559
}
560

561
static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **ret) {
×
562
        const char *dn;
×
563
        char *t = NULL;
×
564

565
        assert(ret);
×
566
        assert(controller);
×
567

568
        dn = controller_to_dirname(controller);
×
569

570
        if (isempty(path) && isempty(suffix))
×
571
                t = path_join("/sys/fs/cgroup", dn);
×
572
        else if (isempty(path))
×
573
                t = path_join("/sys/fs/cgroup", dn, suffix);
×
574
        else if (isempty(suffix))
×
575
                t = path_join("/sys/fs/cgroup", dn, path);
×
576
        else
577
                t = path_join("/sys/fs/cgroup", dn, path, suffix);
×
578
        if (!t)
×
579
                return -ENOMEM;
580

581
        *ret = t;
×
582
        return 0;
×
583
}
584

585
static int join_path_unified(const char *path, const char *suffix, char **ret) {
327,296✔
586
        char *t;
327,296✔
587

588
        assert(ret);
327,296✔
589

590
        if (isempty(path) && isempty(suffix))
343,353✔
591
                t = strdup("/sys/fs/cgroup");
2,585✔
592
        else if (isempty(path))
324,711✔
593
                t = path_join("/sys/fs/cgroup", suffix);
13,472✔
594
        else if (isempty(suffix))
311,239✔
595
                t = path_join("/sys/fs/cgroup", path);
121,407✔
596
        else
597
                t = path_join("/sys/fs/cgroup", path, suffix);
189,832✔
598
        if (!t)
327,296✔
599
                return -ENOMEM;
600

601
        *ret = t;
327,296✔
602
        return 0;
327,296✔
603
}
604

605
int cg_get_path(const char *controller, const char *path, const char *suffix, char **ret) {
327,553✔
606
        int r;
327,553✔
607

608
        assert(ret);
327,553✔
609

610
        if (!controller) {
327,553✔
611
                char *t;
257✔
612

613
                /* If no controller is specified, we return the path *below* the controllers, without any
614
                 * prefix. */
615

616
                if (isempty(path) && isempty(suffix))
257✔
617
                        return -EINVAL;
618

619
                if (isempty(suffix))
257✔
620
                        t = strdup(path);
×
621
                else if (isempty(path))
257✔
622
                        t = strdup(suffix);
×
623
                else
624
                        t = path_join(path, suffix);
257✔
625
                if (!t)
257✔
626
                        return -ENOMEM;
627

628
                *ret = path_simplify(t);
257✔
629
                return 0;
257✔
630
        }
631

632
        if (!cg_controller_is_valid(controller))
327,296✔
633
                return -EINVAL;
634

635
        r = cg_all_unified();
327,296✔
636
        if (r < 0)
327,296✔
637
                return r;
638
        if (r > 0)
327,296✔
639
                r = join_path_unified(path, suffix, ret);
327,296✔
640
        else
641
                r = join_path_legacy(controller, path, suffix, ret);
×
642
        if (r < 0)
327,296✔
643
                return r;
644

645
        path_simplify(*ret);
327,296✔
646
        return 0;
327,296✔
647
}
648

649
static int controller_is_v1_accessible(const char *root, const char *controller) {
×
650
        const char *cpath, *dn;
×
651

652
        assert(controller);
×
653

654
        dn = controller_to_dirname(controller);
×
655

656
        /* If root if specified, we check that:
657
         * - possible subcgroup is created at root,
658
         * - we can modify the hierarchy. */
659

660
        cpath = strjoina("/sys/fs/cgroup/", dn, root, root ? "/cgroup.procs" : NULL);
×
661
        return access_nofollow(cpath, root ? W_OK : F_OK);
×
662
}
663

664
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **ret) {
24,086✔
665
        int r;
24,086✔
666

667
        assert(controller);
24,086✔
668
        assert(ret);
24,086✔
669

670
        if (!cg_controller_is_valid(controller))
24,086✔
671
                return -EINVAL;
672

673
        r = cg_all_unified();
24,086✔
674
        if (r < 0)
24,086✔
675
                return r;
676
        if (r > 0) {
24,086✔
677
                /* In the unified hierarchy all controllers are considered accessible,
678
                 * except for the named hierarchies */
679
                if (startswith(controller, "name="))
24,086✔
680
                        return -EOPNOTSUPP;
681
        } else {
682
                /* Check if the specified controller is actually accessible */
683
                r = controller_is_v1_accessible(NULL, controller);
×
684
                if (r < 0)
×
685
                        return r;
686
        }
687

688
        return cg_get_path(controller, path, suffix, ret);
24,086✔
689
}
690

691
int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags) {
8,076✔
692
        _cleanup_free_ char *fs = NULL;
8,076✔
693
        int r;
8,076✔
694

695
        assert(path);
8,076✔
696
        assert(name);
8,076✔
697
        assert(value || size <= 0);
8,076✔
698

699
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
8,076✔
700
        if (r < 0)
8,076✔
701
                return r;
702

703
        return RET_NERRNO(setxattr(fs, name, value, size, flags));
8,076✔
704
}
705

706
int cg_get_xattr(const char *path, const char *name, void *value, size_t size) {
×
707
        _cleanup_free_ char *fs = NULL;
×
708
        ssize_t n;
×
709
        int r;
×
710

711
        assert(path);
×
712
        assert(name);
×
713

714
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
×
715
        if (r < 0)
×
716
                return r;
717

718
        n = getxattr(fs, name, value, size);
×
719
        if (n < 0)
×
720
                return -errno;
×
721

722
        return (int) n;
×
723
}
724

725
int cg_get_xattr_malloc(const char *path, const char *name, char **ret) {
19,812✔
726
        _cleanup_free_ char *fs = NULL;
19,812✔
727
        int r;
19,812✔
728

729
        assert(path);
19,812✔
730
        assert(name);
19,812✔
731

732
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
19,812✔
733
        if (r < 0)
19,812✔
734
                return r;
735

736
        return lgetxattr_malloc(fs, name, ret);
19,812✔
737
}
738

739
int cg_get_xattr_bool(const char *path, const char *name) {
423✔
740
        _cleanup_free_ char *fs = NULL;
423✔
741
        int r;
423✔
742

743
        assert(path);
423✔
744
        assert(name);
423✔
745

746
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
423✔
747
        if (r < 0)
423✔
748
                return r;
749

750
        return getxattr_at_bool(AT_FDCWD, fs, name, /* flags= */ 0);
423✔
751
}
752

753
int cg_remove_xattr(const char *path, const char *name) {
39,908✔
754
        _cleanup_free_ char *fs = NULL;
39,908✔
755
        int r;
39,908✔
756

757
        assert(path);
39,908✔
758
        assert(name);
39,908✔
759

760
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
39,908✔
761
        if (r < 0)
39,908✔
762
                return r;
763

764
        return RET_NERRNO(removexattr(fs, name));
79,816✔
765
}
766

767
int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) {
50,973✔
768
        _cleanup_fclose_ FILE *f = NULL;
50,973✔
769
        const char *fs, *controller_str = NULL;  /* avoid false maybe-uninitialized warning */
50,973✔
770
        int unified, r;
50,973✔
771

772
        assert(pid >= 0);
50,973✔
773
        assert(ret_path);
50,973✔
774

775
        if (controller) {
50,973✔
776
                if (!cg_controller_is_valid(controller))
50,638✔
777
                        return -EINVAL;
778
        } else
779
                controller = SYSTEMD_CGROUP_CONTROLLER;
780

781
        unified = cg_unified_controller(controller);
50,973✔
782
        if (unified < 0)
50,973✔
783
                return unified;
784
        if (unified == 0) {
50,973✔
785
                if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
×
786
                        controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
787
                else
788
                        controller_str = controller;
×
789
        }
790

791
        fs = procfs_file_alloca(pid, "cgroup");
50,973✔
792
        r = fopen_unlocked(fs, "re", &f);
50,973✔
793
        if (r == -ENOENT)
50,973✔
794
                return -ESRCH;
795
        if (r < 0)
47,977✔
796
                return r;
797

798
        for (;;) {
49,147✔
799
                _cleanup_free_ char *line = NULL;
48,562✔
800
                char *e;
48,562✔
801

802
                r = read_line(f, LONG_LINE_MAX, &line);
48,562✔
803
                if (r < 0)
48,562✔
804
                        return r;
805
                if (r == 0)
48,552✔
806
                        return -ENODATA;
807

808
                if (unified) {
48,552✔
809
                        e = startswith(line, "0:");
48,552✔
810
                        if (!e)
48,552✔
811
                                continue;
585✔
812

813
                        e = strchr(e, ':');
47,967✔
814
                        if (!e)
47,967✔
815
                                continue;
×
816
                } else {
817
                        char *l;
×
818

819
                        l = strchr(line, ':');
×
820
                        if (!l)
×
821
                                continue;
×
822

823
                        l++;
×
824
                        e = strchr(l, ':');
×
825
                        if (!e)
×
826
                                continue;
×
827
                        *e = 0;
×
828

829
                        assert(controller_str);
×
830
                        r = string_contains_word(l, ",", controller_str);
×
831
                        if (r < 0)
×
832
                                return r;
833
                        if (r == 0)
×
834
                                continue;
×
835
                }
836

837
                _cleanup_free_ char *path = strdup(e + 1);
47,967✔
838
                if (!path)
47,967✔
839
                        return -ENOMEM;
840

841
                /* Refuse cgroup paths from outside our cgroup namespace */
842
                if (startswith(path, "/../"))
47,967✔
843
                        return -EUNATCH;
844

845
                /* Truncate suffix indicating the process is a zombie */
846
                e = endswith(path, " (deleted)");
47,967✔
847
                if (e)
47,967✔
848
                        *e = 0;
1,240✔
849

850
                *ret_path = TAKE_PTR(path);
47,967✔
851
                return 0;
47,967✔
852
        }
853
}
854

855
int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret_path) {
20,901✔
856
        _cleanup_free_ char *path = NULL;
20,901✔
857
        int r;
20,901✔
858

859
        assert(ret_path);
20,901✔
860

861
        if (!pidref_is_set(pidref))
20,901✔
862
                return -ESRCH;
863
        if (pidref_is_remote(pidref))
41,802✔
864
                return -EREMOTE;
865

866
        // XXX: Ideally we'd use pidfd_get_cgroupid() + cg_path_from_cgroupid() here, to extract this
867
        // bit of information from pidfd directly. However, the latter requires privilege and it's
868
        // not entirely clear how to handle cgroups from outer namespace.
869

870
        r = cg_pid_get_path(controller, pidref->pid, &path);
20,901✔
871
        if (r < 0)
20,901✔
872
                return r;
873

874
        /* Before we return the path, make sure the procfs entry for this pid still matches the pidref */
875
        r = pidref_verify(pidref);
20,901✔
876
        if (r < 0)
20,901✔
877
                return r;
878

879
        *ret_path = TAKE_PTR(path);
20,901✔
880
        return 0;
20,901✔
881
}
882

883
int cg_is_empty(const char *controller, const char *path) {
4✔
884
        _cleanup_fclose_ FILE *f = NULL;
4✔
885
        pid_t pid;
4✔
886
        int r;
4✔
887

888
        assert(path);
4✔
889

890
        r = cg_enumerate_processes(controller, path, &f);
4✔
891
        if (r == -ENOENT)
4✔
892
                return true;
893
        if (r < 0)
4✔
894
                return r;
895

896
        r = cg_read_pid(f, &pid, CGROUP_DONT_SKIP_UNMAPPED);
4✔
897
        if (r < 0)
4✔
898
                return r;
899

900
        return r == 0;
4✔
901
}
902

903
int cg_is_empty_recursive(const char *controller, const char *path) {
12,457✔
904
        int r;
12,457✔
905

906
        assert(path);
12,457✔
907

908
        /* The root cgroup is always populated */
909
        if (controller && empty_or_root(path))
12,457✔
910
                return false;
911

912
        r = cg_unified_controller(controller);
12,457✔
913
        if (r < 0)
12,457✔
914
                return r;
915
        if (r > 0) {
12,457✔
916
                _cleanup_free_ char *t = NULL;
12,457✔
917

918
                /* On the unified hierarchy we can check empty state
919
                 * via the "populated" attribute of "cgroup.events". */
920

921
                r = cg_read_event(controller, path, "populated", &t);
12,457✔
922
                if (r == -ENOENT)
12,457✔
923
                        return true;
924
                if (r < 0)
4,784✔
925
                        return r;
926

927
                return streq(t, "0");
4,784✔
928
        } else {
929
                _cleanup_closedir_ DIR *d = NULL;
×
930
                char *fn;
×
931

932
                r = cg_is_empty(controller, path);
×
933
                if (r <= 0)
×
934
                        return r;
935

936
                r = cg_enumerate_subgroups(controller, path, &d);
×
937
                if (r == -ENOENT)
×
938
                        return true;
939
                if (r < 0)
×
940
                        return r;
941

942
                while ((r = cg_read_subgroup(d, &fn)) > 0) {
×
943
                        _cleanup_free_ char *p = NULL;
×
944

945
                        p = path_join(path, fn);
×
946
                        free(fn);
×
947
                        if (!p)
×
948
                                return -ENOMEM;
949

950
                        r = cg_is_empty_recursive(controller, p);
×
951
                        if (r <= 0)
×
952
                                return r;
953
                }
954
                if (r < 0)
×
955
                        return r;
956

957
                return true;
×
958
        }
959
}
960

961
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path) {
23✔
962
        _cleanup_free_ char *controller = NULL, *path = NULL;
23✔
963
        int r;
23✔
964

965
        assert(spec);
23✔
966

967
        if (*spec == '/') {
23✔
968
                if (!path_is_normalized(spec))
15✔
969
                        return -EINVAL;
970

971
                if (ret_path) {
15✔
972
                        r = path_simplify_alloc(spec, &path);
15✔
973
                        if (r < 0)
15✔
974
                                return r;
975
                }
976

977
        } else {
978
                const char *e;
8✔
979

980
                e = strchr(spec, ':');
8✔
981
                if (e) {
8✔
982
                        controller = strndup(spec, e-spec);
6✔
983
                        if (!controller)
6✔
984
                                return -ENOMEM;
985
                        if (!cg_controller_is_valid(controller))
6✔
986
                                return -EINVAL;
987

988
                        if (!isempty(e + 1)) {
3✔
989
                                path = strdup(e+1);
2✔
990
                                if (!path)
2✔
991
                                        return -ENOMEM;
992

993
                                if (!path_is_normalized(path) ||
2✔
994
                                    !path_is_absolute(path))
2✔
995
                                        return -EINVAL;
996

997
                                path_simplify(path);
1✔
998
                        }
999

1000
                } else {
1001
                        if (!cg_controller_is_valid(spec))
2✔
1002
                                return -EINVAL;
1003

1004
                        if (ret_controller) {
1✔
1005
                                controller = strdup(spec);
1✔
1006
                                if (!controller)
1✔
1007
                                        return -ENOMEM;
1008
                        }
1009
                }
1010
        }
1011

1012
        if (ret_controller)
18✔
1013
                *ret_controller = TAKE_PTR(controller);
18✔
1014
        if (ret_path)
18✔
1015
                *ret_path = TAKE_PTR(path);
18✔
1016
        return 0;
1017
}
1018

1019
int cg_mangle_path(const char *path, char **ret) {
465✔
1020
        _cleanup_free_ char *c = NULL, *p = NULL;
465✔
1021
        int r;
465✔
1022

1023
        assert(path);
465✔
1024
        assert(ret);
465✔
1025

1026
        /* First, check if it already is a filesystem path */
1027
        if (path_startswith(path, "/sys/fs/cgroup"))
465✔
1028
                return path_simplify_alloc(path, ret);
461✔
1029

1030
        /* Otherwise, treat it as cg spec */
1031
        r = cg_split_spec(path, &c, &p);
4✔
1032
        if (r < 0)
4✔
1033
                return r;
1034

1035
        return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, ret);
8✔
1036
}
1037

1038
int cg_get_root_path(char **ret_path) {
14,285✔
1039
        char *p, *e;
14,285✔
1040
        int r;
14,285✔
1041

1042
        assert(ret_path);
14,285✔
1043

1044
        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
14,285✔
1045
        if (r < 0)
14,285✔
1046
                return r;
14,285✔
1047

1048
        e = endswith(p, "/" SPECIAL_INIT_SCOPE);
14,285✔
1049
        if (!e)
14,285✔
1050
                e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
79✔
1051
        if (!e)
79✔
1052
                e = endswith(p, "/system"); /* even more legacy */
79✔
1053
        if (e)
14,285✔
1054
                *e = 0;
14,206✔
1055

1056
        *ret_path = p;
14,285✔
1057
        return 0;
14,285✔
1058
}
1059

1060
int cg_shift_path(const char *cgroup, const char *root, const char **ret_shifted) {
11,147✔
1061
        _cleanup_free_ char *rt = NULL;
11,147✔
1062
        char *p;
11,147✔
1063
        int r;
11,147✔
1064

1065
        assert(cgroup);
11,147✔
1066
        assert(ret_shifted);
11,147✔
1067

1068
        if (!root) {
11,147✔
1069
                /* If the root was specified let's use that, otherwise
1070
                 * let's determine it from PID 1 */
1071

1072
                r = cg_get_root_path(&rt);
2,077✔
1073
                if (r < 0)
2,077✔
1074
                        return r;
1075

1076
                root = rt;
2,077✔
1077
        }
1078

1079
        p = path_startswith(cgroup, root);
11,147✔
1080
        if (p && p > cgroup)
11,147✔
1081
                *ret_shifted = p - 1;
2✔
1082
        else
1083
                *ret_shifted = cgroup;
11,145✔
1084

1085
        return 0;
1086
}
1087

1088
int cg_pid_get_path_shifted(pid_t pid, const char *root, char **ret_cgroup) {
13,979✔
1089
        _cleanup_free_ char *raw = NULL;
13,979✔
1090
        const char *c;
13,979✔
1091
        int r;
13,979✔
1092

1093
        assert(pid >= 0);
13,979✔
1094
        assert(ret_cgroup);
13,979✔
1095

1096
        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
13,979✔
1097
        if (r < 0)
13,979✔
1098
                return r;
1099

1100
        r = cg_shift_path(raw, root, &c);
10,973✔
1101
        if (r < 0)
10,973✔
1102
                return r;
1103

1104
        if (c == raw) {
10,973✔
1105
                *ret_cgroup = TAKE_PTR(raw);
10,973✔
1106
                return 0;
10,973✔
1107
        }
1108

1109
        return strdup_to(ret_cgroup, c);
×
1110
}
1111

1112
int cg_path_decode_unit(const char *cgroup, char **ret_unit) {
32,397✔
1113
        assert(cgroup);
32,397✔
1114
        assert(ret_unit);
32,397✔
1115

1116
        size_t n = strcspn(cgroup, "/");
32,397✔
1117
        if (n < 3)
32,397✔
1118
                return -ENXIO;
1119

1120
        char *c = strndupa_safe(cgroup, n);
32,391✔
1121
        c = cg_unescape(c);
32,391✔
1122

1123
        if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
32,391✔
1124
                return -ENXIO;
1125

1126
        return strdup_to(ret_unit, c);
32,383✔
1127
}
1128

1129
static bool valid_slice_name(const char *p, size_t n) {
114,875✔
1130

1131
        if (!p)
114,875✔
1132
                return false;
1133

1134
        if (n < STRLEN("x.slice"))
114,861✔
1135
                return false;
1136

1137
        if (memcmp(p + n - 6, ".slice", 6) == 0) {
114,844✔
1138
                char buf[n+1], *c;
58,159✔
1139

1140
                memcpy(buf, p, n);
58,159✔
1141
                buf[n] = 0;
58,159✔
1142

1143
                c = cg_unescape(buf);
58,159✔
1144

1145
                return unit_name_is_valid(c, UNIT_NAME_PLAIN);
58,159✔
1146
        }
1147

1148
        return false;
1149
}
1150

1151
static const char *skip_slices(const char *p) {
40,855✔
1152
        assert(p);
40,855✔
1153

1154
        /* Skips over all slice assignments */
1155

1156
        for (;;) {
124,555✔
1157
                size_t n;
82,705✔
1158

1159
                p += strspn(p, "/");
82,705✔
1160

1161
                n = strcspn(p, "/");
82,705✔
1162
                if (!valid_slice_name(p, n))
82,705✔
1163
                        return p;
40,855✔
1164

1165
                p += n;
41,850✔
1166
        }
1167
}
1168

1169
int cg_path_get_unit(const char *path, char **ret) {
16,753✔
1170
        _cleanup_free_ char *unit = NULL;
16,753✔
1171
        const char *e;
16,753✔
1172
        int r;
16,753✔
1173

1174
        assert(path);
16,753✔
1175
        assert(ret);
16,753✔
1176

1177
        e = skip_slices(path);
16,753✔
1178

1179
        r = cg_path_decode_unit(e, &unit);
16,753✔
1180
        if (r < 0)
16,753✔
1181
                return r;
1182

1183
        /* We skipped over the slices, don't accept any now */
1184
        if (endswith(unit, ".slice"))
16,743✔
1185
                return -ENXIO;
1186

1187
        *ret = TAKE_PTR(unit);
16,743✔
1188
        return 0;
16,743✔
1189
}
1190

1191
int cg_path_get_unit_path(const char *path, char **ret) {
8,912✔
1192
        _cleanup_free_ char *path_copy = NULL;
8,912✔
1193
        char *unit_name;
8,912✔
1194

1195
        assert(path);
8,912✔
1196
        assert(ret);
8,912✔
1197

1198
        path_copy = strdup(path);
8,912✔
1199
        if (!path_copy)
8,912✔
1200
                return -ENOMEM;
1201

1202
        unit_name = (char *)skip_slices(path_copy);
8,912✔
1203
        unit_name[strcspn(unit_name, "/")] = 0;
8,912✔
1204

1205
        if (!unit_name_is_valid(cg_unescape(unit_name), UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
8,912✔
1206
                return -ENXIO;
1207

1208
        *ret = TAKE_PTR(path_copy);
8,909✔
1209

1210
        return 0;
8,909✔
1211
}
1212

1213
int cg_pid_get_unit(pid_t pid, char **ret_unit) {
559✔
1214
        _cleanup_free_ char *cgroup = NULL;
559✔
1215
        int r;
559✔
1216

1217
        assert(ret_unit);
559✔
1218

1219
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
559✔
1220
        if (r < 0)
559✔
1221
                return r;
1222

1223
        return cg_path_get_unit(cgroup, ret_unit);
555✔
1224
}
1225

1226
int cg_pidref_get_unit(const PidRef *pidref, char **ret) {
417✔
1227
        _cleanup_free_ char *unit = NULL;
417✔
1228
        int r;
417✔
1229

1230
        assert(ret);
417✔
1231

1232
        if (!pidref_is_set(pidref))
417✔
1233
                return -ESRCH;
1234
        if (pidref_is_remote(pidref))
834✔
1235
                return -EREMOTE;
1236

1237
        r = cg_pid_get_unit(pidref->pid, &unit);
417✔
1238
        if (r < 0)
417✔
1239
                return r;
1240

1241
        r = pidref_verify(pidref);
413✔
1242
        if (r < 0)
413✔
1243
                return r;
1244

1245
        *ret = TAKE_PTR(unit);
413✔
1246
        return 0;
413✔
1247
}
1248

1249
/**
1250
 * Skip session-*.scope, but require it to be there.
1251
 */
1252
static const char *skip_session(const char *p) {
14,800✔
1253
        size_t n;
14,800✔
1254

1255
        if (isempty(p))
14,800✔
1256
                return NULL;
1257

1258
        p += strspn(p, "/");
14,796✔
1259

1260
        n = strcspn(p, "/");
14,796✔
1261
        if (n < STRLEN("session-x.scope"))
14,796✔
1262
                return NULL;
1263

1264
        if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
14,636✔
1265
                char buf[n - 8 - 6 + 1];
19✔
1266

1267
                memcpy(buf, p + 8, n - 8 - 6);
19✔
1268
                buf[n - 8 - 6] = 0;
19✔
1269

1270
                /* Note that session scopes never need unescaping,
1271
                 * since they cannot conflict with the kernel's own
1272
                 * names, hence we don't need to call cg_unescape()
1273
                 * here. */
1274

1275
                if (!session_id_valid(buf))
19✔
1276
                        return NULL;
19✔
1277

1278
                p += n;
19✔
1279
                p += strspn(p, "/");
19✔
1280
                return p;
19✔
1281
        }
1282

1283
        return NULL;
1284
}
1285

1286
/**
1287
 * Skip user@*.service, but require it to be there.
1288
 */
1289
static const char *skip_user_manager(const char *p) {
15,190✔
1290
        size_t n;
15,190✔
1291

1292
        if (isempty(p))
15,190✔
1293
                return NULL;
1294

1295
        p += strspn(p, "/");
15,186✔
1296

1297
        n = strcspn(p, "/");
15,186✔
1298
        if (n < STRLEN("user@x.service"))
15,186✔
1299
                return NULL;
1300

1301
        if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
15,026✔
1302
                char buf[n - 5 - 8 + 1];
390✔
1303

1304
                memcpy(buf, p + 5, n - 5 - 8);
390✔
1305
                buf[n - 5 - 8] = 0;
390✔
1306

1307
                /* Note that user manager services never need unescaping,
1308
                 * since they cannot conflict with the kernel's own
1309
                 * names, hence we don't need to call cg_unescape()
1310
                 * here. */
1311

1312
                if (parse_uid(buf, NULL) < 0)
390✔
1313
                        return NULL;
390✔
1314

1315
                p += n;
390✔
1316
                p += strspn(p, "/");
390✔
1317

1318
                return p;
390✔
1319
        }
1320

1321
        return NULL;
1322
}
1323

1324
static const char *skip_user_prefix(const char *path) {
15,190✔
1325
        const char *e, *t;
15,190✔
1326

1327
        assert(path);
15,190✔
1328

1329
        /* Skip slices, if there are any */
1330
        e = skip_slices(path);
15,190✔
1331

1332
        /* Skip the user manager, if it's in the path now... */
1333
        t = skip_user_manager(e);
15,190✔
1334
        if (t)
15,190✔
1335
                return t;
1336

1337
        /* Alternatively skip the user session if it is in the path... */
1338
        return skip_session(e);
14,800✔
1339
}
1340

1341
int cg_path_get_user_unit(const char *path, char **ret) {
7,655✔
1342
        const char *t;
7,655✔
1343

1344
        assert(path);
7,655✔
1345
        assert(ret);
7,655✔
1346

1347
        t = skip_user_prefix(path);
7,655✔
1348
        if (!t)
7,655✔
1349
                return -ENXIO;
1350

1351
        /* And from here on it looks pretty much the same as for a system unit, hence let's use the same
1352
         * parser. */
1353
        return cg_path_get_unit(t, ret);
211✔
1354
}
1355

1356
int cg_pid_get_user_unit(pid_t pid, char **ret_unit) {
120✔
1357
        _cleanup_free_ char *cgroup = NULL;
120✔
1358
        int r;
120✔
1359

1360
        assert(ret_unit);
120✔
1361

1362
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
120✔
1363
        if (r < 0)
120✔
1364
                return r;
1365

1366
        return cg_path_get_user_unit(cgroup, ret_unit);
120✔
1367
}
1368

1369
int cg_path_get_machine_name(const char *path, char **ret_machine) {
104✔
1370
        _cleanup_free_ char *u = NULL;
104✔
1371
        const char *sl;
104✔
1372
        int r;
104✔
1373

1374
        r = cg_path_get_unit(path, &u);
104✔
1375
        if (r < 0)
104✔
1376
                return r;
1377

1378
        sl = strjoina("/run/systemd/machines/unit:", u);
520✔
1379
        return readlink_malloc(sl, ret_machine);
104✔
1380
}
1381

1382
int cg_pid_get_machine_name(pid_t pid, char **ret_machine) {
104✔
1383
        _cleanup_free_ char *cgroup = NULL;
104✔
1384
        int r;
104✔
1385

1386
        assert(ret_machine);
104✔
1387

1388
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
104✔
1389
        if (r < 0)
104✔
1390
                return r;
1391

1392
        return cg_path_get_machine_name(cgroup, ret_machine);
104✔
1393
}
1394

1395
int cg_path_get_session(const char *path, char **ret_session) {
8,293✔
1396
        _cleanup_free_ char *unit = NULL;
8,293✔
1397
        char *start, *end;
8,293✔
1398
        int r;
8,293✔
1399

1400
        assert(path);
8,293✔
1401

1402
        r = cg_path_get_unit(path, &unit);
8,293✔
1403
        if (r < 0)
8,293✔
1404
                return r;
1405

1406
        start = startswith(unit, "session-");
8,292✔
1407
        if (!start)
8,292✔
1408
                return -ENXIO;
1409
        end = endswith(start, ".scope");
225✔
1410
        if (!end)
225✔
1411
                return -ENXIO;
1412

1413
        *end = 0;
225✔
1414
        if (!session_id_valid(start))
225✔
1415
                return -ENXIO;
1416

1417
        if (!ret_session)
224✔
1418
                return 0;
1419

1420
        return strdup_to(ret_session, start);
224✔
1421
}
1422

1423
int cg_pid_get_session(pid_t pid, char **ret_session) {
696✔
1424
        _cleanup_free_ char *cgroup = NULL;
696✔
1425
        int r;
696✔
1426

1427
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
696✔
1428
        if (r < 0)
696✔
1429
                return r;
1430

1431
        return cg_path_get_session(cgroup, ret_session);
696✔
1432
}
1433

1434
int cg_pidref_get_session(const PidRef *pidref, char **ret) {
249✔
1435
        int r;
249✔
1436

1437
        if (!pidref_is_set(pidref))
249✔
1438
                return -ESRCH;
249✔
1439
        if (pidref_is_remote(pidref))
498✔
1440
                return -EREMOTE;
1441

1442
        _cleanup_free_ char *session = NULL;
249✔
1443
        r = cg_pid_get_session(pidref->pid, &session);
249✔
1444
        if (r < 0)
249✔
1445
                return r;
1446

1447
        r = pidref_verify(pidref);
199✔
1448
        if (r < 0)
199✔
1449
                return r;
1450

1451
        if (ret)
199✔
1452
                *ret = TAKE_PTR(session);
199✔
1453
        return 0;
1454
}
1455

1456
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid) {
8,008✔
1457
        _cleanup_free_ char *slice = NULL;
8,008✔
1458
        char *start, *end;
8,008✔
1459
        int r;
8,008✔
1460

1461
        assert(path);
8,008✔
1462

1463
        r = cg_path_get_slice(path, &slice);
8,008✔
1464
        if (r < 0)
8,008✔
1465
                return r;
1466

1467
        start = startswith(slice, "user-");
8,008✔
1468
        if (!start)
8,008✔
1469
                return -ENXIO;
1470

1471
        end = endswith(start, ".slice");
418✔
1472
        if (!end)
418✔
1473
                return -ENXIO;
1474

1475
        *end = 0;
418✔
1476
        if (parse_uid(start, ret_uid) < 0)
418✔
1477
                return -ENXIO;
×
1478

1479
        return 0;
1480
}
1481

1482
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid) {
429✔
1483
        _cleanup_free_ char *cgroup = NULL;
429✔
1484
        int r;
429✔
1485

1486
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
429✔
1487
        if (r < 0)
429✔
1488
                return r;
1489

1490
        return cg_path_get_owner_uid(cgroup, ret_uid);
429✔
1491
}
1492

1493
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret) {
48✔
1494
        int r;
48✔
1495

1496
        if (!pidref_is_set(pidref))
48✔
1497
                return -ESRCH;
48✔
1498
        if (pidref_is_remote(pidref))
48✔
1499
                return -EREMOTE;
1500

1501
        uid_t uid;
48✔
1502
        r = cg_pid_get_owner_uid(pidref->pid, &uid);
48✔
1503
        if (r < 0)
48✔
1504
                return r;
1505

1506
        r = pidref_verify(pidref);
8✔
1507
        if (r < 0)
8✔
1508
                return r;
1509

1510
        if (ret)
8✔
1511
                *ret = uid;
8✔
1512

1513
        return 0;
1514
}
1515

1516
int cg_path_get_slice(const char *p, char **ret_slice) {
15,861✔
1517
        const char *e = NULL;
15,861✔
1518

1519
        assert(p);
15,861✔
1520
        assert(ret_slice);
15,861✔
1521

1522
        /* Finds the right-most slice unit from the beginning, but stops before we come to
1523
         * the first non-slice unit. */
1524

1525
        for (;;) {
48,479✔
1526
                const char *s;
32,170✔
1527
                int n;
32,170✔
1528

1529
                n = path_find_first_component(&p, /* accept_dot_dot = */ false, &s);
32,170✔
1530
                if (n < 0)
32,170✔
1531
                        return n;
×
1532
                if (!valid_slice_name(s, n))
32,170✔
1533
                        break;
1534

1535
                e = s;
16,309✔
1536
        }
1537

1538
        if (e)
15,861✔
1539
                return cg_path_decode_unit(e, ret_slice);
15,635✔
1540

1541
        return strdup_to(ret_slice, SPECIAL_ROOT_SLICE);
226✔
1542
}
1543

1544
int cg_pid_get_slice(pid_t pid, char **ret_slice) {
124✔
1545
        _cleanup_free_ char *cgroup = NULL;
124✔
1546
        int r;
124✔
1547

1548
        assert(ret_slice);
124✔
1549

1550
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
124✔
1551
        if (r < 0)
124✔
1552
                return r;
1553

1554
        return cg_path_get_slice(cgroup, ret_slice);
124✔
1555
}
1556

1557
int cg_path_get_user_slice(const char *p, char **ret_slice) {
7,535✔
1558
        const char *t;
7,535✔
1559
        assert(p);
7,535✔
1560
        assert(ret_slice);
7,535✔
1561

1562
        t = skip_user_prefix(p);
7,535✔
1563
        if (!t)
7,535✔
1564
                return -ENXIO;
1565

1566
        /* And now it looks pretty much the same as for a system slice, so let's just use the same parser
1567
         * from here on. */
1568
        return cg_path_get_slice(t, ret_slice);
198✔
1569
}
1570

1571
int cg_pid_get_user_slice(pid_t pid, char **ret_slice) {
×
1572
        _cleanup_free_ char *cgroup = NULL;
×
1573
        int r;
×
1574

1575
        assert(ret_slice);
×
1576

1577
        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
×
1578
        if (r < 0)
×
1579
                return r;
1580

1581
        return cg_path_get_user_slice(cgroup, ret_slice);
×
1582
}
1583

1584
bool cg_needs_escape(const char *p) {
17,177✔
1585

1586
        /* Checks if the specified path is a valid cgroup name by our rules, or if it must be escaped. Note
1587
         * that we consider escaped cgroup names invalid here, as they need to be escaped a second time if
1588
         * they shall be used. Also note that various names cannot be made valid by escaping even if we
1589
         * return true here (because too long, or contain the forbidden character "/"). */
1590

1591
        if (!filename_is_valid(p))
17,177✔
1592
                return true;
1593

1594
        if (IN_SET(p[0], '_', '.'))
17,173✔
1595
                return true;
1596

1597
        if (STR_IN_SET(p, "notify_on_release", "release_agent", "tasks"))
17,167✔
1598
                return true;
2✔
1599

1600
        if (startswith(p, "cgroup."))
17,165✔
1601
                return true;
1602

1603
        for (CGroupController c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
240,282✔
1604
                const char *q;
223,119✔
1605

1606
                q = startswith(p, cgroup_controller_to_string(c));
223,119✔
1607
                if (!q)
223,119✔
1608
                        continue;
223,119✔
1609

1610
                if (q[0] == '.')
×
1611
                        return true;
1612
        }
1613

1614
        return false;
1615
}
1616

1617
int cg_escape(const char *p, char **ret) {
16,898✔
1618
        _cleanup_free_ char *n = NULL;
16,898✔
1619

1620
        /* This implements very minimal escaping for names to be used as file names in the cgroup tree: any
1621
         * name which might conflict with a kernel name or is prefixed with '_' is prefixed with a '_'. That
1622
         * way, when reading cgroup names it is sufficient to remove a single prefixing underscore if there
1623
         * is one. */
1624

1625
        /* The return value of this function (unlike cg_unescape()) needs free()! */
1626

1627
        if (cg_needs_escape(p)) {
16,898✔
1628
                n = strjoin("_", p);
7✔
1629
                if (!n)
7✔
1630
                        return -ENOMEM;
1631

1632
                if (!filename_is_valid(n)) /* became invalid due to the prefixing? Or contained things like a slash that cannot be fixed by prefixing? */
7✔
1633
                        return -EINVAL;
1634
        } else {
1635
                n = strdup(p);
16,891✔
1636
                if (!n)
16,891✔
1637
                        return -ENOMEM;
1638
        }
1639

1640
        *ret = TAKE_PTR(n);
16,898✔
1641
        return 0;
16,898✔
1642
}
1643

1644
char* cg_unescape(const char *p) {
99,685✔
1645
        assert(p);
99,685✔
1646

1647
        /* The return value of this function (unlike cg_escape())
1648
         * doesn't need free()! */
1649

1650
        if (p[0] == '_')
99,685✔
1651
                return (char*) p+1;
14✔
1652

1653
        return (char*) p;
1654
}
1655

1656
#define CONTROLLER_VALID                        \
1657
        DIGITS LETTERS                          \
1658
        "_"
1659

1660
bool cg_controller_is_valid(const char *p) {
402,038✔
1661
        const char *t, *s;
402,038✔
1662

1663
        if (!p)
402,038✔
1664
                return false;
1665

1666
        if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
402,038✔
1667
                return true;
1668

1669
        s = startswith(p, "name=");
119,711✔
1670
        if (s)
119,711✔
1671
                p = s;
32✔
1672

1673
        if (IN_SET(*p, 0, '_'))
119,711✔
1674
                return false;
1675

1676
        for (t = p; *t; t++)
768,040✔
1677
                if (!strchr(CONTROLLER_VALID, *t))
648,340✔
1678
                        return false;
1679

1680
        if (t - p > NAME_MAX)
119,700✔
1681
                return false;
×
1682

1683
        return true;
1684
}
1685

1686
int cg_slice_to_path(const char *unit, char **ret) {
7,428✔
1687
        _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
7,428✔
1688
        const char *dash;
7,428✔
1689
        int r;
7,428✔
1690

1691
        assert(unit);
7,428✔
1692
        assert(ret);
7,428✔
1693

1694
        if (streq(unit, SPECIAL_ROOT_SLICE))
7,428✔
1695
                return strdup_to(ret, "");
7✔
1696

1697
        if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
7,421✔
1698
                return -EINVAL;
1699

1700
        if (!endswith(unit, ".slice"))
7,410✔
1701
                return -EINVAL;
1702

1703
        r = unit_name_to_prefix(unit, &p);
7,409✔
1704
        if (r < 0)
7,409✔
1705
                return r;
1706

1707
        dash = strchr(p, '-');
7,409✔
1708

1709
        /* Don't allow initial dashes */
1710
        if (dash == p)
7,409✔
1711
                return -EINVAL;
1712

1713
        while (dash) {
7,642✔
1714
                _cleanup_free_ char *escaped = NULL;
238✔
1715
                char n[dash - p + sizeof(".slice")];
238✔
1716

1717
#if HAS_FEATURE_MEMORY_SANITIZER
1718
                /* msan doesn't instrument stpncpy, so it thinks
1719
                 * n is later used uninitialized:
1720
                 * https://github.com/google/sanitizers/issues/926
1721
                 */
1722
                zero(n);
1723
#endif
1724

1725
                /* Don't allow trailing or double dashes */
1726
                if (IN_SET(dash[1], 0, '-'))
238✔
1727
                        return -EINVAL;
1728

1729
                strcpy(stpncpy(n, p, dash - p), ".slice");
236✔
1730
                if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
236✔
1731
                        return -EINVAL;
1732

1733
                r = cg_escape(n, &escaped);
236✔
1734
                if (r < 0)
236✔
1735
                        return r;
1736

1737
                if (!strextend(&s, escaped, "/"))
236✔
1738
                        return -ENOMEM;
1739

1740
                dash = strchr(dash+1, '-');
236✔
1741
        }
1742

1743
        r = cg_escape(unit, &e);
7,404✔
1744
        if (r < 0)
7,404✔
1745
                return r;
1746

1747
        if (!strextend(&s, e))
7,404✔
1748
                return -ENOMEM;
1749

1750
        *ret = TAKE_PTR(s);
7,404✔
1751
        return 0;
7,404✔
1752
}
1753

1754
int cg_is_threaded(const char *path) {
×
1755
        _cleanup_free_ char *fs = NULL, *contents = NULL;
×
1756
        _cleanup_strv_free_ char **v = NULL;
×
1757
        int r;
×
1758

1759
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, "cgroup.type", &fs);
×
1760
        if (r < 0)
×
1761
                return r;
1762

1763
        r = read_full_virtual_file(fs, &contents, NULL);
×
1764
        if (r == -ENOENT)
×
1765
                return false; /* Assume no. */
1766
        if (r < 0)
×
1767
                return r;
1768

1769
        v = strv_split(contents, NULL);
×
1770
        if (!v)
×
1771
                return -ENOMEM;
1772

1773
        /* If the cgroup is in the threaded mode, it contains "threaded".
1774
         * If one of the parents or siblings is in the threaded mode, it may contain "invalid". */
1775
        return strv_contains(v, "threaded") || strv_contains(v, "invalid");
×
1776
}
1777

1778
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
48,960✔
1779
        _cleanup_free_ char *p = NULL;
48,960✔
1780
        int r;
48,960✔
1781

1782
        r = cg_get_path(controller, path, attribute, &p);
48,960✔
1783
        if (r < 0)
48,960✔
1784
                return r;
1785

1786
        return write_string_file(p, value, WRITE_STRING_FILE_DISABLE_BUFFER);
48,960✔
1787
}
1788

1789
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
29,647✔
1790
        _cleanup_free_ char *p = NULL;
29,647✔
1791
        int r;
29,647✔
1792

1793
        r = cg_get_path(controller, path, attribute, &p);
29,647✔
1794
        if (r < 0)
29,647✔
1795
                return r;
1796

1797
        return read_one_line_file(p, ret);
29,647✔
1798
}
1799

1800
int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret) {
25,644✔
1801
        _cleanup_free_ char *value = NULL;
25,644✔
1802
        uint64_t v;
25,644✔
1803
        int r;
25,644✔
1804

1805
        assert(ret);
25,644✔
1806

1807
        r = cg_get_attribute(controller, path, attribute, &value);
25,644✔
1808
        if (r == -ENOENT)
25,644✔
1809
                return -ENODATA;
1810
        if (r < 0)
22,361✔
1811
                return r;
1812

1813
        if (streq(value, "max")) {
22,361✔
1814
                *ret = CGROUP_LIMIT_MAX;
4,667✔
1815
                return 0;
4,667✔
1816
        }
1817

1818
        r = safe_atou64(value, &v);
17,694✔
1819
        if (r < 0)
17,694✔
1820
                return r;
1821

1822
        *ret = v;
17,694✔
1823
        return 0;
17,694✔
1824
}
1825

1826
int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret) {
59✔
1827
        _cleanup_free_ char *value = NULL;
59✔
1828
        int r;
59✔
1829

1830
        assert(ret);
59✔
1831

1832
        r = cg_get_attribute(controller, path, attribute, &value);
59✔
1833
        if (r == -ENOENT)
59✔
1834
                return -ENODATA;
1835
        if (r < 0)
59✔
1836
                return r;
1837

1838
        r = parse_boolean(value);
59✔
1839
        if (r < 0)
59✔
1840
                return r;
1841

1842
        *ret = r;
59✔
1843
        return 0;
59✔
1844
}
1845

1846
int cg_get_owner(const char *path, uid_t *ret_uid) {
35✔
1847
        _cleanup_free_ char *f = NULL;
35✔
1848
        struct stat stats;
35✔
1849
        int r;
35✔
1850

1851
        assert(ret_uid);
35✔
1852

1853
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &f);
35✔
1854
        if (r < 0)
35✔
1855
                return r;
1856

1857
        if (stat(f, &stats) < 0)
35✔
1858
                return -errno;
16✔
1859

1860
        r = stat_verify_directory(&stats);
19✔
1861
        if (r < 0)
19✔
1862
                return r;
1863

1864
        *ret_uid = stats.st_uid;
19✔
1865
        return 0;
19✔
1866
}
1867

1868
int cg_get_keyed_attribute_full(
34,251✔
1869
                const char *controller,
1870
                const char *path,
1871
                const char *attribute,
1872
                char **keys,
1873
                char **ret_values,
1874
                CGroupKeyMode mode) {
1875

1876
        _cleanup_free_ char *filename = NULL, *contents = NULL;
34,251✔
1877
        const char *p;
34,251✔
1878
        size_t n, i, n_done = 0;
34,251✔
1879
        char **v;
34,251✔
1880
        int r;
34,251✔
1881

1882
        /* Reads one or more fields of a cgroup v2 keyed attribute file. The 'keys' parameter should be an strv with
1883
         * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
1884
         * entries as 'keys'. On success each entry will be set to the value of the matching key.
1885
         *
1886
         * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. If mode
1887
         * is set to GG_KEY_MODE_GRACEFUL we ignore missing keys and return those that were parsed successfully. */
1888

1889
        r = cg_get_path(controller, path, attribute, &filename);
34,251✔
1890
        if (r < 0)
34,251✔
1891
                return r;
1892

1893
        r = read_full_file(filename, &contents, NULL);
34,251✔
1894
        if (r < 0)
34,251✔
1895
                return r;
1896

1897
        n = strv_length(keys);
27,934✔
1898
        if (n == 0) /* No keys to retrieve? That's easy, we are done then */
27,934✔
1899
                return 0;
1900

1901
        /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
1902
        v = newa0(char*, n);
27,934✔
1903

1904
        for (p = contents; *p;) {
94,985✔
1905
                const char *w = NULL;
1906

1907
                for (i = 0; i < n; i++)
162,036✔
1908
                        if (!v[i]) {
104,840✔
1909
                                w = first_word(p, keys[i]);
94,985✔
1910
                                if (w)
94,985✔
1911
                                        break;
1912
                        }
1913

1914
                if (w) {
94,985✔
1915
                        size_t l;
37,789✔
1916

1917
                        l = strcspn(w, NEWLINE);
37,789✔
1918
                        v[i] = strndup(w, l);
37,789✔
1919
                        if (!v[i]) {
37,789✔
1920
                                r = -ENOMEM;
×
1921
                                goto fail;
×
1922
                        }
1923

1924
                        n_done++;
37,789✔
1925
                        if (n_done >= n)
37,789✔
1926
                                goto done;
27,934✔
1927

1928
                        p = w + l;
9,855✔
1929
                } else
1930
                        p += strcspn(p, NEWLINE);
57,196✔
1931

1932
                p += strspn(p, NEWLINE);
67,051✔
1933
        }
1934

1935
        if (mode & CG_KEY_MODE_GRACEFUL)
×
1936
                goto done;
×
1937

1938
        r = -ENXIO;
1939

1940
fail:
×
1941
        free_many_charp(v, n);
34,251✔
1942
        return r;
1943

1944
done:
27,934✔
1945
        memcpy(ret_values, v, sizeof(char*) * n);
27,934✔
1946
        if (mode & CG_KEY_MODE_GRACEFUL)
27,934✔
1947
                return n_done;
9,855✔
1948

1949
        return 0;
1950
}
1951

1952
int cg_mask_to_string(CGroupMask mask, char **ret) {
16,713✔
1953
        _cleanup_free_ char *s = NULL;
16,713✔
1954
        bool space = false;
16,713✔
1955
        CGroupController c;
16,713✔
1956
        size_t n = 0;
16,713✔
1957

1958
        assert(ret);
16,713✔
1959

1960
        if (mask == 0) {
16,713✔
1961
                *ret = NULL;
8,073✔
1962
                return 0;
8,073✔
1963
        }
1964

1965
        for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
120,960✔
1966
                const char *k;
112,320✔
1967
                size_t l;
112,320✔
1968

1969
                if (!FLAGS_SET(mask, CGROUP_CONTROLLER_TO_MASK(c)))
112,320✔
1970
                        continue;
35,553✔
1971

1972
                k = cgroup_controller_to_string(c);
76,767✔
1973
                l = strlen(k);
76,767✔
1974

1975
                if (!GREEDY_REALLOC(s, n + space + l + 1))
76,767✔
1976
                        return -ENOMEM;
1977

1978
                if (space)
76,767✔
1979
                        s[n] = ' ';
68,127✔
1980
                memcpy(s + n + space, k, l);
76,767✔
1981
                n += space + l;
76,767✔
1982

1983
                space = true;
76,767✔
1984
        }
1985

1986
        assert(s);
8,640✔
1987

1988
        s[n] = 0;
8,640✔
1989
        *ret = TAKE_PTR(s);
8,640✔
1990

1991
        return 0;
8,640✔
1992
}
1993

1994
int cg_mask_from_string(const char *value, CGroupMask *ret) {
6,354✔
1995
        CGroupMask m = 0;
6,354✔
1996

1997
        assert(ret);
6,354✔
1998
        assert(value);
6,354✔
1999

2000
        for (;;) {
56,792✔
2001
                _cleanup_free_ char *n = NULL;
50,438✔
2002
                CGroupController v;
56,792✔
2003
                int r;
56,792✔
2004

2005
                r = extract_first_word(&value, &n, NULL, 0);
56,792✔
2006
                if (r < 0)
56,792✔
2007
                        return r;
×
2008
                if (r == 0)
56,792✔
2009
                        break;
2010

2011
                v = cgroup_controller_from_string(n);
50,438✔
2012
                if (v < 0)
50,438✔
2013
                        continue;
722✔
2014

2015
                m |= CGROUP_CONTROLLER_TO_MASK(v);
49,716✔
2016
        }
2017

2018
        *ret = m;
6,354✔
2019
        return 0;
6,354✔
2020
}
2021

2022
int cg_mask_supported_subtree(const char *root, CGroupMask *ret) {
508✔
2023
        CGroupMask mask;
508✔
2024
        int r;
508✔
2025

2026
        /* Determines the mask of supported cgroup controllers. Only includes controllers we can make sense of and that
2027
         * are actually accessible. Only covers real controllers, i.e. not the CGROUP_CONTROLLER_BPF_xyz
2028
         * pseudo-controllers. */
2029

2030
        r = cg_all_unified();
508✔
2031
        if (r < 0)
508✔
2032
                return r;
508✔
2033
        if (r > 0) {
508✔
2034
                _cleanup_free_ char *controllers = NULL, *path = NULL;
508✔
2035

2036
                /* In the unified hierarchy we can read the supported and accessible controllers from
2037
                 * the top-level cgroup attribute */
2038

2039
                r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
508✔
2040
                if (r < 0)
508✔
2041
                        return r;
2042

2043
                r = read_one_line_file(path, &controllers);
508✔
2044
                if (r < 0)
508✔
2045
                        return r;
2046

2047
                r = cg_mask_from_string(controllers, &mask);
508✔
2048
                if (r < 0)
508✔
2049
                        return r;
2050

2051
                /* Mask controllers that are not supported in unified hierarchy. */
2052
                mask &= CGROUP_MASK_V2;
508✔
2053

2054
        } else {
2055
                CGroupController c;
×
2056

2057
                /* In the legacy hierarchy, we check which hierarchies are accessible. */
2058

2059
                mask = 0;
×
2060
                for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
×
2061
                        CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
×
2062
                        const char *n;
×
2063

2064
                        if (!FLAGS_SET(CGROUP_MASK_V1, bit))
×
2065
                                continue;
×
2066

2067
                        n = cgroup_controller_to_string(c);
×
2068
                        if (controller_is_v1_accessible(root, n) >= 0)
×
2069
                                mask |= bit;
×
2070
                }
2071
        }
2072

2073
        *ret = mask;
508✔
2074
        return 0;
508✔
2075
}
2076

2077
int cg_mask_supported(CGroupMask *ret) {
246✔
2078
        _cleanup_free_ char *root = NULL;
246✔
2079
        int r;
246✔
2080

2081
        r = cg_get_root_path(&root);
246✔
2082
        if (r < 0)
246✔
2083
                return r;
2084

2085
        return cg_mask_supported_subtree(root, ret);
246✔
2086
}
2087

2088
int cg_kernel_controllers(Set **ret) {
×
2089
        _cleanup_set_free_ Set *controllers = NULL;
×
2090
        _cleanup_fclose_ FILE *f = NULL;
×
2091
        int r;
×
2092

2093
        assert(ret);
×
2094

2095
        /* Determines the full list of kernel-known controllers. Might include controllers we don't actually support
2096
         * and controllers that aren't currently accessible (because not mounted). This does not include "name="
2097
         * pseudo-controllers. */
2098

2099
        r = fopen_unlocked("/proc/cgroups", "re", &f);
×
2100
        if (r == -ENOENT) {
×
2101
                *ret = NULL;
×
2102
                return 0;
×
2103
        }
2104
        if (r < 0)
×
2105
                return r;
2106

2107
        /* Ignore the header line */
2108
        (void) read_line(f, SIZE_MAX, NULL);
×
2109

2110
        for (;;) {
×
2111
                _cleanup_free_ char *controller = NULL;
×
2112
                int enabled = 0;
×
2113

2114
                if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
×
2115

2116
                        if (ferror(f))
×
2117
                                return -errno;
×
2118

2119
                        if (feof(f))
×
2120
                                break;
2121

2122
                        return -EBADMSG;
2123
                }
2124

2125
                if (!enabled)
×
2126
                        continue;
×
2127

2128
                if (!cg_controller_is_valid(controller))
×
2129
                        return -EBADMSG;
2130

2131
                r = set_ensure_consume(&controllers, &string_hash_ops_free, TAKE_PTR(controller));
×
2132
                if (r < 0)
×
2133
                        return r;
2134
        }
2135

2136
        *ret = TAKE_PTR(controllers);
×
2137

2138
        return 0;
×
2139
}
2140

2141
/* The hybrid mode was initially implemented in v232 and simply mounted cgroup2 on
2142
 * /sys/fs/cgroup/systemd. This unfortunately broke other tools (such as docker) which expected the v1
2143
 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mounts v2 on
2144
 * /sys/fs/cgroup/unified and maintains "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility
2145
 * with other tools.
2146
 *
2147
 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep
2148
 * cgroup v2 process management but disable the compat dual layout, we return true on
2149
 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and false on cg_hybrid_unified().
2150
 */
2151
static thread_local bool unified_systemd_v232;
2152

2153
int cg_unified_cached(bool flush) {
661,907✔
2154
        static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
661,907✔
2155

2156
        struct statfs fs;
661,907✔
2157

2158
        /* Checks if we support the unified hierarchy. Returns an
2159
         * error when the cgroup hierarchies aren't mounted yet or we
2160
         * have any other trouble determining if the unified hierarchy
2161
         * is supported. */
2162

2163
        if (flush)
661,907✔
2164
                unified_cache = CGROUP_UNIFIED_UNKNOWN;
18,311✔
2165
        else if (unified_cache >= CGROUP_UNIFIED_NONE)
643,596✔
2166
                return unified_cache;
661,907✔
2167

2168
        if (statfs("/sys/fs/cgroup/", &fs) < 0)
33,213✔
2169
                return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\") failed: %m");
×
2170

2171
        if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
33,213✔
2172
                log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
33,152✔
2173
                unified_cache = CGROUP_UNIFIED_ALL;
33,152✔
2174
        } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
61✔
2175
                if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
×
2176
                    F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
×
2177
                        log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
×
2178
                        unified_cache = CGROUP_UNIFIED_SYSTEMD;
×
2179
                        unified_systemd_v232 = false;
×
2180
                } else {
2181
                        if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0) {
×
2182
                                if (errno == ENOENT) {
×
2183
                                        /* Some other software may have set up /sys/fs/cgroup in a configuration we do not recognize. */
2184
                                        log_debug_errno(errno, "Unsupported cgroupsv1 setup detected: name=systemd hierarchy not found.");
×
2185
                                        return -ENOMEDIUM;
×
2186
                                }
2187
                                return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
×
2188
                        }
2189

2190
                        if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
×
2191
                                log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
×
2192
                                unified_cache = CGROUP_UNIFIED_SYSTEMD;
×
2193
                                unified_systemd_v232 = true;
×
2194
                        } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
×
2195
                                log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
×
2196
                                unified_cache = CGROUP_UNIFIED_NONE;
×
2197
                        } else {
2198
                                log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
×
2199
                                          (unsigned long long) fs.f_type);
2200
                                unified_cache = CGROUP_UNIFIED_NONE;
×
2201
                        }
2202
                }
2203
        } else if (F_TYPE_EQUAL(fs.f_type, SYSFS_MAGIC)) {
61✔
2204
                return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
61✔
2205
                                       "No filesystem is currently mounted on /sys/fs/cgroup.");
2206
        } else
2207
                return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
×
2208
                                       "Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2209
                                       (unsigned long long)fs.f_type);
2210

2211
        return unified_cache;
33,152✔
2212
}
2213

2214
int cg_unified_controller(const char *controller) {
103,134✔
2215
        int r;
103,134✔
2216

2217
        r = cg_unified_cached(false);
103,134✔
2218
        if (r < 0)
103,134✔
2219
                return r;
2220

2221
        if (r == CGROUP_UNIFIED_NONE)
103,134✔
2222
                return false;
2223

2224
        if (r >= CGROUP_UNIFIED_ALL)
103,134✔
2225
                return true;
2226

2227
        return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
×
2228
}
2229

2230
int cg_all_unified(void) {
513,974✔
2231
        int r;
513,974✔
2232

2233
        r = cg_unified_cached(false);
513,974✔
2234
        if (r < 0)
513,974✔
2235
                return r;
2236

2237
        return r >= CGROUP_UNIFIED_ALL;
513,974✔
2238
}
2239

2240
int cg_hybrid_unified(void) {
26,487✔
2241
        int r;
26,487✔
2242

2243
        r = cg_unified_cached(false);
26,487✔
2244
        if (r < 0)
26,487✔
2245
                return r;
2246

2247
        return r == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
26,487✔
2248
}
2249

2250
int cg_is_delegated(const char *path) {
19✔
2251
        int r;
19✔
2252

2253
        assert(path);
19✔
2254

2255
        r = cg_get_xattr_bool(path, "trusted.delegate");
19✔
2256
        if (!ERRNO_IS_NEG_XATTR_ABSENT(r))
19✔
2257
                return r;
2258

2259
        /* If the trusted xattr isn't set (preferred), then check the untrusted one. Under the assumption
2260
         * that whoever is trusted enough to own the cgroup, is also trusted enough to decide if it is
2261
         * delegated or not this should be safe. */
2262
        r = cg_get_xattr_bool(path, "user.delegate");
6✔
2263
        return ERRNO_IS_NEG_XATTR_ABSENT(r) ? false : r;
6✔
2264
}
2265

2266
int cg_is_delegated_fd(int fd) {
214✔
2267
        int r;
214✔
2268

2269
        assert(fd >= 0);
214✔
2270

2271
        r = getxattr_at_bool(fd, /* path= */ NULL, "trusted.delegate", /* flags= */ 0);
214✔
2272
        if (!ERRNO_IS_NEG_XATTR_ABSENT(r))
214✔
2273
                return r;
2274

2275
        r = getxattr_at_bool(fd, /* path= */ NULL, "user.delegate", /* flags= */ 0);
200✔
2276
        return ERRNO_IS_NEG_XATTR_ABSENT(r) ? false : r;
200✔
2277
}
2278

2279
int cg_has_coredump_receive(const char *path) {
2✔
2280
        int r;
2✔
2281

2282
        assert(path);
2✔
2283

2284
        r = cg_get_xattr_bool(path, "user.coredump_receive");
2✔
2285
        if (ERRNO_IS_NEG_XATTR_ABSENT(r))
2✔
2286
                return false;
×
2287

2288
        return r;
2289
}
2290

2291
const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2292
        [CGROUP_IO_RBPS_MAX]    = CGROUP_LIMIT_MAX,
2293
        [CGROUP_IO_WBPS_MAX]    = CGROUP_LIMIT_MAX,
2294
        [CGROUP_IO_RIOPS_MAX]   = CGROUP_LIMIT_MAX,
2295
        [CGROUP_IO_WIOPS_MAX]   = CGROUP_LIMIT_MAX,
2296
};
2297

2298
static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2299
        [CGROUP_IO_RBPS_MAX]    = "IOReadBandwidthMax",
2300
        [CGROUP_IO_WBPS_MAX]    = "IOWriteBandwidthMax",
2301
        [CGROUP_IO_RIOPS_MAX]   = "IOReadIOPSMax",
2302
        [CGROUP_IO_WIOPS_MAX]   = "IOWriteIOPSMax",
2303
};
2304

2305
DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
4,264✔
2306

2307
bool is_cgroup_fs(const struct statfs *s) {
26✔
2308
        return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
28✔
2309
               is_fs_type(s, CGROUP2_SUPER_MAGIC);
2✔
2310
}
2311

2312
bool fd_is_cgroup_fs(int fd) {
1✔
2313
        struct statfs s;
1✔
2314

2315
        if (fstatfs(fd, &s) < 0)
1✔
2316
                return -errno;
×
2317

2318
        return is_cgroup_fs(&s);
1✔
2319
}
2320

2321
static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2322
        [CGROUP_CONTROLLER_CPU] = "cpu",
2323
        [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
2324
        [CGROUP_CONTROLLER_CPUSET] = "cpuset",
2325
        [CGROUP_CONTROLLER_IO] = "io",
2326
        [CGROUP_CONTROLLER_BLKIO] = "blkio",
2327
        [CGROUP_CONTROLLER_MEMORY] = "memory",
2328
        [CGROUP_CONTROLLER_DEVICES] = "devices",
2329
        [CGROUP_CONTROLLER_PIDS] = "pids",
2330
        [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
2331
        [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
2332
        [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
2333
        [CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind",
2334
        [CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES] = "bpf-restrict-network-interfaces",
2335
};
2336

2337
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
381,929✔
2338

2339
CGroupMask get_cpu_accounting_mask(void) {
1,719,893✔
2340
        static CGroupMask needed_mask = (CGroupMask) -1;
1,719,893✔
2341

2342
        /* On kernel ≥4.15 with unified hierarchy, cpu.stat's usage_usec is
2343
         * provided externally from the CPU controller, which means we don't
2344
         * need to enable the CPU controller just to get metrics. This is good,
2345
         * because enabling the CPU controller comes at a minor performance
2346
         * hit, especially when it's propagated deep into large hierarchies.
2347
         * There's also no separate CPU accounting controller available within
2348
         * a unified hierarchy.
2349
         *
2350
         * This combination of factors results in the desired cgroup mask to
2351
         * enable for CPU accounting varying as follows:
2352
         *
2353
         *                   ╔═════════════════════╤═════════════════════╗
2354
         *                   ║     Linux ≥4.15     │     Linux <4.15     ║
2355
         *   ╔═══════════════╬═════════════════════╪═════════════════════╣
2356
         *   ║ Unified       ║ nothing             │ CGROUP_MASK_CPU     ║
2357
         *   ╟───────────────╫─────────────────────┼─────────────────────╢
2358
         *   ║ Hybrid/Legacy ║ CGROUP_MASK_CPUACCT │ CGROUP_MASK_CPUACCT ║
2359
         *   ╚═══════════════╩═════════════════════╧═════════════════════╝
2360
         *
2361
         * We check kernel version here instead of manually checking whether
2362
         * cpu.stat is present for every cgroup, as that check in itself would
2363
         * already be fairly expensive.
2364
         *
2365
         * Kernels where this patch has been backported will therefore have the
2366
         * CPU controller enabled unnecessarily. This is more expensive than
2367
         * necessary, but harmless. ☺️
2368
         */
2369

2370
        if (needed_mask == (CGroupMask) -1) {
1,719,893✔
2371
                if (cg_all_unified()) {
622✔
2372
                        struct utsname u;
622✔
2373
                        assert_se(uname(&u) >= 0);
622✔
2374

2375
                        if (strverscmp_improved(u.release, "4.15") < 0)
622✔
2376
                                needed_mask = CGROUP_MASK_CPU;
×
2377
                        else
2378
                                needed_mask = 0;
622✔
2379
                } else
2380
                        needed_mask = CGROUP_MASK_CPUACCT;
×
2381
        }
2382

2383
        return needed_mask;
1,719,893✔
2384
}
2385

2386
bool cpu_accounting_is_cheap(void) {
2,227✔
2387
        return get_cpu_accounting_mask() == 0;
2,227✔
2388
}
2389

2390
static const char* const managed_oom_mode_table[_MANAGED_OOM_MODE_MAX] = {
2391
        [MANAGED_OOM_AUTO] = "auto",
2392
        [MANAGED_OOM_KILL] = "kill",
2393
};
2394

2395
DEFINE_STRING_TABLE_LOOKUP(managed_oom_mode, ManagedOOMMode);
37,941✔
2396

2397
static const char* const managed_oom_preference_table[_MANAGED_OOM_PREFERENCE_MAX] = {
2398
        [MANAGED_OOM_PREFERENCE_NONE] = "none",
2399
        [MANAGED_OOM_PREFERENCE_AVOID] = "avoid",
2400
        [MANAGED_OOM_PREFERENCE_OMIT] = "omit",
2401
};
2402

2403
DEFINE_STRING_TABLE_LOOKUP(managed_oom_preference, ManagedOOMPreference);
18,747✔
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