Fix a null-dereference introduced by another mis-fix in r9919.
[sgt/putty] / contrib / cygtermd / sel.c
CommitLineData
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
26typedef struct bufchain_tag {
27 struct bufchain_granule *head, *tail;
28 size_t buffersize; /* current amount of buffered data */
29} bufchain;
30struct bufchain_granule {
31 struct bufchain_granule *next;
32 size_t buflen, bufpos;
33 char buf[BUFFER_GRANULE];
34};
35
36static void bufchain_init(bufchain *ch)
37{
38 ch->head = ch->tail = NULL;
39 ch->buffersize = 0;
40}
41
42static 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
54static size_t bufchain_size(bufchain *ch)
55{
56 return ch->buffersize;
57}
58
59static 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
96static 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
118static 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
128struct sel {
129 void *ctx;
130 sel_rfd *rhead, *rtail;
131 sel_wfd *whead, *wtail;
132};
133
134struct 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
144struct 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
154sel *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
165sel_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
189sel_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
213size_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
219void sel_wfd_setfd(sel_wfd *wfd, int fd)
220{
221 wfd->fd = fd;
222}
223
224void sel_rfd_setfd(sel_rfd *rfd, int fd)
225{
226 rfd->fd = fd;
227}
228
229void sel_rfd_freeze(sel_rfd *rfd)
230{
231 rfd->frozen = 1;
232}
233
234void sel_rfd_unfreeze(sel_rfd *rfd)
235{
236 rfd->frozen = 0;
237}
238
239int 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
260int 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
279void 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
288void *sel_get_ctx(sel *sel) { return sel->ctx; }
289void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; }
290void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; }
291void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; }
292void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; }
293void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; }
294
295int 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}