server_run.c 2.55 KB
#include <sys/select.h> /* for select system call and related */
#include <string.h>     /* for memset and stuff */
#include <stdlib.h>     /* for exit */
#include <errno.h>      /* for errno */
#include <unistd.h>

#include "include/logger.h"
#include "include/server.h"
#include "include/socket.h"
#include "include/signalHandling.h"

#undef  MAX
#define MAX(x,y) ((x) > (y) ? (x) : (y))

static
fd_set
server_select(SERVER this) {
	fd_set rfds;

	memcpy(&rfds, &(this->fdset), sizeof(fd_set));

	/*
	 * wait for handles to become ready
	 */
	if (-1 == select((this->max_fd)+1, &rfds, NULL, NULL, NULL))
	{
		switch (errno) {
			default:
			case EBADF:
			case EINVAL:
			case ENOMEM:
				doShutdown = 1;
				/* Fallthrough */

			case EINTR:
				logger_log(this->logger, LOGGER_CRIT,
						"select systemcall failed: [%s] - service terminated",
						strerror(errno));
				exit(EXIT_FAILURE); /* @TODO do real shutdown here */
		}
	}

	return rfds;
}

static
void
server_handle_accept(SERVER this, fd_set * rfds)
{
	if (FD_ISSET(this->sock->handle, rfds)) {
		int  fd;
		char remoteAddr[16] = "";
		SOCK acc;

		acc = sock_accept(this->sock, remoteAddr);

		if (-1 != acc->handle) {
			((this->conns)[acc->handle].sock)->handle = fd; // save the socket handle
			FD_SET(fd, &(this->fdset));
			this->max_fd = MAX(fd, this->max_fd);
		} else {
			delete(&acc);
		}

		FD_CLR(this->sock->handle, rfds);
	}
}

static
void
server_close_conn(SERVER this, unsigned int handle)
{
	delete(&((this->conns)[handle].sock));
	FD_CLR(handle, &(this->fdset));
}

static
int
server_read(SERVER this, fd_set * rfds)
{
	unsigned int i;
	size_t _read;
	char buffer[1024];

	for (i=3; i<=this->max_fd; i++) {
		if (FD_ISSET(i, rfds)) {
			memset(buffer, 0, 1024);
			switch (_read = read(i, buffer, 1023)) {
				case 0:
					/*
					 * normal close: write remaining data
					 */
					/* FALLTHROUGH */

				case -1: 
					/*
					 * read failure / close connection
					 * FALLTHROUGH
					 */
					server_close_conn(this, i);
					break;

				default:
					if (NULL != this->read_hook) {
						this->read_hook(buffer);
					}
					break;
			}
		}
	}
	
	return 0;
}

void
server_run(SERVER this)
{
	/*
	 * @TODO again a hint...add verbosity to logger....
	 */
    logger_log(this->logger, LOGGER_INFO, "service started");

    while (!doShutdown) /* until error or signal  */
    {
        fd_set rfds;
        int    i;

		rfds = server_select(this);

        /*
         * handle accept
         */
		server_handle_accept(this, &rfds);

        /* handle reads */
		server_read(this, &rfds);
    }
}

// vim: set ts=4 sw=4: