/*
** server.c -- a stream socket server demo
*/

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

#define PORT "3490"  // the port users will be connecting to

#define BACKLOG 10	 // how many pending connections queue will hold
typedef void *jamaica_sockoptval_t;
typedef int jamaica_getsockopt_socklen_t;



 #define JAMAICA_NATIVE_NETWORK_SOCKET_GET_OPTION_SO_LINGER(socketDescriptor,flag,value,result) \
      do \
      { \
        struct linger                __linger; \
        jamaica_getsockopt_socklen_t __len; \
        \
        flag  = 0; \
        value = 0; \
        \
        __len = sizeof(__linger); \
        (result) = (getsockopt(socketDescriptor,SOL_SOCKET,SO_LINGER,(jamaica_sockoptval_t)&__linger,&__len) == 0) ? JAMAICA_NATIVE_OK : JAMAICA_NATIVE_ERROR; \
        if ((result) == JAMAICA_NATIVE_OK) \
        { \
          /* make sure all data in __value has been written to avoid undefined values */ \
          assert(__len >= sizeof(__linger)); \
          (flag)  = __linger.l_onoff; \
          (value) = __linger.l_linger; \
        } \
      } \
      while (0)



typedef enum
{
  JAMAICA_NATIVE_ERROR,
  JAMAICA_NATIVE_OK
} jamaica_native_error;


 #define JAMAICA_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_LINGER(socketDescriptor,flag,value,result) \
      do \
      { \
        struct linger __linger; \
        \
        __linger.l_linger = value; \
        __linger.l_onoff  = flag; \
        \
        (result) = (setsockopt(socketDescriptor,SOL_SOCKET,SO_LINGER,(jamaica_sockoptval_t)&__linger,sizeof(__linger)) == 0) ? JAMAICA_NATIVE_OK : JAMAICA_NATIVE_ERROR; \
      } \
   while (0) 


void sigchld_handler(int s)
{
	// waitpid() might overwrite errno, so we save and restore it:
	int saved_errno = errno;

	while(waitpid(-1, NULL, WNOHANG) > 0);

	errno = saved_errno;
}


// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
	if (sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in*)sa)->sin_addr);
	}

	return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
	int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
	struct addrinfo hints, *servinfo, *p;
	struct sockaddr_storage their_addr; // connector's address information
	socklen_t sin_size;
	struct sigaction sa;
	int yes=1;
	char s[INET6_ADDRSTRLEN];
	int rv,result,flag,optval;	

	struct linger __linger;

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE; // use my IP

	if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and bind to the first we can
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
				p->ai_protocol)) == -1) {
			perror("server: socket");
			continue;
		}

		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
				sizeof(int)) == -1) {
			perror("setsockopt");
			exit(1);
		}
	
		JAMAICA_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_LINGER(sockfd,
                                                         1,
                                                         65535,
                                                         result
                                                         );
if(result == JAMAICA_NATIVE_OK)		
{
	printf("\n Linger was succefful");
}
else
{
 printf("\n Sockopt failre\n");
}
	
	JAMAICA_NATIVE_NETWORK_SOCKET_GET_OPTION_SO_LINGER(sockfd,
                                                           flag,
                                                           optval,
                                                           result
                                                          );
	printf("result:%d optval:%d\n", result,optval);

		if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("server: bind");
			continue;
		}

		break;
	}

	freeaddrinfo(servinfo); // all done with this structure

	if (p == NULL)  {
		fprintf(stderr, "server: failed to bind\n");
		exit(1);
	}

	if (listen(sockfd, BACKLOG) == -1) {
		perror("listen");
		exit(1);
	}

//	sa.sa_handler = sigchld_handler; // reap all dead processes
//	sigemptyset(&sa.sa_mask);
// 	sa.sa_flags = SA_RESTART;
//	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
//		perror("sigaction");
//		exit(1);
//	}

	printf("server: waiting for connections...\n");

	while(1) {  // main accept() loop
		sin_size = sizeof their_addr;
		new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
		if (new_fd == -1) {
			perror("accept");
			continue;
		}

		inet_ntop(their_addr.ss_family,
			get_in_addr((struct sockaddr *)&their_addr),
			s, sizeof s);
		printf("server: got connection from %s\n", s);

		if (!fork()) { // this is the child process
			close(sockfd); // child doesn't need the listener
			if (send(new_fd, "Hello, world!", 13, 0) == -1)
				perror("send");
			close(new_fd);
			exit(0);
		}
		close(new_fd);  // parent doesn't need this
	}

	return 0;
}

