/***********************************************************
 * Threaded echo server                                    *
 * demonstration for a threaded server                     *
 * Verteilte Systeme II Kapitel 6                          *
 ***********************************************************
 * 1998 by Frank Kargl (frank.kargl@informatik.uni-ulm.de) *
 ***********************************************************
 * Usage: echos <port> (default port = 1099)               *
 ***********************************************************/

/* required by solaris */
#define _REENTRANT

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>

/* default port to use */
#define DEFPORT 1099
/* max line length */
#define MAXLINE 1024 

/***********************************************************
 *                                                         *
 * Function:                                               *
 * void usage(char* name)                                  *
 * print usage message                                     *
 * Parameters:                                             * 
 *  name - name of executable                              *
 * Return:                                                 *
 *  -                                                      *
 *                                                         *
 ***********************************************************/

void usage(char* name) {
    printf("%s - a simple echo server\n", name);
    printf("Usage: %s <port> (default port = %d)\n", name, DEFPORT);
    exit(1);
}

/***********************************************************
 *                                                         *
 * Function:                                               *
 * void echo_lines(void* arg)                              *
 * echo lines on socket                                    *
 * Parameters:                                             * 
 *  int* - socket to use (has to be casted)                *
 * Return:                                                 *
 *  -                                                      *
 *                                                         *
 ***********************************************************/

void* echo_lines(void* arg) {

    ssize_t n;				/* num bytes read */
    char line[MAXLINE];			/* line buffer */
    FILE *fpin, *fpout;			/* for buffered I/O */
    int linec = 1;			/* line count */
    int sock;				/* socket to use */

    /* extract argument */
    sock = (int)*(int *)arg;
    /* get file streams */
    fpin = fdopen(sock, "r");
    fpout = fdopen(sock, "w");
    /* set line buffered */
    setvbuf(fpin, NULL, _IOLBF, BUFSIZ);
    setvbuf(fpout, NULL, _IOLBF, BUFSIZ);

    /* loop until other end closes connection */
    while(1) {
	if (fgets(line, MAXLINE, fpin) == NULL) {
	    return NULL;		/* connection closed by foreign end */
	}
	fprintf(fpout, "line %d: %s", linec++, line);
    }
}

int main(int argc, char** argv) {
    
    int lsocket;			/* listen socket */
    int asocket;			/* accept socket */
    int port = DEFPORT;			/* port to use */
    struct sockaddr_in servaddr;	/* server address */
    struct sockaddr_in cliaddr;		/* client address */
    int cliaddr_len;			/* length of client address */
    int ret;				/* generic return value */
    pthread_t tid;			/* thread id */

    /* check for port */
    if (argc == 2) {
	port = atoi(argv[1]);
    } else if (argc != 1) {
	usage(argv[0]);
    }
    
    /* open listen socket */
    lsocket = socket(AF_INET, SOCK_STREAM, 0);
    if (lsocket == -1) {
	perror("Can't open lsocket");
	exit(1);
    }
    
    /* bind socket to port */
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);
    ret = bind(lsocket, (struct sockaddr*) &servaddr, sizeof(servaddr));
    if (ret == -1) {
	perror("Can't bind lsocket");
	exit(1);
    }
    
    /* listen to socket */
    ret = listen(lsocket, 10);
    if (ret == -1) {
	perror("Can't listen to lsocket");
	exit(1);
    }
    
    /* server loop */
    while (1) {
	
	/* accept connection */
	cliaddr_len = sizeof(cliaddr);
	asocket = accept(lsocket, (struct sockaddr*) &cliaddr, &cliaddr_len);
	if (ret == -1) {
	    perror("Can't accept on lsocket");
	    exit(1);
	}

	/* "log" connection */
	printf("Connection from %s, port %d\n",
	       	inet_ntoa(cliaddr.sin_addr),
		ntohs(cliaddr.sin_port));
	
	/* create a new thread to handle this request */
	ret = pthread_create( &tid, NULL, echo_lines, (void *) &asocket);
	if (ret != 0) {
	    printf("Thread creation error\n");
	    exit(1);
	}

    }
    
    /* when do we close lsocket ? */
    /* propably should catch signal or so */

}
