/* echoserver.c - Simple TCP server that accepts connections
 *            and echoes back everything it receives.
 *            If in DEBUG mode (either define DEBUG, say
 *              #define DEBUG
 *            or compile the program with the qualifier -DDEBUG
 *               gcc -DDEBUG ..
 *            ) it also writes what it receives to the screen.
 *            It listens on port PROTOPORT set to 5194
 *            or to the port you specify on command line.
 *            Compile as
 *              % gcc -Wall -g [-DDEBUG] -o echoserver echoserver.c
 *            Run as
 *              % echoserver [port] &
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>


// If we are waiting reading from a pipe and
// the interlocutor dies abruptly (say because
// of ^C or kill -9), then we receive a SIGPIPE
// signal. Here we handle that.
void sig_pipe(int n) 
{
   fprintf(stderr, "Broken pipe signal\n");
}

extern int errno;

/* Print an error message and exit (Comer+Stevens). See "man stdarg".
   Use it as a regular printf statement with format and perhaps parameters.
 */
int errexit(const char *format, ...)
{
        va_list args;

        va_start(args, format);
        vfprintf(stderr, format, args);
        va_end(args);
        exit(1);
}


/*In an infinite loop, it reads characters from sockfd and writes them
  to the same socket (and to the screen if DEBUG is defined).
*/
void
chr_echo(int sockfd)
{
  for ( ; ; ) {
    ssize_t n;
    char c;
  
    n = read(sockfd, &c, 1);
    if (n > 0) {
       write(sockfd, &c, 1);
#ifdef DEBUG
       putchar(c);
#endif
    } else if ( n < 0) { 
       fprintf(stderr, "Negative return from Read\n");
       if (errno == EINTR) /* IO was interrupted by a signal */
           continue;
       return;
    } else  /* connection closed by other end */
      return;
  }
}


#define PROTOPORT 5194 /* default protocol port number*/
#define QLEN 6

/* Create a server listening socket waiting on port 'port'
   with queue size 'qz'*/
int listensocket(int port, int qz)
{
  int ls;
  int option_value;   /* needed for setsockopt             */ 
  struct sockaddr_in serv;
  
  ls = socket(AF_INET, SOCK_STREAM, 0);
  
  bzero((char *)&serv, sizeof(serv)); /* clears out buffer of this size*/
  serv.sin_family = AF_INET;
  serv.sin_port = htons((u_short)port); /* 80 */
  serv.sin_addr.s_addr = htonl(INADDR_ANY); /* any interface ????*/
  
  /* Make listening socket's port reusable - must appear before bind */
  option_value = 1;
  if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, 
		 sizeof(option_value)) < 0)
    errexit("setsockopt failure\n");
  
  /* Bind a local address to the socket */
  if (bind(ls, (struct sockaddr *)&serv, sizeof(serv)) < 0) 
    errexit("bind failed on port %d\n", port);
  
  /* Specify size of request queue */
  if (listen(ls, qz) < 0)
    errexit("listen failed on port %d\n", port);
  
    return ls;
}

/* Return a connected socket obtain by an accept call
   on the listening socket lsd. It returns -1 if
   the accept was interrupted by a signal.
 */
int connected_socket(int lsd)
{
  int                 csd;
  size_t	      clilen;
  struct sockaddr_in  cliaddr;

  clilen = sizeof(cliaddr);
    if ( (csd = accept(lsd, (struct sockaddr *) &cliaddr,
			  (socklen_t *)&clilen)) < 0) {
      if (errno == EINTR)
	return -1;
      else {
	exit(1);
      }
    }
    return csd;
}

/****************************************************************
 * Program: echoserver
 * Purpose: In a loop, accept a connection, echo back all you receive.
 * Syntax: echoserver [port]
 *            port:protocol port number to use
 * Note: The port argument is optional. If no protocol port is
 *       specified, the server uses the default given by PROTOPORT.
 ****************************************************************/
int
main(int argc, char *argv[])
{
  int    sd;             /* listening socket descriptor       */
  int    port;           /* protocol port number              */
 
  /* Check command-line argument for protocol port and extract
   * port number if one is specified. Otherwise, use the default
   * port value given by constant PROTOPORT
   */
  if (argc > 1) {         /* If protocol port is specified */
    port = atoi(argv[1]); /* Convert to binary */
  } else {
    port = PROTOPORT;    /* use default port number */
  }
/* port could have been set with just
   port = (argc > 1) ? atoi(argv[1]) : PROTOPORT;
 */
    
  sd = listensocket(port, 15);
 
  /* Establish handling of the SIGPIPE signal */
  if (signal(SIGPIPE, sig_pipe) == SIG_ERR) {
     fprintf(stderr, "Unable to set up signal handler\n");
  }
  
  /* Main server loop - accept and handle requests */
  for ( ; ; ) {
    int    sd2;            /* connected socket descriptor */

    while ( ( sd2 = connected_socket(sd) ) < 0 )
      ;
    chr_echo(sd2);
    close(sd2);
    printf("****************************  END ****************************\n");
  }
  /* the listening socket sd is closed automatically when we exit */
}



