~aritra1911/mc_server

6b55ae394504e30b70546c53f60886ecaa5474ab — Aritra Sarkar 1 year, 11 months ago 1b9ea61
Finish rest of the error reporting
4 files changed, 409 insertions(+), 110 deletions(-)

M events.c
M events.h
M main.c
M session.c
M events.c => events.c +306 -67
@@ 63,62 63,165 @@ static void recv_and_send(int tid, void *arg);
static int
writers_init(writers_t *writersp)
{
    char err_str[STRERROR_BUFLEN];

    writersp->count = 0;
    pthread_mutex_init(&writersp->mutex, NULL);
    pthread_cond_init(&writersp->done, NULL);

    if ( pthread_mutex_init(&writersp->mutex, NULL) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_init()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    if ( pthread_cond_init(&writersp->done, NULL) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_cond_init()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    return 0;
}

static int
writers_increment(writers_t *writersp)
{
    pthread_mutex_lock(&writersp->mutex);
    char err_str[STRERROR_BUFLEN];

    if ( pthread_mutex_lock(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_lock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    writersp->count++;
    pthread_mutex_unlock(&writersp->mutex);

    if ( pthread_mutex_unlock(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_unlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    return 0;
}

static int
writers_decrement(writers_t *writersp)
{
    pthread_mutex_lock(&writersp->mutex);
    if ( !(--writersp->count) )
        pthread_cond_signal(&writersp->done);
    pthread_mutex_unlock(&writersp->mutex);
    return 0;
    char err_str[STRERROR_BUFLEN];
    int retval = 0;

    if ( pthread_mutex_lock(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_lock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    if ( !(--writersp->count) ) {
        if ( pthread_cond_signal(&writersp->done) ) {
            get_err_str(errno, err_str);
            fprintf(stderr, " ERR : %s:%i => `pthread_cond_signal()`: %s\n",
                    __FILE__, __LINE__ - 3, err_str);
            retval = -1;
        }
    }

    if ( pthread_mutex_unlock(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_unlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }
    return retval;
}

static int
writers_wait(writers_t *writersp)
{
    pthread_mutex_lock(&writersp->mutex);
    while ( writersp->count )
        pthread_cond_wait(&writersp->done, &writersp->mutex);
    pthread_mutex_unlock(&writersp->mutex);
    return 0;
    char err_str[STRERROR_BUFLEN];
    int retval = 0;

    if ( pthread_mutex_lock(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_lock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    while ( writersp->count ) {
        if ( pthread_cond_wait(&writersp->done, &writersp->mutex) ) {
            get_err_str(errno, err_str);
            fprintf(stderr, " ERR : %s:%i => `pthread_cond_wait()`: %s\n",
                    __FILE__, __LINE__ - 3, err_str);
            retval = -1;
            break;
        }
    }

    if ( pthread_mutex_unlock(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_unlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    return retval;
}

static int
writers_destroy(writers_t *writersp)
{
    char err_str[STRERROR_BUFLEN];

    writersp->count = 0xDEAD4BAD;   /* prime */
    pthread_mutex_destroy(&writersp->mutex);
    pthread_cond_destroy(&writersp->done);

    if ( pthread_mutex_destroy(&writersp->mutex) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_mutex_destroy()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    if ( pthread_cond_destroy(&writersp->done) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_cond_destroy()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    return 0;
}

void
int
process_events(int num_events, tpool_t *tpoolp, session_t *sessionp)
{
    size_t i;
    int *fds, *fdp, listener;
    int *fds, *fdp, listener, retval;
    char err_str[STRERROR_BUFLEN];
    accept_params_t *accept_paramsp;
    ras_params_t *ras_paramsp;
    writers_t writers;

    fds = calloc(sessionp->fd_count + 1, sizeof(int));
    listener = sessionp->pfds[0].fd;
    writers_init(&writers);
    retval = 0;

    if ( writers_init(&writers) == -1 ) {
        fprintf(stderr, " ERR : %s:%i => `writers_init()`: Failed to"
                                       " initialize `writers_t` structure\n",
                __FILE__, __LINE__ - 3);
        return -1;
    }

    if ( !(fds = calloc(sessionp->fd_count + 1, sizeof(int))) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `calloc()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    /* Collect the `fds` that we need to `recv()` or `accept()` on so
     * that we can give a window of time where we do not impose a read


@@ 131,59 234,125 @@ process_events(int num_events, tpool_t *tpoolp, session_t *sessionp)
        }
    }

    pthread_rwlock_unlock(&sessionp->lock);
    if ( pthread_rwlock_unlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `calloc()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        retval = -1;
        goto free_fds;
    }

    /* for each `fd` in `fds`, we need to do a `recv()` unless it's the
     * `listener` for which we `accept()` a new client. If the thread
     * pool's work queue is full, `tpool_add_work()` shall block while
     * adding new work. Since we do not impose a read lock on `session`
     * at this window of time, even if every queued work require a
     * write lock on `session` can proceed and drain the queue and
     * write lock on `session`, they can proceed to drain the queue and
     * allow new work to be added. */

    for (fdp = fds; *fdp; fdp++) {
        if ( *fdp == listener ) {
            /* TODO: `accept()` can be called inside of
             *       `client_accept()`. */
            accept_paramsp = malloc(sizeof(accept_params_t));
            if ( !(accept_paramsp = malloc(sizeof(accept_params_t))) ) {
                get_err_str(errno, err_str);
                fprintf(stderr, " ERR : %s:%i => `malloc()`: %s\n",
                        __FILE__, __LINE__ - 3, err_str);
                retval = -1;
                break;
            }

            accept_paramsp->sessionp = sessionp;
            accept_paramsp->writersp = &writers;
            tpool_add_work(tpoolp, accept_client, accept_paramsp);
            writers_increment(&writers);

            if ( tpool_add_work(tpoolp, accept_client,
                                        accept_paramsp) == -1 ) {
                fprintf(stderr, " ERR : %s:%i => `tpool_add_work()`:"
                                      " Failed to add work to queue!\n",
                        __FILE__, __LINE__ - 4);
                retval = -1;
                break;
            }

            if ( writers_increment(&writers) == -1 ) {
                fprintf(stderr, " ERR : %s:%i => `writers_increment()`:"
                                      " Unable to increment writers count!\n",
                        __FILE__, __LINE__ - 3);
                retval = -1;
                break;
            }
        } else {
            ras_paramsp = malloc(sizeof(ras_params_t));
            if ( !(ras_paramsp = malloc(sizeof(ras_params_t))) ) {
                get_err_str(errno, err_str);
                fprintf(stderr, " ERR : %s:%i => `malloc()`: %s\n",
                        __FILE__, __LINE__ - 3, err_str);
                retval = -1;
                break;
            }

            ras_paramsp->fd = *fdp;
            ras_paramsp->sessionp = sessionp;
            ras_paramsp->writersp = &writers;

            /* This potentially holds a write lock on `session` in
             * case session data needs to be modified like for eg. a
             * nick change or client disconnect. Once it figures out
             * that `session` doesn't need any modification or such a
             * modification has already been performed, it releases
             * the write lock and switches to a read lock for
             * distribution or announcement to all the other clients.
             */
            tpool_add_work(tpoolp, recv_and_send, ras_paramsp);
            writers_increment(&writers);
             * case session data needs to be modified like for eg. in
             * case of a nick change or client disconnect. Once it
             * figures out that `session` doesn't need any modification
             * or such a modification has already been performed, it
             * releases the write lock and switches to a read lock for
             * distribution or announcement of message to all the other
             * clients. */

            if ( tpool_add_work(tpoolp, recv_and_send, ras_paramsp) == -1 ) {
                fprintf(stderr, " ERR : %s:%i => `tpool_add_work()`:"
                                      " Failed to add work to queue!\n",
                        __FILE__, __LINE__ - 3);
                retval = -1;
                break;
            }

            if ( writers_increment(&writers) == -1 ) {
                fprintf(stderr, " ERR : %s:%i => `writers_increment()`:"
                                      " Failed to increment writers count!\n",
                        __FILE__, __LINE__ - 3);
                retval = -1;
                break;
            }
        }
    }

    free(fds);

    /* Wait for all writer threads to release the lock before
     * read-locking `session` for `poll()`ing again. */
    writers_wait(&writers);

    writers_destroy(&writers);
    if ( writers_wait(&writers) == -1 ) {
        fprintf(stderr, " ERR : %s:%i => `writers_wait()`:"
                              " Failed to wait for writers to return!\n",
                __FILE__, __LINE__ - 3);
        retval = -1;
    }

free_fds:
    free(fds);

    if ( writers_destroy(&writers) == -1 ) {
        fprintf(stderr, " ERR : %s:%i => `writers_destroy()`:"
                              " Failed to destroy writers structure!\n",
                __FILE__, __LINE__ - 3);
        retval = -1;
    }

    pthread_rwlock_rdlock(&sessionp->lock);
    if ( pthread_rwlock_rdlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_rdlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        retval = -1;
    }

    return retval;
}

static void
accept_client(int thread_id, void *arg)
{
    int listener, newfd;
    int listener, newfd, ret = 0;
    char err_str[STRERROR_BUFLEN];
    struct sockaddr_storage remoteaddr;
    socklen_t addrlen;


@@ 202,40 371,68 @@ accept_client(int thread_id, void *arg)
    listener = sessionp->pfds[0].fd;
    addrlen = sizeof remoteaddr;

    newfd = accept(listener, (struct sockaddr *) &remoteaddr,
                                                 &addrlen);
    if ( (newfd = accept(listener, (struct sockaddr *) &remoteaddr,
                                                       &addrlen)) == -1 ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i => `accept()`: %s\n",
                thread_id, __FILE__, __LINE__ - 4, err_str);
        return;
    }

    pthread_rwlock_wrlock(&sessionp->lock);
    if ( pthread_rwlock_wrlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                              " `pthread_rwlock_wrlock()`: %s\n",
                thread_id, __FILE__, __LINE__ - 4, err_str);
        return;
    }

    /* Add client to `pfds` */
    add_client(newfd, sessionp);
    if ( add_client(newfd, sessionp) == -1 ) {
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i => `add_client()`:"
                              " Failed to add client!\n",
                thread_id, __FILE__, __LINE__ - 3);
        ret = 1;
    }

    if ( writers_decrement(writersp) == -1 ) {
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i => `writers_decrement()`:"
                              " Failed to decrement writers count!\n",
                thread_id, __FILE__, __LINE__ - 3);
        ret = 1;
    }

    writers_decrement(writersp);
    if ( pthread_rwlock_unlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                              " `pthread_rwlock_unlock()`: %s\n",
                thread_id, __FILE__, __LINE__ - 4, err_str);
        return;
    }

    pthread_rwlock_unlock(&sessionp->lock);
    if ( ret ) return;

    /* Convert client'd IP Address to human readable text form */
    /* Convert client's IP Address to human readable text form */
    if ( !inet_ntop(remoteaddr.ss_family,
                    get_in_addr((struct sockaddr *) &remoteaddr),
                    remoteIP, INET6_ADDRSTRLEN) ) {

        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] Failed to convert client's IP "
                        "Address to text form : `inet_ntop()`: %s\n",
                thread_id, err_str);
    }

    printf("INFO : [THREAD #%02i] New connection from client %s on socket "
                                 "%i\n", thread_id, remoteIP, newfd);
    printf("INFO : [THREAD #%02i] New connection from client %s on socket"
                                " %i\n", thread_id, remoteIP, newfd);
}

static void
recv_and_send(int thread_id, void *arg)
{
    int fd, announce;
    int fd, announce, ret = 0;
    ssize_t num_bytes;
    size_t i;
    char buf[BUFLEN] = "";
    char buf[BUFLEN] = "", err_str[STRERROR_BUFLEN];
    session_t *sessionp;
    writers_t *writersp;
    ResponseError err_code;


@@ 249,15 446,25 @@ recv_and_send(int thread_id, void *arg)
    announce = 0;

    if ( (num_bytes = recv(fd, buf, BUFLEN, 0)) == -1 ) {
        /* TODO: Handle Error */
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i => `recv()`: %s\n",
                thread_id, __FILE__, __LINE__ - 3, err_str);
        return;
    }

    pthread_rwlock_wrlock(&sessionp->lock);
    if ( pthread_rwlock_wrlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                              " `pthread_rwlock_wrlock()`: %s\n",
                thread_id, __FILE__, __LINE__ - 4, err_str);
        return;
    }

    /* Determine index from fd */
    for (i = 0; i < sessionp->fd_count; i++)
    for (i = 0; i < sessionp->fd_count; i++) {
        if ( sessionp->pfds[i].fd == fd )
            break;
    }

    if ( !num_bytes ) {
        if ( *sessionp->clients[i].nick ) {


@@ 282,23 489,55 @@ recv_and_send(int thread_id, void *arg)

        if ( buf[0] == RES_QUIT ) {
disconnect:
            remove_client(i, sessionp);
            close(fd);
            printf("DBUG : [THREAD #%02i] Socket %i disconnected\n", thread_id, fd);
            if ( remove_client(i, sessionp) == -1 ) {
                fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                                      " `remove_client()`:"
                                      " Failed to remove client!\n",
                        thread_id, __FILE__, __LINE__ - 4);
                ret = 1;
            } else {
                close(fd);
                printf("DBUG : [THREAD #%02i] Socket %i disconnected!\n",
                       thread_id, fd);
            }
        }
    }

    writers_decrement(writersp);
    pthread_rwlock_unlock(&sessionp->lock);
    if ( writers_decrement(writersp) ) {
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i => `writers_decrement()`:"
                              " Failed to decrement writers count!\n",
                thread_id, __FILE__, __LINE__ - 3);
        ret = 1;
    }

    if ( pthread_rwlock_unlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                              " `pthread_rwlock_unlock()`: %s\n",
                thread_id, __FILE__, __LINE__ - 4, err_str);
        return;
    }

    if ( ret ) return;

    if ( announce ) {
        printf("DBUG : [THREAD #%02i] Announcing...\n", thread_id);
        if ( buf[0] == RES_ERROR )
            send(fd, buf, num_bytes, 0);
        else {
        if ( buf[0] == RES_ERROR ) {
            if ( send(fd, buf, num_bytes, 0) == -1 ) {
                get_err_str(errno, err_str);
                fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                                      " `send()` to fd = %i failed: %s\n",
                        thread_id, __FILE__, __LINE__ - 4, fd, err_str);
            }
        } else {
            pthread_rwlock_rdlock(&sessionp->lock);
            for (i = 1 /* avoid listener */; i < sessionp->fd_count; i++) {
                send(sessionp->pfds[i].fd, buf, num_bytes, 0);
                if ( send(sessionp->pfds[i].fd, buf, num_bytes, 0) == -1 ) {
                    get_err_str(errno, err_str);
                    fprintf(stderr, " ERR : [THREAD #%02i] %s:%i =>"
                                          " `send()` to fd = %i failed: %s\n",
                            thread_id, __FILE__, __LINE__ - 4, fd, err_str);
                }
            }
            pthread_rwlock_unlock(&sessionp->lock);
        }

M events.h => events.h +1 -1
@@ 4,6 4,6 @@
# include "session.h"
# include "tpool.h"

void process_events(int num_events, tpool_t *, session_t *sessionp);
int process_events(int num_events, tpool_t *, session_t *sessionp);

#endif  /* __EVENTS_H_ */

M main.c => main.c +45 -20
@@ 103,24 103,21 @@ static int get_listener(prefs_t *prefs)

        /* Try to get a socket file descriptor */
        if ( (listener = socket(p->ai_family, p->ai_socktype,
                                              p->ai_protocol)) == -1 ) {
                                              p->ai_protocol)) == -1 )
            /* We'll continue, perhaps the next address in the list will
             * succeed */
            continue;
        }

        /* Reuse the `listener` sockid on restart. Without this the OS might
         * complain about "address already in use" upon restarting the server */
        if ( setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
                        &yes, sizeof yes) == -1 ) {
                        &yes, sizeof yes) == -1 )
            continue;
        }

        /* We now have a listening socket id. Let try `bind()`ing to it */
        if ( bind(listener, p->ai_addr, p->ai_addrlen) == -1 ) {
        if ( bind(listener, p->ai_addr, p->ai_addrlen) == -1 )
            /* Let's try binding to the next one in the list */
            continue;
        }

        /* We've got bound. No need to look at rest of the addresses in the
         * list */


@@ 134,7 131,7 @@ static int get_listener(prefs_t *prefs)
    if ( !p ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => Couldn't `bind()` to anything: %s\n",
                __FILE__, __LINE__ - 17, err_str);
                __FILE__, __LINE__ - 16, err_str);
        return -1;
    }



@@ 162,8 159,8 @@ static void print_usage(const char *program)
           "Options:\n"
           "        -4                  Listen on IPv4 address only.\n"
           "        -6                  Listen on IPv6 address only.\n"
           "        -h,-help,           Show this help and exit.\n"
           "        -V,-version         Show git commit hash and exit.\n",
           "        -h,--help           Show this help and exit.\n"
           "        -V,--version        Show git commit hash and exit.\n",
           program);
}



@@ 262,18 259,24 @@ int main(int argc, char **argv)
        return EXIT_SUCCESS;

    if ( (listener = get_listener(&prefs)) == -1 ) {
        fprintf(stderr, " ERR : Failed to listen on port %s\n", prefs.port);
        fprintf(stderr, " ERR : %s:%i => `get_listener()`: Failed to listen on"
                              " port %s\n",
                __FILE__, __LINE__ - 3, prefs.port);
        return EXIT_FAILURE;
    }

    if ( session_init(&session, listener) == -1 ) {
        fprintf(stderr, " ERR : Session initialization failed!\n");
        fprintf(stderr, " ERR : %s:%i => `session_init()`: Session"
                              " initialization failed!\n",
                __FILE__, __LINE__ - 3);
        return EXIT_FAILURE;
    }

    if ( !tpool_init(&tpool, NUM_THREADS  /* TODO: Accept as an arg */,
                             MAX_QUEUE_SZ, 0) ) {
        fprintf(stderr, " ERR : Failed to initialize thread pool\n");
        fprintf(stderr, " ERR : %s:%i => `tpool_init()`: Failed to initialize"
                              " thread pool!\n", __FILE__, __LINE__ - 3);
        exit_code = EXIT_FAILURE;
        goto destroy_session_and_exit;
    }



@@ 288,7 291,13 @@ int main(int argc, char **argv)
     * everytime `poll()` returns until the server receives a SIGINT.
     */

    pthread_rwlock_rdlock(&session.lock);
    if ( pthread_rwlock_rdlock(&session.lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_rdlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        exit_code = EXIT_FAILURE;
        goto destroy_tpool_and_exit;
    }

    while ( 1 ) {
        if ( (num_events = poll(session.pfds, session.fd_count, -1)) == -1 ) {


@@ 297,7 306,7 @@ int main(int argc, char **argv)
                 * any allocated memory in heap and then exit */

                printf("DBUG : `poll()` interrupted!\n");
                break;  /* Break the outer infinite while(1) loop */
                break;  /* bail out */
            }

            get_err_str(errno, err_str);


@@ 310,11 319,23 @@ int main(int argc, char **argv)
        printf("DBUG : Handling events...\n");
        /* `process_events` lets go of the read lock briefly in order
         * to add work to queue and then locks it back again. */
        process_events(num_events, &tpool, &session);
        if ( process_events(num_events, &tpool, &session) == -1 ) {
            printf(" ERR : %s:%i => `process_events()`:"
                                  " Failed to handle events!\n",
                   __FILE__, __LINE__ - 3);
            exit_code = EXIT_FAILURE;
            goto destroy_tpool_and_exit;
        }
        printf("DBUG : All events handled!\n");
    }

    pthread_rwlock_unlock(&session.lock);
    if ( pthread_rwlock_unlock(&session.lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_unlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        exit_code = EXIT_FAILURE;
        goto destroy_tpool_and_exit;
    }

    /* ^C breaks `poll()` with EINTR which then we use to break the
     * outer infinite while(1) loop and hence we can finally reach this


@@ 325,16 346,20 @@ int main(int argc, char **argv)
destroy_tpool_and_exit:
    printf("INFO : Destroying thread pool...\n");
    if ( tpool_destroy(&tpool, 1) == -1 ) {
        fprintf(stderr, " ERR : Failed to destroy thread pool!\n"
                        "     : Watch out for those memory leaks!\n");
        fprintf(stderr, " ERR : %s:%i => `tpool_destroy()`: Failed to destroy"
                              " thread pool!\n"
                        "     : Watch out for those memory leaks!\n",
                __FILE__, __LINE__ - 4);
        exit_code = EXIT_FAILURE;
    }

destroy_session_and_exit:
    printf("INFO : Destroying session...\n");
    if ( session_destroy(&session) == -1 ) {
        fprintf(stderr, " ERR : Failed to destroy session!\n"
                        "     : Watch out for those memory leaks!\n");
        fprintf(stderr, " ERR : %s:%i => `session_destroy()`: Failed to"
                              " destroy session!\n"
                        "     : Watch out for those memory leaks!\n",
                __FILE__, __LINE__ - 4);
        exit_code = EXIT_FAILURE;
    }


M session.c => session.c +57 -22
@@ 21,36 21,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>

#include "session.h"
#include "helpers.h"

int
session_init(session_t *sessionp, int listener)
{
    char err_str[STRERROR_BUFLEN];

    /* Let's set up polling, we start with a room of 2 clients + the
     * listening server, so 3. As clients join, we'll dynamically grow
     * to accomodate for them */
    sessionp->fd_count = 0;
    sessionp->fd_size = 3;

    if ( !(sessionp->pfds =
           malloc(sessionp->fd_size * sizeof(struct pollfd))) ) {
        fprintf(stderr,
                " ERR : `malloc()` failed! Did we run out of memory?\n");
        return -1;
    if ( !(sessionp->pfds = malloc(sessionp->fd_size *
                                   sizeof(struct pollfd))) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `malloc()`: %s\n",
                __FILE__, __LINE__ - 4, err_str);
        goto ret_err;
    }

    /* We use `calloc()` here, since a null ("") nick means the nick
     * is not set. We can therefore omit the step where we `memset()`
     * it to all zeroes. */
    if ( !(sessionp->clients =
           calloc(sessionp->fd_size, sizeof(client_t))) ) {
        fprintf(stderr,
                " ERR : `calloc()` failed! Did we run out of memory?\n");
        free(sessionp->pfds);
        return -1;
    if ( !(sessionp->clients = calloc(sessionp->fd_size,
                                      sizeof(client_t))) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `calloc()`: %s\n",
                __FILE__, __LINE__ - 4, err_str);
        goto free_pfds_and_ret_err;
    }

    if ( pthread_rwlock_init(&sessionp->lock, NULL) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_init()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        goto free_clients_and_ret_err;
    }

    /* Listener always gets the index 0. */


@@ 58,26 70,41 @@ session_init(session_t *sessionp, int listener)
    sessionp->pfds[0].events = POLLIN;  /* Detect incoming traffic */
    sessionp->fd_count++;

    pthread_rwlock_init(&sessionp->lock, NULL);

    return 0;

free_clients_and_ret_err:
    free(sessionp->clients);
free_pfds_and_ret_err:
    free(sessionp->pfds);
ret_err:
    return -1;
}

int
add_client(int cfd, session_t *sessionp)
{
    char err_str[STRERROR_BUFLEN];

    if ( sessionp->fd_count == sessionp->fd_size ) {
        /* We've run out of room, double `fd_size` and `realloc()` */
        sessionp->fd_size *= 2;

        printf("DBUG : We call `realloc()`\n");
        if ( !(sessionp->pfds = realloc(sessionp->pfds,
               sessionp->fd_size * sizeof(struct pollfd))) )
            goto realloc_failed;
               sessionp->fd_size * sizeof(struct pollfd))) ) {
            get_err_str(errno, err_str);
            fprintf(stderr, " ERR : %s:%i => `realloc()`: %s\n",
                    __FILE__, __LINE__ - 4, err_str);
            return -1;
        }

        if ( !(sessionp->clients = realloc(sessionp->clients,
               sessionp->fd_size * sizeof(client_t))) )
            goto realloc_failed;
               sessionp->fd_size * sizeof(client_t))) ) {
            get_err_str(errno, err_str);
            fprintf(stderr, " ERR : %s:%i => `realloc()`: %s\n",
                    __FILE__, __LINE__ - 4, err_str);
            return -1;
        }
    }

    /* Add client's fd to the array */


@@ 87,10 114,6 @@ add_client(int cfd, session_t *sessionp)
    sessionp->fd_count++;

    return 0;

realloc_failed:
    fprintf(stderr, " ERR : `realloc()` failed! Did we run out of memory?\n");
    return -1;
}

int


@@ 105,6 128,9 @@ remove_client(size_t index, session_t *sessionp)
     * by one and it will be overwritten by the next connecting
     * client. */

    if ( index >= sessionp->fd_count )
        return -1;

    if ( --sessionp->fd_count != index ) {
        sessionp->pfds[index] = sessionp->pfds[sessionp->fd_count];



@@ 119,8 145,17 @@ remove_client(size_t index, session_t *sessionp)
int
session_destroy(session_t *sessionp)
{
    pthread_rwlock_destroy(&sessionp->lock);
    char err_str[STRERROR_BUFLEN];

    free(sessionp->pfds);
    free(sessionp->clients);

    if ( pthread_rwlock_destroy(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_destroy()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        return -1;
    }

    return 0;
}