/* ipc.c - A program to showcase interprocess communication  */
/* Uses sockets, pipes, and fifos */
/* Must be compiled with ANSI compiler */
/* Programmed for Physics 673 - Dr. D.M. Gingrich */
/* Author: R.A. Davis  November 20, 1996 */

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

#define SOCKET_PATH  "./socket"

main()
{
    int ParentId,ChildId,MyId,ChildStatus,Status,pipe1[2],addrlen;
    int pipe2[2],parentlen,outlen,nbytes,FifoDes,SocketDes;
    int ns;
    char *FifoPath = "./ipc.fifo";
    char *outmsg;
    char inmsg[BUFSIZ];
    struct sockaddr_un  cli_addr,serv_addr;

    printf("Starting program.\n");

    /* First the main program displays its PID and the PID of the shell */

    MyId = getpid();
    ParentId = getppid();
    printf("PARENT: My process ID is %i\n",MyId);
    printf("PARENT: My parent (the grandparent) is %i\n",ParentId);
    printf("PARENT: Let's see if we're right!\n");

    /* Flush the buffers and run a ps to see what's up */

    fflush(stdout);
    system("ps -lu $USER | grep 'a.out'");

    /* Open 2 pipes */
 
    Status = pipe(pipe1);
    printf("PARENT: Pipe 1 read stream on %i\n",pipe1[1]);    
    printf("PARENT: Pipe 1 write stream on %i\n",pipe1[2]);

    /* This block is executed if there is a problem with the pipe */

    if (Status < 0) {
        printf("PARENT: Error in creating pipe 1 - terminating.");
        exit(2); 
      }

    Status = pipe(pipe2);
    printf("PARENT: Pipe 2 read stream on %i\n",pipe2[1]);    
    printf("PARENT: Pipe 2 write stream on %i\n",pipe2[2]);

    /* This block is executed if there is a problem with the pipe */

    if (Status < 0) {
        printf("PARENT: Error in creating pipe 2 - terminating.");
        exit(2); 
      }
    fflush(stdout);

    /* Create a FIFO file with read write permissions */
    /* First delete existing FIFO if any */

    Status = unlink(FifoPath);
    if (Status != 0) {
      printf("PARENT: Warning - error deleting fifo file (maybe it just doesn't exist.  Not to worry.\n");
    }

    Status = mkfifo(FifoPath,00600);

    if (Status < 0) {
        printf("PARENT: Error in creating FIFO file - terminating.");
        exit(4); 
      }
    fflush(stdout);

    /* Fork the child */

    ChildId = fork();

    /* This block is executed if there is a problem with the fork */

    if (ChildId < 0) {
        printf("PARENT: Error in forking child - terminating.");
        exit(1); 
      }

    /* ************ PARENT ************* */
    /* Parent executes this block - ps to see child running */

    if (ChildId > 0) {
        printf("PARENT: I have given birth to a process ID %i\n",ChildId);
        fflush(stdout);
        system("ps -lu $USER | grep 'a.out'");

    /* !!!! SOCKETS !!!! */
    /* Open a socket in the parent and bind to address */

        SocketDes = socket(AF_UNIX,SOCK_STREAM,0);
        if (SocketDes < 0) {
          printf("PARENT: Problem opening socket - terminating.\n");
          exit(7);
        }
        printf("PARENT: Socket opened with descriptor : %i\n",SocketDes);

        serv_addr.sun_family = AF_UNIX;
        strcpy(serv_addr.sun_path,SOCKET_PATH);
        addrlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
        Status = bind(SocketDes,(struct sockaddr *) &serv_addr,addrlen);
        if (Status < 0) {
          printf("PARENT: Problem binding socket to address - terminating.\n");
          exit(8);
        }
        fflush(stdout);

    /* Listen to indicate readiness of socket */

       listen(SocketDes,5);

    /* Block while waiting to accept socket connection from child */

       addrlen = sizeof(cli_addr);
       ns = accept(SocketDes,(struct sockaddr *) &cli_addr, &addrlen);
       printf("PARENT: New socket connection accepted on descriptor %i\n",ns);

    /* Close original socket */

       close(SocketDes);
       fflush(stdout);

    /* Read from socket */

       printf("PARENT: Waiting for message on socket.\n");
       nbytes = read(ns,inmsg,BUFSIZ);
       while (nbytes == 0) {   /* Loop until message in socket */
         nbytes = read(ns,inmsg,BUFSIZ);
       }

    /* Check to see if read is successful */

        if (nbytes < 0) {
          printf("PARENT: Socket read errno %i\n",errno);
          printf("PARENT: Error reading from socket - terminating.\n");
          exit(3); }
        printf("PARENT: Number of bytes read from socket = %i\n",nbytes);

    /* Print incoming message */

        printf("PARENT: Incoming message from socket: %s\n",inmsg);
        fflush(stdout);

    /* Write to socket */

        outmsg = "Another test of the socket";
        outlen = strlen(outmsg);
        printf("PARENT: Outgoing length to socket: %i\n",outlen);
        printf("PARENT: Outgoing message to socket: %s\n",outmsg);

    /* Make sure write is successful */

        nbytes = write(ns,outmsg,outlen);
        if (nbytes < 0) {
          printf("PARENT: Problem writing to socket - terminating.\n");
          exit(3); }
        printf("PARENT: Number of bytes written to socket = %i\n",nbytes);
        fflush(stdout);


    /* !!!! PIPES !!!! */
    /* Now close the read pipe on 2 and write pipe on 1 */

        close(pipe1[1]);
        close(pipe2[0]);

    /* Write to pipe 2 */

        outmsg = "You look great kid!";
        outlen = strlen(outmsg);
        printf("PARENT: Outgoing message length: %i\n",outlen);
        printf("PARENT: Outgoing message to pipe: %s\n",outmsg);
        nbytes = write(pipe2[1],outmsg,outlen);
        if (nbytes < 0) {
          printf("PARENT: Problem writing to pipe - terminating.\n");
          exit(3); }
        printf("PARENT: Number of bytes written to pipe = %i\n",nbytes);
        fflush(stdout);

    /* Read from pipe 1 */

        nbytes = read(pipe1[0],inmsg,BUFSIZ);

    /* Check to see if read is successful */

        if (nbytes < 0) {
          printf("PARENT: Error reading from pipe - terminating");
          exit(3); }
        printf("PARENT: Number of bytes read from pipe = %i\n",nbytes);

    /* Print incoming message */

        printf("PARENT: Incoming message from pipe: %s\n",inmsg);
        fflush(stdout);

    /* Close remaining pipe file descriptors */

        close(pipe1[0]);
        close(pipe2[1]);

    /* !!!! FIFO !!!! */
    /* Open the FIFO for read/write */

        FifoDes = open(FifoPath,O_RDWR);
        if (FifoDes < 0) {
          printf("PARENT: Error in opening FIFO - terminating.");
          exit(5); 
      }
    /* Read from FIFO */

        nbytes = read(FifoDes,inmsg,BUFSIZ);

    /* Check to see if read is successful */

        if (nbytes < 0) {
          printf("PARENT: Error reading from FIFO - terminating");
          exit(6); }
        printf("PARENT: Number of bytes read from FIFO = %i\n",nbytes);
        fflush(stdout);

    /* Print incoming message */

        printf("PARENT: Incoming message from FIFO: %s\n",inmsg);

    /* Write to FIFO */

        outmsg = "Here's a FIFO to you kid!";
        outlen = strlen(outmsg);
        printf("PARENT: Writing message to FIFO : %s\n",outmsg);
        nbytes = write(FifoDes,outmsg,outlen);
        if (nbytes < 0) {
          printf("PARENT: Problem writing to FIFO - terminating.");
          exit(6); }
        printf("PARENT: Number of bytes written to FIFO = %i\n",nbytes);
        fflush(stdout);

    /* Close and delete FIFO */

       close(FifoDes);
       Status = unlink(FifoPath);
       if (Status != 0) {
         printf("PARENT: Warning - error deleting fifo file.\n");
       }

    /* Close and unlink socket */

       close(ns);
       Status = unlink(SOCKET_PATH);
       if (Status != 0) {
         printf("PARENT: Warning - error deleting socket address.\n");
       }

    /* Now wait for child to die */

        printf("PARENT: Now we wait for baby to die.\n");
        ChildStatus = waitpid(ChildId,0,WNOHANG);/* Use NOHANG to avoid stop */
        while (ChildStatus == 0) {  /* Wait for child to die */
          system("ps -lu $USER | grep ' Z '");  /* look for zombie */
          ChildStatus = waitpid(ChildId,0,WNOHANG);  
        }
        printf("PARENT: Child has terminated. Zombie should be cleaned up.\n");
        fflush(stdout);
        system("ps -lu $USER | grep 'a.out'");   /* ps to see child is gone */
        printf("PARENT: Now we terminate.\n");
      }


    /* ********** CHILD *********** */
    /* The child executes this block - displays its id and parent id */

    if (ChildId == 0) {
        printf("CHILD: I'm alive - I'M ALIVE!\n");
        MyId = getpid();
        ParentId = getppid();
        printf("CHILD: I think my name is %i\n",MyId);
        printf("CHILD: My parent is %i\n",ParentId);
    
    /* !!!! SOCKET !!!! */
    /* Open a socket in the child - sleep to allow parent to set up */

        sleep(3);
        SocketDes = socket(AF_UNIX,SOCK_STREAM,0);
        if (SocketDes < 0) {
          printf("CHILD: Problem opening socket - terminating.\n");
          exit(7);
        }
        printf("CHILD: Socket opened with descriptor : %i\n",SocketDes);
        bzero ((char *) &serv_addr, sizeof(serv_addr)); /* Clear address */
        serv_addr.sun_family = AF_UNIX;
        strcpy(serv_addr.sun_path, SOCKET_PATH);
        addrlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);

    /* Connect to parent socket */
 
        Status = connect(SocketDes, (struct sockaddr *) &serv_addr,addrlen);
        if (Status < 0) {
          printf("CHILD: Connect error %i\n",errno);
          printf("CHILD: Problem connecting to parent socket - exiting.\n");
          exit(9);
        }
        fflush(stdout);

    /* Write to socket */

        outmsg = "Test of socket";
        outlen = strlen(outmsg);
        printf("CHILD: Outgoing length to socket: %i\n",outlen);
        printf("CHILD: Outgoing message to socket: %s\n",outmsg);

    /* Make sure write is successful */

        nbytes = write(SocketDes,outmsg,outlen);
        if (nbytes < 0) {
          printf("CHILD: Problem writing to socket - terminating.");
          exit(3); }
        printf("CHILD: Number of bytes written to socket = %i\n",nbytes);
        fflush(stdout);

    /* Read from socket */

       printf("CHILD: Waiting for message on socket.\n");
       nbytes = read(SocketDes,inmsg,BUFSIZ);
       while (nbytes == 0) {   /* Loop until message in socket */
         nbytes = read(ns,inmsg,BUFSIZ);
       }

    /* Check to see if read is successful */

        if (nbytes < 0) {
          printf("CHILD: Socket read errno %i\n",errno);
          printf("CHILD: Error reading from socket - terminating.\n");
          exit(3); }
        printf("CHILD: Number of bytes read from socket = %i\n",nbytes);

    /* Print incoming message */

        printf("CHILD: Incoming message from socket: %s\n",inmsg);
        fflush(stdout);

    /* !!!! PIPES !!!! */
    /* Now close the read pipe on 1 and write pipe on 2 */

        close(pipe1[0]);
        close(pipe2[1]);

    /* Write to pipe 1 */


        outmsg = "Hows it going pops?";
        outlen = strlen(outmsg);
        printf("CHILD: Outgoing message length: %i\n",outlen);
        printf("CHILD: Outgoing message to pipe: %s\n",outmsg);

    /* Make sure write is successful */

        nbytes = write(pipe1[1],outmsg,outlen);
        if (nbytes < 0) {
          printf("CHILD: Problem writing to pipe - terminating.");
          exit(3); }
        printf("CHILD: Number of bytes written to pipe = %i\n",nbytes);
        fflush(stdout);

    /* Read from pipe 2 */

        bzero ((char *) inmsg, BUFSIZ); /* Clear variable */
        nbytes = read(pipe2[0],inmsg,BUFSIZ);

    /* Check to see if read successful */
 
        if (nbytes < 0) {
          printf("CHILD: Problem reading from pipe - terminating.");
          exit(3); }
        printf("CHILD: Number of bytes read from pipe = %i\n",nbytes);

    /* Print out message read from pipe */

        printf("CHILD: Message read from pipe : %s\n",inmsg);
        fflush(stdout);

    /* Close remaining file descriptors */

        close(pipe1[1]);
        close(pipe2[0]);

    /* !!!! FIFO !!!! */
    /* Open the FIFO for read/write */

        FifoDes = open(FifoPath,O_RDWR);
        if (FifoDes < 0) {
          printf("CHILD: Error in opening FIFO - terminating.");
          exit(5); 
      }

    /* Write to FIFO */

        outmsg = "Here's a message through the FIFO.";
        outlen = strlen(outmsg);
        printf("CHILD: Writing message to FIFO : %s\n",outmsg);
        nbytes = write(FifoDes,outmsg,outlen);
        if (nbytes < 0) {
          printf("CHILD: Problem writing to FIFO - terminating.");
          exit(6); }
        printf("CHILD: Number of bytes written to FIFO = %i\n",nbytes);
        fflush(stdout);

    /* Sleep to make sure parent picks up message first */

        sleep(5);

    /* Read from FIFO */

        bzero ((char *) inmsg, BUFSIZ); /* Clear variable */
        nbytes = read(FifoDes,inmsg,BUFSIZ);
        while (nbytes == 0) {
          nbytes = read(FifoDes,inmsg,BUFSIZ);
        }

    /* Check to see if read is successful */

        if (nbytes < 0) {
          printf("CHILD: Error reading from FIFO - terminating");
          exit(6); }
        printf("CHILD: Number of bytes read from FIFO = %i\n",nbytes);

    /* Print incoming message */

        printf("CHILD: Incoming message from FIFO: %s\n",inmsg);
        fflush(stdout);

    /* Close socket */

        Status = close(SocketDes);
        if (Status != 0) {
          printf("CHILD: Warning - error deleting socket address.\n");
        }

    /* Now child sleeps before termination */

        printf("CHILD: Now I go to sleep before I die.\n");
        fflush(stdout);
        sleep(5);       /* Child sleeps to run for a while */
        printf("CHILD: My life is half over!\n");
        fflush(stdout);
        sleep(5);
        printf("CHILD: Now I die. Look for my Zombie!\n");
        exit(0);


      }
    return(0);
  }