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

systemd / systemd / 13935887515

18 Mar 2025 07:10PM UTC coverage: 71.913% (-0.03%) from 71.946%
13935887515

push

github

web-flow
Several fixes and cleanups around sd_listen_fds() (#36788)

15 of 24 new or added lines in 5 files covered. (62.5%)

993 existing lines in 54 files now uncovered.

296157 of 411825 relevant lines covered (71.91%)

710024.94 hits per line

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

88.37
/src/shared/fdset.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <stddef.h>
6
#include <unistd.h>
7

8
#include "sd-daemon.h"
9

10
#include "alloc-util.h"
11
#include "async.h"
12
#include "dirent-util.h"
13
#include "fd-util.h"
14
#include "fdset.h"
15
#include "log.h"
16
#include "macro.h"
17
#include "parse-util.h"
18
#include "path-util.h"
19
#include "set.h"
20
#include "stat-util.h"
21

22
#define MAKE_SET(s) ((Set*) s)
23
#define MAKE_FDSET(s) ((FDSet*) s)
24

25
FDSet* fdset_new(void) {
21,813✔
26
        return MAKE_FDSET(set_new(NULL));
21,813✔
27
}
28

29
static void fdset_shallow_freep(FDSet **s) {
17,187✔
30
        /* Destroys the set, but does not free the fds inside, like fdset_free()! */
31
        set_free(MAKE_SET(*ASSERT_PTR(s)));
17,187✔
32
}
17,187✔
33

34
int fdset_new_array(FDSet **ret, const int fds[], size_t n_fds) {
3,182✔
35
        _cleanup_(fdset_shallow_freep) FDSet *s = NULL;
3,182✔
36
        int r;
3,182✔
37

38
        assert(ret);
3,182✔
39
        assert(fds || n_fds == 0);
3,182✔
40

41
        s = fdset_new();
3,182✔
42
        if (!s)
3,182✔
43
                return -ENOMEM;
44

45
        FOREACH_ARRAY(fd, fds, n_fds) {
6,367✔
46
                r = fdset_put(s, *fd);
3,185✔
47
                if (r < 0)
3,185✔
48
                        return r;
49
        }
50

51
        *ret = TAKE_PTR(s);
3,182✔
52
        return 0;
3,182✔
53
}
54

55
int fdset_steal_first(FDSet *fds) {
90,922✔
56
        void *p;
90,922✔
57

58
        p = set_steal_first(MAKE_SET(fds));
90,922✔
59
        if (!p)
90,922✔
60
                return -ENOENT;
61

62
        return PTR_TO_FD(p);
16,558✔
63
}
64

65
void fdset_close(FDSet *fds, bool async) {
71,107✔
66
        int fd;
71,107✔
67

68
        while ((fd = fdset_steal_first(fds)) >= 0) {
84,495✔
69
                /* Valgrind's fd might have ended up in this set here, due to fdset_new_fill(). We'll ignore
70
                 * all failures here, so that the EBADFD that valgrind will return us on close() doesn't
71
                 * influence us */
72

73
                /* When reloading duplicates of the private bus connection fds and suchlike are closed here,
74
                 * which has no effect at all, since they are only duplicates. So don't be surprised about
75
                 * these log messages. */
76

77
                if (DEBUG_LOGGING) {
13,388✔
78
                        _cleanup_free_ char *path = NULL;
12,237✔
79

80
                        (void) fd_get_path(fd, &path);
12,237✔
81
                        log_debug("Closing set fd %i (%s)", fd, strna(path));
12,241✔
82
                }
83

84
                if (async)
13,388✔
85
                        (void) asynchronous_close(fd);
12✔
86
                else
87
                        (void) close(fd);
13,376✔
88
        }
89
}
71,107✔
90

91
FDSet* fdset_free(FDSet *s) {
67,756✔
92
        fdset_close(s, /* async= */ false);
67,756✔
93
        set_free(MAKE_SET(s));
67,756✔
94
        return NULL;
67,756✔
95
}
96

97
FDSet* fdset_free_async(FDSet *s) {
3,181✔
98
        fdset_close(s, /* async= */ true);
3,181✔
99
        set_free(MAKE_SET(s));
3,181✔
100
        return NULL;
3,181✔
101
}
102

103
int fdset_put(FDSet *s, int fd) {
68,923✔
104
        assert(s);
68,923✔
105
        assert(fd >= 0);
68,923✔
106

107
        /* Avoid integer overflow in FD_TO_PTR() */
108
        if (fd == INT_MAX)
68,923✔
109
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing invalid fd: %d", fd);
×
110

111
        return set_put(MAKE_SET(s), FD_TO_PTR(fd));
68,923✔
112
}
113

114
int fdset_consume(FDSet *s, int fd) {
1,128✔
115
        int r;
1,128✔
116

117
        assert(s);
1,128✔
118
        assert(fd >= 0);
1,128✔
119

120
        r = fdset_put(s, fd);
1,128✔
121
        if (r < 0)
1,128✔
122
                safe_close(fd);
×
123

124
        return r;
1,128✔
125
}
126

127
int fdset_put_dup(FDSet *s, int fd) {
17,493✔
128
        _cleanup_close_ int copy = -EBADF;
17,493✔
129
        int r;
17,493✔
130

131
        assert(s);
17,493✔
132
        assert(fd >= 0);
17,493✔
133

134
        copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
17,493✔
135
        if (copy < 0)
17,493✔
136
                return -errno;
×
137

138
        r = fdset_put(s, copy);
17,493✔
139
        if (r < 0)
17,493✔
140
                return r;
×
141

142
        return TAKE_FD(copy);
143
}
144

145
bool fdset_contains(FDSet *s, int fd) {
1,269✔
146
        assert(s);
1,269✔
147
        assert(fd >= 0);
1,269✔
148

149
        /* Avoid integer overflow in FD_TO_PTR() */
150
        if (fd == INT_MAX) {
1,269✔
151
                log_debug("Refusing invalid fd: %d", fd);
×
152
                return false;
×
153
        }
154

155
        return set_contains(MAKE_SET(s), FD_TO_PTR(fd));
1,269✔
156
}
157

158
int fdset_remove(FDSet *s, int fd) {
48,489✔
159
        assert(s);
48,489✔
160
        assert(fd >= 0);
48,489✔
161

162
        /* Avoid integer overflow in FD_TO_PTR() */
163
        if (fd == INT_MAX)
48,489✔
164
                return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "Refusing invalid fd: %d", fd);
×
165

166
        return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
48,489✔
167
}
168

169
int fdset_new_fill(
13,546✔
170
                int filter_cloexec, /* if < 0 takes all fds, otherwise only those with O_CLOEXEC set (1) or unset (0) */
171
                FDSet **ret) {
172

173
        _cleanup_(fdset_shallow_freep) FDSet *s = NULL;
×
174
        _cleanup_closedir_ DIR *d = NULL;
13,546✔
175
        int r;
13,546✔
176

177
        assert(ret);
13,546✔
178

179
        /* Creates an fdset and fills in all currently open file descriptors. Also set all collected fds
180
         * to CLOEXEC. */
181

182
        d = opendir("/proc/self/fd");
13,546✔
183
        if (!d) {
13,546✔
184
                if (errno == ENOENT && proc_mounted() == 0)
×
185
                        return -ENOSYS;
186

187
                return -errno;
×
188
        }
189

190
        s = fdset_new();
13,546✔
191
        if (!s)
13,546✔
192
                return -ENOMEM;
193

194
        FOREACH_DIRENT(de, d, return -errno) {
168,241✔
195
                int fd;
127,603✔
196

197
                if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
127,603✔
198
                        continue;
×
199

200
                fd = parse_fd(de->d_name);
127,603✔
201
                if (fd < 0)
127,603✔
202
                        return fd;
203

204
                if (fd < 3)
127,603✔
205
                        continue;
40,638✔
206
                if (fd == dirfd(d))
86,965✔
207
                        continue;
13,546✔
208

209
                if (filter_cloexec >= 0) {
73,419✔
210
                        int fl;
73,418✔
211

212
                        /* If user asked for that filter by O_CLOEXEC. This is useful so that fds that have
213
                         * been passed in can be collected and fds which have been created locally can be
214
                         * ignored, under the assumption that only the latter have O_CLOEXEC set. */
215

216
                        fl = fcntl(fd, F_GETFD);
73,418✔
217
                        if (fl < 0)
73,418✔
218
                                return -errno;
×
219

220
                        if (FLAGS_SET(fl, FD_CLOEXEC) != !!filter_cloexec)
73,418✔
221
                                continue;
26,392✔
222
                }
223

224
                /* We need to set CLOEXEC manually only if we're collecting non-CLOEXEC fds. */
225
                if (filter_cloexec <= 0) {
47,026✔
226
                        r = fd_cloexec(fd, true);
47,026✔
227
                        if (r < 0)
47,026✔
228
                                return r;
229
                }
230

231
                r = fdset_put(s, fd);
47,027✔
232
                if (r < 0)
47,027✔
233
                        return r;
234
        }
235

236
        *ret = TAKE_PTR(s);
13,546✔
237
        return 0;
13,546✔
238
}
239

240
int fdset_cloexec(FDSet *fds, bool b) {
3,921✔
241
        int r;
3,921✔
242

243
        assert(fds);
3,921✔
244

245
        int fd;
3,921✔
246
        FDSET_FOREACH(fd, fds) {
19,867✔
247
                r = fd_cloexec(fd, b);
15,946✔
248
                if (r < 0)
15,946✔
249
                        return r;
×
250
        }
251

252
        return 0;
3,921✔
253
}
254

255
int fdset_new_listen_fds(FDSet **ret, bool unset) {
459✔
256
        _cleanup_(fdset_shallow_freep) FDSet *s = NULL;
459✔
257
        int n, r;
459✔
258

259
        assert(ret);
459✔
260

261
        /* Creates an fdset and fills in all passed file descriptors */
262

263
        n = sd_listen_fds(unset);
459✔
264
        if (n < 0)
459✔
265
                return n;
266
        if (n == 0) {
459✔
267
                *ret = NULL;
459✔
268
                return 0;
459✔
269
        }
270

271
        s = fdset_new();
×
272
        if (!s)
×
273
                return -ENOMEM;
274

NEW
275
        for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
×
276
                r = fdset_put(s, fd);
×
277
                if (r < 0)
×
278
                        return r;
279
        }
280

281
        *ret = TAKE_PTR(s);
×
NEW
282
        return n;
×
283
}
284

285
int fdset_to_array(FDSet *fds, int **ret) {
140✔
286
        unsigned j = 0, m;
140✔
287
        int *a;
140✔
288

289
        assert(ret);
140✔
290

291
        m = fdset_size(fds);
140✔
292
        if (m > INT_MAX) /* We want to be able to return an "int" */
140✔
293
                return -ENOMEM;
140✔
294
        if (m == 0) {
140✔
295
                *ret = NULL; /* suppress array allocation if empty */
136✔
296
                return 0;
136✔
297
        }
298

299
        a = new(int, m);
4✔
300
        if (!a)
4✔
301
                return -ENOMEM;
302

303
        int fd;
4✔
304
        FDSET_FOREACH(fd, fds)
8✔
305
                a[j++] = fd;
4✔
306

307
        assert(j == m);
4✔
308

309
        *ret = TAKE_PTR(a);
4✔
310
        return (int) m;
4✔
311
}
312

313
int fdset_close_others(FDSet *fds) {
137✔
314
        _cleanup_free_ int *a = NULL;
137✔
315
        int n;
137✔
316

317
        n = fdset_to_array(fds, &a);
137✔
318
        if (n < 0)
137✔
319
                return n;
320

321
        return close_all_fds(a, n);
137✔
322
}
323

324
unsigned fdset_size(FDSet *fds) {
239✔
325
        return set_size(MAKE_SET(fds));
239✔
326
}
327

328
bool fdset_isempty(FDSet *fds) {
20,137✔
329
        return set_isempty(MAKE_SET(fds));
20,137✔
330
}
331

332
int fdset_iterate(FDSet *s, Iterator *i) {
24,944✔
333
        void *p;
24,944✔
334

335
        if (!set_iterate(MAKE_SET(s), i, &p))
24,944✔
336
                return -ENOENT;
24,944✔
337

338
        return PTR_TO_FD(p);
20,439✔
339
}
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