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

#include <stdlib.h>
#include <stdio.h>
#include <string.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(int sock)                               *
 * echo lines on socket                                    *
 * Parameters:                                             * 
 *  int - socket to use                                    *
 * Return:                                                 *
 *  -                                                      *
 *                                                         *
 ***********************************************************/

void echo_lines(int sock) {

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

    /* first 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;			/* 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 */

    /* 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));
	
	/* fork a new process to handle this request */
	ret = fork();
	switch (ret) {
	    case -1:	/* error */
			perror("Fork error");
	    		exit(1);
			break;
	    case 0:	/* child */
			close(lsocket);
			echo_lines(asocket);
			exit(0);
			break;
	    default:	/* parent */
			close(asocket);
	}

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

}
