]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fdset.c
845c19b1cf7826c44a0afa1fabf72d74f884b2c1
[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 "alloc-util.h"
29 #include "dirent-util.h"
30 #include "fd-util.h"
31 #include "fdset.h"
32 #include "log.h"
33 #include "macro.h"
34 #include "parse-util.h"
35 #include "path-util.h"
36 #include "set.h"
37
38 #define MAKE_SET(s) ((Set*) s)
39 #define MAKE_FDSET(s) ((FDSet*) s)
40
41 FDSet *fdset_new(void) {
42 return MAKE_FDSET(set_new(NULL));
43 }
44
45 int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds) {
46 unsigned i;
47 FDSet *s;
48 int r;
49
50 assert(ret);
51
52 s = fdset_new();
53 if (!s)
54 return -ENOMEM;
55
56 for (i = 0; i < n_fds; i++) {
57
58 r = fdset_put(s, fds[i]);
59 if (r < 0) {
60 set_free(MAKE_SET(s));
61 return r;
62 }
63 }
64
65 *ret = s;
66 return 0;
67 }
68
69 FDSet* fdset_free(FDSet *s) {
70 void *p;
71
72 while ((p = set_steal_first(MAKE_SET(s)))) {
73 /* Valgrind's fd might have ended up in this set here,
74 * due to fdset_new_fill(). We'll ignore all failures
75 * here, so that the EBADFD that valgrind will return
76 * us on close() doesn't influence us */
77
78 /* When reloading duplicates of the private bus
79 * connection fds and suchlike are closed here, which
80 * has no effect at all, since they are only
81 * duplicates. So don't be surprised about these log
82 * messages. */
83
84 log_debug("Closing left-over fd %i", PTR_TO_FD(p));
85 close_nointr(PTR_TO_FD(p));
86 }
87
88 set_free(MAKE_SET(s));
89 return NULL;
90 }
91
92 int fdset_put(FDSet *s, int fd) {
93 assert(s);
94 assert(fd >= 0);
95
96 return set_put(MAKE_SET(s), FD_TO_PTR(fd));
97 }
98
99 int fdset_put_dup(FDSet *s, int fd) {
100 int copy, r;
101
102 assert(s);
103 assert(fd >= 0);
104
105 copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
106 if (copy < 0)
107 return -errno;
108
109 r = fdset_put(s, copy);
110 if (r < 0) {
111 safe_close(copy);
112 return r;
113 }
114
115 return copy;
116 }
117
118 bool fdset_contains(FDSet *s, int fd) {
119 assert(s);
120 assert(fd >= 0);
121
122 return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
123 }
124
125 int fdset_remove(FDSet *s, int fd) {
126 assert(s);
127 assert(fd >= 0);
128
129 return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
130 }
131
132 int fdset_new_fill(FDSet **_s) {
133 _cleanup_closedir_ DIR *d = NULL;
134 struct dirent *de;
135 int r = 0;
136 FDSet *s;
137
138 assert(_s);
139
140 /* Creates an fdset and fills in all currently open file
141 * descriptors. */
142
143 d = opendir("/proc/self/fd");
144 if (!d)
145 return -errno;
146
147 s = fdset_new();
148 if (!s) {
149 r = -ENOMEM;
150 goto finish;
151 }
152
153 FOREACH_DIRENT(de, d, return -errno) {
154 int fd = -1;
155
156 r = safe_atoi(de->d_name, &fd);
157 if (r < 0)
158 goto finish;
159
160 if (fd < 3)
161 continue;
162
163 if (fd == dirfd(d))
164 continue;
165
166 r = fdset_put(s, fd);
167 if (r < 0)
168 goto finish;
169 }
170
171 r = 0;
172 *_s = TAKE_PTR(s);
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 }