Server Code (continued)
               /*
                *  Loop continuously, waiting for connection requests
                *  and performing the service.
                */
 
               length = sizeof(client_addr);
               while (1) {
                    if ((recfd = accept(socket_fd,
                        (struct sockaddr_in *)&client_addr, &length)) < 0) {
                         perror("could not accept call");
                         exit(1);
                    }
                    (void) run_server(socket_fd, recfd);
               }
          }
 
          int
          run_server(socket_fd, resfd)
          int socket_fd;
          int resfd;
          {
               switch (fork()) {
                  case -1:
                    perror("fork failed!\n");
                    exit(1);
                  default: /* parent */
                    close(resfd);
                    return;
                  case 0:  /* child */
                    close(socket_fd);
                    perform_actions(resfd);
                    exit(0);
               }
          }
 
          int
          perform_actions(conn_fd)
          int conn_fd;
          {
               int done = 0;    /* indicates all data is read        */
               int where = 0;   /* points to where we are in buffer  */
               int nbytes;      /* the number of bytes read          */
               char reply;      /* a 'y' or a 'n' to reply to client */
               char buf[BUFSIZ];/* buffer to hold the name           */
               char cmd[BUFSIZ];     /* used to figure out if the  */
               char junkbuf[BUFSIZ]; /* specified user is logged   */
               FILE *fp;             /* onto the system            */
 
               /* Read the user name from the client.  */
               do {
                    if ((nbytes = read(conn_fd, &buf[where],
                                                    BUFSIZ-where)) < 0) {
                         perror("read of data failed!");
                         exit(1);
                    }
                    where += nbytes;
                    if (buf[where - 1] == '\0') {
                         done = 1;
                    }
               } while (!done);
 
               /*
                *  Determine if the user is logged on...
                */
 
               sprintf(cmd, "who | grep '^%s '", buf);
               if ((fp = popen(cmd, "r")) == NULL
                            || fgets(junkbuf, BUFSIZ, fp) == NULL) {
                    reply = 'n';
               } else {
                    reply = 'y';
               }
 
               /*
                *  Send a "y" of a "n" to the client.
                */
               if (write(conn_fd, &reply, 1) < 0) {
                    perror("write failed!");
                    exit(1);
               }
 
               (void)read(conn_fd, &buf[0], 1);
          }
 
Notes
 
        o The normal way to terminate a connection is with the close( )
          system call
 
        o The close( ) system call attempts to send any undelivered data
 
        o You can also use the shutdown( ) routine
            -- Provides more control
            -- Can stop both incoming and outgoing data
 
        o Although not necessary when you use TCP, it's a good idea
          for the peer that did the last write to wait for the other
          side to consume the data.
            -- We do that by issueing a read( ) call after we send the
               last piece of data
            -- The read( ) call will block until the peer shuts down the
               connection
            -- That way, we know the peer read the last piece of data
            -- In some transport providers (other than TCP), it is
               requires that you do that, because if you close the
               connection immediately after the last write, the last
               piece of data may be lost.
Zombies and Child Cleanup
        o We create a child process in the server part of the application
 
        o When a child process exits, the kernel keeps the child's exit
          status around until the parent collects it
 
        o If the parent does not collect the information, the child
          remains a zombie process
 
        o When a child exits, it sends a SIGCHLD signal to the parent,
          and the parent is expected to issue the wait3 call to
          get the exit status of the child.
            -- That cleans up the zombie
 
        o So, you must set up a signal handler to catch SIGCHLD and
          clean up the zombie
 
        o But, you don't want to be interrupted in certain system
          calls (like fork), because that would cause the
          system call to fail.
 
        o So we will use sigblock and sigsetmask to block
          critical sections
            -- However, when we are blocked, we may have several
               children die, and only one signal is posted
            -- So, we must loop in the signal handler to clean up
               all exited children
 
        o In Solaris, if you issue sigset(SIGCHLD, SIG_IGN)
          then the OS will automatically clean up all zombies for you
            -- You don't have to have a specific handler to do it.
 
        o But other versions of UNIX still require a handler.
 
The following code illustrates what you have to do.
 
I've used a compile variable called OLD_SIGHANDLER to use both
the solaris way and the non-solaris way.  When you compile on a non-Solaris
machine (like acf5), you should use the -DOLD_SIGHANDLER flag to the
cc command line.
 
 
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <string.h>
#include <values.h>
 
extern int errno;
 
main()
{
   ....
 
   int mask;                       /* signal mask                    */
   void handler();                 /* child signal handler           */
 
#ifdef OLD_SIGHANDLER
   (void)sigset(SIGCHLD, handler);
#else
   (void)sigset(SIGCHLD, SIG_IGN);
#endif
 
   .......
 
   /*
    *  Loop continuously, waiting for connection requests
    *  and performing the service.
    */
   length = sizeof(client_addr);
   while (1) {
      if ((recfd = accept(socket_fd, (struct sockaddr *)&client_addr,
                          &length)) < 0) {
         /*
          *  If we failed because we were interrupted (that can
          *  happen because we got a SIGCHLD), simply try again.
          */
         if (errno == EINTR) {
             continue;
         }
         perror("could not accept call");
         exit(1);
      }
      (void) run_server(socket_fd, recfd);
   }
   .....
 
}
 
#ifdef OLD_SIGHANDLER
int
run_server(socket_fd, resfd)
int socket_fd;
int resfd;
{
    mask = sigblock(sigmask(SIGCHLD));
    switch (fork()) {
       case -1:
          perror("fork failed!\n");
          exit(1);
 
       default: /* parent */
          (void) close(recfd);
          (void) sigsetmask(mask);
          /*
           * Break out of switch and continue loop.
           */
          break;
 
       case 0:  /* child */
          (void) close(socket_fd);
          (void) sigsetmask(mask);
          perform_actions(recfd);
          exit(0);
    }
}
#else
int
run_server(socket_fd, resfd)
int socket_fd;
int resfd;
{
     switch (fork()) {
          case -1:
               perror("fork failed!\n");
               exit(1);
          default: /* parent */
               close(resfd);
               return;
          case 0:  /* child */
               close(socket_fd);
               perform_actions(resfd);
               exit(0);
     }
}
#endif
 
#ifdef OLD_SIGHANDLER
void
handler()
{
        while (wait3(NULL, WNOHANG, NULL) > 0)
                ;
}
#endif
 
 
Now lets look at the client side of the application.....
 
Client Code
          #include <stdio.h>
          #include <netdb.h>
          #include <signal.h>
          #include <sys/types.h>
          #include <sys/socket.h>
          #include <netinet/in.h>
 
          #define SERV_PORT  5134
          #define MAXNAME    1024
 
          main(argc, argv)
          int argc;
          char **argv;
          {
               int fd;                   /* fd into transport provider */
               int length;               /* length of message          */
               char buf[BUFSIZ];         /* holds message from server  */
               struct hostent *hp;       /* holds IP address of server */
               struct sockaddr_in myaddr; /* address that client uses  */
               struct sockaddr_in servaddr; /* the server's full addr  */
 
               /*
                *  Check for proper usage
                */
 
               if (argc != 3) {
                    fprintf(stderr,"Usage: %s user host\n", argv[0]);
                    exit(2);
               }
         
               /*
                *  Get a socket into TCP/IP
                */
               if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                    perror("socket failed!");
                    exit(1);
               }
 
               /*
                *  Bind to an arbitrary return address
                */
 
               bzero((char *)&myaddr, sizeof(myaddr));
               myaddr.sin_family = AF_INET;
               myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
               myaddr.sin_port = htons(0);
               if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
                    perror("bind failed!");
                    exit(1);
               }
         
               /*
                *  Fill in the server's address and the data
                */
 
               bzero((char *)&servaddr, sizeof(servaddr));
               servaddr.sin_family = AF_INET;
               servaddr.sin_port = htons(SERV_PORT);
               hp = gethostbyname(argv[2]);
 
               if (hp == 0) {
                    fprintf(stderr, "could not obtain address of %s\n",
                         argv[2]);
                    exit(1);
               }
 
               bcopy(hp->h_addr_list[0], (caddr_t)&servaddr.sin_addr,
                    hp->h_length);
 
               /*
                *  Connect to the server
                */
 
               if (connect(fd, (struct sockaddr *)&servaddr,
                                    sizeof(servaddr)) < 0) {
                    perror("connect failed!");
                    exit(1);
               }
         
               /*
                *  Write the user name and read the server's response
                */
 
               length = strlen(argv[1]) + 1;
 
               if (write(fd, argv[1], length) != length) {
                    perror("write failed!\n");
                    exit(1);
               }
 
               if (read(fd, buf, 1) == -1) {
                    perror("read failed!\n");
                    exit(1);
               }
 
               if (buf[0] == 'y') {
                    printf("%s is logged on server\n", argv[1]);
               } else {
                    printf("%s is not logged on server\n", argv[1]);
               }
               exit(0);
          }