5e4a475d |
1 | /* |
2 | * sel.c: implementation of sel.h. |
3 | */ |
4 | |
5 | #include <stddef.h> |
6 | #include <string.h> |
7 | #include <errno.h> |
8 | #include <assert.h> |
9 | |
10 | #include <fcntl.h> |
11 | #include <unistd.h> |
12 | #include <sys/time.h> |
13 | #include <sys/types.h> |
14 | #include <sys/select.h> |
15 | |
16 | #include "sel.h" |
17 | #include "malloc.h" |
18 | |
19 | /* ---------------------------------------------------------------------- |
20 | * Chunk of code lifted from PuTTY's misc.c to manage buffers of |
21 | * data to be written to an fd. |
22 | */ |
23 | |
24 | #define BUFFER_GRANULE 512 |
25 | |
26 | typedef struct bufchain_tag { |
27 | struct bufchain_granule *head, *tail; |
28 | size_t buffersize; /* current amount of buffered data */ |
29 | } bufchain; |
30 | struct bufchain_granule { |
31 | struct bufchain_granule *next; |
32 | size_t buflen, bufpos; |
33 | char buf[BUFFER_GRANULE]; |
34 | }; |
35 | |
36 | static void bufchain_init(bufchain *ch) |
37 | { |
38 | ch->head = ch->tail = NULL; |
39 | ch->buffersize = 0; |
40 | } |
41 | |
42 | static void bufchain_clear(bufchain *ch) |
43 | { |
44 | struct bufchain_granule *b; |
45 | while (ch->head) { |
46 | b = ch->head; |
47 | ch->head = ch->head->next; |
48 | sfree(b); |
49 | } |
50 | ch->tail = NULL; |
51 | ch->buffersize = 0; |
52 | } |
53 | |
54 | static size_t bufchain_size(bufchain *ch) |
55 | { |
56 | return ch->buffersize; |
57 | } |
58 | |
59 | static void bufchain_add(bufchain *ch, const void *data, size_t len) |
60 | { |
61 | const char *buf = (const char *)data; |
62 | |
63 | if (len == 0) return; |
64 | |
65 | ch->buffersize += len; |
66 | |
67 | if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) { |
68 | size_t copylen = BUFFER_GRANULE - ch->tail->buflen; |
69 | if (copylen > len) |
70 | copylen = len; |
71 | memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen); |
72 | buf += copylen; |
73 | len -= copylen; |
74 | ch->tail->buflen += copylen; |
75 | } |
76 | while (len > 0) { |
77 | struct bufchain_granule *newbuf; |
78 | size_t grainlen = BUFFER_GRANULE; |
79 | if (grainlen > len) |
80 | grainlen = len; |
81 | newbuf = snew(struct bufchain_granule); |
82 | newbuf->bufpos = 0; |
83 | newbuf->buflen = grainlen; |
84 | memcpy(newbuf->buf, buf, grainlen); |
85 | buf += grainlen; |
86 | len -= grainlen; |
87 | if (ch->tail) |
88 | ch->tail->next = newbuf; |
89 | else |
90 | ch->head = ch->tail = newbuf; |
91 | newbuf->next = NULL; |
92 | ch->tail = newbuf; |
93 | } |
94 | } |
95 | |
96 | static void bufchain_consume(bufchain *ch, size_t len) |
97 | { |
98 | struct bufchain_granule *tmp; |
99 | |
100 | assert(ch->buffersize >= len); |
101 | while (len > 0) { |
102 | size_t remlen = len; |
103 | assert(ch->head != NULL); |
104 | if (remlen >= ch->head->buflen - ch->head->bufpos) { |
105 | remlen = ch->head->buflen - ch->head->bufpos; |
106 | tmp = ch->head; |
107 | ch->head = tmp->next; |
108 | sfree(tmp); |
109 | if (!ch->head) |
110 | ch->tail = NULL; |
111 | } else |
112 | ch->head->bufpos += remlen; |
113 | ch->buffersize -= remlen; |
114 | len -= remlen; |
115 | } |
116 | } |
117 | |
118 | static void bufchain_prefix(bufchain *ch, void **data, size_t *len) |
119 | { |
120 | *len = ch->head->buflen - ch->head->bufpos; |
121 | *data = ch->head->buf + ch->head->bufpos; |
122 | } |
123 | |
124 | /* ---------------------------------------------------------------------- |
125 | * The actual implementation of the sel interface. |
126 | */ |
127 | |
128 | struct sel { |
129 | void *ctx; |
130 | sel_rfd *rhead, *rtail; |
131 | sel_wfd *whead, *wtail; |
132 | }; |
133 | |
134 | struct sel_rfd { |
135 | sel *parent; |
136 | sel_rfd *prev, *next; |
137 | sel_readdata_fn_t readdata; |
138 | sel_readerr_fn_t readerr; |
139 | void *ctx; |
140 | int fd; |
141 | int frozen; |
142 | }; |
143 | |
144 | struct sel_wfd { |
145 | sel *parent; |
146 | sel_wfd *prev, *next; |
147 | sel_written_fn_t written; |
148 | sel_writeerr_fn_t writeerr; |
149 | void *ctx; |
150 | int fd; |
151 | bufchain buf; |
152 | }; |
153 | |
154 | sel *sel_new(void *ctx) |
155 | { |
156 | sel *sel = snew(struct sel); |
157 | |
158 | sel->ctx = ctx; |
159 | sel->rhead = sel->rtail = NULL; |
160 | sel->whead = sel->wtail = NULL; |
161 | |
162 | return sel; |
163 | } |
164 | |
165 | sel_wfd *sel_wfd_add(sel *sel, int fd, |
166 | sel_written_fn_t written, sel_writeerr_fn_t writeerr, |
167 | void *ctx) |
168 | { |
169 | sel_wfd *wfd = snew(sel_wfd); |
170 | |
171 | wfd->written = written; |
172 | wfd->writeerr = writeerr; |
173 | wfd->ctx = ctx; |
174 | wfd->fd = fd; |
175 | bufchain_init(&wfd->buf); |
176 | |
177 | wfd->next = NULL; |
178 | wfd->prev = sel->wtail; |
179 | if (sel->wtail) |
180 | sel->wtail->next = wfd; |
181 | else |
182 | sel->whead = wfd; |
183 | sel->wtail = wfd; |
184 | wfd->parent = sel; |
185 | |
186 | return wfd; |
187 | } |
188 | |
189 | sel_rfd *sel_rfd_add(sel *sel, int fd, |
190 | sel_readdata_fn_t readdata, sel_readerr_fn_t readerr, |
191 | void *ctx) |
192 | { |
193 | sel_rfd *rfd = snew(sel_rfd); |
194 | |
195 | rfd->readdata = readdata; |
196 | rfd->readerr = readerr; |
197 | rfd->ctx = ctx; |
198 | rfd->fd = fd; |
199 | rfd->frozen = 0; |
200 | |
201 | rfd->next = NULL; |
202 | rfd->prev = sel->rtail; |
203 | if (sel->rtail) |
204 | sel->rtail->next = rfd; |
205 | else |
206 | sel->rhead = rfd; |
207 | sel->rtail = rfd; |
208 | rfd->parent = sel; |
209 | |
210 | return rfd; |
211 | } |
212 | |
213 | size_t sel_write(sel_wfd *wfd, const void *data, size_t len) |
214 | { |
215 | bufchain_add(&wfd->buf, data, len); |
216 | return bufchain_size(&wfd->buf); |
217 | } |
218 | |
219 | void sel_wfd_setfd(sel_wfd *wfd, int fd) |
220 | { |
221 | wfd->fd = fd; |
222 | } |
223 | |
224 | void sel_rfd_setfd(sel_rfd *rfd, int fd) |
225 | { |
226 | rfd->fd = fd; |
227 | } |
228 | |
229 | void sel_rfd_freeze(sel_rfd *rfd) |
230 | { |
231 | rfd->frozen = 1; |
232 | } |
233 | |
234 | void sel_rfd_unfreeze(sel_rfd *rfd) |
235 | { |
236 | rfd->frozen = 0; |
237 | } |
238 | |
239 | int sel_wfd_delete(sel_wfd *wfd) |
240 | { |
241 | sel *sel = wfd->parent; |
242 | int ret; |
243 | |
244 | if (wfd->prev) |
245 | wfd->prev->next = wfd->next; |
246 | else |
247 | sel->whead = wfd->next; |
248 | if (wfd->next) |
249 | wfd->next->prev = wfd->prev; |
250 | else |
251 | sel->wtail = wfd->prev; |
252 | |
253 | bufchain_clear(&wfd->buf); |
254 | |
255 | ret = wfd->fd; |
256 | sfree(wfd); |
257 | return ret; |
258 | } |
259 | |
260 | int sel_rfd_delete(sel_rfd *rfd) |
261 | { |
262 | sel *sel = rfd->parent; |
263 | int ret; |
264 | |
265 | if (rfd->prev) |
266 | rfd->prev->next = rfd->next; |
267 | else |
268 | sel->rhead = rfd->next; |
269 | if (rfd->next) |
270 | rfd->next->prev = rfd->prev; |
271 | else |
272 | sel->rtail = rfd->prev; |
273 | |
274 | ret = rfd->fd; |
275 | sfree(rfd); |
276 | return ret; |
277 | } |
278 | |
279 | void sel_free(sel *sel) |
280 | { |
281 | while (sel->whead) |
282 | sel_wfd_delete(sel->whead); |
283 | while (sel->rhead) |
284 | sel_rfd_delete(sel->rhead); |
285 | sfree(sel); |
286 | } |
287 | |
288 | void *sel_get_ctx(sel *sel) { return sel->ctx; } |
289 | void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; } |
290 | void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; } |
291 | void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; } |
292 | void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; } |
293 | void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; } |
294 | |
295 | int sel_iterate(sel *sel, long timeout) |
296 | { |
297 | sel_rfd *rfd; |
298 | sel_wfd *wfd; |
299 | fd_set rset, wset; |
300 | int maxfd = 0; |
301 | struct timeval tv, *ptv; |
302 | char buf[65536]; |
303 | int ret; |
304 | |
305 | FD_ZERO(&rset); |
306 | FD_ZERO(&wset); |
307 | |
308 | for (rfd = sel->rhead; rfd; rfd = rfd->next) { |
309 | if (rfd->fd >= 0 && !rfd->frozen) { |
310 | FD_SET(rfd->fd, &rset); |
311 | if (maxfd < rfd->fd + 1) |
312 | maxfd = rfd->fd + 1; |
313 | } |
314 | } |
315 | |
316 | for (wfd = sel->whead; wfd; wfd = wfd->next) { |
317 | if (wfd->fd >= 0 && bufchain_size(&wfd->buf)) { |
318 | FD_SET(wfd->fd, &wset); |
319 | if (maxfd < wfd->fd + 1) |
320 | maxfd = wfd->fd + 1; |
321 | } |
322 | } |
323 | |
324 | if (timeout < 0) { |
325 | ptv = NULL; |
326 | } else { |
327 | ptv = &tv; |
328 | tv.tv_sec = timeout / 1000; |
329 | tv.tv_usec = 1000 * (timeout % 1000); |
330 | } |
331 | |
332 | do { |
333 | ret = select(maxfd, &rset, &wset, NULL, ptv); |
334 | } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); |
335 | |
336 | if (ret < 0) |
337 | return errno; |
338 | |
339 | /* |
340 | * Just in case one of the callbacks destroys an rfd or wfd we |
341 | * had yet to get round to, we must loop from the start every |
342 | * single time. Algorithmically irritating, but necessary |
343 | * unless we want to store the rfd structures in a heavyweight |
344 | * tree sorted by fd. And let's face it, if we cared about |
345 | * good algorithmic complexity it's not at all clear we'd be |
346 | * using select in the first place. |
347 | */ |
348 | do { |
349 | for (wfd = sel->whead; wfd; wfd = wfd->next) |
350 | if (wfd->fd >= 0 && FD_ISSET(wfd->fd, &wset)) { |
351 | void *data; |
352 | size_t len; |
353 | |
354 | FD_CLR(wfd->fd, &wset); |
355 | bufchain_prefix(&wfd->buf, &data, &len); |
356 | ret = write(wfd->fd, data, len); |
357 | assert(ret != 0); |
358 | if (ret < 0) { |
359 | if (wfd->writeerr) |
360 | wfd->writeerr(wfd, errno); |
361 | } else { |
362 | bufchain_consume(&wfd->buf, len); |
363 | if (wfd->written) |
364 | wfd->written(wfd, bufchain_size(&wfd->buf)); |
365 | } |
366 | break; |
367 | } |
368 | } while (wfd); |
369 | do { |
370 | for (rfd = sel->rhead; rfd; rfd = rfd->next) |
371 | if (rfd->fd >= 0 && !rfd->frozen && FD_ISSET(rfd->fd, &rset)) { |
372 | FD_CLR(rfd->fd, &rset); |
373 | ret = read(rfd->fd, buf, sizeof(buf)); |
374 | if (ret < 0) { |
375 | if (rfd->readerr) |
376 | rfd->readerr(rfd, errno); |
377 | } else { |
378 | if (rfd->readdata) |
379 | rfd->readdata(rfd, buf, ret); |
380 | } |
381 | break; |
382 | } |
383 | } while (rfd); |
384 | |
385 | return 0; |
386 | } |