/*
 * BiffSocko
 * server.c
 * this is a tcp/ip server template.  It has all the basics of a tcp server 
 * that only needs slight modifications to be integrated to any server program.  
 * server.c will basically bind/listen/accept/fork/kill child processes/etc.
 * all you have to do is plug in your read/write functions in the fork() section
 * of child processes, located below.
 *
 * Exit Codes
 * 0    Success
 * 1    Wrong Command Line Parameters
 * 2    Unable to open socket()
 * 3    Can't bind() to port 
 * 4    Unable to listen() to port
 * 5    Can't accept() connections
 * 6    Can't fork()
 * 99   SIGINT
 * all other abnormal return exit codes from connection functions which do the 
 * recev/send stuff for this program
 */ 

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <pthread.h>

#define PORT 2031
#define BACKLOG 200
#define NUM 1024

/*****************************************************************************
 *              Global Variables (yucky)
 *****************************************************************************/
int sd;                                         /* socket discriptor for socket
function */


/*****************************************************************************
 *              Function Definitions
 *****************************************************************************/
void closer(int);                    /* this will close the open master socket upon SIGINT */
void *client_function(void *ptr);

int main (int argc, char *argv[])
{
        struct sockaddr_in home_addr;          /* network address structures */
        pthread_t *client;                     /* new client connections thread */
        int con;                               /* number of connections */
        int accept_sd;                         /* socket discriptor for accept function */
        int alen;                              /* from adderlen */
        struct sockaddr_in connecting_addr;    /* network address structures */



	/*****************************************************************************
	 * check for correct usage 
	 *****************************************************************************/
	if(argc != 1){
        	fprintf(stderr, "%s does not accept arguments\n", argv[0]);
        	exit(1);
	}

	/*****************************************************************************
	 * open socket
	 *****************************************************************************/
	if(!(sd=socket(AF_INET, SOCK_STREAM, 0))){
	        fprintf(stderr, "%s is unable to create a socket. exiting\n", argv[0]);
	        exit(2);
	}

	/*****************************************************************************
	 * do the sockaddr_in thing for home_addr
	 *****************************************************************************/
	home_addr.sin_family = AF_INET;
	home_addr.sin_port = htons(PORT);
	home_addr.sin_addr.s_addr = INADDR_ANY;
	bzero(&(home_addr.sin_zero), 0);


	/*****************************************************************************
	 * binding to port
	 *****************************************************************************/
	if((bind(sd, (struct sockaddr *)&home_addr, sizeof(struct sockaddr))) == -1){
	        fprintf(stderr, "%s could not bind to port. exiting\n", argv[0]);
	        close(sd);
	        exit(3);
	}

	printf("network server running on port %d\n", PORT);

	/*****************************************************************************
	 * listen to the port
	 *****************************************************************************/
	if((listen(sd, BACKLOG)) == -1){
	        fprintf(stderr, "%s unable to listen to port. exiting\n", argv[0]);
	        close(sd);
	        exit(4);
	}


	/*****************************************************************************
	 * clean up runaway processes & catch signals for closing out nicely
	 *****************************************************************************/
	 signal(SIGCHLD, SIG_IGN);
	 signal(SIGINT, closer);

	/*****************************************************************************
	 * this is the part that does the work, we will accept() the connection, 
	 * and then fork() off and run the *connection functions*  
	 *****************************************************************************/
	con=0;
	/* hey save some space for me!! */
	client = malloc (NUM * sizeof *client);
	while(1){
		/* accept the connecting socket */
	        alen=sizeof(connecting_addr);
	        if(!(accept_sd=accept(sd, (struct sockaddr *) &connecting_addr, &alen))){
	                fprintf(stderr, "program is unable to accept connections. exiting\n");
	                close(sd);
	                exit(5);
	        }

		/* start a snazzy new thread */
       		 if((pthread_create( &client[con],NULL,client_function,&accept_sd))!= 0){
			printf("can't create new thread .. exiting\n");
			exit(1);
		}

		pthread_join( client[con], NULL);
       		con++;
       		close(accept_sd);
	}
	exit(0);
}

/*****************************************************************************
 * client_function()
 * does the reading and writing for the network connection
 *****************************************************************************/
void *client_function(void *ptr){
        char *buffer=malloc(NUM);
	int *accept_sock;

	accept_sock = ptr;

        recv(*accept_sock, buffer, NUM,0);
        send(*accept_sock,buffer,strlen(buffer),0);
	memset(buffer,0,NUM);
	return NULL;
}

/*****************************************************************************
 *  closer()
 * this will take care of closing up the master socket descriptor upon 
 * SIGINT or other signals.  Eventually, this will also log termination 
 * errors to syslog
 *****************************************************************************/
void closer(int SIGX)
{

        printf("exiting on Signal %d\n", SIGX);
        close(sd);
        exit(99);
}