/* Author: Pete Broadwell, Grinnell College
   Minor Adjustments for Linux made 11/29/00 by Henry M. Walker

   This program spawns multiple server child processes to handle socket
   connection requests from multiple clients. In this way, the parent
   server process handles the client connection requests recursively.
   This program represents an alteration of the normal readers-writers
   problem, in that the parent server effectively assigns one writer
   to each reader, and the two communicate through a proprietary connected
   streaming socket.

*/

#include <sys/types.h>
#include <time.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>

#define SOCKET_NAME "Com2" /* name of the socket file */
#define NUM_READERS 3 /* number of reader (client) processes to be spawned */
#define MAX_READ 10.0 /* maximum number of integers that clients will read */

/* procedure to remove socket file and exit */
void clean_up(int cond, int *sock)
{ printf("Exiting now.\n");
  close(*sock); /* close listening socket descriptor */
  unlink(SOCKET_NAME); /* remove the socket file */
  exit(cond);
} /* end of clean_up */

int main(void)
{ pid_t pid;
  int p_count;
  pid_t proc[2*NUM_READERS];

  /* spawn reader (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 client == reader */
          int sock; /* variable for listening socket descriptor */
          struct sockaddr_un cli_name; /* socket address structure */
          int count;
          int ints_read; /* variable to store the number of integers each
                            client will read */
          int value; /* variable for number read from socket */
          time_t seed; /* variable to initialize random number generator */

          time(&seed);           /* initialize the random number generator */
          srand(seed + p_count); /* with the current system time */

          sleep(1); /* give server time to create the socket */
          printf("Client/reader process %d active.\n", p_count);

          /* set the socket descriptor */
          sock = socket(AF_UNIX, SOCK_STREAM, 0);
          if (sock < 0)
            { perror("Error opening channel");
              clean_up(1, &sock);
            }

          /* set the physical address (cli_name) of the socket descriptor */
          bzero(&cli_name, sizeof(cli_name));
          cli_name.sun_family = AF_UNIX;
          strcpy(cli_name.sun_path, SOCKET_NAME);

          /* connect to the socket */
          if (connect(sock, (struct sockaddr *)&cli_name, sizeof(cli_name)) < 0)
            { perror("Error establishing communications");
              clean_up(1, &sock);
            }

          /* randomly generate the number of integers the client will read */
          ints_read = 1 + (int) (MAX_READ * rand() / (RAND_MAX + 1.0));

          printf("Client %d wants to read %d integers.\n", p_count, ints_read);
          write(sock, &ints_read, 4);

          /* read values from socket */
          for (count = 1; count <= ints_read; count++)
            { read(sock, &value, 4);
              printf("Client %d has received %d from socket.\n", p_count, value);
            }
          printf("Client %d done.\n", p_count);
          close(sock); /* close connection to the socket */
          exit(0);
        }
      else proc[p_count - 1] = pid;
    } /* end of processing for client */

  { /* processing for server */
    int sock; /* listening socket descriptor */
    int connect_sock;  /* connected socket descriptor */
    struct sockaddr_un serv_name;
    int count, i, data, write_length;
    size_t len; /* variable to store the length of the serv__name structure */

    /* set socket descriptor */
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0)
      { perror("Error opening channel");
        clean_up(1, &sock);
      }

    /* set the physical address (serv_name) of the socket descriptor */
    bzero(&serv_name, sizeof(serv_name));
    serv_name.sun_family = AF_UNIX;
    strcpy(serv_name.sun_path, SOCKET_NAME);

    /* bind the socket address to the socket descriptor */
    if (bind(sock, (struct sockaddr *)&serv_name, sizeof(serv_name)) < 0)
      { perror("Error naming channel");
        clean_up(1, &sock);
      }

    listen(sock, NUM_READERS); /* listen for connections on the socket */
    printf("Server/writer is alive and waiting for socket connections.\n");

    /* accept connection requests from clients */
    for (i = 1; i <= NUM_READERS; i++)
      { connect_sock = accept(sock, (struct sockaddr *)&serv_name, &len);

        /* spawn a child of the server process to handle each separate
           connection request from the clients */
        pid = fork();

        if (-1 == pid)
        { perror("Error in fork");
          clean_up(1, &sock);
        }

        if (0 == pid)
          { /* processing for child of server */ 
            printf("Server child %d active.\n", i);
            close(sock); /* close the listening socket for this process */
            /* read the number of integers the client wants to read */
            read(connect_sock, &write_length, 4);
            printf("Server child %d now writing %d integers to socket.\n", 
                   i, write_length);

            /* write the requested number of integers in ascending order to the
               reader's connected socket descriptor. */
            for (count = 1; count <= write_length; count++)
              { data = count + ((i - 1) * 10);
                write(connect_sock, &data, 4);
                printf("Server child %d has written %d to socket.\n", i, data);
              }
            close(connect_sock); /* close the connected socket for the reader */
            exit(0);
          } /* end of processing for child of server */
        else proc[NUM_READERS + p_count - 1] = pid;
      }

    for (i = 0; i < (2 * NUM_READERS); i++) {
      waitpid(proc[i], NULL, 0); /* Wait for all child processes to terminate */
    }
    printf ("server cleaning up\n");
    clean_up(0, &sock); /* Exit with no errors */
  } /* end of processing for server */

} /* end of main */

