Main Page   Class Hierarchy   Compound List   File List   Compound Members  

CAlarmServer.cpp

00001 // Author:
00002 //   Jason Venema
00003 //   NSCL
00004 //   Michigan State University
00005 //   East Lansing, MI 48824-1321
00006 //   mailto:venemaja@msu.edu
00007 //
00008 // Copyright
00009 //   NSCL All rights reserved.
00010 //
00011 // See CAlarmServer.h for a description of this class.
00012 //
00013 
00014 #include <iostream.h>
00015 #include <stdio.h>
00016 #include <string.h>
00017 #include <CAlarmServer.h>
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 #include <sys/socket.h>
00021 
00022 extern gdbm_error gdbm_errno;           // errno for gdbm
00023 extern int errno;                       // in case the server dies
00024 
00040 CAlarmServer::CAlarmServer()
00041 {
00042   // See if "daqalarm" is configured
00043   struct servent* pServ = getservbyname("daqalarm", "tcp");
00044   if(pServ) {
00045     Int_t nPort = pServ->s_port;
00046     char port[4];
00047     sprintf(port, "%d", nPort);
00048     m_sPort = string(port);
00049   }
00050   else {
00051     // default to 2702
00052     m_sPort = "2702";
00053   }
00054 
00055   GDBM_FILE DBFWriter;
00056 
00057   // We open these files so that they will be created. That way, we don't
00058   // have to worry about doing this later, during the actual fulfilling of
00059   // client requests.
00060   if(!(DBFWriter = gdbm_open(".alarmcount", 512, GDBM_WRCREAT, 
00061                              S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0))) {
00062     string reason = "Attempting to open .alarmcount while ";
00063     reason += "constructing CAlarmServer";
00064     CGDBMException dbme(reason, gdbm_errno);
00065     throw dbme;
00066   }
00067   gdbm_close(DBFWriter);
00068   if(!(DBFWriter = gdbm_open(".alarmdb", 512, GDBM_WRCREAT,
00069                              S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0))) {
00070     string reason = "Attempting to open .alarmcount while ";
00071     reason += "constructing CAlarmServer";
00072     CGDBMException dbme(reason, gdbm_errno);
00073     throw dbme;
00074   }
00075 
00076   // It is VERY important to close the gdbm file every time we are done writing
00077   // to it or reading from it, so that some cleanup operations may be performed
00078   gdbm_close(DBFWriter);
00079 }
00080 
00087 CAlarmServer::CAlarmServer(const CAlarmServer& aCAlarmServer)
00088 {
00089   m_sExpId = aCAlarmServer.m_sExpId;
00090   m_sAlarmId = aCAlarmServer.m_sAlarmId;
00091 }
00092 
00113 bool
00114 CAlarmServer::operator()()
00115 {
00116   CSocket* newSock;   // the new socket
00117   string client_ip;   // the client ip address
00118   pid_t child_pid;    // the pid of the child process
00119   if((child_pid = fork()) < 0) {
00120     // If we can't even fork a new process, we had better bail out!
00121     cerr << "Error while attempting to fork a new process." << endl;
00122     return 0;
00123   }
00124 
00125   // The parent will wait for the child to die, and restart it if need be...
00126   else if(child_pid > 0) {
00127     int x;
00128     wait(&x);
00129     cerr << "Exit status was " << x << endl;
00130 
00131     //If the exit status is SIGHUP, then someone asked us to shutdown
00132     if(x == SIGHUP) {
00133       cout << "Received hangup!" << endl;
00134       return 0;
00135     }
00136 
00137     // Otherwise, something happened that shouldn't have and we try
00138     // to restart the server.
00139     else {
00140       cerr << "Server dying!" << endl;
00141       perror(strerror(errno));
00142       return 1;
00143     }
00144   }
00145 
00146   // The child process will execute this part...
00147   else {
00148     try {
00149       CSocket sock;
00150 
00151       // The following code is so that the server can be restarted immediately
00152       // upon exiting, without having to wait for the OS to free up the address
00153       // space it was using.
00154       int fd = sock.getSocketFd();
00155       int state = 1;
00156       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &state, sizeof(int));
00157 
00158       // Now we bind, listen and accept any requests
00159       sock.Bind(m_sPort);
00160 
00161       // BUGBUG Listen has a parameter nBacklog -- should we change it?
00162       // The value is defaulted to 5, meaning that the limit on the queue size
00163       // for incoming connections is 5. Additional connections are refused.
00164       sock.Listen();
00165       while(1) {
00166         newSock = sock.Accept(client_ip);
00167 
00168         // Get the id and reason of the experiment making the request
00169         void* pBuf = (void*)(new char[512]);
00170 
00171         // Block until some command comes through the socket
00172         int nB = newSock->Read(pBuf, 512);
00173 
00174         // Break that command down so we can process it
00175         char command[512];
00176         sprintf(command, "%s", (char*)pBuf);
00177         char* pCommand = command;
00178         char* temp;
00179         int reason;
00180         string fac, mess, date;
00181         temp = strsep(&pCommand, " ");
00182         m_sExpId = string(temp);  // set the experiment ID for this request
00183         temp = strsep(&pCommand, " ");
00184         reason = atoi(temp);      // this is why we're being called
00185 
00186         // Now we figure out what the request is all about
00187         switch(reason) {
00188         case LOG: {
00189           // Break the command up into the alarm information so it can
00190           // be logged to the logfile
00191           temp = strsep(&pCommand, " ");
00192           m_sAlarmId = string(temp);
00193           temp = strsep(&pCommand, "~");
00194           fac = string(temp);
00195           temp = strsep(&pCommand, "~");
00196           mess = string(temp);
00197           temp = strsep(&pCommand, "\n");
00198           date = string(temp);
00199           
00200           // Make sure the information is valid
00201           if(!(fac.size()) ||
00202              !(mess.size()) ||
00203              !(date.size())) {
00204             CAlarmServerException ase
00205               ("Attempting to parse log request in CAlarmServer::operator()",
00206                pCommand);
00207             throw ase;
00208           }
00209 
00210           // Let the Log() function take care of the rest
00211           Log(fac, mess, date);
00212           break;
00213         }
00214         case ACKNOWLEDGE:
00215         case DISMISS:     {
00216 
00217           // Break the command down further and get the alarm id to edit
00218           temp = strsep(&pCommand, " \n");
00219           int id = atoi(temp);
00220           char szBuf[20];
00221           sprintf(szBuf, "%d", id);
00222           m_sAlarmId = string(szBuf);
00223 
00224           // Let the EditAlarm() function do the rest
00225           EditAlarm(reason);
00226           break;
00227         }
00228         case UPDATE: {
00229 
00230           // We perform the update stuff here, because we need to use the
00231           // socket that is already opened to send update information to the
00232           // client.
00233           GDBM_FILE DBFReader;
00234           if(!(DBFReader = gdbm_open(".alarmdb", 512 ,GDBM_READER, 0, 0))) {
00235             string reason = "Attempting to open .alarmdb in ";
00236             reason += "CAlarmServer::Update()";
00237             CGDBMException dbme(reason, gdbm_errno);
00238             throw dbme;
00239           }
00240           string packet = "";  // this will be the final packet we send
00241           string sPiece;       // this is just a piece of the packet
00242           datum NextKey;
00243           datum ContentVal;
00244 
00245           // Start by retrieving the first key from the database
00246           datum KeyVal = gdbm_firstkey(DBFReader);
00247           while(KeyVal.dptr) {
00248 
00249             // Now we examine every key, and "build up" our packet with
00250             // each entry for this experiment
00251             NextKey = gdbm_nextkey(DBFReader, KeyVal);
00252             ContentVal = gdbm_fetch(DBFReader, KeyVal);
00253             sPiece = string(ContentVal.dptr);
00254             if(atoi(&(KeyVal.dptr)[0]) == atoi(m_sExpId.c_str())) {
00255               sPiece.resize(ContentVal.dsize);
00256               packet += string(sPiece);
00257               packet += "\n";
00258             }
00259             KeyVal = NextKey;
00260           }
00261 
00262           // This eof tells the caller that the packet is done. Now we
00263           // send it out!
00264           packet += "eof";
00265           newSock->Write((void*)packet.c_str(), packet.size());
00266           gdbm_close(DBFReader);
00267           break;
00268         }
00269         case INIT: {
00270           Init(newSock, &sock);
00271           break;
00272         }
00273         case CREATE: {
00274           CreateExperiment();
00275           break;
00276         }
00277         case HISTORY: {
00278           string sHistory = GetExperimentHistory();
00279           newSock->Write((void*)sHistory.c_str(), sHistory.size());
00280         }
00281         }
00282         
00283         // Perform some memory management here
00284         newSock->Shutdown();
00285         delete newSock;
00286         delete [] (char*)pBuf;
00287       }
00288     }
00289     catch (CException& e) {
00290       cerr << "Caught exception while attempting to listen on port " 
00291            << m_sPort << endl;
00292       cerr << "Reason was: " << e.ReasonText() << endl;
00293       cerr << e.WasDoing() << endl;
00294       exit(2);
00295     }
00296   }
00297 }
00298 
00315 void
00316 CAlarmServer::Log(string& srFacility, string& srMessage, string& srDate)
00317 {
00318   // First we create key and content values to store in the database.
00319   // The content consists of the alarm information, the key is of the
00320   // form "m_sExpId.m_sAlarmId" 
00321   string key = m_sExpId + "." + m_sAlarmId;
00322   string content = "ALARMID: ";
00323   content += m_sAlarmId;
00324   content += " FACILITY: ";
00325   content += srFacility;
00326   content += " STATUS: n ";
00327   content += "MESSAGE: ";
00328   content += srMessage;
00329   content += " DATE: ";
00330   content += srDate;
00331 
00332   // Items of type "datum" are stored in a gdbm database. A datum
00333   // consists of two fields: char* dptr and int dsize.
00334   datum key_value,
00335     content_value;
00336   key_value.dptr = (char*)(key.c_str());
00337   key_value.dsize = strlen(key.c_str());
00338   content_value.dptr = (char*)(content.c_str());
00339   content_value.dsize = strlen(content.c_str());
00340 
00341   // We can only allow one writer to open the file with write access
00342   // at a time.  We need to write the information to log it.
00343   GDBM_FILE DBFWriter;
00344   
00345   // There may be problems opening the database file
00346   if(!(DBFWriter = gdbm_open(".alarmdb", 0, GDBM_WRCREAT, 
00347                              S_IRUSR | S_IWUSR | S_IRGRP | 
00348                              S_IROTH, 0))) {
00349     string reason = "Attempting to open .alarmdb in CAlarmServer::Log()";
00350     CGDBMException dbme(reason, gdbm_errno);
00351     throw dbme;
00352   }
00353 
00354   // Need to make sure that the information was successfully stored
00355   if(gdbm_store(DBFWriter, key_value, content_value, GDBM_INSERT)) {
00356     string reason = "Attemping to store in .alarmdb in CAlarmServer::Log()";
00357     CGDBMException dbme(reason, gdbm_errno);
00358     throw dbme;
00359     gdbm_close(DBFWriter);
00360   }
00361 
00362   // A gdbm_open **needs** to be accompanied by a gdbm_close, in
00363   // order for gdbm to "tidy up" the database file entries.
00364   gdbm_close(DBFWriter);
00365   
00366   // Now we need to increment the number of alarms this experiment
00367   // has in the .alarmcount file
00368   GDBM_FILE DBFReader;
00369   if(!(DBFReader = gdbm_open
00370        (".alarmcount", 512, GDBM_READER, 644, 0))) {
00371     string reason = "Attempting to open .alarmcount in CAlarmServer::Log()";
00372     CGDBMException dbme(reason, gdbm_errno);
00373     throw dbme;
00374   }
00375 
00376   // So we create a key and see if it already exists in the database
00377   datum Key;
00378   Key.dptr = const_cast<char*>(m_sExpId.c_str());
00379   Key.dsize = strlen(m_sExpId.c_str());
00380   
00381   // We check to see if the experiment has any alarms logged yet.
00382   int KeyExists = gdbm_exists(DBFReader, Key);
00383   
00384   // There are some alarms logged for this experiment, so we need
00385   // to update the current entry...
00386   datum NewContent;
00387   if(KeyExists) {
00388     datum ContentValue = gdbm_fetch(DBFReader, Key);
00389     int nOldCount = atoi(ContentValue.dptr);
00390     char NewCount[5];
00391     sprintf(NewCount, "%d", ++nOldCount);
00392     NewContent.dptr = NewCount;
00393     NewContent.dsize = strlen(NewCount);
00394   }
00395   
00396   // or, there haven't been any alarms logged here yet, so we simply
00397   // create a new entry
00398   else {
00399     char NewCount[1];
00400     sprintf(NewCount, "%d", 1);
00401     NewContent.dptr = NewCount;
00402     NewContent.dsize = strlen(NewCount);
00403   }
00404   gdbm_close(DBFReader);
00405   
00406   // Now we need to open a new writer to write the new entry
00407   if(!(DBFWriter = gdbm_open(".alarmcount", 0, GDBM_WRCREAT,
00408                              S_IRUSR | S_IWUSR | S_IRGRP |
00409                              S_IROTH, 0))) {
00410     string reason = "Attempting to open .alarmcount in CAlarmServer::Log()";
00411     CGDBMException dbme(reason, gdbm_errno);
00412     throw dbme;
00413   }
00414   
00415   // We tell gdbm to replace the old entry with the new one
00416   // with the flag GDBM_REPLACE
00417   int ret = gdbm_store(DBFWriter, Key, NewContent, GDBM_REPLACE);
00418   gdbm_close(DBFWriter);
00419   if(ret) {
00420     string reason = "Attempting to store new alarm count in ";
00421     reason += ".alarmcount while in CAlarmServer::Log()";
00422     CGDBMException dbme(reason, gdbm_errno);
00423     throw dbme;
00424   }
00425 }
00426 
00441 void
00442 CAlarmServer::EditAlarm(Int_t nReason)
00443 {
00444   GDBM_FILE DBFReader;
00445   GDBM_FILE DBFWriter;
00446   
00447   // First open the database for reading and find the key for this
00448   // alarm and experiment
00449   if(!(DBFReader = gdbm_open(".alarmdb", 512, GDBM_READER, 644, 0))) {
00450     string why = "Attempting to open .alarmdb in CAlarmServer::EditAlarm()";
00451     CGDBMException dbme(why, gdbm_errno);
00452     throw dbme;
00453   }
00454   datum Key;
00455   string sKey = m_sExpId + "." + m_sAlarmId;
00456   Key.dptr = const_cast<char*>(sKey.c_str());
00457   Key.dsize = strlen(sKey.c_str());
00458   datum ContentValue = gdbm_fetch(DBFReader, Key);
00459   
00460   // Pull out the alarm data
00461   if(!ContentValue.dptr) {
00462     string why = "Attempting to fetch data from .alarmdb in ";
00463     why += "CAlarmServer::EditAlarm()";
00464     CGDBMException dbme(why, gdbm_errno);
00465     throw dbme;
00466   }
00467   gdbm_close(DBFReader);
00468   
00469   // Get ready to change the datum by opening up for writing
00470   if(!(DBFWriter = gdbm_open(".alarmdb", 0, GDBM_WRCREAT,
00471                              S_IRUSR | S_IWUSR | S_IRGRP |
00472                              S_IROTH, 0))) {
00473     string why = "Attempting to open .alarmdb in CAlarmServer::EditAlarm()";
00474     CGDBMException dbme(why, gdbm_errno);
00475     throw dbme;
00476   }
00477   
00478   // We will rebuild the content string based on the old content
00479   // and the new status
00480   string sNewContent = "";
00481   char* token;
00482   
00483   // Now we rebuild the content string with the new STATUS field
00484   // Note that there are two cases, one for Acknowledge and one for Dismiss
00485   if(nReason == ACKNOWLEDGE) {
00486     while(token = strsep(&ContentValue.dptr, " ")) {
00487       string tok = string(token);
00488       if(tok == "STATUS:") {
00489         sNewContent += tok;
00490         sNewContent += " ";
00491         token = strsep(&ContentValue.dptr, " ");
00492         tok = string(token);
00493         if(tok == "n")
00494           sNewContent += "a";
00495         else
00496           sNewContent += tok;
00497       }
00498       else {
00499         sNewContent += tok;
00500       }
00501       sNewContent += " ";
00502     }
00503   }
00504 
00505   // This is for a Dismissal request
00506   else if(nReason == DISMISS) {
00507     while(token = strsep(&ContentValue.dptr, " ")) {
00508       string tok = string(token);
00509       if(tok == "STATUS:") {
00510         sNewContent += tok;
00511         sNewContent += " ";
00512         token = strsep(&ContentValue.dptr, " ");
00513         tok = string(token);
00514         if(tok == "a")
00515           sNewContent += "d";
00516         else
00517           sNewContent += tok;
00518       }
00519       else {
00520         sNewContent += tok;
00521       }
00522       sNewContent += " ";
00523     }
00524   }
00525   
00526   // Create the new content datum
00527   sNewContent.resize(ContentValue.dsize);
00528   datum NewContent;
00529   NewContent.dptr = const_cast<char*>(sNewContent.c_str());
00530   NewContent.dsize = sNewContent.length();
00531   
00532   // Instruct gdbm_store to replace the entry with the same key,
00533   // since it is the old entry
00534   int ret = gdbm_store(DBFWriter, Key, NewContent, GDBM_REPLACE);
00535   if(ret) {
00536     gdbm_close(DBFWriter);
00537     string why = "Attempting to store acknowledgement in .alarmdb ";
00538     why += "in CAlarmServer::EditAlarm()";
00539     CGDBMException dbme(why, gdbm_errno);
00540     throw dbme;
00541   }
00542 
00543   // Don't forget how important it is to close this file!
00544   gdbm_close(DBFWriter);
00545 }
00546 
00560 void
00561 CAlarmServer::Init(CSocket* newSock, CSocket* sock)
00562 {
00563   // We open a reader to get the initial alarm count 
00564   // for this experiment
00565   GDBM_FILE DBFReader;
00566   if(!(DBFReader = gdbm_open(".alarmcount", 512, GDBM_READER, 0, 0))) {
00567     string reason = "Attempting to open .alarmcount in CAlarmServer::Init()";
00568     CGDBMException dbme(reason, gdbm_errno);
00569     throw dbme;
00570   }
00571   datum Key;
00572   Key.dptr = const_cast<char*>(m_sExpId.c_str());
00573   Key.dsize = strlen(m_sExpId.c_str());
00574 
00575   // Attempt to fetch the alarm count from the database
00576   datum ContentValue = gdbm_fetch(DBFReader, Key);
00577   gdbm_close(DBFReader);
00578 
00579   // If there is no count, then this is a new experiment, and its alarm
00580   // count is set to zero.
00581   if(!ContentValue.dptr) {
00582     datum NewContent;
00583     char content[4];
00584     sprintf(content, "%d", 0);
00585     NewContent.dptr = content;
00586     NewContent.dsize = strlen(content);
00587     ContentValue = NewContent;
00588   }
00589 
00590   // Now we will write the alarm count to the socket and send it
00591   // off to the alarm displayer to find out if the user really wants
00592   // us to create this new experiment
00593   int nB = newSock->Write((void*)ContentValue.dptr, ContentValue.dsize);
00594 }
00595 
00596 void
00597 CAlarmServer::CreateExperiment() 
00598 {
00599   GDBM_FILE DBFWriter;
00600   if(!(DBFWriter = gdbm_open(".alarmcount", 0, GDBM_WRCREAT,
00601                              S_IRUSR | S_IWUSR | S_IRGRP |
00602                              S_IROTH, 0))) {
00603     string reason = "Trying to open .alarmcount in CAlarmServer::Init()";
00604     CGDBMException dbme(reason, gdbm_errno);
00605     throw dbme;
00606   }
00607   datum Key;
00608   Key.dptr  = const_cast<char*>(m_sExpId.c_str());
00609   Key.dsize = strlen(m_sExpId.c_str());
00610   datum NewContent;
00611   char content[4];
00612   sprintf(content, "%d", 0);
00613   NewContent.dptr = content;
00614   NewContent.dsize = strlen(content);
00615 
00616   gdbm_store(DBFWriter, Key, NewContent, GDBM_REPLACE);
00617   gdbm_close(DBFWriter);
00618 }
00619 
00620 string
00621 CAlarmServer::GetExperimentHistory()
00622 {
00623   GDBM_FILE DBFReader;
00624   if(!(DBFReader = gdbm_open(".alarmdb", 512, GDBM_READER, 0, 0))) {
00625     string reason = "Trying to open .alarmcount in CAlarmServer::Init()";
00626     CGDBMException dbme(reason, gdbm_errno);
00627     throw dbme;
00628   }
00629   string packet = "";  // this will be the final packet we send
00630   string sPiece;       // this is just a piece of the packet
00631   datum NextKey;
00632   datum ContentVal;
00633   
00634   // Start by retrieving the first key from the database
00635   datum KeyVal = gdbm_firstkey(DBFReader);
00636   while(KeyVal.dptr) {
00637     
00638     // Now we examine every key, and "build up" our packet with
00639     // each entry for this experiment
00640     NextKey = gdbm_nextkey(DBFReader, KeyVal);
00641     ContentVal = gdbm_fetch(DBFReader, KeyVal);
00642     sPiece = string(ContentVal.dptr);
00643     if(atoi(&(KeyVal.dptr)[0]) == atoi(m_sExpId.c_str())) {
00644       sPiece.resize(ContentVal.dsize);
00645       packet += string(sPiece);
00646       packet += "\n";
00647     }
00648     KeyVal = NextKey;
00649   }
00650   packet += "eof";
00651   gdbm_close(DBFReader);
00652   return packet;
00653 }

Generated at Tue May 21 12:10:50 2002 for CAlarmLogger by doxygen1.2.9.1 written by Dimitri van Heesch, © 1997-2001