/* 
   This file is part of Practical Distributed Processing
   Copyright (C) 2006-2007 Phillip J. Brooke and Richard F. Paige
*/

#include "compat.h"
#include "constants.h"
#include "netstr.h"
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

char *             buffer;
int                exit_flag;
int                on            = 1;
int                pid;
int                recv_value;
int                s1;
int                s2;
socklen_t          socket_in_len;
struct sockaddr_in a;

void create_socket(void)
{
  printf("Server starting...\n");

  /* Allocate space for buffer. */
  buffer = malloc(BUFFER_SIZE);

  /* Create a socket to listen on. */
  if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) > 0)
    printf("The socket has been created\n");
  else
    {
      perror("Could not create socket");
      exit(EXIT_FAILURE);
    }

  /* Reuse local addresses when binding the socket.  See socket(7). */
  if (setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    {
      perror("Problem setting socket option");
      exit(EXIT_FAILURE);
    }

  /* Describe the addresses we'll accept connections from. */
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = INADDR_ANY;
  a.sin_port = htons(EXAMPLE_PORT);

  /* Bind the socket to the address. */
  if (bind(s1, (struct sockaddr *) &a, sizeof(a)) == 0)
    printf("Bound socket\n");
  else
    {
      perror("Could not bind socket");
      exit(EXIT_FAILURE);
    }

  /* Listen on the socket. */
  printf("Setting socket to listen...");
  if (listen(s1, 5) != 0)
    {
      perror("Problem listening on our_socket");
      exit(EXIT_FAILURE);
    }
  printf(" listening on socket\n");

  /* We will need to know the length of a struct sockaddr_in. */
  socket_in_len = sizeof(struct sockaddr_in);
}

void parent_after_fork(void) 
{
  printf("Process PID=%d forked child PID=%d to handle connection from %s\n",
	 getpid(), pid, inet_ntoa(a.sin_addr));
  /* We're done with s2 -- the child handles that. */
  if (close(s2) != 0)
    perror("Warning: problem closing s2");
}

void child_after_fork(void)
{
  printf("Process PID=%d is forked child to handle connection from %s\n",
	 getpid(), inet_ntoa(a.sin_addr));
  /* Only the parent needs our_socket. */
  if (close(s1) != 0)
    perror("Warning: problem closing s1");

  /* Loop until the connection closes, printing whatever the client
     sends. */
  exit_flag = 1;
  while (exit_flag) {
    recv_value = NSrecv(s2, buffer, BUFFER_SIZE, 0);
    if (recv_value == 0) {
      printf("Closing connection...\n");
      exit_flag = 0;
    } else if (recv_value < 0) {
      perror("Problem with recv");
      exit(EXIT_FAILURE);
    } else {
      printf("Received: %s\n", buffer);
      /* Send an anagram back. */
      {
	int send_val;
	char *bufferfry;
	char *newln_position;
	  
	/* We don't want any newlines in the anagram, so replace the
	   first (if any) with end-of-string. */
	newln_position = strchr(buffer, '\n');
	if (newln_position != NULL) {
	  *newln_position = '\0';
	}
	/* Get our anagram and send it. */
	bufferfry = (char *)strfry(buffer);
	printf("(%d) Sending response...%s\n", getpid(), bufferfry);
	send_val = send(s2, bufferfry, recv_value, 0);
	if (send_val < 0) 
	  {
	    perror("Send failed!");
	    exit(EXIT_FAILURE);
	  }
	printf("(%d) Sent response\n", getpid());
      }
    }
    free(buffer);
  } 

  /* Close the inbound socket. */

  if (close(s2) != 0)
    perror("Warning: problem closing s2");
  printf("Child PID=%d exiting.\n", getpid());
  exit(EXIT_SUCCESS);
}

void get_inbound_connection(void)
{
  /* Accept the next connection. */
  s2 = accept(s1, (struct sockaddr *) &a, &socket_in_len);
  if (s2 == -1)
    {
      perror("Problem accepting connection");
      exit(EXIT_FAILURE);
    }
  else
    {
      printf("Process PID=%d accepted connection from client on %s; forking\n",
	     getpid(), inet_ntoa(a.sin_addr));
      pid = fork();
      if (pid != 0)
	parent_after_fork();
      else
	child_after_fork();
    }
}

int main ()
{
  printf("Server starting with PID=%d...\n", getpid());
  signal(SIGCHLD, SIG_IGN);
  create_socket();
  /* Loop forever. */
  while (1) { 
    get_inbound_connection();
  }
}

