qserver.cc
// $Id: qserver.cc,v 1.3 2002/03/31 06:25:00 vickery Exp $
/*
* This is the server's main module for the qrsh project
* developed in CS-701 Spring 2002.
*
* C. Vickery
*
* $Log: qserver.cc,v $
* Revision 1.3 2002/03/31 06:25:00 vickery
* Added a check for dangling command line options.
*
* Revision 1.2 2002/03/28 19:57:04 vickery
* Added support for SIGTERM as well as SIGINT. This code now
* implements Assignment 3.
*
* Revision 1.1 2002/03/23 04:56:33 vickery
* Initial revision
*
*
*/
#include "qserver.h"
#include <getopt.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
static const int BACKLOG = 5;
static const int MAX_HOST_NAME = 256;
static const char* VERSION = "$Revision: 1.3 $";
// Command line options
// -----------------------------------------------------------------
static const char* usageMsg = "Usage: qserver [options]n"
" Options:n"
" -p --port <number> Port number (default is 0x8000)n"
" -l --logfile <file> Log file name (default is ./qserver.log)n"
" -o --overwrite Overwrite logfile (default is to append)n"
" -v --version Display version number, and exitn"
" -h --help Display this message, and exitn";
static const char* short_options = "p:l:ovh?";
static struct option long_options[] =
{
{ "port", required_argument, 0, 'p' },
{ "logfile", required_argument, 0, 'l' },
{ "overwrite", no_argument, 0, 'o' },
{ "version", no_argument, 0, 'v' },
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 },
};
// signal_handler()
// ------------------------------------------------------------------
/*
* Invoke do_exit() when a signal arrives.
*/
void
signal_handler( int signum )
{
exit( -1 );
}
// do_exit()
// ------------------------------------------------------------------
/*
* Called when a signal arrives or on normal exit.
* Do orderly shutdown of server.
*/
void
do_exit( int code, char *reason )
{
if ( -1 == code )
closeLog( "Signal Received" );
else
closeLog( reason );
}
// main()
// ------------------------------------------------------------------
/*
* Process command line arguments, open a log file, become a
* daemon, accept connections from clients, and for a process for
* each client.
*
* Arguments
*
* The standard arguments to main().
*
*/
int
main(int argc, char *argv[], char *envp[] )
{
// Process command line options
int wellKnownPort = 0x8000;
char logfilePath[ PATH_MAX ];
bool overwriteLogfile = false;
// Specify directory for logfile if not given
if ( (*argv[0] != '.') && (*argv[0] != '/') )
strcpy( logfilePath, "./" );
else
strcpy( logfilePath, "" );
strcat( logfilePath, argv[0] );
strcat( logfilePath, ".log" );
int c;
while ( -1 !=
( c = getopt_long( argc,
argv,
short_options,
long_options, 0 ) ) )
{
switch ( c )
{
case 'p':
wellKnownPort = strtol( optarg, 0, 0);
if ( ( wellKnownPort < 0 ) ||
( (wellKnownPort < 1024) && (geteuid() != 0) ) )
{
fprintf( stderr, "Bad port number: %sn", optarg );
exit( 1 );
}
break;
case 'l':
strncpy( logfilePath, optarg, sizeof( logfilePath ) -1 );
break;
case 'o':
overwriteLogfile = true;
break;
case 'v':
printf( "%s: %sn", argv[0], VERSION );
exit( 0 );
case '?':
case 'h':
default:
fprintf( stderr, usageMsg );
exit( 1 );
}
}
// Check for dangling command line options.
if ( optind < argc )
{
fprintf( stderr, "unexpected value: %sn", argv[ optind ] );
fprintf( stderr, usageMsg );
exit( 1 );
}
#ifdef DEBUG
printf( "Logfile: %s (%s)n"
"Port: 0x%04X (%d)n",
logfilePath, (overwriteLogfile ? "overwrite" : "append" ),
wellKnownPort, wellKnownPort );
#endif
// Create and bind this server's well-known socket
// ----------------------------------------------------------------
char thisHostName[MAX_HOST_NAME];
struct hostent *hp;
int server_fd, client_fd;
struct sockaddr_in server_sockaddr, client_sockaddr;
// Get local host's name and IP address
if ( gethostname( thisHostName, MAX_HOST_NAME ) < 0)
{
perror( "gethostname" );
exit( EXIT_FAILURE );
}
if ((hp = gethostbyname(thisHostName)) == NULL)
{
fprintf( stderr,
"qserver: cannot get local host informationn" );
exit( EXIT_FAILURE );
}
// Initialize local sockaddr struct.
memset( &server_sockaddr, 0, sizeof( server_sockaddr ) );
server_sockaddr.sin_family = AF_INET;
memcpy( &server_sockaddr.sin_addr, hp->h_addr_list[0], hp->h_length );
server_sockaddr.sin_port = htons( wellKnownPort );
// Create and bind the well-known socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror( "socket" );
exit( EXIT_FAILURE );
}
if ( bind( server_fd,
(struct sockaddr *) &server_sockaddr,
sizeof server_sockaddr) < 0)
{
perror( "bind" );
exit( EXIT_FAILURE );
}
listen( server_fd, BACKLOG );
// Log startup message.
char msgBuf[ 256 ];
sprintf( msgBuf,
"qserver pid %d listening on %s port 0x%04X",
getpid(), thisHostName, wellKnownPort );
openLog( logfilePath, msgBuf, overwriteLogfile );
// Set up exit processing
on_exit( do_exit, (char *) "Normal Exit" );
signal( SIGINT, signal_handler );
signal( SIGTERM, signal_handler );
// Event Loop
// ----------------------------------------------------------------
while ( true )
{
// Accept a connection
socklen_t saSize = sizeof client_sockaddr;
client_fd = accept( server_fd,
(struct sockaddr *) &client_sockaddr, &saSize );
if (client_fd < 0)
{
perror( "accept" );
exit( EXIT_FAILURE );
}
// Log the connection
hp = gethostbyaddr( (const char *)&client_sockaddr.sin_addr,
4, AF_INET );
writeLog( "CONNECT", hp->h_name );
// Read from the connection until it closes or a shutdown message
// ( SHUT ) is received.
char inBuf[8192];
while ( true )
{
int bytesRead = read( client_fd, &inBuf, sizeof( inBuf ) );
if ( 0 == bytesRead )
{
// Client closed the socket
writeLog( "DISCONNECT", hp->h_name );
close( client_fd );
break;
}
else
{
// Write the message to the log file and check for SHUT
inBuf[ bytesRead ] = '0';
writeLog( "TESTING", inBuf );
if ( 0 == strcmp( inBuf, "SHUT" ) )
exit( 0 );
}
}
}
}