qclient.cc


      /*  $Id: qclient.cc,v 2.7 2002/05/18 22:12:02 vickery Exp $
       *
       *      This is a client program that acts as the intermediary between
       *      the user and the qserver program for the Remote Shell Project.
       *
       *  Functions:
       *
       *      processMessage()  Process a message from the server.
       *      useage()          Print command syntax.
       *      main()            Main function for the module.
       *
       *      CS-701, Spring 2002
       *      C. Vickery
       *      
       *
       *	    $Log: qclient.cc,v $
       *	    Revision 2.7  2002/05/18 22:12:02  vickery
       *	    Took out a debugging message and cleaned up some of the error
       *	    messages.
       *
       *	    Revision 2.6  2002/05/14 04:21:11  vickery
       *	    Handle SC_SHUT messages to print message body from server.
       *
       *	    Revision 2.5  2002/05/12 04:31:19  vickery
       *	    Allow remote host to be specified without -h, but check
       *	    for dangling options.
       *
       *	    Revision 2.4  2002/05/12 00:46:29  vickery
       *	    Basic client fucntionality in place now.
       *	    Maintains state based on whether waiting for a command line
       *	    or stdin, and sends appropriate types of messages to server.
       *
       *	    Revision 2.3  2002/05/07 05:07:55  vickery
       *	    Snapshot: User authentication and prompt string processing.
       *
       *	    Revision 2.2  2002/05/06 06:32:08  vickery
       *	    Introduced the message passing protocol.  Send CS_USER
       *	    message in response to SC_LOGIN message.
       *
       *	    Revision 2.1  2002/04/25 02:59:56  vickery
       *	    First revision for Assignment 4.
       *	    Added -u command line option for specifying user id.
       *
       *	    Revision 1.2  2002/04/07 19:21:46  vickery
       *	    Changed default host name from "localhost"
       *	    to local host's name.  This way, the client
       *	    can connect to a server running on the local
       *	    machine without using the -h option.
       *
       *	    Revision 1.1  2002/03/28 19:57:51  vickery
       *	    Initial revision
       *
       *
       */
      
      #include "qclient.h"
      //  Constants
      //  ------------------------------------------------------------------
      static const  int   HOSTNAME_MAX =   64;
      static const  int   USERNAME_MAX =   32;
      static const  int   BUF_SIZE     = 8192;
      static const  int   NO_TIMEOUT   =   -1;  //  For poll()
      static const  char  *VERSION =
        "$Id: qclient.cc,v 2.7 2002/05/18 22:12:02 vickery Exp $";
      
      //  Module Globals
      //  ------------------------------------------------------------------
      static        char  hostName  [ HOSTNAME_MAX ];
      static        char  serverName[ HOSTNAME_MAX ];
      static        char  userName  [ USERNAME_MAX ];
      
      static        bool  awaiting_command = false;
      
      //  processMessage()
      //  ------------------------------------------------------------------
      /*
       *    Do whatever needs to be done with a message from the server.
       *
       *    Arguments:    fd          Socket for writing to server.
       *                  msg_header  Message header.
       *                  msg_body    Message body.
       *    Returns:      void
       */
      void
      processMessage( int fd, msg_header_t msg_header, char * msg_body )
      {
        int   result;
      
        switch ( msg_header.msg_type )
        {
          case  SC_SHUT:
                fprintf( stderr, "%sn", msg_body );
                exit( 0 );
                break;
      
          case  SC_LOGIN:
                fprintf( stdout, "%sn", msg_body );
                fflush( stdout );
                msg_header.msg_type = CS_USER;
                msg_header.msg_length = strlen( userName );
                result = write_msg( fd, msg_header, userName );
                if ( result < 0 )
                {
                  perror( "Server socket" );
                  exit( 1 );
                }
                break;
      
          case  SC_PROMPT:
                fprintf( stdout, "%s", msg_body );
                fflush( stdout );
                awaiting_command = true;
                break;
      
          case  SC_DENY:
                fprintf( stderr, "Access to server deniedn" );
                exit( 1 );
                break;
      
          case  SC_STDOUT:
                fprintf( stdout, "%s", msg_body );
                break;
                
          case  SC_STDERR:
                fprintf( stderr, "%s", msg_body );
                break;
      
          default:
            fprintf( stderr, "Bad switch at %s line %dn",
                __FILE__, __LINE__ );
            exit( 1 );
        }
        return;
      }
      
      
      //  usage()
      //  ------------------------------------------------------------------
      /*
       *  Display usage message
       */
      void
      usage( const char *cmdName )
      {
        fprintf( stderr, 
            "Usage: %s [options]n"
            " Options:n"
            "   -p or --port <num> "
                                 " Server's port number (default 0x8000)n"
            "   -h or --host <str> "
                                " Server's host name (default %s)n"
            "   -u or --user <name>  User namen"
            "   -v or --version     Print program version number and exitn"
            "   -? or --help        Print this message and exitn", 
            cmdName, hostName );
      }
      
      
      //  main()
      //  ------------------------------------------------------------------
      /*
       *	Process command line arguments, establish communcation with
       *	server, and manage interactions between the user and the server.
       *
       *  Arguments
       *
       *    The standard arguments to main().
       */
      int
      main(int argc, char *argv[], char * envp[] ) 
      {
      
        //  Get logged-on user's name
        struct passwd *passwd_ptr = getpwuid( geteuid() );
        if ( 0 == passwd_ptr )
        {
          perror( "username" );
          exit( 1 );
        }
        strcpy( userName, passwd_ptr->pw_name );
      
        //  Get this host's name for default server name.
        int result = gethostname( hostName, sizeof( hostName ) );
        if ( result < 0 )
        {
          perror( "Unable to determine local host name" );
          exit( 1 );
        }
        strcpy( serverName, hostName );
      
        //  Process command line options
        int           serverPort = 0x8000;
        const char    *short_options = "p:h:?u:v";
        const struct  option long_options[] =
        {
          { "port",     required_argument,  0,  'p' },
          { "host",     required_argument,  0,  'h' },
          { "help",     no_argument,        0,  '?' },
          { "user",     required_argument,  0,  'u' },
          { "version",  no_argument,        0,  'v' },
          { 0,          0,                  0,  0   },
        };
      
        int   optchar;
        while ( -1 != ( optchar = getopt_long( argc, argv, short_options,
                                  long_options, 0 ) ) )
        {
          switch ( optchar )
          {
            //  Server's well-known port number
            case 'p':
              serverPort = strtol( optarg, 0, 0 );
              if (    ( serverPort < 0 ) || ( serverPort > 0xFFFF ) )
              {
                fprintf( stderr, "%s: invalid port numbern", optarg );
                usage( argv[0] );
                exit( 1 );
              }
              break;
      
            //  Server's host name
            case 'h':
              strcpy ( serverName, optarg );
              break;
      
            //  User name
            case 'u':
              strcpy( userName, optarg );
              break;
      
            //  Version number
            case 'v':
              printf( "%s: %sn", argv[0], VERSION );
              exit( 0 );
      
            case '?':
            default :
              usage( argv[ 0 ] );
              exit( 1 );
          }
        }
        if ( optind < argc )
        {
          //  Assume one dangling argument is host name.
          strcpy( serverName, argv[ optind++ ] );
          if ( optind < argc )
          {
            fprintf( stderr, "Unexpected argument: %sn", argv[ optind ] );
            usage( argv[ 0 ] );
            exit( 1 );
          }
        }
      
      
        //  Connect to the server
        struct  hostent     *hp;
        if ( 0 == (hp = gethostbyname( serverName ) ) )
        {
          fprintf( stderr,"%s: %s: unknown host.n", argv[ 0 ], serverName );
          usage( argv[ 0 ] );
          exit( EXIT_FAILURE );
        }
      
        struct sockaddr_in  server_sockaddr;
        memset( &server_sockaddr, 0, sizeof( server_sockaddr ) );
        server_sockaddr.sin_family = AF_INET;
        memcpy( &server_sockaddr.sin_addr, hp->h_addr, hp->h_length );
        server_sockaddr.sin_port = htons( serverPort );
      
        int                 server_fd;
        if ( ( server_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
        {
          perror( "socket" );
          exit( EXIT_FAILURE );
        }
        if ( connect( server_fd, (struct sockaddr *) &server_sockaddr, 
                                          sizeof ( server_sockaddr ) ) < 0 )
        {
          perror( "connect" );
          exit( EXIT_FAILURE );
        }
      
        //  Initialize the array of pollfds
        enum sources { user = 0, server = 1 };
        struct pollfd           fds_to_read[ 2 ];
        fds_to_read[ user   ].fd      = fileno( stdin );
        fds_to_read[ user   ].events  = POLLIN;
        fds_to_read[ server ].fd      = server_fd;
        fds_to_read[ server ].events  = POLLIN;
      
      
        //  Manage input from server and/or user.
      
        msg_header_t    msg_header;
        char            *msg_body   = 0;
        char            inBuf[ BUF_SIZE ];
        int             bytesRead   = 0;
        for ( ;; )
        {
          //  Wait for input.
          if ( poll( fds_to_read, 2, NO_TIMEOUT ) < 0 )
          {
            perror( "poll" );
            exit( 1 );
          }
      
          //  Read and process a server message.
          if ( fds_to_read[ server ].revents & POLLIN )
          {
            result = read_header( server_fd, msg_header);
            if ( result < 0 )
            {
                perror( "reading msg from server" );
                exit( 1 );
            }
            if ( 0 == result )
            {
              printf( "Server closed connection.n" );
              exit( 0 );
            }
      
            msg_body = new char [ msg_header.msg_length + 1];
            memset( msg_body, '0', msg_header.msg_length + 1 );
            result = read_body( server_fd, msg_header, msg_body );
      
            processMessage( server_fd, msg_header, msg_body );
            delete [] msg_body;
          }
      
          //  Read a line from the user, if there is one
          if ( fds_to_read[ user ].revents & POLLIN )
          {
            bytesRead = read( STDIN_FILENO, inBuf, sizeof( inBuf ) );
            if ( 0 == bytesRead )
            {
              //  End of input from stdin
              msg_header.msg_type = CS_EXIT;
              msg_header.msg_length = 0;
              write_msg( server_fd, msg_header, inBuf );
              close( server_fd );
              fprintf( stderr, "n" );
              exit( 0 );
            }
      
            inBuf[ bytesRead ] = '0';  //  Terminate string.
            if ( awaiting_command )
            {
              //  Send a command line to the server
              while ( ( bytesRead >= 0 ) 
                   && ( ( 'r' == inBuf[ bytesRead - 1 ] )
                     || ( 'n' == inBuf[ bytesRead - 1 ] ) ) )
              {
                inBuf[ --bytesRead ] = '0';
              }
              msg_header.msg_type = CS_CMD;
              if ( '0' == inBuf[ 0 ] )
              {
                inBuf[ 0 ] = ' '; //  Avoid 0-length msg
              }
              awaiting_command = false;
            }
            else
            {
              //  Send raw input to as std input.
              msg_header.msg_type = CS_STDIN;
            }
            msg_header.msg_length = strlen( inBuf );
            write_msg( server_fd, msg_header, inBuf );
          }
        }
      }