00001 #include "otsdaq-utilities/Console/ConsoleSupervisor.h"
00002 #include "otsdaq-core/MessageFacility/MessageFacility.h"
00003 #include "otsdaq-core/Macros/CoutMacros.h"
00004 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
00005 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
00006 #include "otsdaq-core/NetworkUtilities/ReceiverSocket.h"
00007 #include <xdaq/NamespaceURI.h>
00008
00009 #include <iostream>
00010 #include <fstream>
00011 #include <string>
00012 #include <dirent.h>
00013 #include <sys/stat.h>
00014 #include <thread>
00015
00016 using namespace ots;
00017
00018
00019
00020
00021
00022 XDAQ_INSTANTIATOR_IMPL(ConsoleSupervisor)
00023
00024
00025 #define USER_CONSOLE_PREF_PATH std::string(getenv("SERVICE_DATA_PATH")) + "/ConsolePreferences/"
00026 #define USERS_PREFERENCES_FILETYPE "pref"
00027
00028 #define QUIET_CFG_FILE std::string(getenv("USER_DATA")) + "/MessageFacilityConfigurations/QuietForwarder.cfg"
00029
00030 #define CONSOLE_SPECIAL_ERROR std::string("||0|||Error|Console|-1||ConsoleSupervisor|")
00031 #define CONSOLE_SPECIAL_WARNING std::string("||0|||Warning|Console|-1||ConsoleSupervisor|")
00032
00033 #undef __MF_SUBJECT__
00034 #define __MF_SUBJECT__ "Console"
00035
00036
00037
00038 ConsoleSupervisor::ConsoleSupervisor(xdaq::ApplicationStub* stub)
00039 : CoreSupervisorBase (stub)
00040 , writePointer_ (0)
00041 , messageCount_ (0)
00042 {
00043 __SUP_COUT__ << "Constructor started." << __E__;
00044
00045 INIT_MF("ConsoleSupervisor");
00046
00047
00048 mkdir(((std::string)USER_CONSOLE_PREF_PATH).c_str(), 0755);
00049
00050 init();
00051
00052 __SUP_COUT__ << "Constructor complete." << __E__;
00053 }
00054
00055
00056 ConsoleSupervisor::~ConsoleSupervisor(void)
00057 {
00058 destroy();
00059 }
00060
00061 void ConsoleSupervisor::init(void)
00062 {
00063
00064 std::thread([](ConsoleSupervisor *cs){ ConsoleSupervisor::messageFacilityReceiverWorkLoop(cs); },this).detach();
00065 }
00066
00067
00068 void ConsoleSupervisor::destroy(void)
00069 {
00070
00071 }
00072
00073
00074
00075
00076
00077 void ConsoleSupervisor::messageFacilityReceiverWorkLoop(ConsoleSupervisor *cs)
00078 {
00079 __COUT__ << std::endl;
00080
00081 std::string configFile = QUIET_CFG_FILE;
00082 FILE *fp = fopen(configFile.c_str(),"r");
00083 if(!fp)
00084 {
00085 __SS__ << "File with port info could not be loaded: " <<
00086 QUIET_CFG_FILE << std::endl;
00087 __COUT__ << "\n" << ss.str();
00088 __SS_THROW__;
00089 }
00090 char tmp[100];
00091 fgets(tmp,100,fp);
00092 fgets(tmp,100,fp);
00093 int myport;
00094 sscanf(tmp,"%*s %d",&myport);
00095
00096 fgets(tmp,100,fp);
00097 char myip[100];
00098 sscanf(tmp,"%*s %s",myip);
00099 fclose(fp);
00100
00101 ReceiverSocket rsock(myip,myport);
00102 try
00103 {
00104 rsock.initialize();
00105 }
00106 catch(...)
00107 {
00108
00109
00110 std::lock_guard<std::mutex> lock(cs->messageMutex_);
00111
00112
00113
00114 if(1)
00115 {
00116 __SS__ << "FATAL Console error. Could not initialize socket on port " <<
00117 myport << ". Perhaps the port is already in use? Check for multiple stale instances of otsdaq processes, or notify admins." <<
00118 " Multiple instances of otsdaq on the same node should be possible, but port numbers must be unique." << std::endl;
00119 __SS_THROW__;
00120 }
00121
00122
00123 __SS__ << "FATAL Console error. Could not initialize socket on port " <<
00124 myport << ". Perhaps it is already in use? Exiting Console receive loop." << std::endl;
00125 __COUT__ << ss.str();
00126
00127
00128 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_ERROR +
00129 ss.str(),
00130 cs->messageCount_++);
00131
00132
00133 if(++cs->writePointer_ == cs->messages_.size())
00134 cs->writePointer_ = 0;
00135
00136 return;
00137 }
00138
00139 std::string buffer;
00140 int i = 0;
00141 int heartbeatCount = 0;
00142 int selfGeneratedMessageCount = 0;
00143
00144 std::map<unsigned int, unsigned int> sourceLastSequenceID;
00145 long long newSourceId;
00146 unsigned int newSequenceId;
00147
00148 while(1)
00149 {
00150
00151
00152 if(rsock.receive(buffer,1 ,0,
00153 false ) != -1)
00154 {
00155 if(i != 200)
00156 {
00157 __COUT__ << "Console has first message." << std::endl;
00158 i = 200;
00159
00160 __MOUT__ << "DEBUG messages look like this." << std::endl;
00161 __MOUT_INFO__ << "INFO messages look like this." << std::endl;
00162 __MOUT_WARN__ << "WARNING messages look like this." << std::endl;
00163 __MOUT_ERR__ << "ERROR messages look like this." << std::endl;
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175 }
00176
00177 if(selfGeneratedMessageCount)
00178 --selfGeneratedMessageCount;
00179 else
00180 heartbeatCount = 0;
00181
00182
00183
00184
00185
00186 std::lock_guard<std::mutex> lock(cs->messageMutex_);
00187
00188 cs->messages_[cs->writePointer_].set(buffer,cs->messageCount_++);
00189
00190
00191
00192 newSourceId = cs->messages_[cs->writePointer_].getSourceIDAsNumber();
00193 newSequenceId = cs->messages_[cs->writePointer_].getSequenceIDAsNumber();
00194
00195
00196
00197
00198 if(newSourceId != -1 && sourceLastSequenceID.find(newSourceId) !=
00199 sourceLastSequenceID.end() &&
00200 ((newSequenceId == 0 &&
00201 sourceLastSequenceID[newSourceId] != (unsigned int)-1) ||
00202 newSequenceId != sourceLastSequenceID[newSourceId] + 1))
00203 {
00204
00205 __SS__ << "Missed packets from " <<
00206 cs->messages_[cs->writePointer_].getSource() << "! Sequence IDs " <<
00207 sourceLastSequenceID[newSourceId] <<
00208 " to " << newSequenceId << "." << std::endl;
00209 std::cout << ss.str();
00210
00211 if(++cs->writePointer_ == cs->messages_.size())
00212 cs->writePointer_ = 0;
00213
00214
00215 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_WARNING +
00216 ss.str(),
00217 cs->messageCount_++);
00218 }
00219
00220
00221 sourceLastSequenceID[newSourceId] = newSequenceId;
00222
00223 if(++cs->writePointer_ == cs->messages_.size())
00224 cs->writePointer_ = 0;
00225
00226 }
00227 else
00228 {
00229 if(i < 120)
00230 ++i;
00231
00232 sleep(1);
00233
00234
00235 if(i != 200 ||
00236 (heartbeatCount < 60*5 && heartbeatCount%60 == 59))
00237 {
00238 ++selfGeneratedMessageCount;
00239 __MOUT__ << "Console is alive and waiting... (if no messages, next heartbeat is in approximately two minutes)" << std::endl;
00240 }
00241 else if(heartbeatCount%(60*30) == 59)
00242 {
00243 ++selfGeneratedMessageCount;
00244 __MOUT__ << "Console is alive and waiting a long time... (if no messages, next heartbeat is in approximately one hour)" << std::endl;
00245 }
00246
00247 ++heartbeatCount;
00248 }
00249
00250
00251
00252
00253 if(i==120 || selfGeneratedMessageCount == 5)
00254 {
00255 __COUT__ << "No messages received at Console Supervisor. Exiting Console messageFacilityReceiverWorkLoop" << std::endl;
00256 break;
00257 }
00258 }
00259
00260 }
00261
00262
00263 void ConsoleSupervisor::defaultPage(xgi::Input * in, xgi::Output * out )
00264 {
00265 __SUP_COUT__ << "ApplicationDescriptor LID=" << getApplicationDescriptor()->getLocalId() << std::endl;
00266 *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame src='/WebPath/html/Console.html?urn=" <<
00267 getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
00268 }
00269
00270
00271
00272
00273 void ConsoleSupervisor::forceSupervisorPropertyValues()
00274 {
00275 CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
00276 "GetConsoleMsgs");
00277
00278
00279 }
00280
00281
00282
00283
00284
00285 void ConsoleSupervisor::request(const std::string& requestType, cgicc::Cgicc& cgiIn,
00286 HttpXmlDocument& xmlOut, const WebUsers::RequestUserInfo& userInfo)
00287 {
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298 if(requestType == "GetConsoleMsgs")
00299 {
00300
00301 std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn,"lcount");
00302 std::string lastUpdateIndexStr = CgiDataUtilities::postData(cgiIn,"lindex");
00303
00304 if(lastUpdateCountStr == "" || lastUpdateIndexStr == "")
00305 {
00306 __SUP_COUT_ERR__ << "Invalid Parameters! lastUpdateCount=" << lastUpdateCountStr <<
00307 ", lastUpdateIndex=" << lastUpdateIndexStr << std::endl;
00308 xmlOut.addTextElementToData("Error","Error - Invalid parameters for GetConsoleMsgs.");
00309 return;
00310 }
00311
00312 clock_t lastUpdateCount;
00313 sscanf(lastUpdateCountStr.c_str(),"%ld",&lastUpdateCount);
00314
00315 unsigned int lastUpdateIndex;
00316 sscanf(lastUpdateIndexStr.c_str(),"%u",&lastUpdateIndex);
00317
00318
00319
00320 insertMessageRefresh(&xmlOut,lastUpdateCount,lastUpdateIndex);
00321 }
00322 else if(requestType == "SaveUserPreferences")
00323 {
00324 int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn,"colorIndex");
00325 int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn,"showSideBar");
00326 int noWrap = CgiDataUtilities::postDataAsInt(cgiIn,"noWrap");
00327 int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn,"messageOnly");
00328 int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn,"hideLineNumers");
00329
00330 __SUP_COUT__ << "requestType " << requestType << std::endl;
00331 __SUP_COUT__ << "colorIndex: " << colorIndex << std::endl;
00332 __SUP_COUT__ << "showSideBar: " << showSideBar << std::endl;
00333 __SUP_COUT__ << "noWrap: " << noWrap << std::endl;
00334 __SUP_COUT__ << "messageOnly: " << messageOnly << std::endl;
00335 __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << std::endl;
00336
00337 if(userInfo.username_ == "")
00338 {
00339 __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_ << std::endl;
00340 xmlOut.addTextElementToData("Error","Error - InvauserInfo.username_user found.");
00341 return;
00342 }
00343
00344 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." + (std::string)USERS_PREFERENCES_FILETYPE;
00345
00346 __SUP_COUT__ << "Save preferences: " << fn << std::endl;
00347 FILE *fp = fopen(fn.c_str(),"w");
00348 if(!fp)
00349 {__SS__;__THROW__(ss.str()+"Could not open file: " + fn);}
00350 fprintf(fp,"colorIndex %d\n",colorIndex);
00351 fprintf(fp,"showSideBar %d\n",showSideBar);
00352 fprintf(fp,"noWrap %d\n",noWrap);
00353 fprintf(fp,"messageOnly %d\n",messageOnly);
00354 fprintf(fp,"hideLineNumers %d\n",hideLineNumers);
00355 fclose(fp);
00356 }
00357 else if(requestType == "LoadUserPreferences")
00358 {
00359 __SUP_COUT__ << "requestType " << requestType << std::endl;
00360
00361 unsigned int colorIndex,showSideBar,noWrap,messageOnly,hideLineNumers;
00362
00363 if(userInfo.username_ == "")
00364 {
00365 __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_ << std::endl;
00366 xmlOut.addTextElementToData("Error","Error - Invalid user found.");
00367 return;
00368 }
00369
00370 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." + (std::string)USERS_PREFERENCES_FILETYPE;
00371
00372 __SUP_COUT__ << "Load preferences: " << fn << std::endl;
00373
00374 FILE *fp = fopen(fn.c_str(),"r");
00375 if(!fp)
00376 {
00377
00378 __SUP_COUT__ << "Returning defaults." << std::endl;
00379 xmlOut.addTextElementToData("colorIndex","0");
00380 xmlOut.addTextElementToData("showSideBar","0");
00381 xmlOut.addTextElementToData("noWrap","1");
00382 xmlOut.addTextElementToData("messageOnly","0");
00383 xmlOut.addTextElementToData("hideLineNumers","1");
00384 return;
00385 }
00386 fscanf(fp,"%*s %u",&colorIndex);
00387 fscanf(fp,"%*s %u",&showSideBar);
00388 fscanf(fp,"%*s %u",&noWrap);
00389 fscanf(fp,"%*s %u",&messageOnly);
00390 fscanf(fp,"%*s %u",&hideLineNumers);
00391 fclose(fp);
00392 __SUP_COUT__ << "colorIndex: " << colorIndex << std::endl;
00393 __SUP_COUT__ << "showSideBar: " << showSideBar << std::endl;
00394 __SUP_COUT__ << "noWrap: " << noWrap << std::endl;
00395 __SUP_COUT__ << "messageOnly: " << messageOnly << std::endl;
00396 __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << std::endl;
00397
00398 char tmpStr[20];
00399 sprintf(tmpStr,"%u",colorIndex);
00400 xmlOut.addTextElementToData("colorIndex",tmpStr);
00401 sprintf(tmpStr,"%u",showSideBar);
00402 xmlOut.addTextElementToData("showSideBar",tmpStr);
00403 sprintf(tmpStr,"%u",noWrap);
00404 xmlOut.addTextElementToData("noWrap",tmpStr);
00405 sprintf(tmpStr,"%u",messageOnly);
00406 xmlOut.addTextElementToData("messageOnly",tmpStr);
00407 sprintf(tmpStr,"%u",hideLineNumers);
00408 xmlOut.addTextElementToData("hideLineNumers",tmpStr);
00409 }
00410
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument *xmlOut,
00436 const time_t lastUpdateCount, const unsigned int lastUpdateIndex)
00437 {
00438
00439
00440
00441 if(lastUpdateIndex > messages_.size() &&
00442 lastUpdateIndex != (unsigned int)-1)
00443 {
00444 __SS__ << "Invalid lastUpdateIndex: " << lastUpdateIndex <<
00445 " messagesArray size = " << messages_.size() << std::endl;
00446 __SS_THROW__;
00447 }
00448
00449
00450
00451 std::lock_guard<std::mutex> lock(messageMutex_);
00452
00453
00454 refreshReadPointer_ = (writePointer_ + messages_.size() - 1) % messages_.size();
00455
00456 sprintf(refreshTempStr_,"%lu",messages_[refreshReadPointer_].getCount());
00457 xmlOut->addTextElementToData("last_update_count",refreshTempStr_);
00458 sprintf(refreshTempStr_,"%u",refreshReadPointer_);
00459 xmlOut->addTextElementToData("last_update_index",refreshTempStr_);
00460
00461 if(!messages_[refreshReadPointer_].getTime())
00462 return;
00463
00464
00465 if(lastUpdateIndex != (unsigned int)-1 &&
00466 messages_[lastUpdateIndex].getCount() == lastUpdateCount)
00467 refreshReadPointer_ = (lastUpdateIndex+1) % messages_.size();
00468 else if(messages_[writePointer_].getTime())
00469 {
00470
00471
00472 xmlOut->addTextElementToData("message_overflow","1");
00473 __SUP_COUT__ << "Overflow was detected!" << std::endl;
00474 refreshReadPointer_ = (writePointer_+1) % messages_.size();
00475 }
00476 else
00477 refreshReadPointer_ = 0;
00478
00479
00480
00481
00482
00483
00484
00485
00486 refreshParent_ = xmlOut->addTextElementToData("messages","");
00487
00488 bool requestOutOfSync = false;
00489 std::string requestOutOfSyncMsg;
00490
00491 for(;
00492 refreshReadPointer_ != writePointer_;
00493 refreshReadPointer_ = (refreshReadPointer_+1) % messages_.size())
00494 {
00495 if(messages_[refreshReadPointer_].getCount() < lastUpdateCount)
00496 {
00497 if(!requestOutOfSync)
00498 {
00499 requestOutOfSync = true;
00500 __SS__ << "Request is out of sync! Message count should be more recent than update clock! " <<
00501 messages_[refreshReadPointer_].getCount() << " < " <<
00502 lastUpdateCount << std::endl;
00503 requestOutOfSyncMsg = ss.str();
00504 }
00505
00506
00507 }
00508
00509
00510 for(refreshIndex_=0; refreshIndex_ < messages_[refreshReadPointer_].fields.size();++refreshIndex_)
00511 xmlOut->addTextElementToParent("message_" +
00512 messages_[refreshReadPointer_].fields[refreshIndex_].fieldName,
00513 messages_[refreshReadPointer_].getField(refreshIndex_), refreshParent_);
00514
00515
00516 sprintf(refreshTempStr_,"%lu",messages_[refreshReadPointer_].getTime());
00517 xmlOut->addTextElementToParent("message_Time",
00518 refreshTempStr_, refreshParent_);
00519
00520 sprintf(refreshTempStr_,"%lu",messages_[refreshReadPointer_].getCount());
00521 xmlOut->addTextElementToParent("message_Count",
00522 refreshTempStr_, refreshParent_);
00523 }
00524
00525 if(requestOutOfSync)
00526 __SUP_COUT__ << requestOutOfSyncMsg;
00527 }
00528
00529
00530
00531
00532
00533
00534
00535
00536