blob: 679e06b06b9c9e04f25104466317a67d553aead8
1 | /*********************************************************************** |
2 | * |
3 | * event_tcp.c -- implementation of event-driven socket I/O. |
4 | * |
5 | * Copyright (C) 2001 Roaring Penguin Software Inc. |
6 | * |
7 | * This program may be distributed according to the terms of the GNU |
8 | * General Public License, version 2 or (at your option) any later version. |
9 | * |
10 | * LIC: GPL |
11 | * |
12 | ***********************************************************************/ |
13 | |
14 | static char const RCSID[] = |
15 | "$Id$"; |
16 | |
17 | #include "event_tcp.h" |
18 | #include <unistd.h> |
19 | #include <fcntl.h> |
20 | #include <sys/types.h> |
21 | #include <stdlib.h> |
22 | #include <errno.h> |
23 | #include <string.h> |
24 | |
25 | static void free_state(EventTcpState *state); |
26 | |
27 | typedef struct EventTcpConnectState_t { |
28 | int fd; |
29 | EventHandler *conn; |
30 | EventTcpConnectFunc f; |
31 | void *data; |
32 | } EventTcpConnectState; |
33 | |
34 | /********************************************************************** |
35 | * %FUNCTION: handle_accept |
36 | * %ARGUMENTS: |
37 | * es -- event selector |
38 | * fd -- socket |
39 | * flags -- ignored |
40 | * data -- the accept callback function |
41 | * %RETURNS: |
42 | * Nothing |
43 | * %DESCRIPTION: |
44 | * Calls accept; if a connection arrives, calls the accept callback |
45 | * function with connected descriptor |
46 | ***********************************************************************/ |
47 | static void |
48 | handle_accept(EventSelector *es, |
49 | int fd, |
50 | unsigned int flags, |
51 | void *data) |
52 | { |
53 | int conn; |
54 | EventTcpAcceptFunc f; |
55 | |
56 | EVENT_DEBUG(("tcp_handle_accept(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); |
57 | conn = accept(fd, NULL, NULL); |
58 | if (conn < 0) return; |
59 | f = (EventTcpAcceptFunc) data; |
60 | |
61 | f(es, conn); |
62 | } |
63 | |
64 | /********************************************************************** |
65 | * %FUNCTION: handle_connect |
66 | * %ARGUMENTS: |
67 | * es -- event selector |
68 | * fd -- socket |
69 | * flags -- ignored |
70 | * data -- the accept callback function |
71 | * %RETURNS: |
72 | * Nothing |
73 | * %DESCRIPTION: |
74 | * Calls accept; if a connection arrives, calls the accept callback |
75 | * function with connected descriptor |
76 | ***********************************************************************/ |
77 | static void |
78 | handle_connect(EventSelector *es, |
79 | int fd, |
80 | unsigned int flags, |
81 | void *data) |
82 | { |
83 | int error = 0; |
84 | socklen_t len = sizeof(error); |
85 | EventTcpConnectState *state = (EventTcpConnectState *) data; |
86 | |
87 | EVENT_DEBUG(("tcp_handle_connect(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); |
88 | |
89 | /* Cancel writable event */ |
90 | Event_DelHandler(es, state->conn); |
91 | state->conn = NULL; |
92 | |
93 | /* Timeout? */ |
94 | if (flags & EVENT_FLAG_TIMEOUT) { |
95 | errno = ETIMEDOUT; |
96 | state->f(es, fd, EVENT_TCP_FLAG_TIMEOUT, state->data); |
97 | free(state); |
98 | return; |
99 | } |
100 | |
101 | /* Check for pending error */ |
102 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
103 | state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); |
104 | free(state); |
105 | return; |
106 | } |
107 | if (error) { |
108 | errno = error; |
109 | state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); |
110 | free(state); |
111 | return; |
112 | } |
113 | |
114 | /* It looks cool! */ |
115 | state->f(es, fd, EVENT_TCP_FLAG_COMPLETE, state->data); |
116 | free(state); |
117 | } |
118 | |
119 | /********************************************************************** |
120 | * %FUNCTION: EventTcp_CreateAcceptor |
121 | * %ARGUMENTS: |
122 | * es -- event selector |
123 | * socket -- listening socket |
124 | * f -- function to call when a connection is accepted |
125 | * data -- extra data to pass to f. |
126 | * %RETURNS: |
127 | * An event handler on success, NULL on failure. |
128 | * %DESCRIPTION: |
129 | * Sets up an accepting socket and calls "f" whenever a new |
130 | * connection arrives. |
131 | ***********************************************************************/ |
132 | EventHandler * |
133 | EventTcp_CreateAcceptor(EventSelector *es, |
134 | int socket, |
135 | EventTcpAcceptFunc f) |
136 | { |
137 | int flags; |
138 | |
139 | EVENT_DEBUG(("EventTcp_CreateAcceptor(es=%p, socket=%d)\n", es, socket)); |
140 | /* Make sure socket is non-blocking */ |
141 | flags = fcntl(socket, F_GETFL, 0); |
142 | if (flags == -1) { |
143 | return NULL; |
144 | } |
145 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { |
146 | return NULL; |
147 | } |
148 | |
149 | return Event_AddHandler(es, socket, EVENT_FLAG_READABLE, |
150 | handle_accept, (void *) f); |
151 | |
152 | } |
153 | |
154 | /********************************************************************** |
155 | * %FUNCTION: free_state |
156 | * %ARGUMENTS: |
157 | * state -- EventTcpState to free |
158 | * %RETURNS: |
159 | * Nothing |
160 | * %DESCRIPTION: |
161 | * Frees all state associated with the TcpEvent. |
162 | ***********************************************************************/ |
163 | static void |
164 | free_state(EventTcpState *state) |
165 | { |
166 | if (!state) return; |
167 | EVENT_DEBUG(("tcp_free_state(state=%p)\n", state)); |
168 | if (state->buf) free(state->buf); |
169 | if (state->eh) Event_DelHandler(state->es, state->eh); |
170 | free(state); |
171 | } |
172 | |
173 | /********************************************************************** |
174 | * %FUNCTION: handle_readable |
175 | * %ARGUMENTS: |
176 | * es -- event selector |
177 | * fd -- the readable socket |
178 | * flags -- ignored |
179 | * data -- the EventTcpState object |
180 | * %RETURNS: |
181 | * Nothing |
182 | * %DESCRIPTION: |
183 | * Continues to fill buffer. Calls callback when done. |
184 | ***********************************************************************/ |
185 | static void |
186 | handle_readable(EventSelector *es, |
187 | int fd, |
188 | unsigned int flags, |
189 | void *data) |
190 | { |
191 | EventTcpState *state = (EventTcpState *) data; |
192 | int done = state->cur - state->buf; |
193 | int togo = state->len - done; |
194 | int nread = 0; |
195 | int flag; |
196 | |
197 | EVENT_DEBUG(("tcp_handle_readable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); |
198 | |
199 | /* Timed out? */ |
200 | if (flags & EVENT_FLAG_TIMEOUT) { |
201 | errno = ETIMEDOUT; |
202 | (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, |
203 | state->data); |
204 | free_state(state); |
205 | return; |
206 | } |
207 | if (state->delim < 0) { |
208 | /* Not looking for a delimiter */ |
209 | /* togo had better not be zero here! */ |
210 | nread = read(fd, state->cur, togo); |
211 | if (nread <= 0) { |
212 | /* Change connection reset to EOF if we have read at least |
213 | one char */ |
214 | if (nread < 0 && errno == ECONNRESET && done > 0) { |
215 | nread = 0; |
216 | } |
217 | flag = (nread) ? EVENT_TCP_FLAG_IOERROR : EVENT_TCP_FLAG_EOF; |
218 | /* error or EOF */ |
219 | (state->f)(es, state->socket, state->buf, done, flag, state->data); |
220 | free_state(state); |
221 | return; |
222 | } |
223 | state->cur += nread; |
224 | done += nread; |
225 | if (done >= state->len) { |
226 | /* Read enough! */ |
227 | (state->f)(es, state->socket, state->buf, done, |
228 | EVENT_TCP_FLAG_COMPLETE, state->data); |
229 | free_state(state); |
230 | return; |
231 | } |
232 | } else { |
233 | /* Looking for a delimiter */ |
234 | while ( (togo > 0) && (nread = read(fd, state->cur, 1)) == 1) { |
235 | togo--; |
236 | done++; |
237 | state->cur++; |
238 | if (*(state->cur - 1) == state->delim) break; |
239 | } |
240 | |
241 | if (nread <= 0) { |
242 | /* Error or EOF -- check for EAGAIN */ |
243 | if (nread < 0 && errno == EAGAIN) return; |
244 | } |
245 | |
246 | /* Some other error, or EOF, or delimiter, or read enough */ |
247 | if (nread < 0) { |
248 | flag = EVENT_TCP_FLAG_IOERROR; |
249 | } else if (nread == 0) { |
250 | flag = EVENT_TCP_FLAG_EOF; |
251 | } else { |
252 | flag = EVENT_TCP_FLAG_COMPLETE; |
253 | } |
254 | (state->f)(es, state->socket, state->buf, done, flag, state->data); |
255 | free_state(state); |
256 | return; |
257 | } |
258 | } |
259 | |
260 | /********************************************************************** |
261 | * %FUNCTION: handle_writeable |
262 | * %ARGUMENTS: |
263 | * es -- event selector |
264 | * fd -- the writeable socket |
265 | * flags -- ignored |
266 | * data -- the EventTcpState object |
267 | * %RETURNS: |
268 | * Nothing |
269 | * %DESCRIPTION: |
270 | * Continues to fill buffer. Calls callback when done. |
271 | ***********************************************************************/ |
272 | static void |
273 | handle_writeable(EventSelector *es, |
274 | int fd, |
275 | unsigned int flags, |
276 | void *data) |
277 | { |
278 | EventTcpState *state = (EventTcpState *) data; |
279 | int done = state->cur - state->buf; |
280 | int togo = state->len - done; |
281 | int n; |
282 | |
283 | /* Timed out? */ |
284 | if (flags & EVENT_FLAG_TIMEOUT) { |
285 | errno = ETIMEDOUT; |
286 | (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, |
287 | state->data); |
288 | free_state(state); |
289 | return; |
290 | } |
291 | |
292 | /* togo had better not be zero here! */ |
293 | n = write(fd, state->cur, togo); |
294 | |
295 | EVENT_DEBUG(("tcp_handle_writeable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); |
296 | if (n <= 0) { |
297 | /* error */ |
298 | if (state->f) { |
299 | (state->f)(es, state->socket, state->buf, done, |
300 | EVENT_TCP_FLAG_IOERROR, |
301 | state->data); |
302 | } else { |
303 | close(fd); |
304 | } |
305 | free_state(state); |
306 | return; |
307 | } |
308 | state->cur += n; |
309 | done += n; |
310 | if (done >= state->len) { |
311 | /* Written enough! */ |
312 | if (state->f) { |
313 | (state->f)(es, state->socket, state->buf, done, |
314 | EVENT_TCP_FLAG_COMPLETE, state->data); |
315 | } else { |
316 | close(fd); |
317 | } |
318 | free_state(state); |
319 | return; |
320 | } |
321 | |
322 | } |
323 | |
324 | /********************************************************************** |
325 | * %FUNCTION: EventTcp_ReadBuf |
326 | * %ARGUMENTS: |
327 | * es -- event selector |
328 | * socket -- socket to read from |
329 | * len -- maximum number of bytes to read |
330 | * delim -- delimiter at which to stop reading, or -1 if we should |
331 | * read exactly len bytes |
332 | * f -- function to call on EOF or when all bytes have been read |
333 | * timeout -- if non-zero, timeout in seconds after which we cancel |
334 | * operation. |
335 | * data -- extra data to pass to function f. |
336 | * %RETURNS: |
337 | * A new EventTcpState token or NULL on error |
338 | * %DESCRIPTION: |
339 | * Sets up a handler to fill a buffer from a socket. |
340 | ***********************************************************************/ |
341 | EventTcpState * |
342 | EventTcp_ReadBuf(EventSelector *es, |
343 | int socket, |
344 | int len, |
345 | int delim, |
346 | EventTcpIOFinishedFunc f, |
347 | int timeout, |
348 | void *data) |
349 | { |
350 | EventTcpState *state; |
351 | int flags; |
352 | struct timeval t; |
353 | |
354 | EVENT_DEBUG(("EventTcp_ReadBuf(es=%p, socket=%d, len=%d, delim=%d, timeout=%d)\n", es, socket, len, delim, timeout)); |
355 | if (len <= 0) return NULL; |
356 | if (socket < 0) return NULL; |
357 | |
358 | /* Make sure socket is non-blocking */ |
359 | flags = fcntl(socket, F_GETFL, 0); |
360 | if (flags == -1) { |
361 | return NULL; |
362 | } |
363 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { |
364 | return NULL; |
365 | } |
366 | |
367 | state = malloc(sizeof(EventTcpState)); |
368 | if (!state) return NULL; |
369 | |
370 | memset(state, 0, sizeof(EventTcpState)); |
371 | |
372 | state->socket = socket; |
373 | |
374 | state->buf = malloc(len); |
375 | if (!state->buf) { |
376 | free_state(state); |
377 | return NULL; |
378 | } |
379 | |
380 | state->cur = state->buf; |
381 | state->len = len; |
382 | state->f = f; |
383 | state->es = es; |
384 | |
385 | if (timeout <= 0) { |
386 | t.tv_sec = -1; |
387 | t.tv_usec = -1; |
388 | } else { |
389 | t.tv_sec = timeout; |
390 | t.tv_usec = 0; |
391 | } |
392 | |
393 | state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_READABLE, |
394 | t, handle_readable, |
395 | (void *) state); |
396 | if (!state->eh) { |
397 | free_state(state); |
398 | return NULL; |
399 | } |
400 | state->data = data; |
401 | state->delim = delim; |
402 | EVENT_DEBUG(("EventTcp_ReadBuf() -> %p\n", state)); |
403 | |
404 | return state; |
405 | } |
406 | |
407 | /********************************************************************** |
408 | * %FUNCTION: EventTcp_WriteBuf |
409 | * %ARGUMENTS: |
410 | * es -- event selector |
411 | * socket -- socket to read from |
412 | * buf -- buffer to write |
413 | * len -- number of bytes to write |
414 | * f -- function to call on EOF or when all bytes have been read |
415 | * timeout -- timeout after which to cancel operation |
416 | * data -- extra data to pass to function f. |
417 | * %RETURNS: |
418 | * A new EventTcpState token or NULL on error |
419 | * %DESCRIPTION: |
420 | * Sets up a handler to fill a buffer from a socket. |
421 | ***********************************************************************/ |
422 | EventTcpState * |
423 | EventTcp_WriteBuf(EventSelector *es, |
424 | int socket, |
425 | char *buf, |
426 | int len, |
427 | EventTcpIOFinishedFunc f, |
428 | int timeout, |
429 | void *data) |
430 | { |
431 | EventTcpState *state; |
432 | int flags; |
433 | struct timeval t; |
434 | |
435 | EVENT_DEBUG(("EventTcp_WriteBuf(es=%p, socket=%d, len=%d, timeout=%d)\n", es, socket, len, timeout)); |
436 | if (len <= 0) return NULL; |
437 | if (socket < 0) return NULL; |
438 | |
439 | /* Make sure socket is non-blocking */ |
440 | flags = fcntl(socket, F_GETFL, 0); |
441 | if (flags == -1) { |
442 | return NULL; |
443 | } |
444 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { |
445 | return NULL; |
446 | } |
447 | |
448 | state = malloc(sizeof(EventTcpState)); |
449 | if (!state) return NULL; |
450 | |
451 | memset(state, 0, sizeof(EventTcpState)); |
452 | |
453 | state->socket = socket; |
454 | |
455 | state->buf = malloc(len); |
456 | if (!state->buf) { |
457 | free_state(state); |
458 | return NULL; |
459 | } |
460 | memcpy(state->buf, buf, len); |
461 | |
462 | state->cur = state->buf; |
463 | state->len = len; |
464 | state->f = f; |
465 | state->es = es; |
466 | |
467 | if (timeout <= 0) { |
468 | t.tv_sec = -1; |
469 | t.tv_usec = -1; |
470 | } else { |
471 | t.tv_sec = timeout; |
472 | t.tv_usec = 0; |
473 | } |
474 | |
475 | state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_WRITEABLE, |
476 | t, handle_writeable, |
477 | (void *) state); |
478 | if (!state->eh) { |
479 | free_state(state); |
480 | return NULL; |
481 | } |
482 | |
483 | state->data = data; |
484 | state->delim = -1; |
485 | EVENT_DEBUG(("EventTcp_WriteBuf() -> %p\n", state)); |
486 | return state; |
487 | } |
488 | |
489 | /********************************************************************** |
490 | * %FUNCTION: EventTcp_Connect |
491 | * %ARGUMENTS: |
492 | * es -- event selector |
493 | * fd -- descriptor to connect |
494 | * addr -- address to connect to |
495 | * addrlen -- length of address |
496 | * f -- function to call with connected socket |
497 | * data -- extra data to pass to f |
498 | * %RETURNS: |
499 | * Nothing |
500 | * %DESCRIPTION: |
501 | * Does a non-blocking connect on fd |
502 | ***********************************************************************/ |
503 | void |
504 | EventTcp_Connect(EventSelector *es, |
505 | int fd, |
506 | struct sockaddr const *addr, |
507 | socklen_t addrlen, |
508 | EventTcpConnectFunc f, |
509 | int timeout, |
510 | void *data) |
511 | { |
512 | int flags; |
513 | int n; |
514 | EventTcpConnectState *state; |
515 | struct timeval t; |
516 | |
517 | /* Make sure socket is non-blocking */ |
518 | flags = fcntl(fd, F_GETFL, 0); |
519 | if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { |
520 | f(es, fd, EVENT_TCP_FLAG_IOERROR, data); |
521 | return; |
522 | } |
523 | |
524 | n = connect(fd, addr, addrlen); |
525 | if (n < 0) { |
526 | if (errno != EINPROGRESS) { |
527 | f(es, fd, EVENT_TCP_FLAG_IOERROR, data); |
528 | return; |
529 | } |
530 | } |
531 | |
532 | if (n == 0) { /* Connect succeeded immediately */ |
533 | f(es, fd, EVENT_TCP_FLAG_COMPLETE, data); |
534 | return; |
535 | } |
536 | |
537 | state = malloc(sizeof(*state)); |
538 | if (!state) { |
539 | f(es, fd, EVENT_TCP_FLAG_IOERROR, data); |
540 | return; |
541 | } |
542 | state->f = f; |
543 | state->fd = fd; |
544 | state->data = data; |
545 | |
546 | if (timeout <= 0) { |
547 | t.tv_sec = -1; |
548 | t.tv_usec = -1; |
549 | } else { |
550 | t.tv_sec = timeout; |
551 | t.tv_usec = 0; |
552 | } |
553 | |
554 | state->conn = Event_AddHandlerWithTimeout(es, fd, EVENT_FLAG_WRITEABLE, |
555 | t, handle_connect, |
556 | (void *) state); |
557 | if (!state->conn) { |
558 | free(state); |
559 | f(es, fd, EVENT_TCP_FLAG_IOERROR, data); |
560 | return; |
561 | } |
562 | } |
563 | |
564 | /********************************************************************** |
565 | * %FUNCTION: EventTcp_CancelPending |
566 | * %ARGUMENTS: |
567 | * s -- an EventTcpState |
568 | * %RETURNS: |
569 | * Nothing |
570 | * %DESCRIPTION: |
571 | * Cancels the pending event handler |
572 | ***********************************************************************/ |
573 | void |
574 | EventTcp_CancelPending(EventTcpState *s) |
575 | { |
576 | free_state(s); |
577 | } |
578 |