/* This is a simple program that forks off a child 
   process and communications take place between the 
   parent and the child in a few different ways.

   This program has been compiled and tested on an 
   alpha with the command 'cc ass8.c'. 

   The first method of communication is demonstrated 
   using half duplex UNIX pipes. The second method is 
   by using named pipes (FIFOs). In this second method,
   I have made use of the half duplex pipes to guarentee 
   the relative timing between the two processes.
   
   After the program has run through, the child terminates
   and after a call(s) to waitpid, the parent then exits normally.

                               -Kalen          */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

main()
{

 pid_t ierr;         /* Variable for return value of fork()             */
 pid_t parent_pid;   /* PID of parent process                           */
 pid_t child_pid;    /* PID of child process                            */
 pid_t child_ppid;   /* PID of child's parent                           */
 int dummy;          /* Variable to contain return value of waitpid()
                        and other temporary return values.   */
 ssize_t io_err;     /* Variable for return value of write() or read()  */

 int *stat;          /* Status of wait function                         */

 int HDP_ptoc[2], HDP_ctop[2]; /* File descriptors for half-duplex pipes:
                              one set for each direction of communication */ 

 char string1[]="Kalen deserves a nine!\n"; /* Messages */
 char string2[]="He sure does!\n";

 char hdp_buffer[80];  /* String to keep messages from half-duplex pipe */
 char fifo_buffer[80]; /* String to keep messages from named pipe       */

 char go[]="go";       /* Message used for relative timing of processes */

 FILE *fifo;           /* Pointer to special named pipe   */


 printf("\nStart of Program!\n\n");

/* Find PID of this process and print it to screen */   

 parent_pid = getpid();
 printf("Before fork: Parent PID: %d\n\n", parent_pid);
 sleep(3);

/* Create the pipes that will be used in the bidirectional communication
   between parent and child. One pipe for each direction - hence the 
   name half-duplex. Complain on error and return with exit status 1
   after exiting.                         */

 if ((dummy = pipe(HDP_ptoc)) == -1 ) {
    printf("Error on opening pipe HDP_ptoc");
    exit(1); }
 if ((dummy = pipe(HDP_ctop)) == -1 ) {
    printf("Error on opening pipe HDP_ctop");
    exit(1); }


/* Execute the fork and complain on error  */

 if ( (ierr = fork()) < 0 ) {
    printf("Error on calling of fork()\n");
    exit(1); }      
 else {
    printf("Fork succeeded.\n");          /* This will get written twice */
    sleep(3);  }
    
 /* Both processes now examine ierr (parent gets child's PID
    returned and child gets 0 returned from fork function) and decide
    if they are the parent or child and will print messages stating 
    what they are and their PIDs. The child will also find
    what is the PID of its parent and print it. 
     
    For interprocess communication using half-duplex pipes,
    the parent and child will close the part of the pipe 
    that they will not use. They will then pass a message back 
    and forth over the pipes and will print the progress on
    the screen.      */
                                          
 printf("\n");
 if (ierr != 0) {                      /* I am the PARENT  */
    child_pid=ierr;
    printf("After fork: Parent: My PID: %d\n", parent_pid);
    printf("After fork: Parent: My Childs PID: %d\n", child_pid);

    close(HDP_ptoc[0]);   /* Close reading half of pipe that 
                             communicates from parent to child */
    close(HDP_ctop[1]);   /* Close writing half of pipe that
                             communicates from child to parent */

    /* Create a named pipe (FIFO) for later use */
    umask(0);
    if ((ierr = mknod("/tmp/KALEN_FIFO", S_IFIFO|S_IRWXU, 0)) == -1) {
         printf("Error on opening named pipe.");
         exit(1); }
    printf("\nJust created named pipe KALEN_FIFO in /tmp \n");


  /* Write to child through half duplex pipe  */

    printf("\n Parent writing child through half-duplex pipe...");
    if ((io_err = 
            write(HDP_ptoc[1], string1, strlen(string1))) == -1) { 
         printf("Error writing string1 to child.");
         exit(1); }

  /* Read message from child through half duplex pipe */

    if ((io_err =
            read(HDP_ctop[0], hdp_buffer, sizeof(hdp_buffer))) <= 0) {
         printf("Error upon parent reading from HDP from child");
         exit(1); }
    printf("\nParent: Message from child: %s\n", hdp_buffer);

  /* Open pipe to FIFO, write to it, and close it. */

    printf("\nParent: Opening FIFO to write");
    fifo = fopen("/tmp/KALEN_FIFO", "w");
    printf(" \nParent: sending message to child through named pipe...");
    fputs(string1, fifo); 
    printf("\nParent: Closing FIFO to write");
    fclose(fifo);
 
   /* Read message from child through FIFO. Do not open FIFO
      until child has written to it (behavior from 1st line). */

    read(HDP_ctop[0], hdp_buffer, sizeof(hdp_buffer)); 
    printf("\nParent: Opening FIFO to read");
    fifo = fopen("/tmp/KALEN_FIFO", "r");
    fgets( fifo_buffer, 80, fifo);
    printf("\nParent: Message from child: %s\n", fifo_buffer);
    printf("\nParent: Closing FIFO to read");
    fclose(fifo);

    /* Wait for the child to terminate */

    do {
        sleep(2);
        printf("\nParent: Is child still there? ChildPID:%d\n", child_pid);
       } while ((dummy = waitpid(child_pid,stat,WNOHANG)) == 0); 
 
    printf("\nParent: Child appears to have exited.");
    printf("\n\nParent: I am still alive: PID: %d\n", parent_pid); 
    printf("Parent is now exiting.\n"); }




 else if ( ierr == 0 ) {                /* I am the CHILD  */
    child_pid = getpid();
    child_ppid = getppid();               /* Get parent's PID   */
    printf("After fork: Child: My PID: %d\n", child_pid);
    printf("After fork: Child: My PPID: %d\n", child_ppid); 

    close(HDP_ptoc[1]);   /* Close writing half of pipe that 
                             communicates from parent to child */
    close(HDP_ctop[0]);   /* Close reading half of pipe that
                             communicates from child to parent */

  /* Read message from parent through half duplex pipe */

    if ((io_err = 
            read(HDP_ptoc[0], hdp_buffer, sizeof(hdp_buffer))) <= 0) {
         printf("Error upon child reading from HDP from parent");
         exit(1); } 
    printf("\nChild: Message from parent: %s\n", hdp_buffer);
 
  /* Send message to parent through half duplex pipe */

    printf("\n Child writing parent through half-duplex pipe...");
    if ((io_err =
            write(HDP_ctop[1], string2, strlen(string2))) == -1) {
         printf("Error writing string2 to parent.");
         exit(1); }
    sleep(4);

  /* Open FIFO and read message from parent and print it. Then 
     open FIFO for writing and send a message back.   */


    printf("\nChild: Opening FIFO to read");
    fifo = fopen("/tmp/KALEN_FIFO", "r");
    fgets( fifo_buffer, 80, fifo);
    printf("\nChild: Message from parent: %s\n", fifo_buffer);
    printf("\nChild: Closing FIFO to read");
    fclose(fifo);
    write(HDP_ctop[1], go, strlen(go));

  /* Send message to parent through FIFO */

    printf("\nChild: Opening FIFO to write");
    fifo = fopen("/tmp/KALEN_FIFO", "w");
    printf(" \nChild: sending message to parent through named pipe...");
    fputs(string2, fifo); 
    fclose(fifo);



    sleep(5);
    printf("Child process now exiting\n"); }

 exit(0);                                      /* Terminate normally   */
}