/*
 * Simple multi-thread chat server.
 *
 * wildcat - 2004
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

#include <sys/ipc.h>
#include <sys/shm.h>

#define MAX_CLIENT 5
#define MAX_TRIES  3

#define O_R 	0x1
#define O_W 	0x2
#define NONE	0x0

typedef struct
{
	int 	s;
	int	state; /* O_R  ||  O_W */
} sock;

#define SHMSZ     (sizeof(sock)*MAX_CLIENT + sizeof(int))	/* last int is used to store current number of connection */
#define CURRENT(x) *(x + SHMSZ - sizeof(int))

void syntax(char **);
void enter_loop(int *);

void init_set(sock *, int);
void delfromset(sock *, int, int);
void dump_set(sock *, int);
void addtoset(sock *, int, int);

int sendtoall(sock *, int, char *, int);
int sendtoone(sock *, int, char *, int, int);

void *process_client(void *);

/* shm things: */
char *create_shm (key_t);

cur

int main (int argc, char **argv)
{
	int ssock;		/*  serveur socket   */
	int csock[MAX_CLIENT];  /*  clients sockets  */
	int i;
	int port;
	
	struct sockaddr_in saddr;
	struct hostent     *n_saddr;

	if (argc != 3)
		syntax(argv);

	if ((port = atoi(argv[2])) == 0) {

		printf("Error, port incorrect...");
		return -1;
	}

	if ( (ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
	
		printf("Error, cannot create socket..\n");
		return -1;
	}
	
	printf("Resolv %s..\n", argv[1]);
	if ( (n_saddr = gethostbyname(argv[1])) == NULL ) {
		perror("Unable to get ip");
		return -1;
	}

	saddr.sin_family = n_saddr->h_addrtype;
	memcpy(&saddr.sin_addr.s_addr, n_saddr->h_addr, n_saddr->h_length);
	saddr.sin_port = htons(port);

	/* binding */
	printf("Trying to bind %s:%d\n", argv[1], port);
	i=MAX_TRIES;
	do {
		printf("Try %d of %d...\n", MAX_TRIES - i + 1, MAX_TRIES);
		
		if (bind(ssock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
		
			perror("Cannot bind port");
			continue;
		}
		else /* we are binding port... */
		{
			listen(ssock, 5);
			printf("Good, we listen %s:%d !\n", argv[1], port);
			enter_loop(&ssock);
			printf("Server shutdown.\n");
			return 1;
		}
		sleep(5); /* wait 5second before retrying */
		
	} while (--i) ;

	return 1;
}


void syntax (char **a)
{
  printf("Syntax: %s <ip> <port>\n", a[0]);
  exit(1);
}

void init_set(sock *set, int nb)
{
	int i;
	for (i=0;i<nb;i++)
	{
		(set+i)->s = 0;
		(set+i)->state = NONE;
	}
	return;
}

void dump_set(sock *set, int max)
{
	int i;
	printf("Dump set:\n");
	for (i=0;i<max;i++)
		printf("\t%d socket -> %d\n", i, (set + i)->s);
}

void addtoset(sock *set, int max, int v)
{
	int i;
	for (i=0;i<max;i++)
		if ((set+i)->s == 0)
		{
			(set+i)->s = v;
			(set+i)->state = NONE;
			break;
		}
	return;
}

void delfromset(sock *set, int max, int v)
{
	int i;
	printf("Closing socket %d\n", v);
	for (i=0; i<max;i++)
		if ((set+i)->s == v)
		{
			close (v);
			(set+i)->s = 0;
			(set+i)->state = NONE;
			CURRENT(cs)--;
			break;
		}
	return;
}

int sendtoone(sock *set, int max, char *buf, int size, int s)
{
	int i,j;
	printf("Sending to one: %s (%d)->%d\n", buf, size, s);
	for (i=0;i<max;i++)
		if ((set+i)->s == s)
		{
			// MAX_TRIES attempt to write
			j=MAX_TRIES;
			do
			{
				if ((set+i)->state == NONE)
				{
					(set+i)->state = O_W;
					if (write((set+i)->s, buf, size) == -1) {
					
						if (errno == EPIPE)
						{
							delfromset(set, max, (set + i)->s);
							*(set)--;
						}
					}
					(set+i)->state = NONE;
					return size;
				}
				sleep(1);
			} while (--i) ;

		}
	return -1;
}

int sendtoall(sock *set, int max, char *buf, int size)
{
	int i,j;
	printf("Sending to all: %s (%d)\n", buf, size);
	for (i=0;i<max;i++)
		if ((set+i)->s != 0)
		{
			// MAX_TRIES attempt to write
			j=MAX_TRIES;
			do
			{
				if ((set+i)->state == NONE)
				{
					(set+i)->state = O_W;
					if (write((set+i)->s, buf, size) == -1) {
					
						if (errno == EPIPE)
						{
							delfromset(set, max, (set + i)->s);
						}
					}
					(set+i)->state = NONE;
					break;
				}
				sleep(1);
			} while (--i) ;

		}
	return;
}

typedef struct
{
	int 	sock;
	key_t	key;
}client;

void enter_loop(int *ss)
{
	pthread_t 		thread[MAX_CLIENT];
    pthread_attr_t 	attr;
	int biggest = 0;
	fd_set c_set;
	fd_set s_set;
	struct timeval t_out, *to;	
	struct sockaddr c_addr;
	int s, len;
	int i, j, k, size;
	char buffer[100];
	sock *cs;
	client *c;
	key_t key = (rand() % 10000) + 1;

	printf(" creation shm avec cle: %d\n", key);
	if ((cs = (sock *)create_shm(key)) == NULL) {
		
		/* erreur SHM */
		return;
	}
	
	pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	
	init_set(cs, MAX_CLIENT);
	while(1)
	{
		FD_ZERO(&s_set);
		FD_SET(*ss, &s_set);
		if (!CURRENT(cs))
			to = NULL;
		else
		{
			t_out.tv_sec  = 0;
			t_out.tv_usec = 500;
			to = &t_out;
		}
		if (select((*ss) + 1, &s_set, NULL, NULL, to) > 0) {
			
			/* client a accepter */	
			len = sizeof(c_addr);
			if ( (s = accept((*ss), (struct sockaddr *) &c_addr, &len)) == -1) {
	
				perror("Error accept");
			}
			else
			{
				printf("New client accepted\n");
				if (CURRENT(cs) == MAX_CLIENT)
				{
					printf("Server full closing connection\n");
					write (s, "Server Full", 13);
					close (s);
				}
				else
				{
					c = (client *) malloc (sizeof(client));
					c->sock = s;
					c->key = key;
					pthread_create(&thread[CURRENT(cs)], &attr, process_client,(void*)c); 
					addtoset(cs,MAX_CLIENT ,s);
					
					CURRENT(cs)++;
				}
				dump_set(cs, MAX_CLIENT);
			}
		}
	}
}



void *process_client(void *a)
{
	client *c = (client *) a;
	int shmid;
	sock *shm;
	fd_set c_set;
	char buffer[100];
	int size;
	struct timeval t_out;

    /*
     * Locate the segment.
     */
    if ((shmid = shmget(c->key, SHMSZ, 0600)) < 0) {
        perror("shmget thread");
        pthread_exit((void *) c->sock);
    }

    /*
     * Now we attach the segment to our data space.
     */
    if ((shm = (sock *) shmat(shmid, NULL, 0)) == (void *) -1) {
        perror("shmat thread");
        pthread_exit((void *) c->sock);
    }
	
	while (1)
	{
		FD_ZERO(&c_set);
		FD_SET(c->sock , &c_set);
		t_out.tv_sec  = 5;
		t_out.tv_usec = 0;
		if ( (select (c->sock + 1, &c_set, NULL, NULL, &t_out)) > 0) 
		{ /* requete bloquante */
			
			/* read from this socket */
			bzero(buffer, sizeof(buffer));
			size = read(c->sock, &buffer, 100);
			if (!size) {
				delfromset(shm, MAX_CLIENT, c->sock);
				break;
			}
			/* send the buffer read to all */
			sendtoall(shm, MAX_CLIENT, &buffer[0], size);
		}
	}
	
	printf("Le process %d s'occupant du socket %d va crever...", getpid(), c->sock);
	delfromset(shm, MAX_CLIENT, c->sock); /* on le delete au cas ou ca serais pas déjà fait... */
	free(c);
	pthread_exit((void *) 1);
}

/*
 * return the shared memory space
 */
char *create_shm (key_t key)
{
    char c;
    int shmid;
    char *shm;

    if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0600)) < 0) 
	{
        perror("shmget pere");
        return NULL;
    }

    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("shmat pere");
        return NULL;
    }
    return shm;
}
