| 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 | } |