/* ....[@@@..[@@@..............[@.................. 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; }