Example #1
0
int chitcpd_send_and_recv_msg(int sockfd, const ChitcpdMsg *req, ChitcpdMsg **resp)
{
    int r;

    if ((r = chitcpd_send_msg(sockfd, req)) < 0)
        return r;

    if ((r = chitcpd_recv_msg(sockfd, resp)) < 0)
        return r;

    return CHITCP_OK;
}
Example #2
0
/*
 * chitcpd_server_thread_func - Server thread function
 *
 * This function will spawn a handler thread (see handler.c) for each
 * new connection on the UNIX socket.
 *
 * args: arguments (a serverinfo_t variable in server_threads_args_t)
 *
 * Returns: Nothing.
 *
 */
void* chitcpd_server_thread_func(void *args)
{
    socklen_t sunSize;
    handler_thread_t *handler_thread;
    server_thread_args_t *sta;
    serverinfo_t *si;
    handler_thread_args_t *ha;
    list_t handler_thread_list;
    int rc;
    ChitcpdMsg *req;
    ChitcpdInitArgs *init_args;
    ChitcpdConnectionType conntype;
    ChitcpdMsg resp_outer = CHITCPD_MSG__INIT;
    ChitcpdResp resp_inner = CHITCPD_RESP__INIT;

    resp_outer.code = CHITCPD_MSG_CODE__RESP;
    resp_outer.resp = &resp_inner;

    /* For naming the handler threads we create (for debugging/logging) */
    int next_thread_id = 0;
    pthread_setname_np(pthread_self(), "unix_server");

    /* Unpack arguments */
    sta = (server_thread_args_t *) args;
    si = sta->si;

    list_init(&handler_thread_list);

    struct sockaddr_un client_addr;

    /* Accept connections on the UNIX socket */
    for(;;)
    {
        socket_t client_socket;

        /* Accept a connection */
        sunSize = sizeof(client_addr);
        if ((client_socket = accept(si->server_socket, (struct sockaddr *)&client_addr, &sunSize)) == -1)
        {
            /* If accept() returns in the CHITCPD_STATE_STOPPING, we don't
             * care what the error is. We just break out of the loop and
             * initiate an orderly shutdown. */
            if(si->state == CHITCPD_STATE_STOPPING)
                break;

            /* If this particular connection fails, no need to kill the entire thread. */
            perror("Could not accept() connection on UNIX socket");
            continue;
        }

        /* We receive a single message, which has to be an INIT message */
        rc = chitcpd_recv_msg(client_socket, &req);
        if (rc < 0)
        {
            if(si->state == CHITCPD_STATE_STOPPING)
                break;
            else
            {
                chilog(ERROR, "Error when receiving lead message through UNIX socket");
                shutdown(client_socket, SHUT_RDWR);
                continue;
            }
        }

        if(req->code != CHITCPD_MSG_CODE__INIT)
        {
            chilog(ERROR, "Expected INIT message, instead got message code %i", req->code);
            chitcpd_msg__free_unpacked(req, NULL);
            shutdown(client_socket, SHUT_RDWR);
            continue;
        }

        /* Unpack INIT request */
        assert(req->init_args != NULL);
        init_args = req->init_args;

        conntype = init_args->connection_type;


        /* There are two types of connections: command connections and debug
         * connections.
         *
         * When a command connection is created, a new thread is created to
         * handle the incoming chisocket commands on that connection (socket,
         * send, recv, etc.)
         *
         * When a debug connection is created, no additional thread is necessary.
         * The connection on the UNIX socket is simply "handed off" to a
         * debug monitor that will be associated with a chiTCP socket.
         * That UNIX socket is then used to send back debug messages.
         */

        if (conntype == CHITCPD_CONNECTION_TYPE__COMMAND_CONNECTION)
        {
            /* Create arguments for handler thread */
            ha = malloc(sizeof(handler_thread_args_t));
            ha->si = si;

            handler_thread = malloc(sizeof(handler_thread_t));
            handler_thread->handler_socket = client_socket;
            pthread_mutex_init(&handler_thread->handler_lock, NULL);

            /* Create handler thread to handle this connection */
            ha->client_socket = handler_thread->handler_socket;
            ha->handler_lock = &handler_thread->handler_lock;
            snprintf(ha->thread_name, 16, "socket-layer-%d", next_thread_id++);
            if (pthread_create(&handler_thread->thread, NULL, chitcpd_handler_dispatch, ha) != 0)
            {
                perror("Could not create a worker thread");

                resp_outer.resp->ret = CHITCP_ETHREAD;
                resp_outer.resp->error_code = 0;
                rc = chitcpd_send_msg(client_socket, &resp_outer);

                free(ha);
                close(ha->client_socket);
                close(si->server_socket);
                // TODO: Perform an orderly shutdown instead of exiting
                pthread_exit(NULL);
            }
            resp_outer.resp->ret = CHITCP_OK;
            resp_outer.resp->error_code = 0;
            rc = chitcpd_send_msg(client_socket, &resp_outer);

            list_append(&handler_thread_list, handler_thread);
        }
        else if(conntype == CHITCPD_CONNECTION_TYPE__DEBUG_CONNECTION)
        {
            int debug_sockfd, debug_event_flags;
            ChitcpdDebugArgs *debug_args;

            /* Unpack debug parameter */
            assert(init_args->debug != NULL);
            debug_args = init_args->debug;

            debug_sockfd = debug_args->sockfd;
            debug_event_flags = debug_args->event_flags;

            rc = chitcpd_init_debug_connection(si, debug_sockfd, debug_event_flags, client_socket);
            if(rc == CHITCP_OK)
            {
                resp_outer.resp->ret = CHITCP_OK;
                resp_outer.resp->error_code = 0;
                rc = chitcpd_send_msg(client_socket, &resp_outer);
            }
            else
            {
                chilog(ERROR, "Error when creating debug connection for socket %i", debug_sockfd);
                resp_outer.resp->ret = CHITCP_EINIT;
                resp_outer.resp->error_code = rc;
                rc = chitcpd_send_msg(client_socket, &resp_outer);

                shutdown(client_socket, SHUT_RDWR);
            }
        }
        else
        {
            chilog(ERROR, "Received INIT message with unknown connection type %i", conntype);
            resp_outer.resp->ret = CHITCP_EINVAL;
            resp_outer.resp->error_code = 0;
            rc = chitcpd_send_msg(client_socket, &resp_outer);
            shutdown(client_socket, SHUT_RDWR);
        }

        chitcpd_msg__free_unpacked(req, NULL);
    }

    while(!list_empty(&handler_thread_list))
    {
        /* For each handler thread we spawned, we close its socket, which
         * will force the thread to exit (and we then join it).
         *
         * Note that closing a handler thread will also free up all chiTCP
         * sockets created through that thread, and will also terminate
         * all associated TCP threads.
         *
         * TODO: We should simply detach those threads, since they can exit
         * before an orderly shutdown and would be left lingering until
         * we call join here. */
        handler_thread_t *ht = list_fetch(&handler_thread_list);

        /* We don't want to shutdown the handler's socket if an operation is
         * in progress. The handler thread may have read a command, but
         * not sent a response back yet */
        pthread_mutex_lock(&ht->handler_lock);
        shutdown(ht->handler_socket, SHUT_RDWR);
        pthread_mutex_unlock(&ht->handler_lock);
        pthread_join(ht->thread, NULL);
        pthread_mutex_destroy(&ht->handler_lock);
        free(ht);
    }

    list_destroy(&handler_thread_list);

    chilog(DEBUG, "Server thread is exiting.");

    pthread_exit(NULL);
}
Example #3
0
/*
 * chitcpd_handler_dispatch - Handler thread function
 *
 * Handles a connection on chitcpd's UNIX socket, and dispatches
 * incoming requests to the appropriate function, using the
 * dispatch table defined above.
 *
 * args: arguments (in handler_thread_args_t)
 *
 * Returns: Nothing.
 *
 */
void* chitcpd_handler_dispatch(void *args)
{
    handler_thread_args_t *ha = (handler_thread_args_t *) args;

    serverinfo_t *si = ha->si;
    socket_t client_socket = ha->client_socket;
    pthread_mutex_t *handler_lock = ha->handler_lock;
    pthread_setname_np(pthread_self(), ha->thread_name);
    ChitcpdMsg *req;
    ChitcpdMsg resp_outer = CHITCPD_MSG__INIT;
    ChitcpdResp resp_inner = CHITCPD_RESP__INIT;
    bool_t done = FALSE; /* Should we keep looping? */
    int rc;

    resp_outer.code = CHITCPD_MSG_CODE__RESP;
    resp_outer.resp = &resp_inner;

    do
    {
        rc = chitcpd_recv_msg(client_socket, &req);
        if (rc < 0)
            break;

        chilog(TRACE, "Received request (code=%s)", handler_code_string(req->code));

        /* We have received a request, so we grab the handler lock to
         * prevent a race condition when the server is shutting down */
        pthread_mutex_lock(handler_lock);

        /* Call handler function using dispatch table */
        rc = handlers[req->code](si, req, &resp_inner);

        chitcpd_msg__free_unpacked(req, NULL);

        if(rc != CHITCP_OK)
        {
            chilog(ERROR, "Error when handling request.");
            /* We don't need to bail out just because one request failed */
        }

        /* Send response */
        rc = chitcpd_send_msg(client_socket, &resp_outer);

        if (resp_inner.has_buf)
        {
            /* This buffer was allocated in RECV. */
            free(resp_inner.buf.data);
            resp_inner.has_buf = FALSE;
        }
        if (resp_inner.socket_state != NULL)
        {
            /* This submessage was allocated in GET_SOCKET_STATE. */
            free(resp_inner.socket_state);
            resp_inner.socket_state = NULL;
        }
        if (resp_inner.socket_buffer_contents != NULL)
        {
            /* This submessage was allocated in GET_SOCKET_BUFFER_CONTENTS. */
            if (resp_inner.socket_buffer_contents->snd.data != NULL)
                free(resp_inner.socket_buffer_contents->snd.data);
            if (resp_inner.socket_buffer_contents->rcv.data != NULL)
                free(resp_inner.socket_buffer_contents->rcv.data);
            free(resp_inner.socket_buffer_contents);
            resp_inner.socket_buffer_contents = NULL;
        }

        /* We're done processing the request (we've run the handler and
         * we've returned a response). We can release the handler lock and,
         * if a shutdown is in progress, it will make sure it can proceed
         * safely */
        pthread_mutex_unlock(handler_lock);

        if (rc < 0)
            break;

    }
    while (!done);

    /* TODO: Be more discerning about what kind of shutdown this is */
    if(si->state == CHITCPD_STATE_STOPPING)
        chilog(DEBUG, "chiTCP daemon is stopping. Freeing open sockets for this handler...");
    else
        chilog(DEBUG, "Daemon client has disconnected. Freeing open sockets for this handler...");

    int freed_sockets = 0;

    for(int i=0; i < si->chisocket_table_size; i++)
    {
        chisocketentry_t *entry = &si->chisocket_table[i];
        if(!entry->available && entry->creator_thread == pthread_self())
        {
            chilog(DEBUG, "Freeing socket %i", i);
            /* TODO: The connection should be aborted (not closed) here.
             * However, we do not currently support the ABORT call or
             * RST's so we simply "force close" each socket. */

            if(entry->actpas_type == SOCKET_ACTIVE)
            {
                /* Any transition to CLOSED will force a termination of the TCP thread */
                chitcpd_update_tcp_state(si, entry, CLOSED);
                pthread_join(entry->socket_state.active.tcp_thread, NULL);
            }
            else if(entry->actpas_type == SOCKET_PASSIVE)
                chitcpd_free_socket_entry(si, entry);

            freed_sockets++;
            /* TODO: Close the connection */
        }
    }
    if (freed_sockets)
        chilog(DEBUG, "Done freeing open sockets.");
    else
        chilog(DEBUG, "This handler had no sockets to free.");

    chilog(DEBUG, "Handler is exiting.");
    free(args);
    return NULL;
}