]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fdset.c
Merge pull request #7379 from yuwata/follow-up-7309
[thirdparty/systemd.git] / src / shared / fdset.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <alloca.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stddef.h>
25
26 #include "sd-daemon.h"
27
28 #include "dirent-util.h"
29 #include "fd-util.h"
30 #include "fdset.h"
31 #include "log.h"
32 #include "macro.h"
33 #include "parse-util.h"
34 #include "path-util.h"
35 #include "set.h"
36
37 #define MAKE_SET(s) ((Set*) s)
38 #define MAKE_FDSET(s) ((FDSet*) s)
39
40 FDSet *fdset_new(void) {
41 return MAKE_FDSET(set_new(NULL));
42 }
43
44 int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds) {
45 unsigned i;
46 FDSet *s;
47 int r;
48
49 assert(ret);
50
51 s = fdset_new();
52 if (!s)
53 return -ENOMEM;
54
55 for (i = 0; i < n_fds; i++) {
56
57 r = fdset_put(s, fds[i]);
58 if (r < 0) {
59 set_free(MAKE_SET(s));
60 return r;
61 }
62 }
63
64 *ret = s;
65 return 0;
66 }
67
68 FDSet* fdset_free(FDSet *s) {
69 void *p;
70
71 while ((p = set_steal_first(MAKE_SET(s)))) {
72 /* Valgrind's fd might have ended up in this set here,
73 * due to fdset_new_fill(). We'll ignore all failures
74 * here, so that the EBADFD that valgrind will return
75 * us on close() doesn't influence us */
76
77 /* When reloading duplicates of the private bus
78 * connection fds and suchlike are closed here, which
79 * has no effect at all, since they are only
80 * duplicates. So don't be surprised about these log
81 * messages. */
82
83 log_debug("Closing left-over fd %i", PTR_TO_FD(p));
84 close_nointr(PTR_TO_FD(p));
85 }
86
87 set_free(MAKE_SET(s));
88 return NULL;
89 }
90
91 int fdset_put(FDSet *s, int fd) {
92 assert(s);
93 assert(fd >= 0);
94
95 return set_put(MAKE_SET(s), FD_TO_PTR(fd));
96 }
97
98 int fdset_put_dup(FDSet *s, int fd) {
99 int copy, r;
100
101 assert(s);
102 assert(fd >= 0);
103
104 copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
105 if (copy < 0)
106 return -errno;
107
108 r = fdset_put(s, copy);
109 if (r < 0) {
110 safe_close(copy);
111 return r;
112 }
113
114 return copy;
115 }
116
117 bool fdset_contains(FDSet *s, int fd) {
118 assert(s);
119 assert(fd >= 0);
120
121 return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
122 }
123
124 int fdset_remove(FDSet *s, int fd) {
125 assert(s);
126 assert(fd >= 0);
127
128 return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
129 }
130
131 int fdset_new_fill(FDSet **_s) {
132 _cleanup_closedir_ DIR *d = NULL;
133 struct dirent *de;
134 int r = 0;
135 FDSet *s;
136
137 assert(_s);
138
139 /* Creates an fdset and fills in all currently open file
140 * descriptors. */
141
142 d = opendir("/proc/self/fd");
143 if (!d)
144 return -errno;
145
146 s = fdset_new();
147 if (!s) {
148 r = -ENOMEM;
149 goto finish;
150 }
151
152 FOREACH_DIRENT(de, d, return -errno) {
153 int fd = -1;
154
155 r = safe_atoi(de->d_name, &fd);
156 if (r < 0)
157 goto finish;
158
159 if (fd < 3)
160 continue;
161
162 if (fd == dirfd(d))
163 continue;
164
165 r = fdset_put(s, fd);
166 if (r < 0)
167 goto finish;
168 }
169
170 r = 0;
171 *_s = s;
172 s = NULL;
173
174 finish:
175 /* We won't close the fds here! */
176 if (s)
177 set_free(MAKE_SET(s));
178
179 return r;
180 }
181
182 int fdset_cloexec(FDSet *fds, bool b) {
183 Iterator i;
184 void *p;
185 int r;
186
187 assert(fds);
188
189 SET_FOREACH(p, MAKE_SET(fds), i) {
190 r = fd_cloexec(PTR_TO_FD(p), b);
191 if (r < 0)
192 return r;
193 }
194
195 return 0;
196 }
197
198 int fdset_new_listen_fds(FDSet **_s, bool unset) {
199 int n, fd, r;
200 FDSet *s;
201
202 assert(_s);
203
204 /* Creates an fdset and fills in all passed file descriptors */
205
206 s = fdset_new();
207 if (!s) {
208 r = -ENOMEM;
209 goto fail;
210 }
211
212 n = sd_listen_fds(unset);
213 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
214 r = fdset_put(s, fd);
215 if (r < 0)
216 goto fail;
217 }
218
219 *_s = s;
220 return 0;
221
222
223 fail:
224 if (s)
225 set_free(MAKE_SET(s));
226
227 return r;
228 }
229
230 int fdset_close_others(FDSet *fds) {
231 void *e;
232 Iterator i;
233 int *a;
234 unsigned j, m;
235
236 j = 0, m = fdset_size(fds);
237 a = alloca(sizeof(int) * m);
238 SET_FOREACH(e, MAKE_SET(fds), i)
239 a[j++] = PTR_TO_FD(e);
240
241 assert(j == m);
242
243 return close_all_fds(a, j);
244 }
245
246 unsigned fdset_size(FDSet *fds) {
247 return set_size(MAKE_SET(fds));
248 }
249
250 bool fdset_isempty(FDSet *fds) {
251 return set_isempty(MAKE_SET(fds));
252 }
253
254 int fdset_iterate(FDSet *s, Iterator *i) {
255 void *p;
256
257 if (!set_iterate(MAKE_SET(s), i, &p))
258 return -ENOENT;
259
260 return PTR_TO_FD(p);
261 }
262
263 int fdset_steal_first(FDSet *fds) {
264 void *p;
265
266 p = set_steal_first(MAKE_SET(fds));
267 if (!p)
268 return -ENOENT;
269
270 return PTR_TO_FD(p);
271 }