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