chiark / gitweb /
hashmap: fix iterators to not skip entries
[elogind.git] / src / shared / 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 "set.h"
27 #include "util.h"
28 #include "macro.h"
29 #include "fdset.h"
30 #include "sd-daemon.h"
31
32 #define MAKE_SET(s) ((Set*) s)
33 #define MAKE_FDSET(s) ((FDSet*) s)
34
35 /* Make sure we can distinguish fd 0 and NULL */
36 #define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
37 #define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
38
39 FDSet *fdset_new(void) {
40         return MAKE_FDSET(set_new(NULL));
41 }
42
43 int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds) {
44         unsigned i;
45         FDSet *s;
46         int r;
47
48         assert(ret);
49
50         s = fdset_new();
51         if (!s)
52                 return -ENOMEM;
53
54         for (i = 0; i < n_fds; i++) {
55
56                 r = fdset_put(s, fds[i]);
57                 if (r < 0) {
58                         set_free(MAKE_SET(s));
59                         return r;
60                 }
61         }
62
63         *ret = s;
64         return 0;
65 }
66
67 FDSet* fdset_free(FDSet *s) {
68         void *p;
69
70         while ((p = set_steal_first(MAKE_SET(s)))) {
71                 /* Valgrind's fd might have ended up in this set here,
72                  * due to fdset_new_fill(). We'll ignore all failures
73                  * here, so that the EBADFD that valgrind will return
74                  * us on close() doesn't influence us */
75
76                 /* When reloading duplicates of the private bus
77                  * connection fds and suchlike are closed here, which
78                  * has no effect at all, since they are only
79                  * duplicates. So don't be surprised about these log
80                  * messages. */
81
82                 log_debug("Closing left-over fd %i", PTR_TO_FD(p));
83                 close_nointr(PTR_TO_FD(p));
84         }
85
86         set_free(MAKE_SET(s));
87         return NULL;
88 }
89
90 int fdset_put(FDSet *s, int fd) {
91         assert(s);
92         assert(fd >= 0);
93
94         return set_put(MAKE_SET(s), FD_TO_PTR(fd));
95 }
96
97 int fdset_consume(FDSet *s, int fd) {
98         int r;
99
100         assert(s);
101         assert(fd >= 0);
102
103         r = fdset_put(s, fd);
104         if (r <= 0)
105                 safe_close(fd);
106
107         return r;
108 }
109
110 int fdset_put_dup(FDSet *s, int fd) {
111         int copy, r;
112
113         assert(s);
114         assert(fd >= 0);
115
116         copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
117         if (copy < 0)
118                 return -errno;
119
120         r = fdset_put(s, copy);
121         if (r < 0) {
122                 safe_close(copy);
123                 return r;
124         }
125
126         return copy;
127 }
128
129 bool fdset_contains(FDSet *s, int fd) {
130         assert(s);
131         assert(fd >= 0);
132
133         return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
134 }
135
136 int fdset_remove(FDSet *s, int fd) {
137         assert(s);
138         assert(fd >= 0);
139
140         return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
141 }
142
143 int fdset_new_fill(FDSet **_s) {
144         _cleanup_closedir_ DIR *d = NULL;
145         struct dirent *de;
146         int r = 0;
147         FDSet *s;
148
149         assert(_s);
150
151         /* Creates an fdset and fills in all currently open file
152          * descriptors. */
153
154         d = opendir("/proc/self/fd");
155         if (!d)
156                 return -errno;
157
158         s = fdset_new();
159         if (!s) {
160                 r = -ENOMEM;
161                 goto finish;
162         }
163
164         while ((de = readdir(d))) {
165                 int fd = -1;
166
167                 if (hidden_file(de->d_name))
168                         continue;
169
170                 r = safe_atoi(de->d_name, &fd);
171                 if (r < 0)
172                         goto finish;
173
174                 if (fd < 3)
175                         continue;
176
177                 if (fd == dirfd(d))
178                         continue;
179
180                 r = fdset_put(s, fd);
181                 if (r < 0)
182                         goto finish;
183         }
184
185         r = 0;
186         *_s = s;
187         s = NULL;
188
189 finish:
190         /* We won't close the fds here! */
191         if (s)
192                 set_free(MAKE_SET(s));
193
194         return r;
195 }
196
197 int fdset_cloexec(FDSet *fds, bool b) {
198         Iterator i;
199         void *p;
200         int r;
201
202         assert(fds);
203
204         SET_FOREACH(p, MAKE_SET(fds), i)
205                 if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
206                         return r;
207
208         return 0;
209 }
210
211 int fdset_new_listen_fds(FDSet **_s, bool unset) {
212         int n, fd, r;
213         FDSet *s;
214
215         assert(_s);
216
217         /* Creates an fdset and fills in all passed file descriptors */
218
219         s = fdset_new();
220         if (!s) {
221                 r = -ENOMEM;
222                 goto fail;
223         }
224
225         n = sd_listen_fds(unset);
226         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
227                 r = fdset_put(s, fd);
228                 if (r < 0)
229                         goto fail;
230         }
231
232         *_s = s;
233         return 0;
234
235
236 fail:
237         if (s)
238                 set_free(MAKE_SET(s));
239
240         return r;
241 }
242
243 int fdset_close_others(FDSet *fds) {
244         void *e;
245         Iterator i;
246         int *a;
247         unsigned j, m;
248
249         j = 0, m = fdset_size(fds);
250         a = alloca(sizeof(int) * m);
251         SET_FOREACH(e, MAKE_SET(fds), i)
252                 a[j++] = PTR_TO_FD(e);
253
254         assert(j == m);
255
256         return close_all_fds(a, j);
257 }
258
259 unsigned fdset_size(FDSet *fds) {
260         return set_size(MAKE_SET(fds));
261 }
262
263 bool fdset_isempty(FDSet *fds) {
264         return set_isempty(MAKE_SET(fds));
265 }
266
267 int fdset_iterate(FDSet *s, Iterator *i) {
268         void *p;
269
270         if (!set_iterate(MAKE_SET(s), i, &p))
271                 return -ENOENT;
272
273         return PTR_TO_FD(p);
274 }
275
276 int fdset_steal_first(FDSet *fds) {
277         void *p;
278
279         p = set_steal_first(MAKE_SET(fds));
280         if (!p)
281                 return -ENOENT;
282
283         return PTR_TO_FD(p);
284 }