Perry's Lab Two Answer
#include "funcs.h"
#define MAXLINE 256 
#define QUIT    "9"
#define TERMINATOR '\01'

int active;
int readn (char *buf, int bytes);
int writen(char *buf, int bytes);

int main(int argc, char **argv)
{
     char *GetTokens(void);
     char QuitPacket[MAXLINE], replyline[MAXLINE], packet[MAXLINE], *username;
     char LegalFirstChars[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
     FILE *more;
     void ConnectToServer(void), SetUpSignals(void);

     SetUpSignals();
     ConnectToServer(); 

     while (username = GetTokens(), username ? strcmp(username, QUIT) : 1)
     {
          if (!username) continue;
          if (strchr(LegalFirstChars, *username) == NULL)
          {
              printf ("Usernames start with a letter or underscore!\n");
              continue;
          }
       
       /*
        ***  Send username packet to server.
        ***
        */
     
          memset(packet, 0, MAXLINE);
          memcpy(packet, username, MAXLINE);
          writen(packet, 1);
          writen(packet + 1, MAXLINE - 1);

       /*  
         ***  Get login info lines for username from server.
        */

          more = Popen("more", "w");
          while (readn(replyline, MAXLINE), *replyline != TERMINATOR)
              fprintf(more, "%s\n", replyline);
          Pclose(more);
     }

  /*
   ***  Send quit packet after normal exit.
   */

     memset(QuitPacket, 0, MAXLINE);
     strcpy(QuitPacket, QUIT);
     writen(QuitPacket, 1);
     writen(QuitPacket + 1, MAXLINE - 1);
}



void SetUpSignals(void)
{
     struct sigaction sa;
     void handler(int signum);

     Sigfillset(&sa.sa_mask);
     sa.sa_handler = handler;
     sa.sa_flags   = 0;
     Sigaction(SIGPIPE, &sa, NULL);
     Sigaction(SIGQUIT, &sa, NULL);
     Sigaction(SIGINT,  &sa, NULL);
     Sigaction(SIGALRM, &sa, NULL);
}


void handler(int signum)
{
     char QuitPacket[MAXLINE];

     memset(QuitPacket, 0, MAXLINE);
     strcpy(QuitPacket, QUIT);
 
     switch (signum)
     {
         case SIGPIPE:  printf("Server disconnected!\n");
                        exit(0);
         case SIGQUIT:
         case SIGINT:
         case SIGALRM:  writen(QuitPacket, 1);
                        writen(QuitPacket + 1, MAXLINE - 1);
                        exit(0);
     }
}




void ConnectToServer()
{
     struct sockaddr_in sin;

     active = Socket(AF_INET, SOCK_STREAM, 0);

     memset(&sin, 0, sizeof(struct sockaddr_in));
     sin.sin_port = htons(3777); 
     sin.sin_family = AF_INET;
     sin.sin_addr.s_addr = inet_addr("153.18.17.12");

     Connect(active, (struct sockaddr *)&sin, sizeof(sin));
}


char *GetTokens(void)
{
     static char line[MAXLINE], *username, *extra = NULL;

     printf("Enter username (9 to quit): ");
     alarm(50);
     fgets(line, MAXLINE, stdin);
     alarm(0);

     username = strtok(line, "\040\t\n");
     if (username) extra = strtok(NULL, "\040\t\n");

     if (extra)
     {
         printf("Only one username per request!\n");
         return NULL;
     }
     
     return username;
} 



int readn(char *buf, int bytes)
{
     int nleft;
     int nread;
     char *mover = buf;

     nleft = bytes;
     
     while (nleft > 0){
         if ((nread = read(active,mover,nleft)) < 0)
              return -1;  /**** Error!! ****/
         else if (nread == 0)
              break;      /****  EOF!   ****/
         nleft -= nread;
         mover += nread;
     }
     return (bytes - nleft);
}


int writen(char *buf, int bytes)
{
     int nleft;
     int nwritten;
     char *mover = buf;

     nleft = bytes;
     while (nleft > 0){
         if ((nwritten = write(active,mover,nleft)) < 0)
              return -1;  /**** Error!! ****/
         nleft -= nwritten;
         mover += nwritten;
     }
     return bytes;
}
#include "funcs.h"

#define MAXLINE 256 
#define QUIT '9'
#define TERMINATOR '\01'

struct whonode
{
     char wholine[MAXLINE];
     struct whonode *next;
};

typedef struct whonode WHONODE;
WHONODE *list;

int readn(int active, char *buffer, int buffer_size);
int writen(int active, char *buffer, int buffer_size);
     pid_t pid;

main()
{
     struct sigaction sa;
     int passive, active;
     socklen_t addr_len;
     char username[MAXLINE];
     struct sockaddr_in client;
     int CreatePassiveSocket(void);
     void SetUpSignals(void);
     void FindSendWhoLines(int active, WHONODE *list, char *username);
     WHONODE *MakeWhoList(void);
   
     SetUpSignals();
     passive = CreatePassiveSocket();
  
     addr_len = sizeof(struct sockaddr_in);
     while (1)   /*****  Server stays alive for ever!!!  *****/
     {
         active = accept(passive, (struct sockaddr *)&client, &addr_len);
         if (active < 0) 
         {
              if (errno == EINTR)
              {
                   errno = 0;
                   continue;
              }
              else
              {
                   perror("Accept error!");
                   exit(1);
              }
         }

         pid = Fork();

         if (pid != 0)   /******  Parent  ******/
         {
              Close(active);
              continue;
         }
         else    /******  Child  ******/
         {
              sa.sa_flags = 0;
              sa.sa_handler = SIG_DFL;
              Sigfillset(&sa.sa_mask);
              Sigaction(SIGCHLD, &sa, NULL);
 
              list = MakeWhoList();

              while(alarm(30), memset(username, 0, MAXLINE),
                    readn(active, username, MAXLINE), *username != QUIT)
              {
                    alarm(0);
                    FindSendWhoLines(active, list, username);
              }         
              exit(0);   /*****  Don't forget this!!!!!  *****/
         }
      }   /***  end while(1)  ***/
}



/**************  Handle timeout/disappearance of client  **************/
void SetUpSignals(void)
{
     struct sigaction sa;
     void handler(int signum); 

     Sigfillset(&sa.sa_mask);    /****  Take care of signals.  ****/
     sa.sa_flags   = 0;
     sa.sa_handler = handler;
     Sigaction(SIGALRM, &sa, NULL);
     Sigaction(SIGPIPE, &sa, NULL);
     Sigaction(SIGCHLD, &sa, NULL);
}


int CreatePassiveSocket(void)
{
     struct sockaddr_in sin;
     int passive;

     passive = Socket(AF_INET, SOCK_STREAM, 0);

     memset(&sin, 0, sizeof(sin));
     sin.sin_port = htons(3777); 
     sin.sin_family = AF_INET;
     sin.sin_addr.s_addr = htonl(INADDR_ANY);

     Bind(passive, (struct sockaddr *)&sin, (socklen_t) sizeof(sin));
     Listen(passive, 1);
     return passive;
}



void handler(int signum)
{
     int status;

     switch (signum)
     {
          case SIGCHLD: while (waitpid(-1, &status, WNOHANG) > 0);
                        break;
          case SIGALRM:
          case SIGPIPE: exit(0);
     }
}



WHONODE *MakeWhoList(void)
{
    WHONODE *list, *prev, *cur, *new;
    FILE *who;
    char wholine[MAXLINE], tempwho[MAXLINE], *username;

  /*
   ***  Make dummy header/trailer of ordered list.
   */

    list = (WHONODE *) Malloc(sizeof(WHONODE));
    strcpy(list->wholine, ""); 
    list->next = (WHONODE *) Malloc(sizeof(WHONODE));
    strcpy(list->next->wholine, "\177");
    list->next->next = NULL;

    who = Popen("who", "r");
    
    while (fgets(wholine, MAXLINE, who))
    {
         prev = list;
         cur  = list->next;
         strcpy(tempwho, wholine);            
         username = strtok(tempwho, "\040\t");

         while (strcmp(username, cur->wholine) > 0)
         {
              prev = cur;
              cur  = cur->next;
         }
                
         new = (WHONODE *) Malloc(sizeof(WHONODE));
         prev->next = new;
         new->next  = cur;
         strcpy(new->wholine, wholine);
    }

    Pclose(who);
    return list;
}





void FindSendWhoLines(int active, WHONODE *list, char *username)
{
     int result;
     char resultline[MAXLINE], tempwho[MAXLINE], *searchuser;

     do
     {
          list = list->next;
          strcpy(tempwho, list->wholine);
          searchuser = strtok(tempwho, "\040\t");
     }
     while (strcmp(username, searchuser) > 0);
     
     if ((result = strcmp(username, searchuser)) < 0)   /*  User not found */
     {
          memset(resultline, 0, MAXLINE);   
          strcpy(resultline, "User not logged in!\n");
          writen(active, resultline, 1);
          writen(active, resultline + 1, MAXLINE - 1);              

          memset(resultline, 0, MAXLINE);
          *resultline = TERMINATOR;
          writen(active, resultline, 1);
          writen(active, resultline + 1, MAXLINE - 1);              
     }
     else  /*  result == 0 */
     {
          do
          {
              memset(resultline, 0, MAXLINE);   
              strcpy(resultline, list->wholine);
              writen(active, resultline, 1);
              writen(active, resultline + 1, MAXLINE - 1);              
               
              list = list->next;
              strcpy(tempwho, list->wholine);
              searchuser = strtok(tempwho, "\040\t");
          } 
          while (strcmp(username, searchuser) == 0);
              
          memset(resultline, 0, MAXLINE);
          *resultline = TERMINATOR;
          writen(active, resultline, 1);
          writen(active, resultline + 1, MAXLINE - 1);              
     }
}



int readn(int fd, char *buf, int bytes)
{
     int nleft;
     int nread;
     char *mover = buf;

     nleft = bytes;
     while (nleft > 0){
         if ((nread = read(fd,mover,nleft)) < 0)
              return -1;  /**** Error!! ****/
         else if (nread == 0)
              break;      /****  EOF!   ****/
         nleft -= nread;
         mover += nread;
     }
     return (bytes - nleft);
}


int writen(int fd, char *buf, int bytes)
{
     int nleft;
     int nwritten;
     char *mover = buf;

     nleft = bytes;
     while (nleft > 0){
         if ((nwritten = write(fd,mover,nleft)) < 0)
              return -1;  /**** Error!! ****/
         nleft -= nwritten;
         mover += nwritten;
     }
     return bytes;
}



/********************************  Session Output  **************************/

$ s&
[1] 18656

$ c
Enter username (9 to quit): foo
User not logged in!

Enter username (9 to quit): 
Enter username (9 to quit):  perry
perry    pts/1        May  5 02:03 (user-2ivfmtn.dialup.mindspring.com)

perry    pts/8        May  5 00:34 (user-2ivfmtn.dialup.mindspring.com)

Enter username (9 to quit): quit
User not logged in!

Enter username (9 to quit): 9

$ c
Enter username (9 to quit): 
[2]+  Stopped                 c

$ c  /* Prove multi-user */
Enter username (9 to quit):   perry
perry    pts/1        May  5 02:03 (user-2ivfmtn.dialup.mindspring.com)

perry    pts/8        May  5 00:34 (user-2ivfmtn.dialup.mindspring.com)

Enter username (9 to quit):

Enter username (9 to quit): 9

$ c
Enter username (9 to quit): foobar
User not logged in!

Enter username (9 to quit): 9

$ fg   /***  Bring back alarmed-out client  ***/
c
foobar
Server disconnected!    /***  SIGPIPE on client side!! ***/

$ c
Enter username (9 to quit):   tran
tran     pts/5        May  4 14:50 (153.18.17.99)

Enter username (9 to quit):   9