chiark / gitweb /
path: avoid an allocation in path_spec_watch
[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(trivial_hash_func, trivial_compare_func));
42 }
43
44 void fdset_free(FDSet *s) {
45         void *p;
46
47         while ((p = set_steal_first(MAKE_SET(s)))) {
48                 /* Valgrind's fd might have ended up in this set here,
49                  * due to fdset_new_fill(). We'll ignore all failures
50                  * here, so that the EBADFD that valgrind will return
51                  * us on close() doesn't influence us */
52
53                 /* When reloading duplicates of the private bus
54                  * connection fds and suchlike are closed here, which
55                  * has no effect at all, since they are only
56                  * duplicates. So don't be surprised about these log
57                  * messages. */
58
59                 log_debug("Closing left-over fd %i", PTR_TO_FD(p));
60                 close_nointr(PTR_TO_FD(p));
61         }
62
63         set_free(MAKE_SET(s));
64 }
65
66 int fdset_put(FDSet *s, int fd) {
67         assert(s);
68         assert(fd >= 0);
69
70         return set_put(MAKE_SET(s), FD_TO_PTR(fd));
71 }
72
73 int fdset_put_dup(FDSet *s, int fd) {
74         int copy, r;
75
76         assert(s);
77         assert(fd >= 0);
78
79         copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
80         if (copy < 0)
81                 return -errno;
82
83         r = fdset_put(s, copy);
84         if (r < 0) {
85                 close_nointr_nofail(copy);
86                 return r;
87         }
88
89         return copy;
90 }
91
92 bool fdset_contains(FDSet *s, int fd) {
93         assert(s);
94         assert(fd >= 0);
95
96         return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
97 }
98
99 int fdset_remove(FDSet *s, int fd) {
100         assert(s);
101         assert(fd >= 0);
102
103         return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
104 }
105
106 int fdset_new_fill(FDSet **_s) {
107         DIR *d;
108         struct dirent *de;
109         int r = 0;
110         FDSet *s;
111
112         assert(_s);
113
114         /* Creates an fdset and fills in all currently open file
115          * descriptors. */
116
117         d = opendir("/proc/self/fd");
118         if (!d)
119                 return -errno;
120
121         s = fdset_new();
122         if (!s) {
123                 r = -ENOMEM;
124                 goto finish;
125         }
126
127         while ((de = readdir(d))) {
128                 int fd = -1;
129
130                 if (ignore_file(de->d_name))
131                         continue;
132
133                 r = safe_atoi(de->d_name, &fd);
134                 if (r < 0)
135                         goto finish;
136
137                 if (fd < 3)
138                         continue;
139
140                 if (fd == dirfd(d))
141                         continue;
142
143                 r = fdset_put(s, fd);
144                 if (r < 0)
145                         goto finish;
146         }
147
148         r = 0;
149         *_s = s;
150         s = NULL;
151
152 finish:
153         closedir(d);
154
155         /* We won't close the fds here! */
156         if (s)
157                 set_free(MAKE_SET(s));
158
159         return r;
160 }
161
162 int fdset_cloexec(FDSet *fds, bool b) {
163         Iterator i;
164         void *p;
165         int r;
166
167         assert(fds);
168
169         SET_FOREACH(p, MAKE_SET(fds), i)
170                 if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
171                         return r;
172
173         return 0;
174 }
175
176 int fdset_new_listen_fds(FDSet **_s, bool unset) {
177         int n, fd, r;
178         FDSet *s;
179
180         assert(_s);
181
182         /* Creates an fdset and fills in all passed file descriptors */
183
184         s = fdset_new();
185         if (!s) {
186                 r = -ENOMEM;
187                 goto fail;
188         }
189
190         n = sd_listen_fds(unset);
191         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
192                 r = fdset_put(s, fd);
193                 if (r < 0)
194                         goto fail;
195         }
196
197         *_s = s;
198         return 0;
199
200
201 fail:
202         if (s)
203                 set_free(MAKE_SET(s));
204
205         return r;
206 }
207
208 int fdset_close_others(FDSet *fds) {
209         void *e;
210         Iterator i;
211         int *a;
212         unsigned j, m;
213
214         j = 0, m = fdset_size(fds);
215         a = alloca(sizeof(int) * m);
216         SET_FOREACH(e, MAKE_SET(fds), i)
217                 a[j++] = PTR_TO_FD(e);
218
219         assert(j == m);
220
221         return close_all_fds(a, j);
222 }
223
224 unsigned fdset_size(FDSet *fds) {
225         return set_size(MAKE_SET(fds));
226 }
227
228 int fdset_iterate(FDSet *s, Iterator *i) {
229         void *p;
230
231         p = set_iterate(MAKE_SET(s), i);
232         if (!p)
233                 return -ENOENT;
234
235         return PTR_TO_FD(p);
236 }