Annotation of embedaddon/libpdel/net/tcp_server.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include <sys/types.h>
! 42: #include <sys/queue.h>
! 43: #include <sys/socket.h>
! 44: #include <netinet/in.h>
! 45: #include <arpa/inet.h>
! 46:
! 47: #include <assert.h>
! 48: #include <errno.h>
! 49: #include <fcntl.h>
! 50: #include <stdio.h>
! 51: #include <stdlib.h>
! 52: #include <syslog.h>
! 53: #include <string.h>
! 54: #include <unistd.h>
! 55: #include <stdarg.h>
! 56: #include <pthread.h>
! 57:
! 58: #include "structs/structs.h"
! 59: #include "structs/type/array.h"
! 60:
! 61: #include "util/pevent.h"
! 62: #include "util/typed_mem.h"
! 63: #include "io/timeout_fp.h"
! 64: #include "net/tcp_server.h"
! 65: #include "sys/alog.h"
! 66:
! 67: /* How long to pause when we reach max # connections */
! 68: #define TCP_SERVER_PAUSE 250 /* 0.25 sec */
! 69:
! 70: /* Server state */
! 71: struct tcp_server {
! 72: struct pevent_ctx *ctx; /* event context */
! 73: struct pevent *conn_event; /* incoming connection event */
! 74: struct pevent *wait_event; /* pause timeout event */
! 75: struct sockaddr_in addr; /* server bound address */
! 76: pthread_mutex_t mutex; /* server mutex */
! 77: u_int num_conn; /* # connections */
! 78: u_int max_conn; /* max # connections */
! 79: u_int conn_timeout; /* timeout for connections */
! 80: int sock; /* listening socket */
! 81: TAILQ_HEAD(, tcp_connection) conn_list; /* connection list */
! 82: void *cookie; /* application private data */
! 83: tcp_setup_t *setup; /* connection setup handler */
! 84: tcp_handler_t *handler; /* connection handler handler */
! 85: tcp_teardown_t *teardown; /* connection teardown handlr */
! 86: const char *mtype; /* typed memory type string */
! 87: char mtype_buf[TYPED_MEM_TYPELEN];
! 88: };
! 89:
! 90: /* Connection state */
! 91: struct tcp_connection {
! 92: pthread_t tid; /* connection thread */
! 93: struct tcp_server *server; /* associated server */
! 94: struct sockaddr_in peer; /* remote side address */
! 95: u_char started; /* thread has started */
! 96: u_char destruct; /* object needs teardown */
! 97: int sock; /* connection socket */
! 98: FILE *fp; /* connection stream (unbuf) */
! 99: TAILQ_ENTRY(tcp_connection) next; /* next in connection list */
! 100: void *cookie; /* application private data */
! 101: };
! 102:
! 103: /* Internal functions */
! 104: static void *tcp_server_connection_main(void *arg);
! 105: static void tcp_server_connection_cleanup(void *arg);
! 106:
! 107: static pevent_handler_t tcp_server_accept;
! 108: static pevent_handler_t tcp_server_restart;
! 109:
! 110: /*
! 111: * Start a new TCP server
! 112: */
! 113: struct tcp_server *
! 114: tcp_server_start(struct pevent_ctx *ctx, void *cookie, const char *mtype,
! 115: struct in_addr ip, u_int16_t port, u_int max_conn, u_int conn_timeout,
! 116: tcp_setup_t *setup, tcp_handler_t *handler, tcp_teardown_t *teardown)
! 117: {
! 118: static const int one = 1;
! 119: struct tcp_server *serv = NULL;
! 120:
! 121: /* Get new object */
! 122: if ((serv = MALLOC(mtype, sizeof(*serv))) == NULL) {
! 123: alogf(LOG_ERR, "%s: %m", "malloc");
! 124: goto fail;
! 125: }
! 126: memset(serv, 0, sizeof(*serv));
! 127: serv->ctx = ctx;
! 128: serv->cookie = cookie;
! 129: serv->sock = -1;
! 130: serv->max_conn = max_conn;
! 131: serv->conn_timeout = conn_timeout;
! 132: serv->setup = setup;
! 133: serv->handler = handler;
! 134: serv->teardown = teardown;
! 135: TAILQ_INIT(&serv->conn_list);
! 136: if (mtype != NULL) {
! 137: strlcpy(serv->mtype_buf, mtype, sizeof(serv->mtype_buf));
! 138: serv->mtype = serv->mtype_buf;
! 139: }
! 140:
! 141: /* Create and bind socket */
! 142: if ((serv->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
! 143: alogf(LOG_ERR, "%s: %m", "socket");
! 144: goto fail;
! 145: }
! 146: (void)fcntl(serv->sock, F_SETFD, 1);
! 147: if (setsockopt(serv->sock, SOL_SOCKET,
! 148: SO_REUSEADDR, (char *)&one, sizeof(one)) == -1) {
! 149: alogf(LOG_ERR, "%s: %m", "setsockopt");
! 150: goto fail;
! 151: }
! 152: #ifdef SO_REUSEPORT
! 153: if (setsockopt(serv->sock, SOL_SOCKET,
! 154: SO_REUSEPORT, (char *)&one, sizeof(one)) == -1) {
! 155: alogf(LOG_ERR, "%s: %m", "setsockopt");
! 156: goto fail;
! 157: }
! 158: #endif
! 159: memset(&serv->addr, 0, sizeof(serv->addr));
! 160: #ifndef __linux__
! 161: serv->addr.sin_len = sizeof(serv->addr);
! 162: #endif
! 163: serv->addr.sin_family = AF_INET;
! 164: serv->addr.sin_port = htons(port);
! 165: serv->addr.sin_addr = ip;
! 166: if (bind(serv->sock,
! 167: (struct sockaddr *)&serv->addr, sizeof(serv->addr)) == -1) {
! 168: alogf(LOG_ERR, "%s: %m", "bind");
! 169: goto fail;
! 170: }
! 171: if (listen(serv->sock, 1024) == -1) {
! 172: alogf(LOG_ERR, "%s: %m", "listen");
! 173: goto fail;
! 174: }
! 175:
! 176: /* Accept incoming connections */
! 177: if (pevent_register(serv->ctx, &serv->conn_event, PEVENT_RECURRING,
! 178: &serv->mutex, tcp_server_accept, serv, PEVENT_READ, serv->sock)
! 179: == -1) {
! 180: alogf(LOG_ERR, "%s: %m", "pevent_register");
! 181: goto fail;
! 182: }
! 183:
! 184: /* Create mutex */
! 185: if ((errno = pthread_mutex_init(&serv->mutex, NULL)) != 0) {
! 186: alogf(LOG_ERR, "%s: %m", "pthread_mutex_init");
! 187: goto fail;
! 188: }
! 189:
! 190: /* Done */
! 191: return (serv);
! 192:
! 193: fail:
! 194: /* Clean up and return error */
! 195: if (serv != NULL) {
! 196: pevent_unregister(&serv->conn_event);
! 197: if (serv->sock != -1)
! 198: (void)close(serv->sock);
! 199: FREE(serv->mtype, serv);
! 200: }
! 201: return (NULL);
! 202: }
! 203:
! 204: /*
! 205: * Stop a TCP server
! 206: */
! 207: void
! 208: tcp_server_stop(struct tcp_server **servp)
! 209: {
! 210: struct tcp_server *const serv = *servp;
! 211: struct tcp_connection *conn;
! 212: int r;
! 213:
! 214: /* Sanity */
! 215: if (serv == NULL)
! 216: return;
! 217: *servp = NULL;
! 218:
! 219: /* Acquire mutex */
! 220: r = pthread_mutex_lock(&serv->mutex);
! 221: assert(r == 0);
! 222:
! 223: /* Stop accepting new connections */
! 224: pevent_unregister(&serv->conn_event);
! 225: pevent_unregister(&serv->wait_event);
! 226:
! 227: /* Close listen socket */
! 228: (void)close(serv->sock);
! 229: serv->sock = -1;
! 230:
! 231: /* Kill all outstanding connections */
! 232: while (!TAILQ_EMPTY(&serv->conn_list)) {
! 233:
! 234: /* Kill active connections; they will clean up themselves */
! 235: TAILQ_FOREACH(conn, &serv->conn_list, next) {
! 236: if (conn->started && conn->tid != 0) {
! 237: pthread_cancel(conn->tid);
! 238: conn->tid = 0; /* don't cancel twice */
! 239: }
! 240: }
! 241:
! 242: /* Wait for outstanding connections to complete */
! 243: r = pthread_mutex_unlock(&serv->mutex);
! 244: assert(r == 0);
! 245: usleep(100000);
! 246: r = pthread_mutex_lock(&serv->mutex);
! 247: assert(r == 0);
! 248: }
! 249:
! 250: /* Free server structure */
! 251: r = pthread_mutex_unlock(&serv->mutex);
! 252: assert(r == 0);
! 253: pthread_mutex_destroy(&serv->mutex);
! 254: FREE(serv->mtype, serv);
! 255: }
! 256:
! 257: /*
! 258: * Get server cookie.
! 259: */
! 260: void *
! 261: tcp_server_get_cookie(struct tcp_server *serv)
! 262: {
! 263: return (serv->cookie);
! 264: }
! 265:
! 266: /*
! 267: * Get connection cookie.
! 268: */
! 269: void *
! 270: tcp_connection_get_cookie(struct tcp_connection *conn)
! 271: {
! 272: return (conn->cookie);
! 273: }
! 274:
! 275: /*
! 276: * Get connection file descriptor.
! 277: */
! 278: int
! 279: tcp_connection_get_fd(struct tcp_connection *conn)
! 280: {
! 281: return (conn->sock);
! 282: }
! 283:
! 284: /*
! 285: * Get connection file stream.
! 286: */
! 287: FILE *
! 288: tcp_connection_get_fp(struct tcp_connection *conn)
! 289: {
! 290: return (conn->fp);
! 291: }
! 292:
! 293: /*
! 294: * Get peer's address.
! 295: */
! 296: void
! 297: tcp_connection_get_peer(struct tcp_connection *conn, struct sockaddr_in *sin)
! 298: {
! 299: memcpy(sin, &conn->peer, sizeof(*sin));
! 300: }
! 301:
! 302: /*********************************************************************
! 303: NEW CONNECTION ACCEPTOR
! 304: *********************************************************************/
! 305:
! 306: /*
! 307: * Accept a new incoming connection.
! 308: *
! 309: * This will be called with the server mutex acquired.
! 310: */
! 311: static void
! 312: tcp_server_accept(void *arg)
! 313: {
! 314: struct tcp_server *const serv = arg;
! 315: struct tcp_connection *conn;
! 316: socklen_t slen = sizeof(conn->peer);
! 317: struct sockaddr_in sin;
! 318: int sock;
! 319:
! 320: /* If maximum number of connections reached, pause a while */
! 321: if (serv->max_conn > 0 && serv->num_conn >= serv->max_conn) {
! 322: pevent_unregister(&serv->wait_event);
! 323: pevent_unregister(&serv->conn_event);
! 324: if (pevent_register(serv->ctx, &serv->wait_event, 0,
! 325: &serv->mutex, tcp_server_restart, serv, PEVENT_TIME,
! 326: TCP_SERVER_PAUSE) == -1)
! 327: alogf(LOG_ERR, "%s: %m", "pevent_register");
! 328: return;
! 329: }
! 330:
! 331: /* Accept next connection */
! 332: if ((sock = accept(serv->sock, (struct sockaddr *)&sin, &slen)) == -1) {
! 333: if (errno != ECONNABORTED && errno != ENOTCONN)
! 334: alogf(LOG_ERR, "%s: %m", "accept");
! 335: return;
! 336: }
! 337: (void)fcntl(sock, F_SETFD, 1);
! 338:
! 339: /* Create connection state structure */
! 340: if ((conn = MALLOC(serv->mtype, sizeof(*conn))) == NULL) {
! 341: alogf(LOG_ERR, "%s: %m", "malloc");
! 342: (void)close(sock);
! 343: return;
! 344: }
! 345: memset(conn, 0, sizeof(*conn));
! 346: conn->server = serv;
! 347: conn->sock = sock;
! 348: conn->peer = sin;
! 349:
! 350: /* Put stream on top of file descriptor */
! 351: if ((conn->fp = timeout_fdopen(conn->sock,
! 352: "r+", serv->conn_timeout)) == NULL) {
! 353: alogf(LOG_ERR, "%s: %m", "timeout_fdopen");
! 354: (void)close(conn->sock);
! 355: FREE(serv->mtype, conn);
! 356: return;
! 357: }
! 358: setbuf(conn->fp, NULL);
! 359:
! 360: /* Spawn connection thread */
! 361: if ((errno = pthread_create(&conn->tid, NULL,
! 362: tcp_server_connection_main, conn)) != 0) {
! 363: conn->tid = 0;
! 364: alogf(LOG_ERR, "%s: %m", "pthread_create");
! 365: fclose(conn->fp);
! 366: FREE(serv->mtype, conn);
! 367: return;
! 368: }
! 369:
! 370: /* Detach thread */
! 371: pthread_detach(conn->tid);
! 372:
! 373: /* Add connection to list */
! 374: TAILQ_INSERT_TAIL(&serv->conn_list, conn, next);
! 375: serv->num_conn++;
! 376: }
! 377:
! 378: /*
! 379: * Start accepting new connections after waiting a while.
! 380: *
! 381: * This will be called with the server mutex acquired.
! 382: */
! 383: static void
! 384: tcp_server_restart(void *arg)
! 385: {
! 386: struct tcp_server *const serv = arg;
! 387:
! 388: /* Accept incoming connections again */
! 389: pevent_unregister(&serv->wait_event);
! 390: pevent_unregister(&serv->conn_event);
! 391: if (pevent_register(serv->ctx, &serv->conn_event, 0,
! 392: &serv->mutex, tcp_server_accept, serv, PEVENT_READ,
! 393: serv->sock) == -1)
! 394: alogf(LOG_ERR, "%s: %m", "pevent_register");
! 395: }
! 396:
! 397: /*********************************************************************
! 398: TCP CONNECTION THREAD
! 399: *********************************************************************/
! 400:
! 401: /*
! 402: * Connection thread main entry point.
! 403: */
! 404: static void *
! 405: tcp_server_connection_main(void *arg)
! 406: {
! 407: struct tcp_connection *const conn = arg;
! 408: struct tcp_server *const serv = conn->server;
! 409:
! 410: /* Push cleanup hook */
! 411: pthread_cleanup_push(tcp_server_connection_cleanup, conn);
! 412: conn->started = 1; /* now it's ok to cancel me */
! 413:
! 414: /* Call application's setup routine */
! 415: if (serv->setup != NULL
! 416: && (conn->cookie = (*serv->setup)(conn)) == NULL)
! 417: goto done;
! 418: conn->destruct = 1;
! 419:
! 420: /* Invoke application handler */
! 421: (*serv->handler)(conn);
! 422:
! 423: done:;
! 424: /* Done */
! 425: pthread_cleanup_pop(1);
! 426: return (NULL);
! 427: }
! 428:
! 429: /*
! 430: * Cleanup routine for tcp_server_connection_main().
! 431: */
! 432: static void
! 433: tcp_server_connection_cleanup(void *arg)
! 434: {
! 435: struct tcp_connection *const conn = arg;
! 436: struct tcp_server *const serv = conn->server;
! 437: int r;
! 438:
! 439: /* Call application destructor */
! 440: if (conn->destruct && serv->teardown != NULL) {
! 441: conn->destruct = 0;
! 442: (*serv->teardown)(conn);
! 443: }
! 444:
! 445: /* Close connection */
! 446: if (conn->fp != NULL) {
! 447: (void)fclose(conn->fp);
! 448: conn->sock = -1;
! 449: }
! 450:
! 451: /* Unlink from server list */
! 452: r = pthread_mutex_lock(&serv->mutex);
! 453: assert(r == 0);
! 454: serv->num_conn--;
! 455: TAILQ_REMOVE(&serv->conn_list, conn, next);
! 456: r = pthread_mutex_unlock(&serv->mutex);
! 457: assert(r == 0);
! 458:
! 459: /* Release connection object */
! 460: FREE(serv->mtype, conn);
! 461: }
! 462:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>