#include "cdevServer.h" #include "ClientAcceptor.h" #include "ClientHandler.h" sig_atomic_t cdevServer::Finished = 0; SignalManager cdevServer::SigManager; cdevGenericServerTagDef cdevServer::tags; // ***************************************************************************** // * This function is an interrupt handler that will be executed whenever the // * program receives a SIGINT. When called it will set the finished flag to 1 // * and trigger the termination of the program. // ***************************************************************************** static void SIGINT_handler (int signo) { cdevServer::Finished = 1; signal(signo, SIGINT_handler); } // ***************************************************************************** // * cdevServer::cdevServer : // * This is the constructor for the class. It will post the listening // * socket, establish registration with the name server and record all // * other pertinent information. // ***************************************************************************** cdevServer::cdevServer (char * DomainName, char * ServerName, unsigned short Port, double Rate) : cdevSessionManager(), serverName (NULL), acceptor (NULL), timer (NULL), status (UNINITIALIZED), serverInfo (NULL) { // ********************************************************************* // * Register the import method for the cdevMessage class with the // * cdevPacket class. // ********************************************************************* cdevPacket::registerImportMethod(cdevMessage::CDEV_PACKET_VERSION, cdevMessage::import); startServer(DomainName, ServerName, Port, Rate, 0); } // ***************************************************************************** // * cdevServer::cdevServer : // * This is the do nothing constructor for the class. // ***************************************************************************** cdevServer::cdevServer ( void ) : cdevSessionManager (), serverName (NULL), acceptor (NULL), timer (NULL), status (UNINITIALIZED), serverInfo (NULL) { // ********************************************************************* // * Register the import method for the cdevMessage class with the // * cdevPacket class. // ********************************************************************* cdevPacket::registerImportMethod(cdevMessage::CDEV_PACKET_VERSION, cdevMessage::import); } // ***************************************************************************** // * cdevServer::~cdevServer : // * This is the destructor for the object. It will close all listening // * sockets and will delete all allocated items. // ***************************************************************************** cdevServer::~cdevServer ( void ) { outputError ( CDEV_SEVERITY_INFO, "CDEV Server", "Server %s is Terminating...", serverName); Reactor.extractHandler(this); if(timer!=NULL) delete timer; if(serverName!=NULL) delete serverName; if(acceptor!=NULL) delete acceptor; } // ***************************************************************************** // * cdevServer::startServer : // * This method will initialize a cdevServer within a given name, will // * register it within the CDEV Name Server and will commence listening // * for connections on the specified port. // ***************************************************************************** int cdevServer::startServer (char * DomainName, char * ServerName, unsigned short Port, double Rate, int searchForPort) { if(acceptor!=NULL) { delete acceptor; acceptor=NULL; } if(timer) { delete timer; timer=NULL; } if(serverName) { delete serverName; serverName = NULL; } if(serverInfo) { delete serverInfo; serverInfo = NULL; } Reactor.extractHandler(this); setTimeoutRate(Rate); serverName = strdup(ServerName); status = UNINITIALIZED; acceptor = new ClientAcceptor(*this); // ********************************************************************* // * Attempt to open the acceptor to receive new connections. // ********************************************************************* if(searchForPort) Port = 0; cdevInetAddr addr(Port); int initialized=!(acceptor->open(addr)); // ********************************************************************* // * Call getLocalAddress to retrieve the actual port number that was // * opened. This value will differ from the specified port if... // * 1) the user specified 0 for the port number -or- // * 2) the user set the searchForPort flag to non-zero. // ********************************************************************* if(acceptor->getLocalAddress(addr)==0) Port = addr.getPortNum(); else Port = (unsigned short)-1; // ********************************************************************* // * ServerInfo must be created after the port has been searched for // * and found. Otherwise, the server will report the original port // * number rather than the actual port number. // ********************************************************************* serverInfo = new ServerInfo(DomainName, ServerName, Port); if(initialized) { // ************************************************************* // * Register the acceptor with the Reactor. // ************************************************************* if(Reactor.registerHandler(acceptor, READ_MASK)!=-1) { // ***************************************************** // * Register this event handler with the Reactor. // ***************************************************** if(Reactor.registerHandler(this, READ_MASK)!=-1) { // ********************************************* // * Schedule the periodic timer if it is // * greater than 0. // ********************************************* if(Rate <= 0.0 || Reactor.registerTimer(this)!=-1) { // ************************************* // * Install a name server timer to // * continuously register this server // * with the Name Server. // ************************************* timer = new cdevNameServerManager(Reactor, DomainName, ServerName, Port); outputError (CDEV_SEVERITY_INFO, "CDEV Server", "Server %s is operational and servicing requests...", serverName); } else { outputError ( CDEV_SEVERITY_SEVERE, "CDEV Server", "Server %s - Unable to register server timer with cdevReactor", serverName); status = CANT_REGISTER_TIMER; } } else { outputError ( CDEV_SEVERITY_SEVERE, "CDEV Server", "Server %s - Unable to register server handler with cdevReactor", serverName); status = CANT_REGISTER_SERVER; } } else { outputError (CDEV_SEVERITY_SEVERE, "CDEV Server", "Server %s - Unable to register listening service with cdevReactor", serverName); status = CANT_REGISTER_LISTENER; } } else { outputError ( CDEV_SEVERITY_SEVERE, "CDEV Server", "Server %s - Unable to open listening socket", serverName); status = CANT_OPEN_SOCKET; } switch (status) { case CANT_REGISTER_TIMER: Reactor.extractHandler(this); case CANT_REGISTER_SERVER: Reactor.extractHandler(acceptor); case CANT_REGISTER_LISTENER: case CANT_OPEN_SOCKET: delete acceptor; acceptor = NULL; break; case SUCCESS: default: status = SUCCESS; break; } return status; } // *************************************************************************** // * cdevServer::runServer : // * This method is called to simultaneously execute all servers that // * reside in the system. // *************************************************************************** void cdevServer::runServer ( void ) { SigManager.installDefaults(); SigManager.installHandler (SIGINT, SIGINT_handler); while (!Finished) Reactor.handleEvents (); } // ***************************************************************************** // * cdevServer::registerClient : // * This is the cdevMessage specific method that is used to send a // * "register" message to the server. // ***************************************************************************** void cdevServer::registerClient ( short localID ) { cdevMessage message(localID,0,0,0,0,0,0,0,NULL,"register"); cdevPacketBinary * packet = new cdevPacketBinary; char * binary; size_t binaryLen; message.streamOut (&binary, &binaryLen); packet->attachData(binary, binaryLen); inbound.enqueue (packet); } // ***************************************************************************** // * cdevServer::unregisterClient : // * This is the cdevMessage specific method that is used to send an // * "unregister" message to the server. // ***************************************************************************** void cdevServer::unregisterClient ( short localID ) { cdevMessage message(localID,0,0,0,0,0,0,0,NULL,"unregister"); cdevPacketBinary * packet = new cdevPacketBinary; char * binary; size_t binaryLen; message.streamOut (&binary, &binaryLen); packet->attachData(binary, binaryLen); inbound.enqueue (packet); trigger.insertEvent(); } // ***************************************************************************** // * cdevServer::newClientSession : // * This method allows the caller to create a new ClientSession object. The // * ClientSession object functions primarily as a pointer to the queue that // * holds packets destined for a specific client, however, the developer // * can create a subclass of the ClientSession that may be used to associate // * additional, client specific information to the structure. // * // * The CLIPClientSession class allows the developer to associate a context // * with the clientID. // ***************************************************************************** ClientSession * cdevServer::newClientSession ( int SocketID, int ClientID, int LocalID ) { return new CLIPClientSession (SocketID, ClientID, LocalID); } // ***************************************************************************** // * cdevServer::newSocketSession : // * This method allows the caller to create a new SocketSession object. The // * SocketSession object functions primarily as a pointer to the queue that // * holds packets destined for a specific socket, however, the developer // * can create a subclass of the SocketSession that may be used to associate // * additional, socket specific information to the structure. // * // * The CLIPSocketSession class allows the developer to associate a // * context map and a tag table with the socket number. // ***************************************************************************** SocketSession * cdevServer::newSocketSession ( int SocketID ) { if(serverInfo) serverInfo->clientCnt++; return new CLIPSocketSession (SocketID); } // ***************************************************************************** // * cdevServer::deleteSocketSession : // * This method is called to delete an existing socket session. // ***************************************************************************** void cdevServer::deleteSocketSession ( SocketSession *socket ) { if(socket) { if(serverInfo) { if(serverInfo->clientCnt>0) serverInfo->clientCnt--; else serverInfo->clientCnt=0; } sockets.remove(socket->getSocketID()); delete socket; } } // ***************************************************************************** // * cdevServer::dequeue : // * This method provides a mechanism for the cdevServer object to only // * dequeue packets that are of the type cdevMessage. // ***************************************************************************** int cdevServer::dequeue ( cdevMessage * &message ) { cdevPacket * packet = NULL; while(packet==NULL && cdevSessionManager::dequeue(packet)==0) { if(packet->getVersion()!=cdevMessage::CDEV_PACKET_VERSION) { delete packet; packet = NULL; } else if(((cdevMessage *)packet)->getOperationCode()==CDEV_SERVER_OP) { processLocal((cdevMessage * &)packet); delete packet; packet = NULL; } } message = (cdevMessage *)packet; return message==NULL?-1:0; } // ***************************************************************************** // * cdevServer::processLocal : // * This method is called to process requests that are being transmitted // * directly to the cdev Generic Server Engine to set or retrieve statistics // * about the operation of the server or its clients. // ***************************************************************************** void cdevServer::processLocal ( cdevMessage * & message ) { CLIPClientSession * client = NULL; CLIPSocketSession * socket = NULL; if((client = (CLIPClientSession *)findLocalClient(message->getClientID()))!=NULL) { socket = (CLIPSocketSession *)findSocket(client->getSocketID()); } if(!strcmp(message->getMessage(), "set ClientInfo")) { cdevData * data = message->getData(); if(data && socket) socket->updateClientInfo(*data); } else if(!strcmp(message->getMessage(), "get ServerInfo")) { if(serverInfo) { message->setOperationCode(CDEV_NORMAL_OP); message->setData(&serverInfo->getServerData(), 1); } else { message->setOperationCode(CDEV_NORMAL_OP); message->setCompletionCode(-1); } enqueue(message); } else if(!strcmp(message->getMessage(), "get ClientInfo")) { IntHashIterator iter(&sockets); ClientHandler * handler = NULL; int socketCnt = 0; int index = 0; cdevData result; iter.first(); while(iter.data()!=NULL) { iter++; socketCnt++; } if(socketCnt) { char ** username = new char *[socketCnt]; char ** group = new char *[socketCnt]; unsigned * uid = new unsigned [socketCnt]; unsigned * gid = new unsigned [socketCnt]; unsigned * pid = new unsigned [socketCnt]; char ** program = new char *[socketCnt]; char ** commandline = new char *[socketCnt]; unsigned * starttime = new unsigned [socketCnt]; unsigned * connecttime = new unsigned [socketCnt]; char ** host = new char *[socketCnt]; char ** os = new char *[socketCnt]; char ** osrelease = new char *[socketCnt]; char ** osversion = new char *[socketCnt]; char ** machine = new char *[socketCnt]; char ** shell = new char *[socketCnt]; unsigned * socketNum = new unsigned[socketCnt]; unsigned * sendPktCnt = new unsigned[socketCnt]; unsigned * recvPktCnt = new unsigned[socketCnt]; iter.first(); while(indexgetUsername(); group[index] = socket->getGroup(); uid[index] = socket->getUid(); gid[index] = socket->getGid(); pid[index] = socket->getPid(); program[index] = socket->getProgram(); commandline[index] = socket->getCommandLine(); starttime[index] = (unsigned)socket->getStartTime(); connecttime[index] = (unsigned)socket->getConnectTime(); host[index] = socket->getHost(); os[index] = socket->getOs(); osrelease[index] = socket->getOsRelease(); osversion[index] = socket->getOsVersion(); machine[index] = socket->getMachine(); shell[index] = socket->getShell(); socketNum[index] = socket->getSocketID(); if(Reactor.getHandler(socketNum[index], (cdevEventHandler *&)handler)==0) { sendPktCnt[index] = handler->getPacketsSent(); recvPktCnt[index] = handler->getPacketsRecv(); } else { sendPktCnt[index] = 0; recvPktCnt[index] = 0; } index++; iter++; } result.insert("username", username, socketCnt); delete username; result.insert("group", group, socketCnt); delete group; result.insert("uid", uid, socketCnt); delete uid; result.insert("gid", gid, socketCnt); delete gid; result.insert("pid", pid, socketCnt); delete pid; result.insert("program", program, socketCnt); delete program; result.insert("commandline", commandline, socketCnt); delete commandline; result.insert("starttime", starttime, socketCnt); delete starttime; result.insert("connecttime", connecttime, socketCnt); delete connecttime; result.insert("host", host, socketCnt); delete host; result.insert("os", os, socketCnt); delete os; result.insert("osrelease", osrelease, socketCnt); delete osrelease; result.insert("osversion", osversion, socketCnt); delete osversion; result.insert("machine", machine, socketCnt); delete machine; result.insert("shell", shell, socketCnt); delete shell; result.insert("socket", socketNum, socketCnt); delete socketNum; result.insert("sendPktCnt", sendPktCnt, socketCnt); delete sendPktCnt; result.insert("recvPktCnt", recvPktCnt, socketCnt); delete recvPktCnt; } message->setOperationCode(CDEV_NORMAL_OP); message->setData(&result, 1); enqueue(message); } } // ***************************************************************************** // * cdevServer::decodePacket : // * This method is used to perform preprocessing on a newly dequeued binary // * packet before it is provided to the caller. This method allows the // * developer to perform special preparations on the packet before providing // * it to the caller. // ***************************************************************************** cdevPacket * cdevServer::decodePacket( cdevPacketBinary * input ) { cdevPacket * packet = NULL; if(input!=NULL) { short version; input->getVersion(version); switch (version) { // ***************************************************** // * If it is a cdevMessage object, I want to call the // * cdevMessage specific decodePacket method. // ***************************************************** case cdevMessage::CDEV_PACKET_VERSION: packet = decodePacket ((cdevMessage *)cdevPacket::import(*input)); break; // ***************************************************** // * Use the cdevSessionManager::decodePacket method // * to handle any other type of packet. // ***************************************************** default: packet = cdevSessionManager::decodePacket(input); break; } if(serverInfo) serverInfo->recvPktCnt++; } return packet; } // ***************************************************************************** // * cdevServer::decodePacket : // * This decodePacket method is designed specifically to preprocess the // * data associated with a cdevMessage object. // ***************************************************************************** cdevPacket * cdevServer::decodePacket (cdevMessage * message ) { CLIPClientSession * client = NULL; CLIPSocketSession * socket = NULL; if(message!=NULL && (client = (CLIPClientSession *)findLocalClient(message->getClientID()))!=NULL && (socket = (CLIPSocketSession *)findSocket(client->getSocketID()))!=NULL) { // ************************************************************* // * If a tagMap has been provided... use it to update // * the tagMap stored in the SocketSession object. // ************************************************************* if(message->getTagMap()!=NULL) { socket->TagMap().updateTagMap(*message->getTagMap()); message->setTagMap(NULL); } // ************************************************************* // * Pass the data and context objects through the tag map to // * convert their tags from the remote integers to the local // * integers. // ************************************************************* if(message->getData()!=NULL) socket->TagMap().remoteToLocal(*message->getData()); if(message->getContext()!=NULL) socket->TagMap().remoteToLocal(*message->getContext()); // ************************************************************* // * If a context has been provided by the client side // * of the connection - perform the following steps... // * // * 1) Add the context to the cdevContextMap for this // * socketID if it does not already exist, and // * obtain the index that identifies the new // * context. // * // * 2) Delete the context from within the message. // * // * 3) Set the context within the message to the // * current context that is specified in the // * ClientSession object. // * // * 4) Set the saveContext flag in the message to // * prevent its inadvertant destruction when the // * message is deleted. // ************************************************************* if(message->getContext()!=NULL) { cdevData * lastContext = client->getContext(); if(lastContext==NULL || *lastContext!=*message->getContext()) { int contextID = socket->ContextMap().insert(*message->getContext()); client->setContext(socket->ContextMap().find(contextID)); } } message->setContext(client->getContext(), 1); // ************************************************************* // * Toggle the local and foreign data indices. // ************************************************************* unsigned int index = message->getForeignDataIndex(); message->setForeignDataIndex(message->getLocalDataIndex()); message->setLocalDataIndex (index); } return message; } // ***************************************************************************** // * cdevSessionManager::encodePacket : // * This method is used to perform postprocessing on a packet that has been // * enqueued to be sent to a client. This method allows the developer to // * perform special preparations on the packet before providing it to the // * client. // ***************************************************************************** cdevPacketBinary * cdevServer::encodePacket ( cdevPacket * input ) { cdevPacketBinary * packet = NULL; if(input!=NULL) { switch(input->getVersion()) { case cdevMessage::CDEV_PACKET_VERSION: packet = encodePacket ((cdevMessage *)input); break; default: packet = cdevSessionManager::encodePacket(input); break; } if(serverInfo) serverInfo->sendPktCnt++; } return packet; } // ***************************************************************************** // * cdevServer::encodePacket : // * This encodePacket method is designed specifically to postprocess the // * data associated with a cdevMessage object. // ***************************************************************************** cdevPacketBinary * cdevServer::encodePacket (cdevMessage * message ) { cdevPacketBinary * result = NULL; CLIPClientSession * client = NULL; CLIPSocketSession * socket = NULL; if(message!=NULL && message->getClientID()>0 && message->getTransIndex()>0 && (client = (CLIPClientSession *)findLocalClient(message->getClientID()))!=NULL && (socket = (CLIPSocketSession *)findSocket(client->getSocketID()))!=NULL) { char * binary = NULL; size_t binaryLen = 0; static cdevMessage outBound; // ************************************************************* // * Remap the data to its foreign design. // ************************************************************* if(message->getData()!=NULL) socket->TagMap().localToRemote(*message->getData()); // ************************************************************* // * Transfer the critical data items into the class. Note that // * we are not returning the deviceList, message, context, // * cancelTransIndex, or operationCode with the return packet. // * // * Also note that the cdevData object is marked as permanent // * and is using a pointer to the same cdevData object that is // * in the message object. // ************************************************************* outBound.clear(); outBound.setClientID (client->getClientID()&0xFFFF); outBound.setTransIndex (message->getTransIndex()); outBound.setLocalDataIndex (message->getForeignDataIndex()); outBound.setForeignDataIndex (message->getLocalDataIndex()); outBound.setOperationCode (message->getOperationCode()); outBound.setCompletionCode (message->getCompletionCode()); outBound.setData (message->getData(), 1); // ************************************************************* // * Create a binary stream from the cdevMessage object and then // * place it into the outbound queue system. // ************************************************************* outBound.streamOut(&binary, &binaryLen); outBound.clear (); result = new cdevPacketBinary; result->attachData(binary, binaryLen); // ************************************************************* // * Remap the data to its local design. // ************************************************************* if(message->getData()!=NULL) socket->TagMap().remoteToLocal(*message->getData()); } return result; }