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>