Beispiel #1
0
/* clear SIGCHLD flag and adjust context if needed
 */
static void
wait_children( struct server_ctx* ctx, int options )
{
    int status, n = 0;
    pid_t pid;

    assert( ctx );
    if (0 == g_childexit) {
        TRACE( (void)tmfputs ("No children exited since last check\n",
                g_flog) );
        return;
    }

    g_childexit = 0;

    TRACE( (void)tmfputs ("Waiting on exited children\n", g_flog) );
    while( 0 < (pid = waitpid( -1, &status, options )) ) {
        TRACE( (void)tmfprintf( g_flog, "Client [%d] has exited.\n", pid) );
        delete_client( ctx, pid );
        ++n;
    }

    if( (-1 == pid) && ( ECHILD != errno ) ) {
        mperror(g_flog, errno, "%s: waitpid", __func__);
    }

    if (n > 0) {
        TRACE( (void)tmfprintf (g_flog, "Cleaned up %d children, "
            "%ld still running\n", n, (long)(ctx->clmax - ctx->clfree)) );
    }

    return;
}
Beispiel #2
0
/* initialize incoming-stream context:
 *      set data type (if possible) and flags
 */
int
init_dstream_ctx( struct dstream_ctx* ds, const char* cmd, const char* fname,
                  ssize_t nmsgs )
{
    extern const char       CMD_UDP[];
    extern const size_t     CMD_UDP_LEN;
    extern const char       CMD_RTP[];
    extern const size_t     CMD_RTP_LEN;

    assert( ds && cmd && (nmsgs > 0) );

    ds->flags = 0;
    ds->pkt = NULL;
    ds->max_pkt = ds->pkt_count = 0;
    ds->mtu = ETHERNET_MTU;

    if( NULL != fname ) {
        ds->stype = UPXDT_UNKNOWN;
        ds->flags |= (F_CHECK_FMT | F_FILE_INPUT);
        TRACE( (void)tmfputs( "File stream, RTP check enabled\n", g_flog ) );
    }
    else if( 0 == strncmp( cmd, CMD_UDP, CMD_UDP_LEN ) ) {
        ds->stype = UPXDT_UNKNOWN;
        ds->flags |= F_CHECK_FMT;
        TRACE( (void)tmfputs( "UDP stream, RTP check enabled\n", g_flog ) );
    }
    else if( 0 == strncmp( cmd, CMD_RTP, CMD_RTP_LEN ) ) {
        ds->stype = UPXDT_RTP_TS;
        ds->flags |= F_SCATTERED;
        TRACE( (void)tmfputs( "RTP (over UDP) stream assumed,"
                    " no checks\n", g_flog ) );
    }
    else {
        TRACE( (void)tmfprintf( g_flog, "%s: "
                    "Irrelevant command [%s]\n", __func__, cmd) );
        return -1;
    }

    ds->pkt = calloc( nmsgs, sizeof(ds->pkt[0]) );
    if( NULL == ds->pkt ) {
        mperror( g_flog, errno, "%s: calloc", __func__ );
        return -1;
    }

    ds->max_pkt = nmsgs;
    return 0;
}
Beispiel #3
0
/* read data from source, determine underlying protocol
 * (if not already known); and process the packet
 * if needed (for RTP - register packet)
 *
 * return the number of octets read from the source
 * into the buffer
 */
static ssize_t
read_packet( struct dstream_ctx* spc, int fd, char* buf, size_t len )
{
    ssize_t n = -1;
    size_t chunk_len = len;

    assert( spc && buf && len );
    assert( fd > 0 );

    /* if *RAW* data specified - read AS IS
     * and exit */
    if( UPXDT_RAW == spc->stype ) {
        return read_buf( fd, buf, len, g_flog );
    }

    /* if it is (or *could* be) RTP, read only MTU bytes
     */
    if( (spc->stype == UPXDT_RTP_TS) || (spc->flags & F_CHECK_FMT) )
        chunk_len = (len > spc->mtu) ? spc->mtu : len;

#ifdef UDPXY_FILEIO
    if( spc->flags & F_FILE_INPUT ) {
        assert( !buf_overrun( buf, len, 0, chunk_len, g_flog ) );
        n = read_frecord( fd, buf, chunk_len, &(spc->stype), g_flog );
        if( n <= 0 ) return n;
    }
    else
#endif /* UDPXY_FILEIO */
    {
        assert( !buf_overrun(buf, len, 0, chunk_len, g_flog) );
        n = read_buf( fd, buf, chunk_len, g_flog );
        if( n <= 0 ) return n;
    }

    if( spc->flags & F_CHECK_FMT ) {
        spc->stype = get_mstream_type( buf, n, g_flog );
        switch (spc->stype) {
            case UPXDT_RTP_TS:
                /* scattered: exclude RTP headers */
                spc->flags |= F_SCATTERED; break;
            case UPXDT_TS:
                spc->flags &= ~F_SCATTERED; break;
            default:
                spc->stype = UPXDT_RAW;
                TRACE( (void)tmfputs( "Unrecognized stream type\n", g_flog ) );
                break;
        } /* switch */

        TRACE( (void)tmfprintf( g_flog, "Established stream as [%s]\n",
               fmt2str( spc->stype ) ) );

        spc->flags &= ~F_CHECK_FMT;
    }

    if( spc->flags & F_SCATTERED )
        if( -1 == register_packet( spc, buf, n ) ) return -1;

    return n;
}
Beispiel #4
0
/* register received packet into registry (for scattered output)
 */
static int
register_packet( struct dstream_ctx* spc, char* buf, size_t len )
{
    struct iovec* new_pkt = NULL;
    static const int DO_VERIFY = 1;

    void* new_buf = buf;
    size_t new_len = len;

    assert( spc->max_pkt > 0 );

    /* enlarge packet registry if needed */
    if( spc->pkt_count >= spc->max_pkt ) {
        spc->max_pkt <<= 1;
        spc->pkt = realloc( spc->pkt, spc->max_pkt * sizeof(spc->pkt[0]) );
        if( NULL == spc->pkt ) {
            mperror( g_flog, errno, "%s: realloc", __func__ );
            return -1;
        }

        TRACE( (void)tmfprintf( g_flog, "RTP packet registry "
                "expanded to [%lu] records\n", (u_long)spc->max_pkt ) );
    }

    /* put packet info into registry */

    new_pkt = &(spc->pkt[ spc->pkt_count ]);

    /*
    TRACE( (void)tmfprintf( stderr, "IN: packet [%lu]: buf=[%p], len=[%lu]\n",
                (u_long)spc->pkt_count, (void*)buf, (u_long)len ) );
    */

    if( 0 != RTP_process( &new_buf, &new_len, DO_VERIFY, g_flog ) ) {
        TRACE( (void)tmfputs("register packet: dropping\n", g_flog) );
        spc->flags |= F_DROP_PACKET;

        return 0;
    }

    new_pkt->iov_base = new_buf;
    new_pkt->iov_len = new_len;

    /*
    TRACE( (void)tmfprintf( stderr, "OUT: packet [%lu]: buf=[%p], len=[%lu]\n",
                (u_long)spc->pkt_count, new_pkt->iov_base,
                (u_long)new_pkt->iov_len ) );
    */

    spc->pkt_count++;
    return 0;
}
Beispiel #5
0
/* print out command-line
 */
void
printcmdln( FILE* stream, const char* msg,
        int argc, char* const argv[] )
{
    int i = 0;

    assert( stream );

    if( msg )
        (void)tmfprintf( stream, "%s: ", msg );
    else
        (void)tmfputs( "", stream );

    for( i = 0; i < argc; ++i )
        (void)fprintf( stream, "%s ", argv[i] );
    (void)fputc( '\n', stream );
}
Beispiel #6
0
void
accept_requests (int sockfd, tmfd_t* asock, size_t* alen)
{
    int                 new_sockfd = -1, err = 0, peer_port = -1,
                        wmark = g_uopt.rcv_lwmark;
    size_t              nmax = *alen, naccepted = 0;
    struct sockaddr_in  cliaddr;
    a_socklen_t         addrlen = sizeof (cliaddr);
    char                peer_addr [128] = "#undef#";
    static const int    YES = 1;

    while (naccepted < nmax) {
        TRACE( (void)tmfputs ("Accepting new connection\n", g_flog) );

        new_sockfd = accept (sockfd, (struct sockaddr*)&cliaddr, &addrlen );
        if (-1 == new_sockfd) {
            err = errno;
            if ((EWOULDBLOCK == err) || (EAGAIN == err)) {
                TRACE((void)tmfputs ("Nothing more to accept\n", g_flog));
                break;
            }
            if ((ECONNABORTED == err) || (ECONNRESET == err) || (EPROTO == err)) {
                TRACE( (void)tmfprintf (g_flog, "Connection aborted/reset "
                    "at accept point, errno=%d\n", err) );
                continue;
            }

            mperror(g_flog, err, "%s: accept", __func__);
            break;
        }

        if (0 != set_nblock (new_sockfd, 1)) {
            (void) close (new_sockfd); /* TODO: error-aware close */
            continue;
        }
        /*
        if (0 != set_timeouts(new_sockfd, new_sockfd, g_uopt.sr_tmout, 0,
            g_uopt.sw_tmout, 0)) {
            (void) close (new_sockfd);
            continue;
        }
        */
        if (wmark > 0) {
            if (0 != setsockopt (new_sockfd, SOL_SOCKET, SO_RCVLOWAT,
                    (char*)&wmark, sizeof(wmark))) {
                mperror (g_flog, errno, "%s: setsockopt SO_RCVLOWAT [%d]",
                    __func__, wmark);
                (void) close (new_sockfd); /* TODO: error-aware close */
                continue;
            } else {
                TRACE( (void)tmfprintf (g_flog, "Receive LOW WATERMARK [%d] applied "
                    "to newly-accepted socket [%d]\n", wmark, new_sockfd) );
            }
        }

        if (g_uopt.tcp_nodelay) {
            if (0 != setsockopt(new_sockfd, IPPROTO_TCP,
                TCP_NODELAY, &YES, sizeof(YES))) {
                    mperror(g_flog, errno, "%s setsockopt TCP_NODELAY",
                        __func__);
            }
        }

        asock [naccepted].fd = new_sockfd;
        asock [naccepted].atime = time (NULL);
        ++naccepted;

        (void) get_peerinfo (new_sockfd, peer_addr,
                sizeof(peer_addr)-1, &peer_port);

        TRACE( (void)tmfprintf( g_flog, "Accepted socket=[%d] from %s:%d "
            "n=%ld/nmax=%ld\n", new_sockfd, peer_addr, peer_port,
            (long)naccepted, (long)nmax) );
    } /* while */

    if (naccepted >= nmax) {
        (void)tmfprintf (g_flog, "Accept limit max=[%d] reached, "
            "%ld already accepted", (long)nmax, (long)naccepted);
    }

    *alen = naccepted;
    TRACE( (void)tmfprintf (g_flog, "%s: Sockets accepted: [%ld]\n",
        __func__, (long)naccepted));
    return;
}
Beispiel #7
0
void
process_requests (tmfd_t* asock, size_t *alen, fd_set* rset, struct server_ctx* srv)
{
    size_t nmax = *alen, i = 0, nserved = 0;
    int rc = 0, served = 0;
    char param[ 128 ] = "\0";
    time_t now = time (NULL);

    /* uncomment to DEBUG */
    TRACE( print_fds (g_flog, "pre-process sockets", asock, nmax) );

    for (; i < nmax; ++i, served = 0) {
        assert (asock[i].fd >= 0);
        assert (asock[i].atime > 0);

        do {
            /* not selected - yet try to time it out */
            if (!FD_ISSET(asock[i].fd, rset)) {
                if ((asock[i].atime + g_uopt.ssel_tmout) < now) {
                    TRACE( (void)tmfprintf (g_flog,
                        "%s: accepted socket [%ld] timed out\n",
                        __func__, (long)asock[i].fd) );
                    ++served;  /* timed out - must close */
                }
                break;
            }

            /* selected */
            TRACE( (void)tmfprintf (g_flog, "acting on accepted socket "
                "[%d] (%d/%d)\n", asock[i].fd, i+1, nmax) );

            ++served;  /* selected - must close regardless */
            rc = read_command( asock[i].fd, srv->cmd, sizeof(srv->cmd),
                    param, sizeof(param) );
            if( 0 != rc ) break;

            rc = process_command (asock[i].fd, srv, param, sizeof(param) );
        } while (0);

        if (0 != rc) {
            TRACE( (void)tmfprintf (g_flog, "error [%d] processing "
                "client socket [%d]\n", rc, asock[i]));
        }

        TRACE( (void)tmfprintf (g_flog, "%s: %s accepted "
            "socket [%d]\n", __func__, (served ? "closing" : "skipping"),
            asock[i].fd) );

        if (served) {
            (void) close (asock[i].fd);
            asock[i].fd = -1;
            ++nserved;
        }
    } /* for */

    TRACE( (void)tmfprintf (g_flog, "Processed [%ld/%ld] accepted sockets\n",
        (long)nserved, (long)nmax) );
    TRACE( print_fds (g_flog, "newly-accepted sockets", asock, nmax) );

    if (nserved >= nmax) {
        *alen = 0;
        TRACE( (void)tmfputs ("All accepted sockets processed\n", g_flog) );
    }
    else {
        shrink_asock (asock, alen, nserved);
    }

    return;
}
Beispiel #8
0
/* process client requests */
int
srv_loop( const char* ipaddr, int port,
             const char* mcast_addr )
{
    int                 rc, maxfd, err, nrdy, i;
    fd_set              rset;
    struct timeval      tmout, idle_tmout, *ptmout = NULL;
    tmfd_t              *asock = NULL;
    size_t              n = 0, nasock = 0, max_nasock = LQ_BACKLOG;
    sigset_t            oset, bset;

    static const long IDLE_TMOUT_SEC = 30;

    assert( (port > 0) && mcast_addr && ipaddr );

    (void)tmfprintf( g_flog, "Server is starting up, max clients = [%u]\n",
                        g_uopt.max_clients );
    asock = calloc (max_nasock, sizeof(*asock));
    if (!asock) {
        mperror (g_flog, ENOMEM, "%s: calloc", __func__);
        return ERR_INTERNAL;
    }

    init_server_ctx( &g_srv, g_uopt.max_clients,
            (ipaddr[0] ? ipaddr : "0.0.0.0") , (uint16_t)port, mcast_addr );

    g_srv.rcv_tmout = (u_short)g_uopt.rcv_tmout;
    g_srv.snd_tmout = RLY_SOCK_TIMEOUT;

    /* NB: server socket is non-blocking! */
    if( 0 != (rc = setup_listener( ipaddr, port, &g_srv.lsockfd,
            g_uopt.lq_backlog )) ) {
        return rc;
    }

    sigemptyset (&bset);
    sigaddset (&bset, SIGINT);
    sigaddset (&bset, SIGQUIT);
    sigaddset (&bset, SIGCHLD);
    sigaddset (&bset, SIGTERM);

    (void) sigprocmask (SIG_BLOCK, &bset, &oset);

    TRACE( (void)tmfprintf( g_flog, "Entering server loop [%s]\n",
        SLOOP_TAG) );
    while (1) {
        FD_ZERO( &rset );
        FD_SET( g_srv.lsockfd, &rset );
        FD_SET( g_srv.cpipe[0], &rset );

        maxfd = (g_srv.lsockfd > g_srv.cpipe[0] ) ? g_srv.lsockfd : g_srv.cpipe[0];
        for (i = 0; (size_t)i < nasock; ++i) {
            assert (asock[i].fd >= 0);
            FD_SET (asock[i].fd, &rset);
            if (asock[i].fd > maxfd) maxfd = asock[i].fd;
        }

        /* if there are accepted sockets - apply specified time-out
         */
        tmout.tv_sec = g_uopt.ssel_tmout;
        tmout.tv_usec = 0;

        idle_tmout.tv_sec = IDLE_TMOUT_SEC;
        idle_tmout.tv_usec = 0;

        /* enforce *idle* select(2) timeout to alleviate signal contention */
        ptmout = ((nasock > 0) && (g_uopt.ssel_tmout > 0)) ? &tmout : &idle_tmout;

        TRACE( (void)tmfprintf( g_flog, "Waiting for input from [%ld] fd's, "
            "%s timeout\n", (long)(2 + nasock), (ptmout ? "with" : "NO")));

        if (ptmout && ptmout->tv_sec) {
            TRACE( (void)tmfprintf (g_flog, "select() timeout set to "
            "[%ld] seconds\n", ptmout->tv_sec) );
        }

        (void) sigprocmask (SIG_UNBLOCK, &bset, NULL);
        if( must_quit() ) {
            TRACE( (void)tmfputs( "Must quit now\n", g_flog ) );
            rc = 0; break;
        }

        nrdy = select (maxfd + 1, &rset, NULL, NULL, ptmout);
        err = errno;
        (void) sigprocmask (SIG_BLOCK, &bset, NULL);

        if( must_quit() ) {
            TRACE( (void)tmfputs( "Must quit now\n", g_flog ) );
            rc = 0; break;
        }
        wait_terminated( &g_srv );

        if( nrdy < 0 ) {
            if (EINTR == err) {
                TRACE( (void)tmfputs ("INTERRUPTED, yet "
                        "will continue.\n", g_flog)  );
                rc = 0; continue;
            }

            mperror( g_flog, err, "%s: select", __func__ );
            break;
        }

        TRACE( (void)tmfprintf (g_flog, "Got %ld requests\n", (long)nrdy) );
        if (0 == nrdy) {    /* time-out */
            tmout_requests (asock, &nasock);
            rc = 0; continue;
        }

        if( FD_ISSET(g_srv.cpipe[0], &rset) ) {
            (void) tpstat_read( &g_srv );
            if (--nrdy <= 0) continue;
        }

        if ((0 < nasock) &&
                 (0 < (nrdy - (FD_ISSET(g_srv.lsockfd, &rset) ? 1 : 0)))) {
            process_requests (asock, &nasock, &rset, &g_srv);
            /* n now contains # (yet) unprocessed accepted sockets */
        }

        if (FD_ISSET(g_srv.lsockfd, &rset)) {
            if (nasock >= max_nasock) {
                (void) tmfprintf (g_flog, "Cannot accept sockets beyond "
                    "the limit [%ld/%ld], skipping\n",
                    (long)nasock, (long)max_nasock);
            }
            else {
                n = max_nasock - nasock; /* append asock */
                accept_requests (g_srv.lsockfd, &(asock[nasock]), &n);
                nasock += n;
            }
        }
    } /* server loop */

    TRACE( (void)tmfprintf( g_flog, "Exited server loop [%s]\n", SLOOP_TAG) );

    for (i = 0; (size_t)i < nasock; ++i) {
        if (asock[i].fd > 0) (void) close (asock[i].fd);
    }
    free (asock);

    /* receive additional (blocked signals) */
    (void) sigprocmask (SIG_SETMASK, &oset, NULL);
    wait_terminated( &g_srv );
    terminate_all_clients( &g_srv );
    wait_all( &g_srv );

    if (0 != close( g_srv.lsockfd )) {
        mperror (g_flog, errno, "server socket close");
    }

    free_server_ctx( &g_srv );

    (void)tmfprintf( g_flog, "Server exits with rc=[%d]\n", rc );
    return rc;
}