tcp_server.c
author viric@mandarina
Mon, 28 Apr 2008 21:37:16 +0200
changeset 89 2692e4742267
parent 53 07500c5c53cb
permissions -rw-r--r--
Moving the utils from stdinmix to tm.

/*
    Terminal Mixer - multi-point multi-user access to terminal applications
    Copyright (C) 2007  LluĂ­s Batlle i Rossell

    Please find the license in the provided COPYING file.
*/
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/select.h>

#include "main.h"
#include "handlers.h"

static int served_sockets = 0;

static int listen_socket = -1; /* not listening */
static int connected_sockets = 0;
static int *conn_sockets = 0;

static void start_listening(int new)
{
    int ls;
    struct sockaddr_in addr;
    int res;

    assert(new > 0);

    ls = socket(AF_INET, SOCK_STREAM, 0);
    if (ls == -1)
        error("Cannot create the tcp listen socket in the server");

    {
        int on = 1;
        res = setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        if (res == -1)
            error("Cannot set SO_REUSEADDR");
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(command_line.tcp_port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    res = bind(ls, (struct sockaddr *) & addr, sizeof(addr));
    if (res == -1)
        error("Error binding tcp to port %i", command_line.tcp_port);

    /* NUANCE: 0 backlog. Why should we assure future connections? */
    res = listen(ls, 0);
    if (res == -1)
        error("Error listening on the binded tcp socket");

    listen_socket = ls;
}

void s_tcp_update_served(int new)
{
    if (new > served_sockets && new > 0 )
    {
        conn_sockets = realloc(conn_sockets, sizeof(*conn_sockets) * new);
        if (listen_socket == -1) /* not listening */
        {
            start_listening(new);
        }
        served_sockets = new;
    }
    else if (new < served_sockets)
    {
        not_implemented("new < served_sockets at s_tcp_update_served");
    }
}

void s_tcp_shutdown()
{
    int i;

    if (listen_socket != -1)
    {
        close(listen_socket);
    }

    for (i=0; i < connected_sockets; ++i)
    {
        close(conn_sockets[i]);
    }
    connected_sockets = 0;
    served_sockets = 0;
}

void s_tcp_prepare_read_fdset(fd_set *read_set, int *maxfd)
{
    int i;

    FD_SET(listen_socket, read_set);
    *maxfd = max(*maxfd, listen_socket);

    for (i=0; i < connected_sockets; ++i)
    {
        /* We only accept if we don't have any
         * connetion opened. */
        FD_SET(conn_sockets[i], read_set);
        *maxfd = max(*maxfd, conn_sockets[i]);
    }
}

static void remove_conn_socket(int n)
{
    int i;

    assert(n >= 0);

    for(i=n+1; i<connected_sockets; ++i)
    {
        conn_sockets[i-1] = conn_sockets[i];
    }

    connected_sockets -= 1;

    /* We shrink the memory until we have an array for served_sockets */
    if (served_sockets <= connected_sockets)
    {
        conn_sockets = realloc(conn_sockets, sizeof(*conn_sockets)
                * connected_sockets);
    }
}

static int accept_connection(int ls)
{
    int cs;
    cs = accept(ls, 0, 0);
    if (cs == -1)
        not_implemented("accept tcp socket error check");

    /* TODO: Prepare unblocking sockets if needed */

    return cs;
}

void s_tcp_process_read_fdset(fd_set *read_set)
{
    int i;
    /* Active streams */
    for (i=0; i < connected_sockets; ++i)
    {
        if (FD_ISSET(conn_sockets[i], read_set))
        {
            int res;
            res = read(conn_sockets[i], stream_buffer,
                    stream_buffer_size);
            if (res == 0)
            {
                close(conn_sockets[i]);
                remove_conn_socket(i);
            } else
            {
                app_control_remote_send_to_stdin(stream_buffer, res);
            }
        }
    }

    /* Listen connection */
    if (FD_ISSET(listen_socket, read_set))
    {
        if (served_sockets > connected_sockets)
        {
            int s;
            s = accept_connection(listen_socket);
            conn_sockets[connected_sockets++] = s;
            welcome_new_client_socket(s);
        } else
        {
            int s;
            s = accept_connection(listen_socket);
            close(s);
        }
    }
}

void s_tcp_send_to_connected(const char *buffer, size_t size)
{
    int i;

    for (i=0; i < connected_sockets; ++i)
    {
        write(conn_sockets[i], buffer, size);
    }
}