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