/***********************************************************
 * display server                                          *
 * a first step towards an dsm server                      *
 * Verteilte Systeme II Kapitel 9                          *
 ***********************************************************
 * 1998 by Frank Kargl (frank.kargl@informatik.uni-ulm.de) *
 ***********************************************************
 * Usage: display <port>                                   *
 ***********************************************************/

/* required by solaris */
#define _REENTRANT

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <synch.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/mman.h>

/* default port to use */
#define DEFPORT 1099
/* string for end of communication */
#define ENDOFCOM "QUIT"

int fd;				/* filedescriptor for /dev/zero */
void* page;			/* pointer to memory page */
long pagesize;			/* size of memory page */
int port = DEFPORT;		/* port to use */
sema_t empty,full;		/* semaphores for thread synch. */

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

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

/***********************************************************
 *                                                         *
 * Function:                                               *
 * void segv(int sig)                                      *
 * segmentation violation signal handler                   *
 * Parameters:                                             * 
 *  sig - signal caught                                    *
 * Return:                                                 *
 *  -                                                      *
 *                                                         *
 ***********************************************************/

void segv(int sig) {

    void* ptr;
    int mode;
    
    fprintf(stderr, "SIGSEGV signal: setting page to RW\n");

    ptr = mmap((void *)page, pagesize, PROT_READ|PROT_WRITE,
	    MAP_SHARED|MAP_FIXED, fd, 0);
    
    /* reinstall signal handler */
    signal(SIGSEGV,segv);

}

/***********************************************************
 *                                                         *
 * Function:                                               *
 * void* producer(void* arg)                               *
 * read messages from udp socket and place them on         *
 * shared memory page                                      *
 * Parameters:                                             * 
 *  void* - unused (should be NULL)                        *
 * Return:                                                 *
 *  void* - unused (always NULL)                           *
 *                                                         *
 ***********************************************************/

void* producer(void* arg) {

    int sock;					/* socket to use */
    int ret;					/* generic return value */
    struct sockaddr_in clientaddr, servaddr;	/* client/server data */
    int clilen;					/* length of clientaddr */

    /* create UDP socket */
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
	perror("Can't open socket");
	exit(1);
    }
    servaddr.sin_family		= AF_INET;
    servaddr.sin_addr.s_addr	= htonl(INADDR_ANY);
    servaddr.sin_port		= htons(port);
    ret = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
    if (ret == -1) {
	perror("Can't bind lsocket");
	exit(1);
    }
    clilen = sizeof(clientaddr);

    printf("Accepting messages\n");

    /* wait for messages */
    while(1) {
	/* wait for empty buffer */
	sema_wait(&empty);
	/* clear page */
	bzero(page, pagesize);
	/* recv one page */
	ret = recvfrom(sock, page, pagesize, 0,
	       	(struct sockaddr *)&clientaddr, &clilen);
	if (ret == -1) {
	    fprintf(stderr, "errno = %d\n", errno);
	    perror("recvfrom error");
	    exit(1);
	}
	printf("Message from %s: ", inet_ntoa(clientaddr.sin_addr));

	/* announce new data */
	sema_post(&full);

	/* check for end of communication */
	if (!strncasecmp(page, ENDOFCOM, strlen(ENDOFCOM))) {
	    break;
	}
    }

    printf("\n QUIT received\n");

    close(sock);
    return NULL;

}

/***********************************************************
 *                                                         *
 * Function:                                               *
 * void* consumer(void* arg)                               *
 * read message from shared memory page and display        *
 * Parameters:                                             * 
 *  void* - unused (should be NULL)                        *
 * Return:                                                 *
 *  void* - unused (always NULL)                           *
 *                                                         *
 ***********************************************************/

void* consumer(void* arg) {

    void* ptr;

    /* endless loop */
    while(1) {
	/* wait for message */
	sema_wait(&full);
	/* check for end of communication */
	if (!strncasecmp(page, ENDOFCOM, strlen(ENDOFCOM))) {
	    break;
	}
	/* print message */
	printf("%s\n", page);

	/* set page to read-only */
	ptr = mmap((void *)page, pagesize, PROT_READ,
	    MAP_SHARED|MAP_FIXED, fd, 0);

	/* announce free buffer */
	sema_post(&empty);
    }
    
    return NULL;
    
}

int main(int argc, char** argv) {
    
    pthread_t tid1, tid2;			/* thread ids */
    int ret;					/* just a return value*/

    /* check for port */
    if (argc == 2) {
	port = atoi(argv[1]);
    } else if (argc != 1) {
	usage(argv[0]);
    }

    /* get this system's pagesize */
    pagesize = sysconf(_SC_PAGESIZE);

    /* create emtpy memory page */
    fd = open("/dev/zero", O_RDWR);
    if (fd == -1) {
	perror("can't open /dev/zero\n");
	exit(1);
    }
    page = mmap((void *)0, pagesize, PROT_READ, MAP_SHARED, fd, 0);
    if (page == MAP_FAILED) {
	perror("can't map memory page\n");
    }
    page='\0';

    /* init semaphores */
    sema_init(&empty, 1, USYNC_THREAD, NULL);
    sema_init(&full, 0, USYNC_THREAD, NULL);

    /* start consumer & producer threads */
    ret = pthread_create( &tid1, NULL, consumer, NULL);
    if (ret != 0) {
	fprintf(stderr, "Thread creation error\n");
	exit(1);
    }
    ret = pthread_create( &tid2, NULL, producer, NULL);
    if (ret != 0) {
	fprintf(stderr, "Thread creation error\n");
	exit(1);
    }

    /* install signal handler */
    signal(SIGSEGV,segv);

    /* wait for threads to finish */
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;

}
