//assignment #4
//compile: cc sockets_server.c -lxnet

#include < stdio.h >

/* ipc stuff */
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >

/* semaphore & shared memory stuff */
#include < sys/ipc.h >
#include < sys/shm.h >
#include < sys/sem.h >
#include < fcntl.h>

/* signal stuff */
#include < signal.h >





/* ipc stuff */
#define PORTNUM	6779



/* semaphore & shared memory stuff */
#define SEM_MODE ( 0200 | 0400 | IPC_CREAT)
#define NUM_OF_SEM 1

void pOperation( void ) ;
void vOperation( void ) ;
void cleanUp() ;
struct sembuf *ptrsembuf ;

union semun
{
  int val ;
  struct semid_ds *buf ;
  ushort *array ;
};

typedef struct
{
   int  i ;
   char buffer[ 8 ] ;
} SharedMemory ;

int semid ;
int shmid ;



/* main */
int main()
{
/* ipc stuff */
	int	clilen, childpid, length ;
	register int 	i,
			TheSocket,
			TheSocketConnection,
			StructureLength ;
	struct sockaddr_in	ServerUnixSocketAddress,
				ClientUnixSocketAddress ;


/* semaphore & shared memory stuff */
	SharedMemory *sMemory ;
	int semnum = 0 ;
	int semval ;
	union semun initvar ;
	initvar.val = 1 ;
        

	/* get semphore */
	if( ( semid = semget( IPC_PRIVATE, NUM_OF_SEM, SEM_MODE ) ) == -1 )
	{
		perror( "semget" ) ;
		exit( -1 ) ;
	}

	/* init count to 1 */
	if( semctl( semid, semnum, SETVAL, initvar ) == -1 )
	{
		perror( "semctl" ) ;
		exit( -1 ) ;
	}

	/* check count is = to 1 */
	if( ( semval = semctl( semid, semnum, GETVAL, initvar ) ) == -1 )
	{
		perror( "semctl getval" ) ;
		exit( -1 ) ;
	}
	printf( "semaphore value is = %d\n", semval ) ;


	/* attempt to attach to an existing memory segment
	// the size of SharedMemory structure
	*/
	if( ( shmid = shmget( IPC_PRIVATE, sizeof( SharedMemory ), IPC_CREAT | 0666 ) ) < 0 ) 
	{
		perror( "shmid" ) ;
		exit( 1 ) ;
	}

	/* attach the shared memory segment
	// get address of sMemory
	*/
	sMemory =(SharedMemory*)shmat( shmid,NULL,0 ) ;
	if( sMemory ==(SharedMemory *) -1 ) 
	{
		perror( "shmat" ) ;
		exit( 1 );
	}

	/* init semaphores */
	ptrsembuf = (struct sembuf*)malloc( sizeof( struct sembuf ) ) ;
	ptrsembuf->sem_op  = 1 ;
	ptrsembuf->sem_num = 0 ;
	ptrsembuf->sem_flg = 0 ;





/* ipc stuff */	
	TheSocket = socket( AF_INET, SOCK_STREAM, 0 ) ;
	if( TheSocket < 0 )
	{
		perror( "server error:  socket() failed" ) ;
		cleanUp() ;
		exit( -1 ) ;
	}
	

	bzero( ( char * ) &ServerUnixSocketAddress,

		sizeof( ServerUnixSocketAddress ) ) ;
	ServerUnixSocketAddress.sin_family	= AF_INET ;
	ServerUnixSocketAddress.sin_addr.s_addr	= htonl( INADDR_ANY ) ;
	ServerUnixSocketAddress.sin_port	= htons( PORTNUM ) ;


	unlink( TheSocket ) ;
	if( bind( TheSocket,
		  (struct sockaddr *)&ServerUnixSocketAddress,
		  sizeof( ServerUnixSocketAddress ) ) < 0 )
	{
		perror( "server error:  connect() failed" ) ;
		cleanUp() ;
		exit( -1 ) ;
	}
		



	if( listen( TheSocket, 5 ) < 0 )
	{
		perror( "server error:  listen() failed" ) ;
		cleanUp() ;
		exit( -1 ) ;
	}


/* set up signal */
	signal(SIGINT, cleanUp) ;



/* the loop */
	sMemory->i = 0 ;
	do

	{
		clilen = sizeof( ClientUnixSocketAddress ) ;
		TheSocketConnection = accept( TheSocket,
					      (struct sockaddr *)&ClientUnixSocketAddress,
					      &clilen ) ;
			  	
		if( TheSocketConnection < 0 )
		{
			perror( "server error:  accept() failed" ) ;
			cleanUp() ;
			exit( -1 ) ;
		}
	
		childpid = fork() ;
		if( childpid < 0 )
		{
			perror( "server error:  fork() failed" ) ;
			cleanUp() ;
			exit( -1 ) ;
		}
		else if( childpid == 0 )
		{
			close( TheSocket ) ;
			

			pOperation() ;
/* critical region */
			  read( TheSocketConnection,
			        sMemory->buffer,
			        length ) ;
			  sleep( 2 ) ;
			  write( TheSocketConnection,
			  	 sMemory->buffer,
			  	 length ) ;		

/* end critical region */
			vOperation() ;
			
			/* close child process */
			exit( 0 ) ;
		}
		close( TheSocketConnection ) ;
	} while( 1 ) ;
}




/* clean up shared memory && semaphores */
void cleanUp( void )
{
	/* remove shared memory */
	if( shmctl( shmid, IPC_RMID, 0 ) == -1 )
	{
		perror( "shmctl" ) ;
		exit( -1 ) ;
	}
	else
  	  printf( "\nshared memory successfully deleted....\n" ) ;

	/* remove semaphore */
	if( semctl( semid, NUM_OF_SEM, IPC_RMID ) == -1 )
	{
		perror( "semctl" ) ;
		exit( -1 ) ;
	}
	else
  	  printf( "semaphore successfully deleted....\n" ) ;
	

	printf( "***********finished serving**************\n\n" ) ;
}




/* P operation */
void pOperation( void )
{
  ptrsembuf->sem_op = -1 ;
  if( semop( semid, ptrsembuf, 1 ) == -1 )
  {
    perror( "semop pOperation" ) ;
    exit( -1 ) ;
  }
}

/* V operation */
void vOperation( void )
{
  ptrsembuf->sem_op = 1 ;
  if( semop( semid, ptrsembuf, 1 ) == -1 )
  {
    perror( "semop vOperation" ) ;
    exit( -1 ) ;
  }
}