/*                 Physics 673 Assignment Number 8                     */
/*                 Bryan L. Caron  November 20, 1996                   */
/*                                                                     */
/* This program demonstrates three methods of InterProcess             */
/* Communications (IPC):                                               */
/*                                                                     */
/*        1 - PIPES (half-duplexed)                                    */
/*        2 - FIFO  (full-duplexed pipes)                              */
/*        3 - SEMAPHORES                                               */
/*                                                                     */
/* Part 1 - PIPES: Build2Pipes()                                       */
/*                                                                     */
/* The function Build2Pipes() is invoked and creates two pipes, one    */
/* to send information from the CHILD to the PARENT, and the other     */
/* to send information from the PARENT to the CHILD.  The information  */
/* sent is just the current process ID.  The information received by   */
/* the CHILD is equivalent to the result of the function getppid()     */
/* (ie. the PARENT process ID).  The information received by the       */
/* PARENT is equivalent to the return value of fork() (ie. the CHILD   */
/* process ID).                                                        */ 
/*                                                                     */
/* Part 2 - FIFO and SEMAPHORES: BuildFifo_Sem()                       */
/*                                                                     */
/* The function BuildFifo_Sem() is invoked and creates a FIFO, opened  */
/* for READ and WRITE by both PARENT and CHILD.  Both PARENT and CHILD */
/* first WRITE to the FIFO, but to prevent either the PARENT or CHILD  */
/* from reading the information it just wrote SEMAPHORES are used to   */
/* control when the READs occur.  Any READ cannot happen until both    */
/* PARENT and CHILD have WRITTEN, and the first READ to occur is that  */
/* for the last process to WRITE.                                      */

#include      /* header file for standard i/o                 */
#include  /* header file for fork(), waitpid() commands   */
#include     /* header file for fork() command               */
#include   /* header file for waitpid() command            */
#include   /* header file for mkfifo                       */
#include      /* header file for file control                 */
#include    /* header file for ftok                         */
#include    /* header file for semaphores                   */

#define PMODE 00660    /* r-w for owner only                           */

int Build2Pipes();
int BuildFifo_Sem();

main()
{

  Build2Pipes();

  BuildFifo_Sem();

  exit(0);
}












int Build2Pipes()
{
int i;
int j;
int k;
int status;

int fd1[2], fd2[2];
int stat1, stat2;
int n_written1, n_written2;
int n_read1, n_read2;

int bufr1, bufr2;
int *bufw1, *bufw2;
int *buffer1, *buffer2;

printf( "*********** HALF-DUPLEXED PIPE COMMUNICATION *************\n" );

/* create pipes for parent and child to communicate through */

stat1 = pipe( fd1 );
stat2 = pipe( fd2 );

if( stat1 || stat2 < 0 ) {
  printf( "\nError creating Pipes.  Exit now ...\n" );
  exit(1);
}

printf( "\nPipe1 stat return value = %d",stat1);
printf( "\nPipe1 read  file descriptor fd1[0] = %d",fd1[0]);
printf( "\nPipe1 write file descriptor fd1[1] = %d\n",fd1[1]);

printf( "\nPipe2 stat return value = %d",stat2);
printf( "\nPipe2 read  file descriptor fd2[0] = %d",fd2[0]);
printf( "\nPipe2 write file descriptor fd2[1] = %d\n",fd2[1]);

/* Print current process id value */

printf( "\nPre-fork Process ID:%d", getpid() );
printf( "\nPre-fork Parent Process ID:%d\n",getppid() );

printf( "\nFork a new process ...\n" );

/* flush stdout stream and fork a new process */

fflush( stdout );

i = fork();

if( i <0 ) {
  printf( "Return code = %d Fork failed. Clean up and exit.\n", i );
  exit(2);
}
else { 
  if( i == 0 ) {

    printf( "\n\t fork() return value = %d",i );
    printf( "\n\t This process is a CHILD with Process ID:%d", getpid() );
    printf( "\n\t CHILD'S Parent Process ID from getppid():%d\n",getppid() );

    close( fd1[0] ); /* close PIPE #1 read  */
    close( fd2[1] ); /* close PIPE #2 write */

    k = getpid();

    bufw1 = &k;
    buffer2 = &bufr2;

/* write via PIPE #1 */

    n_written1 = write( fd1[1] , bufw1 , 1024 );

    if( n_written1 < 0 ) {
      printf( "\nError writing to Pipe.  Exit now ...\n" );
      exit(3);
    }

    printf( "\n\t CHILD: Number of bytes written to Pipe1 = %d",n_written1);
    printf( "\n\t CHILD: Sent buffer is: %d\n", *bufw1 );

/* read via PIPE #2 */

    n_read2 = read( fd2[0] , buffer2 , 1024 );

    if( n_read2 < 0 ) {
      printf( "\nError reading from Pipe.  Exit now ...\n" );
      exit(3);
    }

    printf( "\n\t CHILD: Number of bytes read from Pipe2 = %d",n_read2);
    printf( "\n\t CHILD: Received buffer is: %d\n", bufr2 );

    close( fd1[1] );
    close( fd2[0] );

    fflush( stdout );

    printf( "\n\t CHILD process will now sleep for 20 seconds" );

    sleep( 20 );

    printf( "\n\t CHILD process will now die and become a ZOMBIE\n\n" );

    exit(1);

  }
  else {

    printf( "\n\t fork return value =  %d\n",i );
    printf( "\t This process is a Parent with Process ID:%d\n\n", getpid() );

    fflush( stdout );

    system( "ps -l | grep 'a.out' " );

    close( fd2[0] ); /* close PIPE #2 read  */
    close( fd1[1] ); /* close PIPE #1 write */

    k = getpid();

    bufw2 = &k;
    buffer1 = &bufr1;

/* write via PIPE #2 */

    n_written2 = write( fd2[1] , bufw2 , 1024 );

    if( n_written2 < 0 ) {
      printf( "\nError writing to Pipe.  Exit now ...\n" );
      exit(4);
    }

    printf( "\n\t PARENT: Number of bytes written to Pipe2 = %d",n_written2);
    printf( "\n\t PARENT: Sent buffer is: %d\n",*bufw2 );

/* read via PIPE #1 */

    n_read1 = read( fd1[0] , buffer1 , 1024 );

    if( n_read1 < 0 ) {
      printf( "\nError reading from Pipe.  Exit now ...\n" );
      exit(4);
    }

    printf( "\n\t PARENT: Number of bytes read from Pipe1 = %d",n_read1);
    printf( "\n\t PARENT: Received buffer is: %d\n", bufr1 );

    close( fd1[0] );
    close( fd2[1] );

    printf( "\n\t Waiting for child to finish ... show the ZOMBIE\n" );

    fflush( stdout );

/* Set options = WNOHANG: This lets the parent process continue          */
/* while loop statements occur for whenever j is 0 (child still running) */

    while ( ! (j = waitpid ( i , &status , WNOHANG )) ) {
      fflush(stdout);
      sleep(1);
      system( "ps -l | grep 'a.out' | grep 'Z' " );
    }

    printf( "\n\nwaitpid() return value = %d\n\n",  status );

    system( "ps -l | grep 'a.out' " );

    printf( "\nChild Process ID %d has terminated.", j );
    printf( "\nParent Process ID %d now exits Build2Pipes().\n", getpid() ); 
  }
}

}












int BuildFifo_Sem()
{
int i;
int j;
int k;
int status;
int fifotest1, fifotest2;

int open_flag_1, open_flag_2;

int *buffer_write_address_1;
int *buffer_read_address_1;
int buffer_read_1;
int nwritten_1;
int nread_1;

int *buffer_write_address_2;
int *buffer_read_address_2;
int buffer_read_2;
int nwritten_2;
int nread_2;

int mysemid;
int semval;
int semint;
int semnew;

char *ffile_c2p = "./fifo.c2p";

printf( "\n*********** FIFO with SEMAPHORE COMMUNICATION *************\n" );

/* delete old FIFO if it already exists */

fifotest1 = unlink( ffile_c2p );

if( fifotest1 <0 ) printf( "\nFIFO file does not yet exist ...\n" );

/* create FIFO for PARENT and CHILD */

fifotest2 = mkfifo( ffile_c2p , PMODE );

if( fifotest2 < 0 ) {
printf( "\nError creating FIFO.  Exit now ...\n" );
exit(5);
}

printf( "\nmkfifo return = %d , So FIFO creation is successful.",fifotest2 );
printf( "\nChild to Parent fifo file is:%s\n",ffile_c2p );

/* create semaphore ID for the FIFO file */

mysemid = semget( ftok(ffile_c2p,'A'), 1 , IPC_CREAT | 00600 ); 

if( mysemid < 0 ) {
printf( "\nError creating SEMAPHORE ID.  Exit now ...\n" );
exit(5);
}

/* set semaphore value for the FIFO file */

semint  = semctl( mysemid , 0 , SETVAL , 0 );

if( semint < 0 ) {
printf( "\nError setting SEMAPHORE Value.  Exit now ...\n" );
exit(5);
}

/* get semaphore value for the FIFO file */

semval  = semctl( mysemid , 0 , GETVAL );

if( semval < 0 ) {
printf( "\nError getting SEMAPHORE Value.  Exit now ...\n" );
exit(5);
}

printf( "\nSemaphore ID for FIFO file %s is %d", ffile_c2p , mysemid );
printf( "\nSet Initial Semaphore value = %d\n", semval );

/* Print current process id value */

printf( "\nPre-fork Process ID:%d", getpid() );
printf( "\nPre-fork Parent Process ID:%d\n",getppid() );

printf( "\nFork a new process ...\n" );

/* flush stdout stream and fork a new process */

fflush( stdout );

i = fork();

if( i <0 ) {
  printf( "Return code = %d Fork failed. Clean up and exit.\n", i );
  exit(6);
}
else { 
  if( i == 0 ) {

    printf( "\n\tfork() return value = %d",i );
    printf( "\n\tThis process is a CHILD with Process ID:%d", getpid() );
    printf( "\n\tCHILD'S Parent Process ID:%d\n",getppid() );

/* use FIFO_C2P to communicate information between CHILD and PARENT */

    k = getpid();

    open_flag_1  = open( ffile_c2p , O_RDWR );

    if( open_flag_1 < 0 ) {
      printf( "\nError opening FIFO.  Exit now ...\n" );
      exit(7);
    }

    printf( "\n\tCHILD: fifo file is:%s",ffile_c2p );
    printf( "\n\tCHILD: fifo file descriptor = %d\n", open_flag_1 );
 
    buffer_write_address_1 = &k;

    nwritten_1 = write( open_flag_1 , buffer_write_address_1 , 1024 );

    if( nwritten_1 < 0 ) {
      printf( "\nError writing to  FIFO.  Exit now ...\n" );
      exit(7);
    }

    printf( "\n\tCHILD: Number of bytes written to FIFO = %d", nwritten_1 );
    printf( "\n\tCHILD: Sent buffer is: %d\n",*buffer_write_address_1 );

/* Get SEMAPHORE Value for FIFO */

    semval = semctl( mysemid , 0 , GETVAL );
    if( semval < 0 ) {
      printf( "\nError getting SEMAPHORE Value.  Exit now ...\n" );
      exit(7);
    }
    printf( "\n\tCHILD: Current Semaphore value SemVal = %d", semval );

/* Set SEMAPHORE Value for FIFO */

    semnew = semctl( mysemid , 0 , SETVAL , (semval+1) );
    if( semnew < 0 ) {
      printf( "\nError setting SEMAPHORE Value.  Exit now ...\n" );
      exit(7);
    }

/* get new SEMAPHORE Value for FIFO */

    semval = semctl( mysemid , 0 , GETVAL );

    if( semval < 0 ) {
      printf( "\nError getting SEMAPHORE Value.  Exit now ...\n" );
      exit(7);
    }
    printf( "\n\tCHILD: Increment Semaphore value to SemVal = %d", semval );

    if(semval == 1) {
    printf( "\n\tCHILD: SemVal = 1: Wait for PARENT to WRITE\n" );
    }
    else {
    printf( "\n\tCHILD: SemVal > 1: PARENT and CHILD have WRITTEN" );
    }

/* Wait until both PARENT and CHILD have written to FIFO */

    while( 1 == semctl( mysemid , 0 , GETVAL )) sleep( 1 );

    printf( "\n\tCHILD: Time to READ from FIFO ...\n" );

    buffer_read_address_1 = &buffer_read_1;

    nread_1  = read( open_flag_1 , buffer_read_address_1 , 1024 );

    if( nread_1 < 0 ) {
      printf( "\nError reading from FIFO.  Exit now ...\n" );
      exit(7);
    }

    printf( "\n\tCHILD: Number of bytes read from FIFO = %d", nread_1 );
    printf( "\n\tCHILD: Received buffer is: %d\n\n", buffer_read_1 );

    close( open_flag_1 );

    fflush( stdout );

  }
  else {

    printf( "\n\tfork return value =  %d",i );
    printf( "\n\tThis process is a Parent with Process ID:%d\n", getpid() );

    fflush( stdout );

    system( "ps -l | grep 'a.out' " );

/* use FIFO_C2P to read information about CHILD to PARENT  */

    open_flag_2  = open( ffile_c2p , O_RDWR );

    if( open_flag_2 < 0 ) {
      printf( "\nError opening FIFO.  Exit now ...\n" );
      exit(8);
    }

    printf( "\n\tPARENT: fifo file is:%s",ffile_c2p );
    printf( "\n\tPARENT: fifo file descriptor = %d\n", open_flag_2 );

    k = getpid();

    buffer_write_address_2 = &k;

    nwritten_2 = write(open_flag_2 , buffer_write_address_2 , 1024 );

    if( nwritten_2 < 0 ) {
      printf( "\nError writing to FIFO.  Exit now ...\n" );
      exit(8);
    }

    printf( "\n\tPARENT: Number of bytes written to FIFO = %d", nwritten_2 );
    printf( "\n\tPARENT: Sent buffer is: %d\n",*buffer_write_address_2 );

/* Get SEMAPHORE Value for FIFO */

    semval = semctl( mysemid , 0 , GETVAL );
    if( semval < 0 ) {
      printf( "\nError getting SEMAPHORE Value.  Exit now ...\n" );
      exit(8);
    }
    printf( "\n\tPARENT: Current Semaphore value SemVal = %d", semval );

/* Set SEMAPHORE Value for FIFO */

    semnew = semctl( mysemid , 0 , SETVAL , (semval+1) );
    if( semnew < 0 ) {
      printf( "\nError setting SEMAPHORE Value.  Exit now ...\n" );
      exit(8);
    }

/* Get new SEMAPHORE Value for FIFO */

    semval = semctl( mysemid , 0 , GETVAL );
    if( semval < 0 ) {
      printf( "\nError getting SEMAPHORE Value.  Exit now ...\n" );
      exit(8);
    }
    printf( "\n\tPARENT: Increment Semaphore value to SemVal = %d", semval );

    if(semval == 1) {
    printf( "\n\tPARENT: SemVal = 1: Wait for CHILD to WRITE\n" );
    }
    else {
    printf( "\n\tPARENT: SemVal > 1: PARENT and CHILD have WRITTEN" );
    }

/* Wait for both PARENT and CHILD to write to FIFO */

    while( 1 == semctl( mysemid , 0 , GETVAL )) sleep( 1 );

    printf( "\n\tPARENT: Time to READ from FIFO ...\n" );

    buffer_read_address_2 = &buffer_read_2;

    nread_2 = read( open_flag_2 , buffer_read_address_2 , 1024 );

    if( nread_2 < 0 ) {
      printf( "\nError reading from FIFO.  Exit now ...\n" );
      exit(8);
    }
    printf( "\n\tPARENT: Number of bytes read from FIFO = %d", nread_2 );
    printf( "\n\tPARENT: Received buffer is: %d\n\n", buffer_read_2 );

    close( open_flag_2 );

    printf( "\n\tPARENT: Waiting for child to finish ... show the ZOMBIE\n" );

/* Set options = WNOHANG: This lets the parent process continue          */
/* while loop statements occur for whenever j is 0 (child still running) */

    while ( ! (j = waitpid ( i , &status , WNOHANG )) ) {
      fflush(stdout);
      sleep(1);
      system( "ps -l | grep 'a.out' | grep 'Z' " );
    }

    printf( "\n\nwaitpid() return value = %d\n\n",  status );

    system( "ps -l | grep 'a.out' " );

    printf( "\nChild Process ID %d has terminated.", j );
    printf( "\nParent Process ID %d now exits BuildFifo_Sem()\n", getpid() ); 
    printf( "\nParent Process ID %d now terminates.\n", getpid() ); 
  }
}

}