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 14static 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 25static void free_state(EventTcpState *state); 26 27typedef 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***********************************************************************/ 47static void 48handle_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***********************************************************************/ 77static void 78handle_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***********************************************************************/ 132EventHandler * 133EventTcp_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***********************************************************************/ 163static void 164free_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***********************************************************************/ 185static void 186handle_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***********************************************************************/ 272static void 273handle_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***********************************************************************/ 341EventTcpState * 342EventTcp_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***********************************************************************/ 422EventTcpState * 423EventTcp_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***********************************************************************/ 503void 504EventTcp_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***********************************************************************/ 573void 574EventTcp_CancelPending(EventTcpState *s) 575{ 576 free_state(s); 577} 578