/************************************************************
  File name : server1.c
  Related file: client1.c
  Author : Chee Yap (yap@cs.nyu.edu)
  Date : Nov 2001 (Yap's Web Visualization Class)

  Description : 
		VARIATION ON:
		This is a simple iterative
		server that listens at port 1234.
		It will accept a number (unsigned, integer or
		float) from the client, and returns twice
		the number to the client. You can
		can access this server by running the client
		program (see file client.c), or just using
		"telnet" to connect to the server.

  Usage: Simply type "server" in the host machine to serve
		at default port 1234.  Otherwise type
		"server -p <port number>".

  How to compile :

  On Solaris 2 :

	% gcc -o server server.c -lsocket

  On SGI machies :

	% gcc -o server server.c

************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>


/************************************************************
 GLOBAL VARIABLES
 ************************************************************/

#define SQUARE 1
#define DOUBLE 2

int             server_sock, con_sock;
int             srv_port = 1234;
struct sockaddr_in source;
int		mode = DOUBLE;

/************************************************************
 SIGNAL HANDLERS
 ************************************************************/
/* Close all sockets before the forced quit caused by
   signal */
void
signal_handler(int n)
		/* Chee, 7/23/01: changed from "void signal_handler()".
		   See child_end(int) for explanation of similar change. */
{
	close(con_sock);
	close(server_sock);

	exit(0);
}


/* When the state of a child process is changed, (for example,
   it is terminated, or ended by call exit(),) the server
   should call wait() to get the state. This will prevent
   the child process from becoming a zombie process when it has
   finished. */
void child_end(int n) 	/* Chee, 7/23/01: changed from "void child_end()".
			   But we do nothing with the argument n.
			   Note that child_end is the second argument to
			   sigset(int, func) (see below).
			   In the latest /usr/include/signal.h, the second
			   argument to sigset(int, func) must
			   by a function(int) returning void. */
{
  wait(NULL);
}

/************************************************************
 INITIAL CONNECTION
 ************************************************************/
void
init_servconnection()
{
	struct sockaddr_in server;

   /* STEP (A).  Creating socket */
	server_sock = socket(PF_INET, SOCK_STREAM, 0);
		// it is misconception to use "AF_INET" instead of "PF_INET"
	if (server_sock < 0) {
		perror("socket");
		exit(1);
	}
	server.sin_family = PF_INET;
	server.sin_port = htons(srv_port);
	server.sin_addr.s_addr = INADDR_ANY;

   /* STEP (B).  Bind the specified port to this socket */
	if (bind(server_sock, (struct sockaddr *) & server, sizeof(server))
		 < 0)
	{
		perror("bind socket");
		exit(1);
	}
   /* listen for connection */
	if (listen(server_sock, 5) < 0) {
		perror("listen");
		exit(1);
	}
} //init_servconnection()

/************************************************************
 ITERATIVE SERVER LOOP
	-- accept connection
	-- fork
	-- then close connection (if parent) or 
	   	serve connection (if child)
 ************************************************************/
attenteconnection()
{
	int             lg, pid;

	lg = sizeof(struct sockaddr_in);

/* repeat until accept a successful connect */
	do
	  con_sock = accept(server_sock, (struct sockaddr *) & source, &lg);
	while (con_sock <= 0);

/* Fork a child process to serve the client */
	if ((pid = fork()) != 0)
	{
/* parent process can close the connect and quit */
	  close(con_sock);
	}
	else
	{
/* The child process will handle the client, and the quit when
   finished. */
		handle_req();
		exit(1);
	}
} //attenteconnection()


/************************************************************
 ITERATIVELY SERVING A SINGLE CONNECTION
 ************************************************************/

/*
 handle_req() will get input from the client, parse
   it and write the result back to the client. In this
   case, it will accept a number, either integer or
   float-point value, and return either the square or double
   this value, depending on the mode.

   It will repeat until the input is string "quit" or "bye" or "exit".

   Other commands:
	"version" or "id" will return the Server identification
	"square" will change to squaring mode
	"double" will change to doubling mode
 ************************************************************/

extern int errno;

handle_req()
{
	char            buff[8192];
	int             lg, flag, srv1, is_float, result_i;
	char           *filename, *c, *p;
	struct stat     statres;
	char            req[1024];
	double		result_f;

        flag = 1;
	while (flag) {

/* Try to repeat until read something from the client,
   unless the connection is broken. */
	  do {
	  lg = read(con_sock, req, 1024);
	  if (lg == -1 && errno == EBADF)
	    goto error;
	  } while (lg <= 0);

/* Assume the input will be integer first. */
	  is_float = 0;

	  if (req[lg - 1] != '\n' || req[lg - 2] != '\r') {
		sprintf(req, "Error: Unknown input\r\n");
		write(con_sock, req, strlen(req));
		continue;
	  }

/* Get rid of the trailing new line character and
   carriage return character. */
	  req[lg - 2] = '\0';
/* Get the first part of the line only. (Separated by
   space.) */
	  c = strtok(req, " ");
/* Empty line */
	  if (c == NULL) {
		sprintf(req, "Error: Unknown input\r\n");
		write(con_sock, req, strlen(req));
		continue;
	  }
/* quit */
	  if ((strncmp(c, "quit", 4) == 0)
	  	|| (strncmp(c, "bye", 3) == 0)
	  	|| (strncmp(c, "exit", 4) == 0))
	  {
		flag = 0;
		break;
	  }
/* help */
	  if ((strncmp(c, "help", 4) == 0)
	  	|| (strncmp(c, "?", 1) == 0))
	  {
		sprintf(req,
	"Commands: [quit | bye | exit], [version | id], [double], [square],\r\n");
		write(con_sock, req, strlen(req));
		sprintf(req,
	"   [N | N.N] where N is any string of digits\r\n");
		write(con_sock, req, strlen(req));
		continue;
	  }
/* server version */
	  if ((strncmp(c, "version", 7) == 0)
	  	|| (strncmp(c, "ver", 3) == 0))
	  {
		sprintf(req,
	"Server Version 0.1.  Yap's Visualization Class, Fall 2001\r\n");
		write(con_sock, req, strlen(req));
		continue;
	  }
/* Check every character to make sure the input is a number,
   determining it is an integer or not on the way. */
	  for (p = c; *p /* != NULL */ ; p ++)
	  {
	    if (!isdigit(*p))
	    {
	      if (*p != '.' || is_float)
	      {
/* If the character is not a digit, and not a decimal point,
   then it is not a number. If it is a decimal point, but
   there is already another one, then it is not a legal number.
*/
	        sprintf(req, "Error: Unknown input\r\n");
	        write(con_sock, req, strlen(req));
		is_float = -1;
	        break;
	      }
	      else
/* If we have one decimal point, then the number will not
   be an integer. */
	        is_float = 1;
	    }
	  }
	  if (is_float == 1)
	  {
/* A float-point number */
	    if (mode == DOUBLE) result_f = atof(c) * 2;
	    else result_f = atof(c) * atof(c);	// assume mode==SQUARE
	    sprintf(req, "%f\r\n", result_f);
	    write(con_sock, req, strlen(req));
	  }
	  else if (is_float != -1)
	  {
/* An integer */
	    if (mode == DOUBLE) result_i = atoi(c) * 2;
	    else result_f = atoi(c) * atoi(c);	// assume mode==SQUARE
	    sprintf(req, "%d\r\n", result_i);
	    write(con_sock, req, strlen(req));
	  }
	}

/* Some connection error happened, or quit the loop
   because receiving "quit"  from the client. Will
   close connection and exit this function. */
error:
	close(con_sock);
}// handle_req

/************************************************************
 SERVER USAGE
 ************************************************************/
usage()
{
	fprintf(stderr, "Usage: server [-p port]\n");
	exit(1);
}


/************************************************************
 MAIN PROGRAM
 ************************************************************/

main(int argc,char **argv)
{
	int             lg;

/* Shift the parameter */
	argc--;
	argv++;
	while (argc > 0) {
		if (argv[0][0] != '-')
			usage();
		switch (argv[0][1]) {
		case 'p':
/* Set port number of the server */
			srv_port = atoi(argv[1]);
			argv += 2;
			argc -= 2;
			break;
		default:
			usage();
		}
	}

/* Intercept CHILD signal. This signal occurs when a child process
   changes its status. In this program, it usually means when
   a child process calls exit() to end itself. */
	signal(SIGCHLD, child_end);

/* Intercept some signals which will end this process so
   we can close connection before quit it. */
	signal(SIGTERM, signal_handler);
	signal(SIGQUIT, signal_handler);
	signal(SIGINT, signal_handler);

/* Setup the socket and the port */
	init_servconnection();

/* Wait for connection */
	do {
		attenteconnection();
	} while (1);

}// main


/************************************************************
 THE END
 ************************************************************/
