//
//  server.c
//
//  Created by Julien-Pierre Averous on 16/10/06.
//  Copyright 2006 SourceMac.com. All rights reserved.
//
//


#include "server.h"
#include "hash.h"


static pthread_t	thrd;
static int			sock;
static int			csock;


static int			conf_dumpfree = 0;
static int			fd_freeautodump = -1;


/*
** Server commands prototypes
*/
static void	com_count(char		*buffer);
static void com_maxid(char		*buffer);
static void com_listid(char		*buffer);
static void com_size(char		*buffer);
static void com_dump(char		*buffer);
static void	com_allocsz(char	*buffer);
static void	com_freesz(char		*buffer);
static void	com_leaksz(char		*buffer);
static void com_dmpfree(char	*buffer);
static void	com_exit(char		*buffer);


/*
** Server Runner
*/

static void	*server(void *nth)
{
	char	buffer[1024];
	char	*msg;
	int		sz;
	int		v;
	
	while ((sz = recv(csock, buffer, 1024, 0)) > 0)
		{
		
		// Count frame number
		if (strncmp(buffer, "=CONT=", 6) == 0)
			{
			com_count(buffer);
			continue;
			}
		
		// Get max id
		if (strncmp(buffer, "=MXID=", 6) == 0)
			{
			com_maxid(buffer);
			continue;
			}
		
		// List IDs
		if (strncmp(buffer, "=LIST=", 6) == 0)
			{
			com_listid(buffer);
			continue;
			}
		
		// Get size of a frame id
		if (strncmp(buffer, "=SIZE=", 6) == 0)
			{
			com_size(buffer);
			continue;
			}
		
		// Dump
		if (strncmp(buffer, "=DUMP=", 6) == 0)
			{
			com_dump(buffer);
			continue;
			}
		
		// Get actual allocation size
		if (strncmp(buffer, "=GASZ=", 6) == 0)
			{
			com_allocsz(buffer);
			continue;
			}
		
		// Get actual free-ed size
		if (strncmp(buffer, "=GFSZ=", 6) == 0)
			{
			com_freesz(buffer);
			continue;
			}
		
		// Get actual leak size
		if (strncmp(buffer, "=GLSZ=", 6) == 0)
			{
			com_leaksz(buffer);
			continue;
			}
		
		// Activate dump when free
		if (strncmp(buffer, "=DMPF=", 6) == 0)
			{
			com_dmpfree(buffer);
			continue;
			}
		
		// Exit
		if (strncmp(buffer, "=EXIT=", 6) == 0)
			{
			com_exit(buffer);
			continue;
			}
		
		
		send(csock, "=SERR=Unknow command\n", 21, 0);
		}
		
	close(csock);
	close(sock);
}




/*
** Lib exit
*/
void clear()
{
	/*
	If the soft want to exit : pass the server runner in front process
	and continue to communicate with the lib's user until the user send =EXIT=
	*/
	pthread_cancel(thrd);
	send(csock, "=EXIT==0300=\n", 13, 0);
	server(NULL);
}



/*
** Lib loading : install the server
*/
void __attribute__((constructor)) init_server()
{
	struct sockaddr_in	my_addr;
	struct sockaddr_in	their_addr;
	unsigned int		sin_size;
	int					sz;
	int					yes = 1;
	
	/*
	  Configure the server port & adress
	*/
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(65000);
	my_addr.sin_addr.s_addr = INADDR_ANY;
	memset(&(my_addr.sin_zero), '\0', 8);
	
	
	/*
	  Instanciate an fd for the listener socket
	*/
	sock = socket(AF_INET, SOCK_STREAM, 0);
	
	/*
	  Reuse the port we want to use
	*/
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
		exit(1);
	
	/*
	  Link the listener socket to the port previously configured
	*/
	bind(sock, (struct sockaddr*)&my_addr, sizeof(struct sockaddr));
	listen(sock, 1);
	
	
	/*
	  Wait for a connection. If there is one, a new fd (csock) is given
	  to the socket who manager the new connection.
	*/
	sin_size = sizeof(struct sockaddr);
	csock = accept(sock, (struct sockaddr*)&their_addr, &sin_size);
	
	/*
	  Catch exit
	*/
	atexit(clear);
	
	/*
	  In this point a new connection was done on our server, and the new connection
	  socket is pointed by csock. Run the communication thread manager.
	*/
	send(csock, "=WELC=Hello on the malloc lib server\n", 37, 0);
	pthread_create(&thrd, NULL, server, NULL);
}





/*
** Malloc Function Signals
*/


void	sig_free(void	*ptr)
{
	static char buffer[1024];
	s_frame *frm;


	if (!conf_dumpfree)
		return;
	
	frm = hash_found_ptr(ptr);
	if (!frm)
		return;

	if (fd_freeautodump< 0)
		return;
	
	write(fd_freeautodump, &(frm->id), sizeof(size_t));
	write(fd_freeautodump, &(frm->sz), sizeof(size_t));
	write(fd_freeautodump, frm->ptr, frm->sz);
}




/*
** SERVER COMMAND
*/

/*
** Count frame number
*/
static void	com_count(char	*buffer)
{
	sprintf(buffer, "=CONT=%li\n", hash->count);
	send(csock, buffer, strlen(buffer), 0);
}


/*
** Get max id
*/
static void com_maxid(char	*buffer)
{
	sprintf(buffer, "=MXID=%li\n", hash->id);
	send(csock, buffer, strlen(buffer), 0);
}


/*
** List IDs
*/
static void com_listid(char		*buffer)
{
	int		i;
	s_frame	*frm;
	
	for (i = 0; i < HSH_SIZE; i++)
		{
		frm = hash->frms[i];
		
		/* Consider to manage a buffer */
		while(frm)
			{
			sprintf(buffer, "=LIST=%li\n", frm->id);
			send(csock, buffer, strlen(buffer), 0);
			
			frm = frm->next;
			}
		}
	
	send(csock, "=LIST==ENDL=\n", 13, 0);
}


/*
** Get size of a frame id
*/
static void com_size(char	*buffer)
{
	s_frame *frm;
	int		v;
	
	v = atoi(buffer + 6);
	frm = hash_found_id(v);
	
	if (frm)
		{
		sprintf(buffer, "=SIZE=%li\n", frm->sz);
		send(csock, buffer, strlen(buffer), 0);
		}
	else
		send(csock, "=SIZE==0404=\n", 13, 0);
}


/*
** Dump
*/
static void com_dump(char	*buffer)
{
	s_frame	*frm;
	int		v;
	
	v = atoi(buffer + 6);
	
	if (v >= 0)
		{
		frm = hash_found_id(v);
		if (frm)
			{
			int fd = open("/tmp/md_dump", O_WRONLY | O_CREAT | O_TRUNC, 00666);
			if (fd < 0)
				{
				send(csock, "=DUMP==0505=\n", 13, 0);
				return;
				}
			write(fd, &(frm->id), sizeof(size_t));
			write(fd, &(frm->sz), sizeof(size_t));
			write(fd, frm->ptr, frm->sz);
			close(fd);
			send(csock, "=DUMP=/tmp/md_dump\n", 19, 0);
			}
		else
			send(csock, "=DUMP==0404=\n", 13, 0);
		}
	else
		{
		int	i;
		int fd = open("/tmp/md_dump", O_WRONLY | O_CREAT | O_TRUNC, 00666);
		
		if (fd < 0)
			{
			send(csock, "=DUMP==0505=\n", 13, 0);
			return;
			}
		
		for (i = 0; i < HSH_SIZE; i++)
			{
			frm = hash->frms[i];
			
			/* File dump is [id INT(4)][sz INT(4)][data void(sz)]... */
			while (frm)
				{
				write(fd, &(frm->id), sizeof(size_t));
				write(fd, &(frm->sz), sizeof(size_t));
				write(fd, frm->ptr, frm->sz);
				
				frm = frm->next;
				}
			}
		close(fd);
		send(csock, "=DUMP=/tmp/md_dump\n", 19, 0);
		}
}


/*
** Get actual allocation size
*/
static void	com_allocsz(char	*buffer)
{
	sprintf(buffer, "=GASZ=%li\n", hash->alloc_sz);
	send(csock, buffer, strlen(buffer), 0);
}


/*
** Get actual free-ed size
*/
static void	com_freesz(char		*buffer)
{
	sprintf(buffer, "=GFSZ=%li\n", hash->free_sz);
	send(csock, buffer, strlen(buffer), 0);
}


/*
** Get actual leak size
*/
static void	com_leaksz(char		*buffer)
{
	sprintf(buffer, "=GLSZ=%li\n", hash->alloc_sz - hash->free_sz);
	send(csock, buffer, strlen(buffer), 0);
}


/*
** Activate dump when free
*/
static void com_dmpfree(char	*buffer)
{
	int		v;
	
	v = atoi(buffer + 6);
	
	close(fd_freeautodump);
	if (v)
		{
		fd_freeautodump = open("/tmp/md_freeautodump", O_WRONLY | O_CREAT | O_APPEND, 00666);
		if (fd_freeautodump < 0)
			{
			send(csock, "=DMPF==0505=\n", 13, 0);
			return;
			}
		}
	
	conf_dumpfree = v;
	
	sprintf(buffer, "=DMPF=%i\n", conf_dumpfree);
	send(csock, buffer, strlen(buffer), 0);
}


/*
** Exit
*/
static void	com_exit(char		*buffer)
{
	send(csock, "=EXIT==200=\n", 13, 0);
	close(csock);
	close(sock);
	_Exit(0);
}