main.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. #include <getopt.h>
  4. #include <signal.h>
  5. #include <unistd.h>
  6. #include <linux/limits.h>
  7. #include <climits>
  8. #include <gfa/svc/common/logfile.h>
  9. #include <gfa/svc/common/fileutil.h>
  10. #include <gfa/svc/common/strutil.h>
  11. #include <gfa/svc/common/processclock.h>
  12. #include <gfa/svc/common/instance.h>
  13. #include <gfa/svc/common/debug.h>
  14. #include "projal.h"
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////
  16. #define _USE_SIG_INFO 0
  17. ////////////////////////////////////////////////////////////////////////////////////////////////////
  18. // app control
  19. #define _APPID GFA_APPCTRL_APPID_REMANENT
  20. #define _APPNAME "Remanent"
  21. ////////////////////////////////////////////////////////////////////////////////////////////////////
  22. #define _DEFAULT_SCAN_INTERVAL_MS 1000 // 1 sec
  23. #define _MIN_SCAN_INTERVAL_MS 100 // 100 ms
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25. #define _NANOSECS_PER_SEC 1000000000
  26. #define _MAX_WRITE_CREDITS 60
  27. #define _MAX_WRITES_PER_MINUTE 6
  28. #define _SECS_PER_WRITE_INTERVAL (60 / _MAX_WRITES_PER_MINUTE)
  29. #define _LOGFILE_NAME "remanent.log"
  30. ////////////////////////////////////////////////////////////////////////////////////////////////////
  31. #define _SIG_BLOCK(s) sigprocmask(SIG_BLOCK, (s), NULL)
  32. #define _SIG_UNBLOCK(s) sigprocmask(SIG_UNBLOCK, (s), NULL)
  33. ////////////////////////////////////////////////////////////////////////////////////////////////////
  34. static volatile bool g_fRun = false;
  35. static volatile bool g_fPauseImp = false;
  36. static volatile bool g_fPauseCmd = false;
  37. static volatile bool g_fZombie = false;
  38. static int g_nLastSig = -1;
  39. static sigset_t g_set;
  40. static CLogfile g_lf;
  41. ////////////////////////////////////////////////////////////////////////////////////////////////////
  42. ////////////////////////////////////////////////////////////////////////////////////////////////////
  43. //
  44. #if _USE_SIG_INFO
  45. static void _OnSigAction(int sig, siginfo_t *psi, void *p)
  46. {
  47. TRACE("Received signal '%s', PID: %d, Code: %d.\n", strsignal(sig), psi->si_pid, psi->si_code);
  48. g_lf.Info("Received signal '%s', PID: %d, Code: %d.\n", strsignal(sig), psi->si_pid, psi->si_code);
  49. if((sig != SIGINT) || !psi->si_pid)
  50. {
  51. g_nLastSig = sig;
  52. g_fRun = g_fPauseImp = g_fPauseCmd = g_fZombie = false;
  53. }
  54. }
  55. #else
  56. static void _SigHandler(int sig)
  57. {
  58. g_nLastSig = sig;
  59. g_fRun = g_fPauseImp = g_fPauseCmd = g_fZombie = false;
  60. // g_lf.Info("Received signal '%s'.\n", strsignal(g_nLastSig));
  61. }
  62. #endif
  63. ////////////////////////////////////////////////////////////////////////////////////////////////////
  64. ////////////////////////////////////////////////////////////////////////////////////////////////////
  65. //
  66. static long long _TimeSpecDiffNs(const struct timespec *pts1, const struct timespec *pts2)
  67. {
  68. long long ns1 = (long long)pts1->tv_sec * _NANOSECS_PER_SEC + pts1->tv_nsec;
  69. long long ns2 = (long long)pts2->tv_sec * _NANOSECS_PER_SEC + pts2->tv_nsec;
  70. return (ns2 - ns1);
  71. }
  72. static bool _IntervalElapsed(struct timespec *pts, long nIntvSeconds)
  73. {
  74. struct timespec ts;
  75. clock_gettime(CLOCK_MONOTONIC, &ts);
  76. long long diff = _TimeSpecDiffNs(pts, &ts) / _NANOSECS_PER_SEC;
  77. if(diff >= (long long)nIntvSeconds)
  78. {
  79. pts->tv_sec += nIntvSeconds;
  80. return true;
  81. }
  82. return false;
  83. }
  84. static const char* _GetBaseDir(std::string &rstrBaseDir)
  85. {
  86. char szBaseDir[PATH_MAX];
  87. const char *pszBaseDir = NULL;
  88. #ifdef _LOG_BASE_DIR
  89. pszBaseDir = _LOG_BASE_DIR;
  90. if(!pszBaseDir || !*pszBaseDir || !::DirectoryExist(pszBaseDir))
  91. {
  92. CLogfile::StdErr("Invalid base directory config! Using app directory!\n");
  93. pszBaseDir = ::GetAppDirectory(szBaseDir, sizeof(szBaseDir));
  94. }
  95. rstrBaseDir = pszBaseDir;
  96. #else // _LOG_BASE_DIR
  97. UNUSED(pszBaseDir);
  98. rstrBaseDir = ::GetAppDirectory(szBaseDir, sizeof(szBaseDir));
  99. #endif // _LOG_BASE_DIR
  100. rtrim(rstrBaseDir, "/");
  101. return rstrBaseDir.c_str();
  102. }
  103. ////////////////////////////////////////////////////////////////////////////////////////////////////
  104. static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
  105. {
  106. ctrlmsg_t nCtrlMsg;
  107. while(g_fRun && (nCtrlMsg = ::GfaIpcAppCtrlGetNextCtrlMsg(hAI)))
  108. {
  109. switch(nCtrlMsg)
  110. {
  111. case GFA_APPCTRL_CTRLMSG_STOP:
  112. g_fRun = false;
  113. g_fPauseImp = false;
  114. g_fPauseCmd = false;
  115. g_fZombie = false;
  116. g_lf.Info("Received Control Message 'Stop'\n");
  117. break;
  118. case GFA_APPCTRL_CTRLMSG_PAUSE:
  119. if(!g_fPauseCmd)
  120. {
  121. g_fPauseCmd = true;
  122. if(!g_fPauseImp)
  123. {
  124. ::GfaIpcAppCtrlSetState(hAC, GIAS_Paused);
  125. g_lf.Info("Received Control Message 'Pause'\n");
  126. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Paused));
  127. TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Paused));
  128. }
  129. }
  130. break;
  131. case GFA_APPCTRL_CTRLMSG_RESUME:
  132. if(g_fPauseCmd)
  133. {
  134. g_fPauseCmd = false;
  135. if(!g_fPauseImp)
  136. {
  137. g_lf.Info("Received Control Message 'Resume'\n");
  138. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Running));
  139. ::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
  140. TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Running));
  141. }
  142. }
  143. break;
  144. default:
  145. break;
  146. }
  147. }
  148. }
  149. ////////////////////////////////////////////////////////////////////////////////////////////////////
  150. //
  151. int main(int argc, char **argv)
  152. {
  153. CProcessInstance pi;
  154. int nRet = 0, c;
  155. HSHM hShm = NULL;
  156. void *pShm = NULL;
  157. HAPPCTRL hAC = NULL;
  158. HAPPINFO hAI;
  159. Json::Value root;
  160. bool bDbgInvolved = false;
  161. bool bHasJSON = false;
  162. char szWriteFile[PATH_MAX];
  163. char szBackupFile[PATH_MAX];
  164. char szLogFile[PATH_MAX];
  165. CHECK_UPDATE_SHM_RETVAL rv;
  166. struct timespec tsWriteCredits, tsScanIntv;
  167. unsigned long cntVals = 0;
  168. char *pszEndPtr = NULL;
  169. clock64_t nCycleIntervalMS = _DEFAULT_SCAN_INTERVAL_MS;
  170. int nWriteCredits = _MAX_WRITE_CREDITS;
  171. unsigned int nPendingChanges = 0;
  172. struct sigaction sa;
  173. std::string strBaseDir;
  174. const char *pszBaseDir = NULL;
  175. unsigned long long nUsecWorkTime = 0;
  176. CProcessClock pcWork, pcPerf;
  177. std::string sPerf;
  178. ////////////////////////////////////////////////////////////////////////////////////////////////
  179. // check for multiple instances
  180. if(!pi.LockInstance(UUID_SHM))
  181. {
  182. CLogfile::StdErr("Failed to start instance!\n");
  183. return -1;
  184. }
  185. ////////////////////////////////////////////////////////////////////////////////////////////////
  186. // parse command line options
  187. while((c = getopt(argc, argv, "b:c:d")) != -1)
  188. {
  189. switch(c)
  190. {
  191. case 'b':
  192. if(DirectoryExist(optarg))
  193. {
  194. strBaseDir = optarg;
  195. rtrim(strBaseDir, "/");
  196. pszBaseDir = strBaseDir.c_str();
  197. }
  198. else
  199. {
  200. CLogfile::StdErr("Invalid base directory option!\n");
  201. }
  202. break;
  203. case 'c':
  204. nCycleIntervalMS = strtoll(optarg, &pszEndPtr, 10);
  205. if((((nCycleIntervalMS == LLONG_MIN) || (nCycleIntervalMS == LLONG_MAX)) && (errno == ERANGE)) || (pszEndPtr && *pszEndPtr))
  206. {
  207. nCycleIntervalMS = _DEFAULT_SCAN_INTERVAL_MS;
  208. CLogfile::StdErr("Invalid cycle interval: \"%s\"! Setting default value (%u)\n", optarg, _DEFAULT_SCAN_INTERVAL_MS);
  209. }
  210. else if(nCycleIntervalMS < _MIN_SCAN_INTERVAL_MS)
  211. {
  212. nCycleIntervalMS = _MIN_SCAN_INTERVAL_MS;
  213. CLogfile::StdErr("Invalid cycle interval: \"%s\"! Limiting to minimal value (%u)\n", optarg, _MIN_SCAN_INTERVAL_MS);
  214. }
  215. break;
  216. case 'd':
  217. bDbgInvolved = true;
  218. break;
  219. case '?':
  220. break;
  221. }
  222. }
  223. do
  224. {
  225. g_fZombie = true;
  226. ////////////////////////////////////////////////////////////////////////////////////////////
  227. // get the base directory for output files if not provided by cmd line
  228. if(!pszBaseDir)
  229. pszBaseDir = _GetBaseDir(strBaseDir);
  230. CLogfile::StdOut("Using base directory \"%s\".\n", pszBaseDir);
  231. ////////////////////////////////////////////////////////////////////////////////////////////
  232. // initialize log file
  233. sprintf(szLogFile, "%s/%s", pszBaseDir, _LOGFILE_NAME);
  234. if(!g_lf.Open(szLogFile, true, CLogfile::VB_Inf))
  235. {
  236. CLogfile::StdErr("Failed to create/open log file!\n");
  237. nRet = -1;
  238. break;
  239. }
  240. g_lf.Info("Process started.\n");
  241. ////////////////////////////////////////////////////////////////////////////////////////////
  242. // initialize app control
  243. g_lf.Info("Acquire AppCtrl-Handle.\n");
  244. if(!(hAC = ::GfaIpcAppCtrlAcquire(_APPID, _APPNAME, nCycleIntervalMS * 1000, nCycleIntervalMS * 3000)))
  245. {
  246. g_lf.Error("Failed to acquire AppCtrl-Handle!\n");
  247. nRet = -1;
  248. break;
  249. }
  250. ::GfaIpcAppCtrlSetState(hAC, GIAS_Initializing);
  251. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Initializing));
  252. ////////////////////////////////////////////////////////////////////////////////////////////
  253. // validate config parameters
  254. #ifdef _LOG_DB_NAME
  255. time_t ts;
  256. bool bLogDbActive = false;
  257. if(strlen(_LOG_DB_NAME) >= _RL_MAX_DB_NAME_LENGTH)
  258. {
  259. g_lf.Error("Log database name too long! %s\n", _LOG_DB_NAME);
  260. nRet = -1;
  261. break;
  262. }
  263. if(strlen(_LOG_DB_USER) >= _RL_MAX_DB_USER_LENGTH)
  264. {
  265. g_lf.Error("Log database user name too long! %s\n", _LOG_DB_USER);
  266. nRet = -1;
  267. break;
  268. }
  269. if(strlen(_LOG_DB_PASS) >= _RL_MAX_DB_PASS_LENGTH)
  270. {
  271. g_lf.Error("Log database password too long!\n");
  272. nRet = -1;
  273. break;
  274. }
  275. if(strlen(_LOG_LOGS_TABLE) >= _RL_MAX_TABLE_NAME_LENGTH)
  276. {
  277. g_lf.Error("Log table name too long! %s\n", _LOG_LOGS_TABLE);
  278. nRet = -1;
  279. break;
  280. }
  281. ////////////////////////////////////////////////////////////////////////////////////////////
  282. // configure remanent logger
  283. RLPARAMS rlp;
  284. memset(&rlp, 0, sizeof(rlp));
  285. strncpy(rlp.szDBName, _LOG_DB_NAME, _RL_MAX_DB_NAME_LENGTH - 1);
  286. strncpy(rlp.szDBUser, _LOG_DB_USER, _RL_MAX_DB_USER_LENGTH - 1);
  287. strncpy(rlp.szDBPass, _LOG_DB_PASS, _RL_MAX_DB_PASS_LENGTH - 1);
  288. strncpy(rlp.szLogsTable, _LOG_LOGS_TABLE, _RL_MAX_TABLE_NAME_LENGTH - 1);
  289. #endif // _LOG_DB_NAME
  290. ////////////////////////////////////////////////////////////////////////////////////////////
  291. _CreateJSONWriteFileName(pszBaseDir, szWriteFile, _COUNTOF(szWriteFile));
  292. _CreateJSONBackupFileName(pszBaseDir, szBackupFile, _COUNTOF(szBackupFile));
  293. ////////////////////////////////////////////////////////////////////////////////////////////
  294. // configure signal handling
  295. ::sigfillset(&g_set);
  296. memset(&sa, 0, sizeof(sa));
  297. #if _USE_SIG_INFO
  298. sa.sa_flags = SA_SIGINFO;
  299. sa.sa_sigaction = _OnSigAction;
  300. #else
  301. sa.sa_handler = _SigHandler;
  302. #endif
  303. sigaction(SIGHUP, &sa, NULL); // handles user's terminal disconnect
  304. sigaction(SIGQUIT, &sa, NULL); // handles Ctrl + '\'
  305. sigaction(SIGTERM, &sa, NULL); // handles normal termination
  306. sigaction(SIGABRT, &sa, NULL); // handles abnormal termination (i.e. abort())
  307. sigaction(SIGINT, &sa, NULL); // handles Ctrl + 'C'
  308. #if _USE_SIG_INFO
  309. sa.sa_flags = 0;
  310. #endif
  311. sa.sa_handler = SIG_IGN;
  312. sigaction(SIGTSTP, &sa, NULL); // ignores Ctrl + 'Z'
  313. sigaction(SIGSTOP, &sa, NULL); // ignores Stop
  314. sigaction(SIGCONT, &sa, NULL); // ignores Continue
  315. sigaction(SIGCHLD, &sa, NULL); // ignores child process termination
  316. sigaction(0, &sa, NULL); // ignores shell termination
  317. ////////////////////////////////////////////////////////////////////////////////////////////
  318. // configure timers
  319. clock_gettime(CLOCK_MONOTONIC, &tsWriteCredits);
  320. tsScanIntv.tv_sec = nCycleIntervalMS / 1000;
  321. tsScanIntv.tv_nsec = (nCycleIntervalMS % 1000) * 1000000;
  322. ////////////////////////////////////////////////////////////////////////////////////////////
  323. // read input SHM file, if any ...
  324. pcPerf.ClockTrigger();
  325. bHasJSON = ::ParseJSONFile(pszBaseDir, root, g_lf);
  326. sPerf = CProcessClock::Interval2String(pcPerf.ClockGetElapsed());
  327. if(bHasJSON)
  328. {
  329. g_lf.Info("Parsing time: %s\n", sPerf.c_str());
  330. TRACE("JSON parsing time: %s\n", sPerf.c_str());
  331. }
  332. ////////////////////////////////////////////////////////////////////////////////////////////
  333. if(!(hShm = ::acquire_shm(sizeof(shm_t), 1)))
  334. {
  335. g_lf.Error("Unable to acquire SHM Handle!\n");
  336. nRet = -1;
  337. break;
  338. }
  339. g_lf.Info("Acquired SHM Handle.\n");
  340. if(!(pShm = ::GfaIpcAcquirePointer(hShm)))
  341. {
  342. g_lf.Error("Unable to acquire SHM Pointer!\n");
  343. nRet = -1;
  344. break;
  345. }
  346. g_lf.Info("Acquired SHM Pointer.\n");
  347. ::GfaIpcDumpSHMROT();
  348. #ifdef _LOG_DB_NAME
  349. CRemLogger rl(&rlp);
  350. if(!(bLogDbActive = rl.InitDatabase(false)))
  351. {
  352. g_lf.Error("Failed to initialize Log Database - %s\n", rl.LastError().c_str());
  353. g_lf.Warning("Logging will be disabled!\n");
  354. }
  355. #endif // _LOG_DB_NAME
  356. CRemVarTable map;
  357. CShm_t shm(pShm, hShm);
  358. pcPerf.ClockTrigger();
  359. shm.InitPath(NULL, NULL);
  360. sPerf = CProcessClock::Interval2String(pcPerf.ClockGetElapsed());
  361. TRACE("InitPath (%s).\n", sPerf.c_str());
  362. pcPerf.ClockTrigger();
  363. shm.CreateMembersTable(map);
  364. sPerf = CProcessClock::Interval2String(pcPerf.ClockGetElapsed());
  365. TRACE("CreateMembersTable (%s).\n", sPerf.c_str());
  366. if(bHasJSON)
  367. {
  368. pcPerf.ClockTrigger();
  369. cntVals = map.LoadJSONValues(root);
  370. sPerf = CProcessClock::Interval2String(pcPerf.ClockGetElapsed());
  371. g_lf.Info("Successfully restored %lu values from JSON file (%s).\n", cntVals, sPerf.c_str());
  372. TRACE("Successfully restored %lu remanent values from JSON file (%s).\n", cntVals, sPerf.c_str());
  373. }
  374. else
  375. {
  376. _SIG_BLOCK(&g_set);
  377. bool bRet = ::WriteJSONFile(szWriteFile, szBackupFile, static_cast<CRemanent&>(shm));
  378. _SIG_UNBLOCK(&g_set);
  379. if(!bRet)
  380. {
  381. g_lf.Error("Failed to initialize JSON file! - %s\n", strerror(errno));
  382. TRACE("Failed to initialize JSON file! - %s\n", strerror(errno));
  383. break;
  384. }
  385. }
  386. #ifdef _DL_DB_NAME
  387. CDbPersist perst(_DL_DB_NAME, _DL_TAGS_TABLE, _DL_LOGS_TABLE, _DL_DB_USER, _DL_DB_PASS);
  388. pcPerf.ClockTrigger();
  389. int nRestored = perst.RestoreValues(map, g_lf);
  390. sPerf = CProcessClock::Interval2String(pcPerf.ClockGetElapsed());
  391. if(nRestored > 0)
  392. {
  393. g_lf.Info("Successfully restored %d Database-persistent values from Datalogger Database (%s).\n", nRestored, sPerf.c_str());
  394. TRACE("Successfully restored %d Database-persistent values from Datalogger Database (%s).\n", nRestored, sPerf.c_str());
  395. }
  396. #endif // _DL_DB_NAME
  397. g_fZombie = false;
  398. g_fRun = true;
  399. ::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
  400. g_lf.Info("Enter monitoring loop.\n");
  401. while(g_fRun)
  402. {
  403. ////////////////////////////////////////////////////////////////////////////////////////
  404. // update app control info
  405. if((hAI = ::GfaIpcAppCtrlInfoUpdate(hAC, nUsecWorkTime)))
  406. {
  407. _ProcessCtrlMessages(hAC, hAI);
  408. if(!g_fRun)
  409. break;
  410. // _ProcessStateEvents(hAC, hAI);
  411. }
  412. ////////////////////////////////////////////////////////////////////////////////////////
  413. if(::nanosleep(&tsScanIntv, NULL) < 0)
  414. {
  415. if(!g_fRun)
  416. break;
  417. }
  418. pcWork.ClockTrigger();
  419. if(!g_fPauseImp && !g_fPauseCmd)
  420. {
  421. if(_IntervalElapsed(&tsWriteCredits, _SECS_PER_WRITE_INTERVAL))
  422. {
  423. if(nWriteCredits < _MAX_WRITE_CREDITS)
  424. {
  425. nWriteCredits++;
  426. TRACE("%d sec elapsed - write credits: %d\n", _SECS_PER_WRITE_INTERVAL, nWriteCredits);
  427. }
  428. }
  429. rv.nRetval = shm.CheckUpdateShm(true);
  430. if(rv.nUpdated || nPendingChanges)
  431. {
  432. if(nWriteCredits > 0)
  433. {
  434. if(nPendingChanges)
  435. {
  436. g_lf.Warning("Reacquired write credit(s). Total %d\n", nWriteCredits);
  437. }
  438. TRACE("%u SHM values changed ...\n", rv.nUpdated ? rv.nUpdated : nPendingChanges);
  439. _SIG_BLOCK(&g_set);
  440. bool bRet = ::WriteJSONFile(szWriteFile, szBackupFile, static_cast<CRemanent&>(shm));
  441. _SIG_UNBLOCK(&g_set);
  442. if(!bRet)
  443. {
  444. g_lf.Error("Failed to write JSON file! - %s\n", strerror(errno));
  445. TRACE("Failed to write JSON file! - %s\n", strerror(errno));
  446. g_fRun = false;
  447. g_fZombie = true;
  448. break;
  449. }
  450. nWriteCredits--;
  451. nPendingChanges = 0;
  452. #ifdef _LOG_DB_NAME
  453. if(bLogDbActive)
  454. {
  455. ts = ::time(NULL);
  456. shm.Log(ts, rl);
  457. if(!rl.Flush(ts))
  458. {
  459. g_lf.Error("Failed to log to Database - %s\n", rl.LastError().c_str());
  460. g_lf.Warning("Logging will be disabled!\n");
  461. bLogDbActive = false;
  462. }
  463. }
  464. #endif // _LOG_DB_NAME
  465. TRACE("write credits left: %d\n", nWriteCredits);
  466. }
  467. else if(!nPendingChanges)
  468. {
  469. std::vector<const CRemanent*> vars;
  470. nPendingChanges = rv.nUpdated;
  471. TRACE("%u SHM value(s) changed, but no write credits left!!!\n", rv.nUpdated);
  472. g_lf.Warning("%u SHM value(s) changed, but no write credits left.\n", rv.nUpdated);
  473. #ifdef _DEBUG
  474. if(map.GetMaxUpdateVariables(vars, 5))
  475. {
  476. for(auto i = vars.begin(); i != vars.end(); i++)
  477. {
  478. const CRemanent *pVar = *i;
  479. TRACE("%s -> %llu\n", pVar->GetPath(), pVar->GetUpdateCount());
  480. }
  481. }
  482. #endif // _DEBUG
  483. }
  484. g_lf.Flush();
  485. }
  486. }
  487. nUsecWorkTime = pcWork.ClockGetElapsed() / 1000;
  488. }
  489. TRACE("Process terminating...\n");
  490. g_lf.Info("Leave monitoring loop.\n");
  491. if(!g_fZombie && !bDbgInvolved)
  492. {
  493. _SIG_BLOCK(&g_set);
  494. bool bRet = ::WriteJSONFile(szWriteFile, szBackupFile, static_cast<CRemanent&>(shm));
  495. _SIG_UNBLOCK(&g_set);
  496. if(!bRet)
  497. {
  498. g_lf.Error("Failed to write JSON file! - %s\n", strerror(errno));
  499. TRACE("Failed to write JSON file! - %s\n", strerror(errno));
  500. g_fZombie = true;
  501. }
  502. }
  503. #ifdef _LOG_DB_NAME
  504. if(bLogDbActive)
  505. {
  506. ts = ::time(NULL);
  507. shm.Log(ts, rl);
  508. if(!rl.Flush(ts))
  509. {
  510. g_lf.Error("Failed to log to Database on exit - %s\n", rl.LastError().c_str());
  511. bLogDbActive = false;
  512. }
  513. }
  514. #endif // _LOG_DB_NAME
  515. }
  516. while(false);
  517. if(g_nLastSig >= 0)
  518. {
  519. g_lf.Info("Received signal '%s'.\n", strsignal(g_nLastSig));
  520. g_nLastSig = -1;
  521. }
  522. if(hShm)
  523. {
  524. if(pShm)
  525. {
  526. g_lf.Info("Releasing SHM Pointer ...\n");
  527. ::GfaIpcReleasePointer(hShm, pShm);
  528. }
  529. g_lf.Info("Releasing SHM Handle ...\n");
  530. ::GfaIpcReleaseSHM(hShm);
  531. }
  532. if(g_fZombie)
  533. {
  534. if(hAC)
  535. ::GfaIpcAppCtrlSetState(hAC, GIAS_Zombie);
  536. TRACE("Enter Zombie state ...\n");
  537. g_lf.Warning("Enter Zombie state ...\n");
  538. g_lf.Flush();
  539. pause();
  540. if(g_nLastSig >= 0)
  541. g_lf.Info("Received signal '%s'.\n", strsignal(g_nLastSig));
  542. }
  543. if(hAC)
  544. {
  545. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Terminating));
  546. ::GfaIpcAppCtrlSetState(hAC, GIAS_Terminating);
  547. g_lf.Info("Releasing App Control ...\n");
  548. ::GfaIpcAppCtrlRelease(hAC);
  549. }
  550. g_lf.Info("Process exit.\n\n");
  551. g_lf.Close();
  552. CLogfile::StdErr("Remanent exit.\n");
  553. return 0;
  554. }