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

systemd / systemd / 15479777183

05 Jun 2025 09:07PM UTC coverage: 72.092% (+0.02%) from 72.075%
15479777183

push

github

web-flow
core: adding CGroup for io.systemd.Unit.List (second PR) (#37646)

This PR adds CGroup context/runtime for io.systemd.Unit.List method.

This is follow up for https://github.com/systemd/systemd/pull/37432.

235 of 295 new or added lines in 2 files covered. (79.66%)

3638 existing lines in 66 files now uncovered.

300163 of 416359 relevant lines covered (72.09%)

704675.28 hits per line

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

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

3
#include <sys/ioctl.h>
4
#include <threads.h>
5
#include <unistd.h>
6

7
#include "errno-util.h"
8
#include "fd-util.h"
9
#include "fileio.h"
10
#include "missing_fs.h"
11
#include "missing_magic.h"
12
#include "mountpoint-util.h"
13
#include "parse-util.h"
14
#include "pidfd-util.h"
15
#include "process-util.h"
16
#include "stat-util.h"
17
#include "stdio-util.h"
18
#include "string-util.h"
19

20
static thread_local int have_pidfs = -1;
21

22
int pidfd_check_pidfs(int pid_fd) {
40,031✔
23

24
        /* NB: the passed fd *must* be acquired via pidfd_open(), i.e. must be a true pidfd! */
25

26
        if (have_pidfs >= 0)
40,031✔
27
                return have_pidfs;
40,031✔
28

29
        _cleanup_close_ int our_fd = -EBADF;
40,031✔
30
        if (pid_fd < 0) {
18,965✔
31
                our_fd = pidfd_open(getpid_cached(), /* flags = */ 0);
1✔
32
                if (our_fd < 0)
1✔
UNCOV
33
                        return -errno;
×
34

35
                pid_fd = our_fd;
36
        }
37

38
        return (have_pidfs = fd_is_fs_type(pid_fd, PID_FS_MAGIC));
18,965✔
39
}
40

41
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
8,267✔
42
        static bool cached_supported = true;
8,267✔
43

44
        /* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
45
         *
46
         * Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
47
         * is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
48
         * see below) */
49

50
        assert(fd >= 0);
8,267✔
51

52
        /* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
53
         * call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
54
         * anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
55
        if (have_pidfs == 0 || !cached_supported)
8,267✔
56
                return -EOPNOTSUPP;
57

58
        int nsfd = ioctl(fd, ns_type_cmd);
3,598✔
59
        if (nsfd < 0) {
3,598✔
60
                /* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
61
                 * at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
62
                if (IN_SET(errno, ENOTTY, EINVAL)) {
3,598✔
63
                        cached_supported = false;
3,598✔
64
                        return -EOPNOTSUPP;
3,598✔
65
                }
UNCOV
66
                if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
×
67
                        return -ENOPKG;
68

UNCOV
69
                return -errno;
×
70
        }
71

72
        return nsfd;
73
}
74

75
static int pidfd_get_info(int fd, struct pidfd_info *info) {
60,761✔
76
        static bool cached_supported = true;
60,761✔
77

78
        assert(fd >= 0);
60,761✔
79
        assert(info);
60,761✔
80

81
        if (have_pidfs == 0 || !cached_supported)
60,761✔
82
                return -EOPNOTSUPP;
83

84
        if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
45,307✔
85
                if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
4,344✔
86
                        cached_supported = false;
3,512✔
87
                        return -EOPNOTSUPP;
3,512✔
88
                }
89

90
                return -errno;
832✔
91
        }
92

93
        return 0;
94
}
95

96
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
17,562✔
97
        char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
17,562✔
98
        _cleanup_free_ char *p = NULL;
17,562✔
99
        int r;
17,562✔
100

101
        assert(fd >= 0);
17,562✔
102

103
        xsprintf(path, "/proc/self/fdinfo/%i", fd);
17,562✔
104

105
        r = get_proc_field(path, "Pid", &p);
17,562✔
106
        if (r == -ENOENT)
17,562✔
107
                return -EBADF;
108
        if (r == -ENODATA) /* not a pidfd? */
17,562✔
109
                return -ENOTTY;
110
        if (r < 0)
17,562✔
111
                return r;
112

113
        if (streq(p, "0"))
17,562✔
114
                return -EREMOTE; /* PID is in foreign PID namespace? */
115
        if (streq(p, "-1"))
17,562✔
116
                return -ESRCH;   /* refers to reaped process? */
117

118
        return parse_pid(p, ret);
15,986✔
119
}
120

121
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
58,291✔
122
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
58,291✔
123
        int r;
58,291✔
124

125
        assert(fd >= 0);
58,291✔
126

127
        r = pidfd_get_info(fd, &info);
58,291✔
128
        if (r < 0)
58,291✔
129
                return r;
58,291✔
130

131
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
39,897✔
132

133
        if (ret)
39,897✔
134
                *ret = info.pid;
39,897✔
135
        return 0;
136
}
137

138
int pidfd_get_pid(int fd, pid_t *ret) {
58,291✔
139
        int r;
58,291✔
140

141
        /* Converts a pidfd into a pid. We try ioctl(PIDFD_GET_INFO) (kernel 6.13+) first,
142
         * /proc/self/fdinfo/ as fallback. Well known errors:
143
         *
144
         *    -EBADF   → fd invalid
145
         *    -ESRCH   → fd valid, but process is already reaped
146
         *
147
         * pidfd_get_pid_fdinfo() might additionally fail for other reasons:
148
         *
149
         *    -ENOSYS  → /proc/ not mounted
150
         *    -ENOTTY  → fd valid, but not a pidfd
151
         *    -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
152
         *               (when using PIDFD_GET_INFO this is indistinguishable from -ESRCH)
153
         */
154

155
        assert(fd >= 0);
58,291✔
156

157
        r = pidfd_get_pid_ioctl(fd, ret);
58,291✔
158
        if (r != -EOPNOTSUPP)
58,291✔
159
                return r;
160

161
        return pidfd_get_pid_fdinfo(fd, ret);
17,562✔
162
}
163

164
int pidfd_verify_pid(int pidfd, pid_t pid) {
41,554✔
165
        pid_t current_pid;
41,554✔
166
        int r;
41,554✔
167

168
        assert(pidfd >= 0);
41,554✔
169
        assert(pid > 0);
41,554✔
170

171
        r = pidfd_get_pid(pidfd, &current_pid);
41,554✔
172
        if (r < 0)
41,554✔
173
                return r;
41,554✔
174

175
        return current_pid != pid ? -ESRCH : 0;
39,146✔
176
}
177

178
int pidfd_get_ppid(int fd, pid_t *ret) {
2,416✔
179
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
2,416✔
180
        int r;
2,416✔
181

182
        assert(fd >= 0);
2,416✔
183

184
        r = pidfd_get_info(fd, &info);
2,416✔
185
        if (r < 0)
2,416✔
186
                return r;
2,416✔
187

188
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
1,021✔
189

190
        if (info.ppid == 0) /* See comments in pid_get_ppid() */
1,021✔
191
                return -EADDRNOTAVAIL;
192

193
        if (ret)
1,021✔
194
                *ret = info.ppid;
1,021✔
195
        return 0;
196
}
197

198
int pidfd_get_uid(int fd, uid_t *ret) {
54✔
199
        struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
54✔
200
        int r;
54✔
201

202
        assert(fd >= 0);
54✔
203

204
        r = pidfd_get_info(fd, &info);
54✔
205
        if (r < 0)
54✔
206
                return r;
54✔
207

208
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
45✔
209

210
        if (ret)
45✔
211
                *ret = info.ruid;
45✔
212
        return 0;
213
}
214

UNCOV
215
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
×
216
        struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
×
UNCOV
217
        int r;
×
218

219
        assert(fd >= 0);
×
220

UNCOV
221
        r = pidfd_get_info(fd, &info);
×
UNCOV
222
        if (r < 0)
×
UNCOV
223
                return r;
×
224

UNCOV
225
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
×
226

UNCOV
227
        if (ret)
×
UNCOV
228
                *ret = info.cgroupid;
×
229
        return 0;
230
}
231

232
int pidfd_get_inode_id_impl(int fd, uint64_t *ret) {
40,030✔
233
        static thread_local bool file_handle_supported = true;
40,030✔
234
        int r;
40,030✔
235

236
        assert(fd >= 0);
40,030✔
237

238
        if (file_handle_supported) {
40,030✔
239
                union {
37,915✔
240
                        struct file_handle file_handle;
241
                        uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
242
                } fh = {
37,915✔
243
                        .file_handle.handle_bytes = sizeof(uint64_t),
244
                        .file_handle.handle_type = FILEID_KERNFS,
245
                };
246
                int mnt_id;
37,915✔
247

248
                r = RET_NERRNO(name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH));
37,915✔
249
                if (r >= 0) {
3,691✔
250
                        if (ret)
34,224✔
251
                                *ret = *CAST_ALIGN_PTR(uint64_t, fh.file_handle.f_handle);
34,224✔
252
                        return 0;
34,224✔
253
                }
254
                assert(r != -EOVERFLOW);
3,691✔
255
                if (is_name_to_handle_at_fatal_error(r))
3,691✔
256
                        return r;
257

258
                file_handle_supported = false;
3,691✔
259
        }
260

261
#if SIZEOF_INO_T == 8
262
        struct stat st;
5,806✔
263
        if (fstat(fd, &st) < 0)
5,806✔
UNCOV
264
                return -errno;
×
265

266
        if (ret)
5,806✔
267
                *ret = (uint64_t) st.st_ino;
5,805✔
268
        return 0;
269

270
#elif SIZEOF_INO_T == 4
271
        /* On 32-bit systems (where sizeof(ino_t) == 4), the inode id returned by fstat() cannot be used to
272
         * reliably identify the process, nor can we communicate the origin of the id with the clients.
273
         * Hence let's just refuse to acquire pidfdid through fstat() here. All clients shall also insist on
274
         * the 64-bit id from name_to_handle_at(). */
275
        return -EOPNOTSUPP;
276
#else
277
#  error Unsupported ino_t size
278
#endif
279
}
280

281
int pidfd_get_inode_id(int fd, uint64_t *ret) {
40,029✔
282
        int r;
40,029✔
283

284
        assert(fd >= 0);
40,029✔
285

286
        r = pidfd_check_pidfs(fd);
40,029✔
287
        if (r < 0)
40,029✔
288
                return r;
289
        if (r == 0)
40,029✔
290
                return -EOPNOTSUPP;
291

292
        return pidfd_get_inode_id_impl(fd, ret);
40,029✔
293
}
294

295
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
39,356✔
296
        static thread_local uint64_t cached = 0;
39,356✔
297
        static thread_local pid_t initialized = 0; /* < 0: cached error; == 0: invalid; > 0: valid and pid that was current */
39,356✔
298
        int r;
39,356✔
299

300
        assert(ret);
39,356✔
301

302
        if (initialized == getpid_cached()) {
39,356✔
303
                *ret = cached;
4,598✔
304
                return 0;
4,598✔
305
        }
306
        if (initialized < 0)
34,758✔
307
                return initialized;
308

309
        _cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
74,114✔
310
        if (fd < 0)
34,758✔
UNCOV
311
                return -errno;
×
312

313
        r = pidfd_get_inode_id(fd, &cached);
34,758✔
314
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
34,758✔
UNCOV
315
                return (initialized = -EOPNOTSUPP);
×
316
        if (r < 0)
34,758✔
317
                return r;
318

319
        *ret = cached;
34,758✔
320
        initialized = getpid_cached();
34,758✔
321
        return 0;
34,758✔
322
}
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