chiark / gitweb /
logind: unify how we cast between uid_t and pointers for hashmap keys
[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 #include <unistd.h>
26
27 #include "set.h"
28 #include "util.h"
29 #include "macro.h"
30 #include "fdset.h"
31 #include "sd-daemon.h"
32
33 #define MAKE_SET(s) ((Set*) s)
34 #define MAKE_FDSET(s) ((FDSet*) s)
35
36 /* Make sure we can distuingish fd 0 and NULL */
37 #define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
38 #define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
39
40 FDSet *fdset_new(void) {
41         return MAKE_FDSET(set_new(NULL));
42 }
43
44 int fdset_new_array(FDSet **ret, 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_consume(FDSet *s, int fd) {
99         int r;
100
101         assert(s);
102         assert(fd >= 0);
103
104         r = fdset_put(s, fd);
105         if (r <= 0)
106                 safe_close(fd);
107
108         return r;
109 }
110
111 int fdset_put_dup(FDSet *s, int fd) {
112         int copy, r;
113
114         assert(s);
115         assert(fd >= 0);
116
117         copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
118         if (copy < 0)
119                 return -errno;
120
121         r = fdset_put(s, copy);
122         if (r < 0) {
123                 safe_close(copy);
124                 return r;
125         }
126
127         return copy;
128 }
129
130 bool fdset_contains(FDSet *s, int fd) {
131         assert(s);
132         assert(fd >= 0);
133
134         return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
135 }
136
137 int fdset_remove(FDSet *s, int fd) {
138         assert(s);
139         assert(fd >= 0);
140
141         return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
142 }
143
144 int fdset_new_fill(FDSet **_s) {
145         _cleanup_closedir_ DIR *d = NULL;
146         struct dirent *de;
147         int r = 0;
148         FDSet *s;
149
150         assert(_s);
151
152         /* Creates an fdset and fills in all currently open file
153          * descriptors. */
154
155         d = opendir("/proc/self/fd");
156         if (!d)
157                 return -errno;
158
159         s = fdset_new();
160         if (!s) {
161                 r = -ENOMEM;
162                 goto finish;
163         }
164
165         while ((de = readdir(d))) {
166                 int fd = -1;
167
168                 if (hidden_file(de->d_name))
169                         continue;
170
171                 r = safe_atoi(de->d_name, &fd);
172                 if (r < 0)
173                         goto finish;
174
175                 if (fd < 3)
176                         continue;
177
178                 if (fd == dirfd(d))
179                         continue;
180
181                 r = fdset_put(s, fd);
182                 if (r < 0)
183                         goto finish;
184         }
185
186         r = 0;
187         *_s = s;
188         s = NULL;
189
190 finish:
191         /* We won't close the fds here! */
192         if (s)
193                 set_free(MAKE_SET(s));
194
195         return r;
196 }
197
198 int fdset_cloexec(FDSet *fds, bool b) {
199         Iterator i;
200         void *p;
201         int r;
202
203         assert(fds);
204
205         SET_FOREACH(p, MAKE_SET(fds), i)
206                 if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
207                         return r;
208
209         return 0;
210 }
211
212 int fdset_new_listen_fds(FDSet **_s, bool unset) {
213         int n, fd, r;
214         FDSet *s;
215
216         assert(_s);
217
218         /* Creates an fdset and fills in all passed file descriptors */
219
220         s = fdset_new();
221         if (!s) {
222                 r = -ENOMEM;
223                 goto fail;
224         }
225
226         n = sd_listen_fds(unset);
227         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
228                 r = fdset_put(s, fd);
229                 if (r < 0)
230                         goto fail;
231         }
232
233         *_s = s;
234         return 0;
235
236
237 fail:
238         if (s)
239                 set_free(MAKE_SET(s));
240
241         return r;
242 }
243
244 int fdset_close_others(FDSet *fds) {
245         void *e;
246         Iterator i;
247         int *a;
248         unsigned j, m;
249
250         j = 0, m = fdset_size(fds);
251         a = alloca(sizeof(int) * m);
252         SET_FOREACH(e, MAKE_SET(fds), i)
253                 a[j++] = PTR_TO_FD(e);
254
255         assert(j == m);
256
257         return close_all_fds(a, j);
258 }
259
260 unsigned fdset_size(FDSet *fds) {
261         return set_size(MAKE_SET(fds));
262 }
263
264 bool fdset_isempty(FDSet *fds) {
265         return set_isempty(MAKE_SET(fds));
266 }
267
268 int fdset_iterate(FDSet *s, Iterator *i) {
269         void *p;
270
271         p = set_iterate(MAKE_SET(s), i);
272         if (!p)
273                 return -ENOENT;
274
275         return PTR_TO_FD(p);
276 }
277
278 int fdset_steal_first(FDSet *fds) {
279         void *p;
280
281         p = set_steal_first(MAKE_SET(fds));
282         if (!p)
283                 return -ENOENT;
284
285         return PTR_TO_FD(p);
286 }