/*
* This file is part of rasdaman community.
*
* Rasdaman community is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rasdaman community is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with rasdaman community. If not, see .
*
* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann /
rasdaman GmbH.
*
* For more information please see
* or contact Peter Baumann via .
/
/**
* SOURCE: servercomm.cc
*
* MODULE: servercomm
* CLASS: ServerComm
*
* COMMENTS:
* None
*/
#include "mymalloc/mymalloc.h"
// --- these defs should go into a central constant definition section,
// as they define externally observable behavior -- PB 2003-nov-15
// waiting period until client is considered dead [secs]
#define CLIENT_TIMEOUT 3600
// timeout for select() call at server startup [secs]
#define TIMEOUT_SELECT 30
// period after which the next garbage collection is scheduled [secs]
#define GARBCOLL_INTERVAL 600
// console output describing successful/unsuccessful actions
#define MSG_OK "ok"
#define MSG_FAILED "failed"
// rasserver exit codes (selection of value sometimes unclear :-(
#define EXITCODE_ZERO 0
#define EXITCODE_ONE 1
#define EXITCODE_RASMGR_FAILED 10 // Why 10 ?
// ---
static const char rcsid[] = "@(#)servercomm, ServerComm: $Id: servercomm.cc,v 1.146 2006/01/03 00:23:53 rasdev Exp $";
#include
#include
#include
#include // for time()
#include
#include // for signal()
#include // for alarm(), gethostname()
#include
#ifdef SOLARIS
#define PORTMAP // define to use function declarations for old interfaces
#include
int _rpcpmstart = 0;
// function prototype with C linkage
extern "C" int gethostname(char *name, int namelen);
#else // HPUX
#include
#endif
#include
#include "raslib/rmdebug.hh"
#include "raslib/rminit.hh"
#include "raslib/error.hh"
#include "raslib/minterval.hh"
#include "raslib/parseparams.hh"
#include "compression/tilecompression.hh"
#include "servercomm/servercomm.hh"
#include "qlparser/qtnode.hh"
#include "qlparser/qtdata.hh"
#include "catalogmgr/typefactory.hh"
#include "tilemgr/tile.hh"
#include "relcatalogif/mddtype.hh"
#include "relcatalogif/mdddomaintype.hh"
#include "relcatalogif/settype.hh"
#include "mddmgr/mddcoll.hh"
#include "mddmgr/mddcolliter.hh"
#include "mddmgr/mddobj.hh"
#include "debug.hh"
#ifdef PURIFY
#include
#endif
#include
#include
#include
#include
#include
#include
#include
using namespace std;
// init globals for server initialization
// RMINITGLOBALS('S')
// Once again a function prototype. The first one is for the RPC dispatcher
// function located in the server stub file rpcif_svc.c and the second one
// is for the garbage collection function pointed to by the signal handler.
extern "C"
{
// void rpcif_1( struct svc_req*, register SVCXPRT* );
void garbageCollection( int );
}
extern char* rpcif_1( struct svc_req*, register SVCXPRT* );
extern bool bMemFailed;
// this is a temporary thing
extern int globalOptimizationLevel;
extern unsigned long maxTransferBufferSize;
// At the beginning, no servercomm object exists.
ServerComm* ServerComm::actual_servercomm = 0;
list ServerComm::clientTbl;
unsigned long ServerComm::clientCount = 0;
#include "rnprotocol/srvrasmgrcomm.hh"
extern SrvRasmgrComm rasmgrComm;
/*************************************************************************
* Method name...: ServerComm() (constructor)
************************************************************************/
ServerComm::ServerComm()
: clientTimeout( rasmgrComm.getTimeout() ),
garbageCollectionInterval( GARBCOLL_INTERVAL ),
transactionActive( 0 ),
memUsed( 0 ),
admin( 0 ),
errorText( 0 )
{
if( actual_servercomm )
{
RMInit::logOut << "Internal Error: Tried to instantiate more than one ServerComm object." << endl;
exit( EXITCODE_ONE );
}
// uniqueClientContext = NULL;
isHttpServer = false;
actual_servercomm = this;
}
ServerComm::ServerComm( unsigned long timeOut, unsigned long managementInterval , unsigned long listenPort, char* rasmgrHost, unsigned int rasmgrPort,char* serverName)
: clientTimeout( timeOut ),
garbageCollectionInterval( managementInterval ),
transactionActive( 0 ),
memUsed( 0 ),
admin( 0 ),
errorText( 0 )
{
if( actual_servercomm )
{
RMInit::logOut << "Internal Error: Tried to instantiate more than one ServerComm object." << endl;
exit( EXITCODE_ONE );
}
actual_servercomm = this;
this->listenPort = listenPort;
this->rasmgrHost = rasmgrHost;
this->rasmgrPort = rasmgrPort;
this->serverName = serverName;
isHttpServer = false;
//uniqueClientContext = NULL;
}
/*************************************************************************
* Method name...: ~ServerComm() (destructor)
************************************************************************/
ServerComm::~ServerComm()
{
// delete communication object
if( admin ) delete admin;
actual_servercomm = 0;
}
/*************************************************************************
* Method name...: startRpcServer()
************************************************************************/
void rpcSignalHandler(int sig);
void our_svc_run();
void
ServerComm::startRpcServer()
throw( r_Error )
{
// create administraion object (O2 session is initialized)
admin = AdminIf::instance();
if( !admin )
throw r_Error( r_Error::r_Error_BaseDBMSFailed );
register SVCXPRT *transp;
// install a signal handler to catch alarm signals for garbage collection
signal( SIGALRM, garbageCollection );
RMInit::logOut << "Testing if no other rasdaman RPC server with my listenPort (0x"<< hex << listenPort << dec <<") is already running on this CPU..." << flush;
char* myName = new char[256];
if( gethostname( myName, 256 ) )
RMInit::logOut << endl << "Unable to determine my own hostname. Skipping this test." << endl;
else
if( clnt_create( myName, listenPort, RPCIFVERS, "tcp" ) )
{
RMInit::logOut << MSG_FAILED << endl;
exit( EXITCODE_ZERO );
}
else
RMInit::logOut << MSG_OK << endl;
delete[] myName;
(void) pmap_unset(listenPort, RPCIFVERS);
RMInit::logOut << "Creating UDP services..." << flush;
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL)
{
RMInit::logOut << MSG_FAILED << endl;
throw r_Error( r_Error::r_Error_General );
}
RMInit::logOut << "registering UDP interface..." << flush;
if (!svc_register(transp, listenPort, RPCIFVERS, rpcif_1_caller, IPPROTO_UDP))
{
RMInit::logOut << MSG_FAILED << endl;
throw r_Error( r_Error::r_Error_General );
}
RMInit::logOut << MSG_OK << endl;
RMInit::logOut << "Creating TCP services..." << flush;
transp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (transp == NULL)
{
RMInit::logOut << MSG_FAILED << endl;
throw r_Error( r_Error::r_Error_General );
}
RMInit::logOut << "registering TCP interface..." << flush;
if (!svc_register(transp, listenPort, RPCIFVERS, rpcif_1_caller, IPPROTO_TCP))
{
RMInit::logOut << MSG_FAILED << endl;
throw r_Error( r_Error::r_Error_General );
}
RMInit::logOut << MSG_OK << endl;
RMInit::logOut << "Setting alarm clock for next garbage collection to " << garbageCollectionInterval
<< " secs..."; RMInit::logOut.flush();
alarm( (unsigned int)garbageCollectionInterval );
RMInit::logOut << MSG_OK << endl;
RMInit::logOut << "Global query optimization level is " << globalOptimizationLevel << "." << endl;
signal (SIGTERM, rpcSignalHandler);
informRasMGR(SERVER_AVAILABLE);
RMInit::logOut << "rasdaman server "<0)
{
svc_getreqset(&read_fd_set);
TALK( "RPC Request executed." );
}
if(rasp<=0)
{
TALK( "our_svc_run(): Error: Timeout." ); // or a signal
// execute all pending callbacks. Redirect alarm signal first to make sure no
// reentrance is possible!
// these should abort any pending transaction
signal( SIGALRM, garbageCollectionDummy );
ServerComm::actual_servercomm->callback_mgr.executePending();
signal( SIGALRM, garbageCollection );
timeout.tv_sec= TIMEOUT_SELECT;
timeout.tv_usec=0;
if(accessControl.isClient()==false)
{
// regularly tell the rasmgr that we are available. There is a scenario for DoS-attack (or bug, or firewall-problem)
// when a client allocates itself a server and never calls, so the server is not usable any more.
// but we have to find a smarter way of doing this, we need rasmgr-rasserver communication!
ServerComm::actual_servercomm->informRasMGR(SERVER_REGULARSIG);
}
if(bMemFailed)
{
// no reason to continue
RMInit::logOut << "Internal error: rasserver: memory exhausted." << endl << flush;
exit( EXITCODE_ONE );
}
}
}
LEAVE( "our_svc_run" );
}
void rpcSignalHandler(int sig)
{
static int in_progress=0; // our sema to prevent signal-in-signal
if (in_progress)
return;
in_progress = 1;
// busy wait
#define SIGNAL_WAIT_CYCLES 1000000
for(long j=0;jstopRpcServer();
}
} // rpcSignalHandler()
void
ServerComm::stopRpcServer()
{
RMInit::logOut << "Shutdown request received." << endl;
// Determine when next garbage collection would have occured
unsigned long nextGarbColl = time( NULL );
struct itimerval rttimer;
getitimer( ITIMER_REAL, &rttimer );
nextGarbColl += rttimer.it_value.tv_sec;
RMInit::logOut << "Next garbage collection would have been in " << rttimer.it_value.tv_sec << " sec, at "
<< ctime((time_t*)&nextGarbColl);
RMInit::logOut << "Unregistering interface...";
svc_unregister( listenPort, RPCIFVERS );
RMInit::logOut << "shutting down services...";
abortEveryThingNow();
RMInit::logOut << "informing rasmgr...";
informRasMGR(SERVER_DOWN);
RMInit::logOut << MSG_OK << endl;
RMInit::logOut << "rasdaman server " << serverName <<" is down." << endl;
exit(0);
// svc_exit();
}
// quick hack function used when stopping server to abort transaction and close db
void
ServerComm::abortEveryThingNow()
{
ENTER( "ServerComm::abortEveryThingNow()" );
list::iterator iter;
ServerComm *sc=ServerComm::actual_servercomm;
iter = sc->clientTbl.begin();
while ( iter != sc->clientTbl.end() )
{
ServerComm::ClientTblElt *clnt = *iter;
clnt->transaction.abort();
clnt->database.close();
iter++;
}
LEAVE( "ServerComm::abortEveryThingNow()" );
}
/*************************************************************************
* rpcif_1_caller(struct svc_req *rqstp, SVCXPRT *transp)
* indirect caller for rpcif_1, which is automaticaly generated and can't
* be hand-edited. It provides services for parallel-processing
*************************************************************************/
void rpcif_1_caller(struct svc_req *rqstp, SVCXPRT *transp)
{
ENTER( "rpcif_1_caller(_,_)" );
bool flagTransactionReady=false;
bool isGetExtendedError=false;
switch (rqstp->rq_proc)
{
//case RPCCOMMITTA: no, closeDB only, because abortTA and commitTA
//case RPCABORTTA: do automatically a closeDB and both reporting to RasMGR "available" brings problems
case RPCCLOSEDB: flagTransactionReady=true; break;
case RPCGETERRORINFO: isGetExtendedError = true; break;
}
if(isGetExtendedError == false) ServerComm::actual_servercomm->clearExtendedErrorInfo();
char* errTxt = rpcif_1(rqstp,transp);
if(isGetExtendedError && bMemFailed) {
// the client got the message
// no reason to continue
signal( SIGALRM, garbageCollectionDummy );
ServerComm::actual_servercomm->callback_mgr.executePending();
RMInit::logOut << "Internal error: rasserver panic: memory exhausted, terminating forcefully." << endl << flush;
exit(1);
}
if (errTxt)
{
// this is not necessary, since the client gets an error code '42'
//RMInit::logOut << "rasserver: general exception from server caught by rpcif_1_caller!" << endl << errTxt << endl;
//RMInit::logOut.flush();
//cerr << "rasserver: general exception from server caught by rpcif_1_caller!" << endl << errTxt << endl;
ServerComm::actual_servercomm->setExtendedErrorInfo(errTxt);
free(errTxt);
errTxt = 0;
//Answer "Remote system error " to the client
//svcerr_systemerr (transp); don't think that's necessary any more, for the same reason
}
ServerComm::actual_servercomm->clientEndRequest();
if(flagTransactionReady)
ServerComm::actual_servercomm->informRasMGR(SERVER_AVAILABLE);
LEAVE( "rpcif_1_caller()" );
}
/*************************************************************************
* Method name...: informRasMGR( int what ) with a helper function
************************************************************************/
#include
// writeWholeMessage:
// send message via (open, connected) socket to rasmgr
// called by informRasMGR.
// return values:
// -1 error
// >0 success
int writeWholeMessage(int socket,char *destBuffer,int buffSize)
{
ENTER( "writeWholeMessage( socket=" << socket << ", destBuffer=" << (destBuffer?destBuffer:"(null)") << ", buffSize=" << buffSize << " )" );
// we write the whole message, including the ending '\0', which is already in
// the buffSize provided by the caller
int totalLength=0;
int writeNow;
while(1)
{
writeNow = write(socket,destBuffer+totalLength,buffSize-totalLength);
if(writeNow == -1)
{
TALK( "writeWholeMessage: bad socket write returned " << writeNow << ", errno=" << errno );
if(errno == EINTR)
continue; // read was interrupted by signal (on bad SO's)
LEAVE( "writeWholeMessage(): error, errno=" << errno );
return -1; // another error
}
totalLength+=writeNow;
if( totalLength==buffSize )
break; // THE END
}
LEAVE( "writeWholeMessage() -> " << totalLength );
return totalLength;
}
long infCount=0; // for debug only
// ServerComm::informRasMGR:
// send message code to rasmgr (e.g., "still alive"), incl socket management
// return code:
// none, but function does exit() on error 8-()
void ServerComm::informRasMGR( int what )
{
ENTER( "ServerComm::informRasMGR, what=" << what );
TALK( "Informing dispatcher " << rasmgrHost << " port=" << rasmgrPort << " that server is available" );
//what: 0 - going down
// 1 - available
// 2 - regular signal
if (what == SERVER_AVAILABLE)
accessControl.resetForNewClient();
struct protoent* getprotoptr = getprotobyname("tcp");
struct hostent *hostinfo = gethostbyname(rasmgrHost);
if (hostinfo==NULL)
{
cerr<< serverName<<": informRasMGR: cannot locate RasMGR host "<h_addr;
int sock; // socket for rasmgr communication
bool ok=false; // did we get a socket?
// FIXME: This is _extremely_ ugly. Should be something like
// 10 retries, with exponentially growing waits, such as 0, 1ms, 2ms, 4ms, ...
// have prepared defs, but code is the original one. -- PB 2003-nov-15
// long maxRetry = NUM_RETRIES_SERVER_ALIVE;
// #define NUM_RETRIES_SERVER_ALIVE 10
long retry =0;
long maxRetry=10000000; // ten millions!
long talkInterval=100000;
// creating socket
for(retry=0;retryp_proto);
TALK( "Socket=" << sock << " protocol(tcp)=" << getprotoptr->p_proto );
if(sock<0)
{
// if ( (retry%talkInterval) == 0)
{
cerr << "Error in server '" << serverName << "': cannot open socket to rasmgr, (" << strerror(errno) << ')' << " still retrying..." << endl;
TALK( "ServerComm::informRasMGR: cannot open socket to RasMGR: " << strerror(errno) << "; retry " << retry << " of " << maxRetry );
RMInit::logOut << "Error: cannot open socket to rasmgr, (" << strerror(errno) << ')'<" << result );
if (result < 0)
{
// if( (retry%talkInterval) == 0)
{
cerr << "Error in server '" << serverName << "': Connection to rasmgr failed (still retrying): "<::iterator iter;
iter = clientTbl.begin();
while( iter != clientTbl.end() && (*iter)->clientId != clientId )
{
TALK( " inspecting entry with clientID " << (*iter)->clientId );
iter++;
}
if( iter != clientTbl.end() && clientId && (*iter)->clientId == clientId )
{
returnValue = *iter;
// Valid entry was found, so increase the number of current users and
// reset the client's lastActionTime to now.
(*iter)->currentUsers++;
(*iter)->lastActionTime = time( NULL );
TALK( "valid entry found, current users now: " << (*iter)->currentUsers );
}
}
// this output will be done lateron, in the caller:
// if( returnValue == 0 )
// RMInit::logOut << "Error: client not registered." << endl;
// this trick did not work, broke the HTTP server
// if(isHttpServer==false ) uniqueClientContext = returnValue;
#ifdef RMANDEBUG
ServerComm::printServerStatus( RMInit::logOut ); // pretty verbose
#endif
LEAVE( "ServerComm::getClientContext()" );
return returnValue;
}
void
ServerComm::clientEndRequest()
{
ENTER( "ServerComm::clientEndRequest()" );
#ifdef RMANDEBUG
printServerStatus( RMInit::dbgOut ); // pretty verbose
#endif
// this trick did not work, broke the HTTp server
// if(isHttpServer==false && uniqueClientContext != NULL)
// uniqueClientContext->endRequest();
LEAVE( "ServerComm::clientEndRequest()" );
}
/*************************************************************************
* Method name...: printServerStatus( )
************************************************************************/
void
ServerComm::printServerStatus( ostream& s )
{
unsigned long currentTime = time(NULL);
s << "Server state information at " << endl; // << ctime((time_t*)¤tTime) << endl;
s << " Inactivity time out of clients.: " << clientTimeout << " sec" << endl;
s << " Server management interval.....: " << garbageCollectionInterval << " sec" << endl;
s << " Transaction active.............: " << ( transactionActive ? "yes" : "no" ) << endl;
s << " Max. transfer buffer size......: " << maxTransferBufferSize << " bytes" << endl;
s << " Next available cliend id.......: " << clientCount+1 << endl;
s << " No. of client table entries....: " << clientTbl.size() << endl << endl;
if( !clientTbl.empty() )
{
list::iterator iter;
// display contents of client table
s << "client table dump---------------------------------------------" << endl;
for( iter = clientTbl.begin(); iter != clientTbl.end(); iter++ )
{
if (*iter==NULL)
{
s << "Error: null context found." << endl;
RMInit::logOut << "Error: null context found." << endl;
continue;
}
s << "Client ID : " << (*iter)->clientId << endl
<< "Current Users : " << (*iter)->currentUsers << endl
<< "Client location: " << (*iter)->clientIdText << endl
<< "User name : " << (*iter)->userName << endl
<< "Database in use: " << (*iter)->baseName << endl
<< "Creation time : " << endl // ctime((time_t*)&(*iter)->creationTime)
<< "Last action at : " << endl // ctime((time_t*)&(*iter)->lastActionTime)
<< "MDD collection : " << (*iter)->transferColl << endl
<< "MDD iterator : " << (*iter)->transferCollIter << endl
<< "Current PersMDD: " << (*iter)->assembleMDD << endl
<< "Current MDD : " << (*iter)->transferMDD << endl
<< "Tile vector : " << (*iter)->transTiles << endl
<< "Tile iterator : " << (*iter)->tileIter << endl
<< "Block byte cntr: " << (*iter)->bytesToTransfer << endl << endl;
}
s << "end client table dump-----------------------------------------" << endl;
}
/*
s << "memory map----------------------------------------------------" << endl;
// memorymap(1);
struct mallinfo meminfo = mallinfo();
s << "space in arena : " << meminfo.arena << endl;
s << "number of small blocks : " << meminfo.smblks << endl;
s << "number of ordinary blocks : " << meminfo.ordblks << endl;
s << "space in free ordinary blocks : " << meminfo.fordblks << endl;
s << "space in used ordinary blocks : " << meminfo.uordblks << endl;
s << "additional space from last call: " << meminfo.uordblks - memUsed << endl;
memUsed = meminfo.uordblks;
s << "end memory map------------------------------------------------" << endl << endl;
*/
}
/*************************************************************************
* Method name...: getServerStatus( )
************************************************************************/
void
ServerComm::getServerStatus( ServerStatRes& returnStruct )
{
returnStruct.inactivityTimeout = clientTimeout;
returnStruct.managementInterval = garbageCollectionInterval;
returnStruct.transactionActive = transactionActive;
returnStruct.maxTransferBufferSize = maxTransferBufferSize;
returnStruct.nextClientId = clientCount+1;
returnStruct.clientNumber = clientTbl.size();
if( !clientTbl.empty() )
{
list::iterator iter;
int i;
returnStruct.clientTable.clientTable_len = clientTbl.size();
returnStruct.clientTable.clientTable_val = (RPCClientEntry*) mymalloc( sizeof(RPCClientEntry) * clientTbl.size() );
for( iter = clientTbl.begin(), i=0; iter != clientTbl.end(); iter++, i++ )
{
returnStruct.clientTable.clientTable_val[i].clientId = (*iter)->clientId;
returnStruct.clientTable.clientTable_val[i].clientIdText = strdup( (*iter)->clientIdText );
returnStruct.clientTable.clientTable_val[i].userName = strdup( (*iter)->userName );
returnStruct.clientTable.clientTable_val[i].baseName = strdup( (*iter)->baseName );
returnStruct.clientTable.clientTable_val[i].creationTime = (*iter)->creationTime;
returnStruct.clientTable.clientTable_val[i].lastActionTime = (*iter)->lastActionTime;
returnStruct.clientTable.clientTable_val[i].transferColl = (unsigned long)((*iter)->transferColl);
returnStruct.clientTable.clientTable_val[i].transferIter = (unsigned long)((*iter)->transferCollIter);
returnStruct.clientTable.clientTable_val[i].assembleMDD = (unsigned long)((*iter)->assembleMDD);
returnStruct.clientTable.clientTable_val[i].transferMDD = (unsigned long)((*iter)->transferMDD);
returnStruct.clientTable.clientTable_val[i].transTiles = (unsigned long)((*iter)->transTiles);
returnStruct.clientTable.clientTable_val[i].tileIter = (unsigned long)((*iter)->tileIter);
returnStruct.clientTable.clientTable_val[i].bytesToTransfer = (*iter)->bytesToTransfer;
}
}
struct mallinfo meminfo = mallinfo();
returnStruct.memArena = meminfo.arena;
returnStruct.memSmblks = meminfo.smblks;
returnStruct.memOrdblks = meminfo.ordblks;
returnStruct.memFordblks = meminfo.fordblks;
returnStruct.memUordblks = meminfo.uordblks;
}
/*************************************************************************
* Method name...: addClientTblEntry( ClientTblElt *context )
************************************************************************/
void
ServerComm::addClientTblEntry( ClientTblElt *context ) throw ( r_Error )
{
ENTER( "addClientTblEntry()" );
if (context==NULL)
{
RMInit::logOut << "Error: ServerComm::addClientTblEntry(): client context is NULL." << endl;
throw r_Error( r_Error::r_Error_RefNull );
}
clientTbl.push_back( context );
#ifdef RMANDEBUG
ServerComm::printServerStatus( RMInit::logOut ); // quite verbose
#endif
LEAVE( "addClientTblEntry()" );
}
/*************************************************************************
* Method name...: deleteClientTblEntry( unsigned long clientId )
************************************************************************/
unsigned short
ServerComm::deleteClientTblEntry( unsigned long clientId )
{
ENTER( "deleteClientTblEntry( " << clientId << " )" );
unsigned short returnValue = 0;
ClientTblElt* context = getClientContext( clientId );
if( !context )
{
TALK( "Warning: in ServerComm::deleteClientTblEntry(): null context, " << "client " << clientId << " not found." );
return 1; // desired client id was not found in the client table
}
if( context->currentUsers > 1 )
{
// In this case, the client table entry was under use before our getClientContext() call.
context->release();
TALK( "Client context of user "<transaction.abort();
transactionActive = 0;
}
// close the database if it isn't already closed
// (e.g. after connection breakdowns)
if( strcmp( context->baseName, "none" ) != 0 )
{
RMInit::logOut << "closing database..." << RMInit::logOut.flush();
context->database.close();
// reset database name
delete[] context->baseName;
context->baseName = new char[5];
strcpy( context->baseName, "none" );
}
#ifdef RMANDEBUG
ServerComm::printServerStatus( RMInit::logOut ); // can be pretty verbose
#endif
// remove the entry from the client table
list::iterator iter;
for( iter=clientTbl.begin(); iter != clientTbl.end() && (*iter)->clientId != clientId; iter++ )
;
if ( iter != clientTbl.end() )
clientTbl.erase( iter );
// delete the client table entry data itself
// (delete is controlled by the destructor of the ClientTblElt object)
delete context;
TALK( "client table now has " << clientTbl.size() << " entries." );
LEAVE( "deleteClientTblEntry()" );
return returnValue;
}
/*************************************************************************
* Method name...: ServerComm::getExtendedErrorInfo()
************************************************************************/
const char *
ServerComm::getExtendedErrorInfo()
{
if(errorText == NULL)
return "(no error info)";
return errorText;
}
/*************************************************************************
* Method name...: ServerComm::getExtendedErrorInfo()
************************************************************************/
void ServerComm::setExtendedErrorInfo(const char *newErrorText)
{
clearExtendedErrorInfo();
errorText = new char [strlen(newErrorText)+1];
strcpy(errorText,newErrorText);
}
/*************************************************************************
* Method name...: ServerComm::getExtendedErrorInfo()
************************************************************************/
void ServerComm::clearExtendedErrorInfo()
{
if(errorText) delete[] errorText;
errorText = NULL;
}
/*************************************************************************
* Method name...: ClientTblElt( const char* clientText,
* unsigned long client ) (constructor)
************************************************************************/
ServerComm::ClientTblElt::ClientTblElt( const char* clientText, unsigned long client )
: clientId( client ),
currentUsers(0),
clientIdText(0),
userName(0),
baseName(0),
creationTime(0),
lastActionTime(0),
transferFormat(r_Array),
transferFormatParams(0),
exactFormat(1),
storageFormat(r_Array),
storageFormatParams(0),
encodedData(0),
encodedSize(0),
totalRawSize(0),
totalTransferedSize(0),
transferColl(0),
transferCollIter(0),
transferData(0),
transferDataIter(0),
assembleMDD(0),
transferMDD(0),
transTiles(0),
tileIter(0),
deletableTiles(0),
bytesToTransfer(0),
persMDDCollections(0),
taTimer(0),
transferTimer(0),
evaluationTimer(0),
clientParams(0)
{
ENTER( "ServerComm::ClientTblElt::ClientTblElt( clientText=" << (clientText?clientText:"(null)") << ", client=0x" << hex << client << dec << " )" );
creationTime = time( NULL );
clientIdText = new char[strlen(clientText)+1];
strcpy( clientIdText, clientText );
baseName = new char[5];
strcpy( baseName, "none" );
userName = new char[8];
strcpy( userName, "unknown" );
clientParams = new r_Parse_Params();
clientParams->add("exactformat", &exactFormat, r_Parse_Params::param_type_int);
LEAVE( "ServerComm::ClientTblElt::ClientTblElt()" );
}
ServerComm::ClientTblElt::~ClientTblElt()
{
releaseTransferStructures();
// delete the clientIdText member
delete[] clientIdText;
// delete the baseName member
delete[] baseName;
// delete user name
delete[] userName;
// delete transfer format parameters
if (transferFormatParams != NULL)
delete [] transferFormatParams;
if (storageFormatParams != NULL)
delete [] storageFormatParams;
if(clientParams)
delete clientParams;
}
void
ServerComm::ClientTblElt::release()
{
if( currentUsers == 0 )
RMInit::logOut << "Warning: releasing a non-active client." << endl;
currentUsers--;
lastActionTime = time( NULL );
}
void
ServerComm::ClientTblElt::endRequest()
{
if(currentUsers != 0)
RMInit::logOut << "Warning: Client ended request without releasing context. Forcing release now." << endl;
currentUsers=0;
lastActionTime = time( NULL );
}
void
ServerComm::ClientTblElt::releaseTransferStructures()
{
ENTER( "ServerComm::ClientTblElt::releaseTransferStructures()" );
RMDBGENTER(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "releaseTransferStructures()")
// delete the transfer iterator
if( transferCollIter )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferCollIter" )
delete transferCollIter;
transferCollIter = 0;
}
// delete transfer data
if( transferData )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferData" )
QtNode::QtDataList::iterator dataIter;
// delete list elements
for( dataIter=transferData->begin(); dataIter!=transferData->end(); dataIter++ )
if( *dataIter )
{
// Note: The following consistency check does not hold for the case when data objects occur
// more than once in the result set (e.g., constants).
// Consistency Check: should be the last reference.
// if( (*dataIter)->getRefNo() > 1 )
// {
// RMInit::logOut << endl << "Internal error in releaseTransferStructures: references left, object " << RMInit::logOut.flush();
// (*dataIter)->printStatus( RMInit::logOut );
// RMInit::logOut << endl;
// }
// Just tupel elements which are not further referenced are deleted.
if (*dataIter)
{
(*dataIter)->deleteRef();
(*dataIter) = 0;
}
}
delete transferData;
transferData = 0;
}
// delete the transfer collection
// the transferData will check objects because of the bugfix. therefore the objects may deleted only after the check.
if( transferColl )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferColl" )
transferColl->releaseAll();
delete transferColl;
transferColl = 0;
}
// delete transfer data iterator
if( transferDataIter )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferDataIter" )
delete transferDataIter;
transferDataIter = 0;
}
// delete the temporary PersMDDObj
if( assembleMDD )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release assembleMDD" )
delete assembleMDD;
assembleMDD = 0;
}
// delete the transfer MDDobj
if( transferMDD )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferMDD" )
delete transferMDD;
transferMDD = 0;
}
// vector< Tile* >* transTiles;
if( transTiles )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transTiles" )
// Tiles are deleted by the MDDObject owing them.
// release( transTiles->begin(), transTiles->end() );
delete transTiles;
transTiles = 0;
}
// vector< Tile* >::iterator* tileIter;
if( tileIter )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release tileIter" )
delete tileIter;
tileIter = 0;
}
// delete deletable tiles
if( deletableTiles )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release deletableTiles" )
vector::iterator iter;
for( iter=deletableTiles->begin(); iter!=deletableTiles->end(); iter++ )
if( *iter )
delete *iter;
delete deletableTiles;
deletableTiles = 0;
}
// delete persistent MDD collections
if( persMDDCollections )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release persMDDCollections" )
vector::iterator collIter;
for( collIter=persMDDCollections->begin(); collIter!=persMDDCollections->end(); collIter++ )
if( *collIter )
{
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "before PersMDDColl::releaseAll()" )
(*collIter)->releaseAll();
RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "after PersMDDColl::releaseAll()" )
delete *collIter;
}
delete persMDDCollections;
persMDDCollections = 0;
}
// transfer compression
if (encodedData != NULL)
{
free(encodedData);
encodedData = NULL; encodedSize = 0;
}
#ifdef RMANBENCHMARK
// Attention: taTimer is deleted in either commitTA() or abortTA().
if( evaluationTimer ) delete evaluationTimer;
evaluationTimer = 0;
if( transferTimer ) delete transferTimer;
transferTimer = 0;
#endif
RMDBGEXIT(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "releaseTransferStructures()")
LEAVE( "ServerComm::ClientTblElt::releaseTransferStructures()" );
}
/******************************************************************************************
*** This class shouldn't be here, later it will be put in its own file
******************************************************************************************/
// learned from license.cc
#ifdef LINUX
extern "C" {
extern char *strptime __P ((__const char *__s, __const char *__fmt, struct tm *__tp));
}
#endif
AccessControl accessControl;
AccessControl::AccessControl()
{
initDeltaT=0;
resetForNewClient();
}
AccessControl::~AccessControl()
{
}
void AccessControl::setServerName(const char *serverName)
{
strcpy(this->serverName,serverName);
}
void AccessControl::initSyncro(const char *syncroString)
{
struct tm brokentime;
strptime(syncroString,"%d:%m:%Y:%H:%M:%S",&brokentime);
initDeltaT= difftime (time(NULL) ,mktime(&brokentime) );
// cout<<"DeltaT="< " << CAPABILITY_REFUSED );
return CAPABILITY_REFUSED;
}
*digest=0;
digest++;digest++;
digest[32]=0;
TALK( "Digest="< " << CAPABILITY_REFUSED );
return CAPABILITY_REFUSED;
}
char *rights=strstr(capaQ,"$E")+2;
char *timeout=strstr(capaQ,"$T")+2;
char *cServerName=strstr(capaQ,"$N")+2;
// end of cServername is $D, $->0 by digest
struct tm brokentime;
strptime(timeout,"%d:%m:%Y:%H:%M:%S",&brokentime);
double DeltaT= difftime (mktime(&brokentime),time(NULL) );
//for the moment, DEC makes trouble
// if(DeltaT < initDeltaT) return CAPABILITY_REFUSED; //!!! Capability too old
// cout<<"DeltaT="< server name doesn't match, got: " << cServerName << ", need: " << serverName << " -> " << CAPABILITY_REFUSED );
return CAPABILITY_REFUSED; //!!! Call is not for me
}
okToRead = false; // looks like a 'true' never gets reset: -- PB 2006-jan-02
okToWrite = false; // -dito-
for(int i=0;*rights!='$' && *rights && i<2; rights++,i++)
{
//We only have 2 rights defined now
if(*rights=='R')
okToRead = true;
if(*rights=='W')
okToWrite= true;
}
weHaveClient=true;
TALK( "capability crunched: digest=" << digest << ", rights=" << rights << ", timeout=" << timeout << "(remaining time: " << DeltaT << "), cServerName=" << cServerName << ", okToRead=" << okToRead << ", okToWrite=" << okToWrite << "" );
LEAVE( "AccessControl::crunchCapability() -> 0 (ok)" );
return 0; // OK for now
}
int AccessControl::messageDigest(const char *input,char *output,const char *mdName)
{
EVP_MD_CTX mdctx;
const EVP_MD *md;
unsigned int md_len, i;
unsigned char md_value[100];
OpenSSL_add_all_digests();
md = EVP_get_digestbyname(mdName);
if(!md)
return 0;
EVP_DigestInit(&mdctx, md);
EVP_DigestUpdate(&mdctx,input, strlen(input));
EVP_DigestFinal(&mdctx, md_value, &md_len);
for(i = 0; i < md_len; i++)
sprintf(output+i+i,"%02x", md_value[i]);
return strlen(output);
}
void AccessControl::wantToRead()
{
if(okToRead==false)
{
TALK( "AccessControl::wantToRead(): error: no permission for read operation." );
throw r_Eno_permission(); //r_Error(NO_PERMISSION_FOR_OPERATION);
}
}
void AccessControl::wantToWrite()
{
if(okToWrite==false)
{
TALK( "AccessControl::wantToWrite(): error: no permission for write operation." );
throw r_Eno_permission(); //r_Error(NO_PERMISSION_FOR_OPERATION);
}
}