/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project. All
................................................ contributions are welcome.
....Copyright(C).1995.Melvin.Smith.............. Enjoy.
------------------------------------------------------------------------------
Melvin Smith (aka Fusion) msmith@hom.net
MUD++ development mailing list mudpp@van.ml.org
------------------------------------------------------------------------------
server.cc
*/
#include <signal.h>
#include "config.h"
#include "server.h"
#include "io.h"
Server::Server()
: masterport(4000), masterdesc(0),
type( SOCK_STREAM ), nameserver(0), maxread( MAX_READ ),
maxidle(0), descripts(0)
{
#if !defined(WIN32)
maxdesc = sysconf( _SC_OPEN_MAX );
#else
maxdesc = WinsockInfo.iMaxSockets;
#endif
}
Server::Server( int theport )
: masterport( theport ), masterdesc(0),
type( SOCK_STREAM ), nameserver(0), maxread( MAX_READ ),
maxidle(0), descripts(0)
{
#ifndef WIN32
maxdesc = sysconf( _SC_OPEN_MAX );
#else
// no assurance that winsock has started yet
// if it hasn't then maxdesc will be set to zero.
maxdesc = WinsockInfo.iMaxSockets;
#endif /* WIN32 */
}
Server::~Server()
{
if( master )
master->close();
}
// This is for booting "into" a previous servers process
// space and inheriting the open master descriptor. Skip
// all the master struct creation and just assign the
// descriptor to sock and clean up a few other things.
// This really needs some serious error handling. Also need
// to save the sock struct info in the reboot.tab file
// and recreate it from that.
// --Fusion
int Server::boot( int oldport, int olddesc )
{
// How lucky for us, WIN32 doesn't even support SIGPIPE
// so why bother ignoring it? (generates an error anyway)
#ifndef WIN32
::signal( SIGPIPE, SIG_IGN );
#endif
master = new Socket( "", oldport, olddesc );
master->nonBlock();
masterport = oldport;
masterdesc = olddesc;
#ifndef WIN32
Cout << "SERVER: Ok, this OS supports " << maxdesc
<< " open descriptors per process.\n";
#else
Cout << "SERVER: Ok, this Winsock supports " << maxdesc
<< " open descriptors per machine.\n";
#endif
if( maxdesc > 256 )
maxdesc = 256;
bitwidth = maxdesc / 8;
topdesc = olddesc;
Cout << "SERVER: Rebooted succesfully - ready for connections.\n";
Cout << "SERVER: Port [" << master->getPort() << "]\n";
#ifndef WIN32
gettimeofday( &boot_time, 0 );
#else
struct _timeb timebuffer;
_ftime (& timebuffer);
boot_time.tv_sec = timebuffer.time;
boot_time.tv_usec = timebuffer.millitm * 1000;
#endif
return 0;
}
int Server::boot( int tport )
{
#ifndef WIN32
::signal( SIGPIPE, SIG_IGN );
#endif
master = new Socket( SOCK_STREAM, tport );
if( master->error() )
{
perror( "SERVER: Server::boot() socket not created" );
mudpp_exit(0);
}
master->reuseAddr();
master->noDelay();
master->nonBlock();
if( master->open() )
{
perror( "SERVER: Socket::open()" );
mudpp_exit(0);
}
masterdesc = topdesc = master->getDescriptor();
masterport = master->getPort();
#ifndef WIN32
Cout << "SERVER: Ok, this OS supports " << maxdesc
<< " open descriptors per process.\n";
#else
Cout << "SERVER: Ok, this Winsock supports " << maxdesc
<< " open descriptors per machine.\n";
#endif
if( maxdesc > 256 )
maxdesc = 256;
bitwidth = maxdesc / 8;
Cout << "SERVER: Booted succesfully - ready for connections.\n";
Cout << "SERVER: Port [" << masterport << "]\n";
#ifndef WIN32
gettimeofday( &boot_time, 0 );
#else
struct _timeb timebuffer;
_ftime (& timebuffer);
boot_time.tv_sec = timebuffer.time;
boot_time.tv_usec = timebuffer.millitm * 1000;
#endif
return 0;
}
const timeval & Server::getUpTime()
{
static timeval up;
timeval now_time;
#ifndef WIN32
gettimeofday( &now_time, 0 );
#else
struct _timeb timebuffer;
_ftime (& timebuffer);
now_time.tv_sec = timebuffer.time;
now_time.tv_usec = timebuffer.millitm * 1000;
#endif
up.tv_sec = now_time.tv_sec - boot_time.tv_sec;
return up;
}
int Server::poll()
{
memcpy( &R_SET, &MASTER_SET, bitwidth );
memcpy( &W_SET, &MASTER_SET, bitwidth );
memcpy( &E_SET, &MASTER_SET, bitwidth );
FD_SET( masterdesc, &R_SET );
// Linux modifies timeval * in select so reset every time.
null_time.tv_sec = 0;
null_time.tv_usec = 0;
while( select( topdesc+1, &R_SET, &W_SET, &E_SET, &null_time ) < 0 )
{
if( errno == EINTR )
continue;
Cout << "SERVER: select() error in Poll() - exiting.\n";
mudpp_exit( errno );
}
return FD_ISSET( masterdesc, &R_SET );
}
// I high resolution sleep function
// Best to inline this to remove the overhead of the function call
void Server::sleep( long microsecs )
{
static struct timeval sleep_time;
sleep_time.tv_sec = 0;
sleep_time.tv_usec = microsecs;
while( select( 0, NULL, NULL, NULL, &sleep_time ) < 0 )
{
#ifndef WIN32
if( errno == EINTR )
continue;
Cout << "SERVER: Error select() in Sleep()\n";
mudpp_exit( errno );
#else
if( WSAGetLastError() == WSAEINTR )
continue;
Cout << "SERVER::sleep: Error in select()\n";
mudpp_exit(0);
#endif
}
}
int Server::newConnection()
{
FD_ZERO( &R_SET );
FD_SET( masterdesc, &R_SET );
// Linux modifies timeval * in select so reset every time.
null_time.tv_sec = 0;
null_time.tv_usec = 0;
while( select( masterdesc + 1, &R_SET, 0, 0, &null_time ) < 0 )
{
if( errno == EINTR )
continue;
perror( "SERVER: select() in newConnection() - fatal" );
mudpp_exit( errno );
}
return FD_ISSET( masterdesc, &R_SET );
}
Socket * Server::accept()
{
int desc;
Socket *newSock = master->accept();
if( !newSock )
{
if( errno == EAGAIN || errno == EWOULDBLOCK )
return 0;
return 0;
}
newSock->noDelay();
newSock->getPeerName();
newSock->resolveIP();
desc = newSock->getDescriptor();
if( desc > topdesc )
topdesc = desc;
FD_SET( desc, &MASTER_SET );
descripts++;
Cout << "accept: new desc = " << desc << endl;
return newSock;
}
void Server::addSock( Socket * sock )
{
if( FD_ISSET( sock->getDescriptor(), &MASTER_SET ) )
{
Cout << "SERVER: Error. Attempt to add existing descriptor.\n";
mudpp_exit(0);
}
FD_SET( sock->getDescriptor(), &MASTER_SET );
if( sock->getDescriptor() > topdesc )
topdesc = sock->getDescriptor();
descripts++;
}
void Server::remove( Socket * sock )
{
// I exit on the following two errors because if this happens
// you need to find out why.
int desc = sock->getDescriptor();
if( desc < 0 || desc >= maxdesc )
{
Cout << "SERVER: Close() - desc [" << desc << "] out of bounds.\n";
abort();
}
if( ! FD_ISSET( desc, &MASTER_SET ) )
{
Cout << "SERVER: Invalid descriptor\n";
abort();
}
FD_CLR( desc, &MASTER_SET );
if( !--descripts )
{
topdesc = masterdesc;
return;
}
if( desc == topdesc )
{
for( desc = topdesc-1; !FD_ISSET( desc, &MASTER_SET ); desc-- )
;
topdesc = desc;
}
}
int Server::error( Socket * sock )
{
int desc = sock->getDescriptor();
if( FD_ISSET( desc, &E_SET ) )
{
FD_CLR( desc, &R_SET );
FD_CLR( desc, &W_SET );
return FD_ISSET( desc, &E_SET );
}
return 0;
}