/* Author: Pete Broadwell, Grinnell College
        Minor Adjustments for Linux made 11/29/00 by Henry M. Walker
   This program simulates a scenario of the readers-writers program in which
   multiple reader and multiple writer processes (which are all clients, in
   this case) communicate through a pair of datagram sockets.
   A single server process manages this data transfer by reading integers from
   the writers' socket, processing them, and writing them to the readers'
   socket. The server deals with the reader processes iteratively. */

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>   /* socket command libraries needed by some compilers */
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>

#define WRITE_LENGTH 5 
#define STRING_LENGTH 30 
#define READ_LENGTH 5
#define NUM_READERS 3
#define NUM_WRITERS 3
#define READ_PORT 1229 /* local port on which read read is established */
#define WRITE_PORT 1230 /* local port on which write socket is established */

int read_sock, write_sock; /* global variable for the communication sockets */

/* procedure to remove socket file and exit */
void clean_up(int cond, int *read_sock, int *write_sock)
{ printf("Exiting now.\n");
  /* remove both socket files */ 
  close(*read_sock);
  close(*write_sock);
  exit(cond);
} /* end of clean_up */

int main(void)
{ pid_t pid;
  int p_count;
  pid_t proc[NUM_READERS+NUM_WRITERS];

  /* spawn client processes */
  for (p_count = 1; p_count <= NUM_READERS; p_count++)
    { pid = fork();
      if (-1 == pid)
        { perror ("Error in fork");
          exit (1);
        }

      if (0 == pid)
        { /* processing for reader */
          int read_sock; /* file descriptor for read socket */
          struct sockaddr_in read_name; /* read socket address structure */
          int value; /* variable for number read from socket */
          size_t len; /* variable for the size of the read_name structure */
          int count;
          char msg[STRING_LENGTH];

          /* set the read socket descriptor */
          read_sock = socket(AF_INET, SOCK_DGRAM, 0);
          if (read_sock < 0)
            { perror ("Error opening channel");
              clean_up(1, &read_sock, &write_sock);
            }

          /* set the physical address (read_name) of the read_sock descriptor */
          bzero(&read_name, sizeof(read_name));
          read_name.sin_family = AF_INET;
          read_name.sin_port = htons(READ_PORT);

          sleep(1); /* wait for server to create the read socket */
          printf("Reader %d is alive.\n", p_count);
	  
          /* clean out msg array */
          for (count = 0; count < STRING_LENGTH; count++)
            msg[count] = 0;

          /* write greeting to the read socket */
          len = sizeof(read_name);
          sprintf(msg, "Hello from reader %d", p_count);
          sendto(read_sock, &msg, STRING_LENGTH, 0,
                 (struct sockaddr *)&read_name, len);
          printf("Reader %d has sent  %s  to read socket.\n", p_count, msg);

          /* read integers from the read socket */
          for (count = 0; count < READ_LENGTH; count++)
            { recvfrom(read_sock, &value, 4, 0,
                       (struct sockaddr *)&read_name, &len);
              printf("Reader %d has received %d from read socket.\n", p_count,
                   value);
            }
          printf("Reader %d done.\n", p_count);
          close(read_sock); /* close connection to read socket */
          exit(0);
        }
      else proc[p_count - 1] = pid;
    } /* end of processing for reader */

  /* spawn writer processes */
  for (p_count = 1; p_count <= NUM_WRITERS; p_count++)
    { pid = fork();
      if (-1 == pid)
        { perror ("Error in fork");
          exit (1);
        }

      if (0 == pid)
        { /* processing for writer */
          int write_sock;
          struct sockaddr_in write_name;
          int count, data;
          size_t len;

          /* set write socket descriptor */
          write_sock = socket(AF_INET, SOCK_DGRAM, 0);
          if (write_sock < 0)
            { perror ("Error opening channel");
              clean_up(1, &read_sock, &write_sock);
            }

       /* set the physical address (write_name) of the write_sock descriptor */
          bzero(&write_name, sizeof(write_name));
          write_name.sin_family = AF_INET;
          write_name.sin_port = htons(WRITE_PORT);

          sleep(1); /* wait for server to create write socket */\
          printf("Writer %d is alive.\n", p_count);

          /* write integers to write socket */
          len = sizeof(write_name);
          for (count = 1; count <= WRITE_LENGTH; count++)
            { data = ( (p_count - 1) * 10) + count;
              sendto(write_sock, &data, 4, 0, (struct sockaddr *)&write_name, len);
              printf("Writer %d has written %d to write socket.\n", p_count, data);
            }

          printf("Writer %d done.\n", p_count);
          close(write_sock); /* close connection to write socket */
          exit(0);
        }
      else proc[NUM_READERS + p_count - 1] = pid;
    } /* end of processing for writer */

  { /* processing for main server */
    int read_sock, write_sock;
    struct sockaddr_in read_name, write_name;
    size_t read_len, write_len;
    char msg[STRING_LENGTH];
    int count, place, i, value;

    /* set socket descriptors */
    read_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (read_sock < 0)
      { perror ("Error opening channel");
        clean_up(1, &read_sock, &write_sock);
      }

    write_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (write_sock < 0)
      { perror ("Error opening channel");
        clean_up(1, &read_sock, &write_sock);
      }

    /* set the physical addresses of the socket descriptors */
    bzero(&read_name, sizeof(read_name));
    read_name.sin_family = AF_INET;
    read_name.sin_port = htons(READ_PORT);

    bzero(&write_name, sizeof(write_name));
    write_name.sin_family = AF_INET;
    write_name.sin_port = htons(WRITE_PORT);

    /* bind the socket addresses to the socket descriptors */
    if (bind(read_sock, (struct sockaddr *)&read_name, sizeof(read_name)) < 0)
      { perror ("Error naming channel");
        clean_up(1, &read_sock, &write_sock);
      }

    if (bind(write_sock, (struct sockaddr *)&write_name, sizeof(write_name)) < 0)
      { perror ("Error naming channel");
        clean_up(1, &read_sock, &write_sock);
      }

    printf("Main server is on line.\n");

    /* initialize the msg array */
    for (count = 0; count < STRING_LENGTH; count++)
      msg[count] = 0;

    write_len = sizeof(write_name);
    read_len = sizeof(read_name);

    /* manage data transfer */
    for (count = 1; count <= NUM_READERS; count++)
      { /* read readers' greetings one at a time */ 
        recvfrom(read_sock, &msg, STRING_LENGTH, 0, 
                 (struct sockaddr *)&read_name, &read_len);
        printf("Server has read  %s  from read socket.\n", msg);

        /* read a number of integers from the write socket, alter them, and
           write them to the read socket */
        for (i = 0; i < READ_LENGTH; i++)
          { place = ( (count - 1) * 10) + i;
            recvfrom(write_sock, &value, 4, 0,
                     (struct sockaddr *)&write_name, &write_len);
            printf("Server has read %d from write socket.\n", value);
            value = value * 100;
            sendto(read_sock, &value, 4, 0,
                   (struct sockaddr *)&read_name, read_len);
            printf("Server has written %d to read socket.\n", value);
          }
       }

    for (i = 0; i < NUM_READERS + NUM_WRITERS; i++)
      waitpid(proc[i], NULL, 0); /* Wait for child processes to terminate */
    clean_up(0, &read_sock, &write_sock); /* Exit with no errors */

  } /* end of processing for main server */
} /* end of main */
