Commit 2972347390fe213f71343cc4011e2543e22996fb
1 parent
83ee4f41
Add GammaLib daemon for application statistics management (#3592)
Showing
16 changed files
with
1006 additions
and
53 deletions
ChangeLog
| 1 | -2022-02-18 | |
| 1 | +2022-02-19 | |
| 2 | 2 | |
| 3 | 3 | * Version 2.0.0 released |
| 4 | 4 | ======================== |
| 5 | 5 | |
| 6 | + Add GammaLib daemon for application statistics management (#3592) | |
| 6 | 7 | By default, do not allow log-likelihood decrease in LM optimiser |
| 7 | 8 | Support Galactic coordinates in spatial models (#3988) |
| 8 | 9 | Implement CGRO clock correction (#3973) | ... | ... |
NEWS
| 1 | 1 | New Features and Important Changes in GammaLib 2.0.0 |
| 2 | 2 | |
| 3 | -18 February 2022 | |
| 3 | +19 February 2022 | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | 1. Introduction |
| ... | ... | @@ -14,6 +14,7 @@ made since the last release of GammaLib. |
| 14 | 14 | The following classes have been added: |
| 15 | 15 | - GFunctions |
| 16 | 16 | - GIntegrals |
| 17 | +- GDaemon | |
| 17 | 18 | - GResponseVectorCache |
| 18 | 19 | - GPulsar |
| 19 | 20 | - GPulsarEphemeris |
| ... | ... | @@ -148,6 +149,7 @@ The following methods have been added: |
| 148 | 149 | - gammalib::xml_check_type() |
| 149 | 150 | - gammalib::host_country() |
| 150 | 151 | - gammalib::http_query() |
| 152 | +- gammalib::gamma_filename() | |
| 151 | 153 | - gammalib::check_energy_interval() |
| 152 | 154 | - gammalib::check_prj_x2s_status() |
| 153 | 155 | - gammalib::check_prj_s2x_status() |
| ... | ... | @@ -412,6 +414,14 @@ were added to compute the total solid angle within specified sky region(s) (#178 |
| 412 | 414 | |
| 413 | 415 | 14. Support module |
| 414 | 416 | ------------------ |
| 417 | +Added GDaemon class that implements the GammaLib daemon. The GammaLib daemon collects | |
| 418 | +on a regular basis low-level application statistics and generates from this information | |
| 419 | +high-level statistics on a daily basis. This avoids that the low-level statistics | |
| 420 | +file becomes too large (#3592). | |
| 421 | + | |
| 422 | +Added gammalib::gamma_filename() method that returns the full path for a filename in | |
| 423 | +the users's .gamma directory. | |
| 424 | + | |
| 415 | 425 | Added gammalib::host_country() method that returns the host country on which the code |
| 416 | 426 | is executed. |
| 417 | 427 | ... | ... |
README.md
| 1 | 1 | GammaLib information |
| 2 | 2 | ==================== |
| 3 | -* Version: 2.0.0.dev (18 February 2022) | |
| 3 | +* Version: 2.0.0.dev (19 February 2022) | |
| 4 | 4 | |
| 5 | 5 | [](https://cta-jenkins.irap.omp.eu/job/gammalib-integrate-os/) |
| 6 | 6 | ... | ... |
doc/source/admin/release_history/2.0.rst
| 1 | 1 | .. _2.0: |
| 2 | 2 | |
| 3 | -GammaLib 2.0 release (14 February 2022) | |
| 3 | +GammaLib 2.0 release (19 February 2022) | |
| 4 | 4 | ======================================= |
| 5 | 5 | |
| 6 | 6 | Overview |
| ... | ... | @@ -10,11 +10,11 @@ GammaLib 2.0 is a major release that adds significant functionality. |
| 10 | 10 | |
| 11 | 11 | In particular, this release provides: |
| 12 | 12 | |
| 13 | -* extended support for COMPTEL data analysis | |
| 14 | -* full support of all spatial models for COMPTEL | |
| 13 | +* full support for COMPTEL data analysis | |
| 15 | 14 | * support for pulsar analysis |
| 16 | 15 | * vectorised response computation |
| 17 | 16 | * support Galactic coordinates in spatial models |
| 17 | +* provision of carbon footprint information and statistics | |
| 18 | 18 | |
| 19 | 19 | |
| 20 | 20 | Bug fixes |
| ... | ... | @@ -103,19 +103,18 @@ Improvements |
| 103 | 103 | New features |
| 104 | 104 | ------------ |
| 105 | 105 | |
| 106 | -<<<<<<< HEAD | |
| 107 | -* [`3924 <https://cta-redmine.irap.omp.eu/issues/3924>`_] - | |
| 108 | - Add ``GModelSpatialRadialGeneralGauss`` and ``GModelSpatialEllipticalGeneralGauss`` classes | |
| 109 | -======= | |
| 110 | 106 | * [`3973 <https://cta-redmine.irap.omp.eu/issues/3973>`_] - |
| 111 | 107 | Add ``GCOMBvc`` and ``GCOMBvcs`` classes to support COMPTEL pulsar analysis, revise |
| 112 | 108 | ``GCOMSelection`` interface to support both orbital and pulsar phase selection, |
| 113 | 109 | extend ``GCOMObservation`` interface to handle Solar System Barycentre Data |
| 114 | ->>>>>>> 3973-add-pulsar-analysis | |
| 110 | +* [`3924 <https://cta-redmine.irap.omp.eu/issues/3924>`_] - | |
| 111 | + Add ``GModelSpatialRadialGeneralGauss`` and ``GModelSpatialEllipticalGeneralGauss`` classes | |
| 115 | 112 | * [`3910 <https://cta-redmine.irap.omp.eu/issues/3910>`_] - |
| 116 | 113 | Add ``GCOMModelDRM`` class |
| 117 | 114 | * [`3606 <https://cta-redmine.irap.omp.eu/issues/3606>`_] - |
| 118 | 115 | Add ``GModelSpectralBins`` class |
| 116 | +* [`3592 <https://cta-redmine.irap.omp.eu/issues/3592>`_] - | |
| 117 | + Add GammaLib daemon for application statistics management | |
| 119 | 118 | * [`3527 <https://cta-redmine.irap.omp.eu/issues/3527>`_] - |
| 120 | 119 | Add ``GCTABackground2D`` class |
| 121 | 120 | * [`3469 <https://cta-redmine.irap.omp.eu/issues/3469>`_] - | ... | ... |
include/GApplication.hpp
| ... | ... | @@ -136,7 +136,7 @@ protected: |
| 136 | 136 | void set_log_chatter(void); |
| 137 | 137 | void set_log_filename(void); |
| 138 | 138 | void write_statistics(void); |
| 139 | - std::string strdate(void) const; | |
| 139 | + void start_daemon(void) const; | |
| 140 | 140 | |
| 141 | 141 | // Protected data members |
| 142 | 142 | std::string m_name; //!< Application name | ... | ... |
include/GDaemon.hpp
0 → 100644
| 1 | +/*************************************************************************** | |
| 2 | + * GDaemon.hpp - Daemon class * | |
| 3 | + * ----------------------------------------------------------------------- * | |
| 4 | + * copyright (C) 2022 by Juergen Knoedlseder * | |
| 5 | + * ----------------------------------------------------------------------- * | |
| 6 | + * * | |
| 7 | + * This program is free software: you can redistribute it and/or modify * | |
| 8 | + * it under the terms of the GNU General Public License as published by * | |
| 9 | + * the Free Software Foundation, either version 3 of the License, or * | |
| 10 | + * (at your option) any later version. * | |
| 11 | + * * | |
| 12 | + * This program is distributed in the hope that it will be useful, * | |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| 15 | + * GNU General Public License for more details. * | |
| 16 | + * * | |
| 17 | + * You should have received a copy of the GNU General Public License * | |
| 18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. * | |
| 19 | + * * | |
| 20 | + ***************************************************************************/ | |
| 21 | +/** | |
| 22 | + * @file GDaemon.hpp | |
| 23 | + * @brief Daemon class definition | |
| 24 | + * @author Juergen Knoedlseder | |
| 25 | + */ | |
| 26 | + | |
| 27 | +#ifndef GDAEMON_HPP | |
| 28 | +#define GDAEMON_HPP | |
| 29 | + | |
| 30 | +/* __ Includes ___________________________________________________________ */ | |
| 31 | +#include <string> | |
| 32 | +#include "GBase.hpp" | |
| 33 | +#include "GTools.hpp" | |
| 34 | +#include "GFilename.hpp" | |
| 35 | +#include "GLog.hpp" | |
| 36 | + | |
| 37 | +/* __ Forward declarations _______________________________________________ */ | |
| 38 | +class GCsv; | |
| 39 | + | |
| 40 | +/* __ Constants __________________________________________________________ */ | |
| 41 | + | |
| 42 | + | |
| 43 | +/***********************************************************************//** | |
| 44 | + * @class GDaemon | |
| 45 | + * | |
| 46 | + * @brief Daemon class | |
| 47 | + * | |
| 48 | + * @todo Add class description. | |
| 49 | + ***************************************************************************/ | |
| 50 | +class GDaemon : public GBase { | |
| 51 | + | |
| 52 | +public: | |
| 53 | + // Constructors and destructors | |
| 54 | + GDaemon(void); | |
| 55 | + GDaemon(const GDaemon& daemon); | |
| 56 | + virtual ~GDaemon(void); | |
| 57 | + | |
| 58 | + // Operators | |
| 59 | + GDaemon& operator=(const GDaemon& daemon); | |
| 60 | + | |
| 61 | + // Implemented pure virtual base class methods | |
| 62 | + virtual void clear(void); | |
| 63 | + virtual GDaemon* clone(void) const; | |
| 64 | + virtual std::string classname(void) const; | |
| 65 | + virtual std::string print(const GChatter& chatter = NORMAL) const; | |
| 66 | + | |
| 67 | + // Other methods | |
| 68 | + void start(void); | |
| 69 | + bool alive(void) const; | |
| 70 | + | |
| 71 | +protected: | |
| 72 | + // Protected methods | |
| 73 | + void init_members(void); | |
| 74 | + void copy_members(const GDaemon& daemon); | |
| 75 | + void free_members(void); | |
| 76 | + void create_lock_file(void); | |
| 77 | + void delete_lock_file(void) const; | |
| 78 | + GFilename lock_filename(void) const; | |
| 79 | + GFilename statistics_filename(void) const; | |
| 80 | + pid_t lock_pid(void) const; | |
| 81 | + void update_statistics(void); | |
| 82 | + void update_high_level_statistics(const GCsv& statistics); | |
| 83 | + | |
| 84 | + // Protected members | |
| 85 | + pid_t m_pid; //!< Process ID | |
| 86 | + int m_period; //!< Wake-up period in seconds | |
| 87 | + GLog m_log; //!< Logger | |
| 88 | + GChatter m_chatter; //!< Chattiness of logger | |
| 89 | +}; | |
| 90 | + | |
| 91 | + | |
| 92 | +/***********************************************************************//** | |
| 93 | + * @brief Return class name | |
| 94 | + * | |
| 95 | + * @return String containing the class name ("GDaemon"). | |
| 96 | + ***************************************************************************/ | |
| 97 | +inline | |
| 98 | +std::string GDaemon::classname(void) const | |
| 99 | +{ | |
| 100 | + return ("GDaemon"); | |
| 101 | +} | |
| 102 | + | |
| 103 | + | |
| 104 | +/***********************************************************************//** | |
| 105 | + * @brief Returns daemon lock filename | |
| 106 | + * | |
| 107 | + * @return Daemon lock filename. | |
| 108 | + ***************************************************************************/ | |
| 109 | +inline | |
| 110 | +GFilename GDaemon::lock_filename(void) const | |
| 111 | +{ | |
| 112 | + return (gammalib::gamma_filename("daemon.lock")); | |
| 113 | +} | |
| 114 | + | |
| 115 | +#endif /* GDAEMON_HPP */ | ... | ... |
include/GTools.hpp
include/GammaLib.hpp
include/Makefile.am
pyext/GDaemon.i
0 → 100644
| 1 | +/*************************************************************************** | |
| 2 | + * GDaemon.i - Daemon class * | |
| 3 | + * ----------------------------------------------------------------------- * | |
| 4 | + * copyright (C) 2022 by Juergen Knoedlseder * | |
| 5 | + * ----------------------------------------------------------------------- * | |
| 6 | + * * | |
| 7 | + * This program is free software: you can redistribute it and/or modify * | |
| 8 | + * it under the terms of the GNU General Public License as published by * | |
| 9 | + * the Free Software Foundation, either version 3 of the License, or * | |
| 10 | + * (at your option) any later version. * | |
| 11 | + * * | |
| 12 | + * This program is distributed in the hope that it will be useful, * | |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| 15 | + * GNU General Public License for more details. * | |
| 16 | + * * | |
| 17 | + * You should have received a copy of the GNU General Public License * | |
| 18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. * | |
| 19 | + * * | |
| 20 | + ***************************************************************************/ | |
| 21 | +/** | |
| 22 | + * @file GDaemon.i | |
| 23 | + * @brief Daemon class definition | |
| 24 | + * @author Juergen Knoedlseder | |
| 25 | + */ | |
| 26 | +%{ | |
| 27 | +/* Put headers and other declarations here that are needed for compilation */ | |
| 28 | +#include "GDaemon.hpp" | |
| 29 | +%} | |
| 30 | + | |
| 31 | + | |
| 32 | +/***********************************************************************//** | |
| 33 | + * @class GDaemon | |
| 34 | + * | |
| 35 | + * @brief Daemon class | |
| 36 | + ***************************************************************************/ | |
| 37 | +class GDaemon : public GBase { | |
| 38 | + | |
| 39 | +public: | |
| 40 | + // Constructors and destructors | |
| 41 | + GDaemon(void); | |
| 42 | + //GDaemon(const GDaemon& daemon); | |
| 43 | + virtual ~GDaemon(void); | |
| 44 | + | |
| 45 | + // Implemented pure virtual base class methods | |
| 46 | + virtual void clear(void); | |
| 47 | + virtual GDaemon* clone(void) const; | |
| 48 | + virtual std::string classname(void) const; | |
| 49 | + | |
| 50 | + // Other methods | |
| 51 | + void start(void); | |
| 52 | + bool alive(void) const; | |
| 53 | +}; | |
| 54 | + | |
| 55 | + | |
| 56 | +/***********************************************************************//** | |
| 57 | + * @brief GDaemon class extension | |
| 58 | + ***************************************************************************/ | |
| 59 | +%extend GDaemon { | |
| 60 | + GDaemon copy() { | |
| 61 | + return (*self); | |
| 62 | + } | |
| 63 | +}; | ... | ... |
pyext/GTools.i
pyext/gammalib/support.i
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * support.i - Support module * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2011-2020 by Jurgen Knodlseder * | |
| 4 | + * copyright (C) 2011-2022 by Jurgen Knodlseder * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -82,3 +82,4 @@ |
| 82 | 82 | %include "GUrlFile.i" |
| 83 | 83 | %include "GUrlString.i" |
| 84 | 84 | %include "GFilename.i" |
| 85 | +%include "GDaemon.i" | ... | ... |
src/app/GApplication.cpp
| ... | ... | @@ -28,7 +28,7 @@ |
| 28 | 28 | #ifdef HAVE_CONFIG_H |
| 29 | 29 | #include <config.h> |
| 30 | 30 | #endif |
| 31 | -#include <pwd.h> // user/passwd function | |
| 31 | +//#include <pwd.h> // user/passwd function | |
| 32 | 32 | #include <fcntl.h> // for file locking |
| 33 | 33 | #include <unistd.h> // access() function |
| 34 | 34 | #include <cstdio> // std::fopen(), etc. functions |
| ... | ... | @@ -37,6 +37,7 @@ |
| 37 | 37 | #include "GFits.hpp" |
| 38 | 38 | #include "GFitsHDU.hpp" |
| 39 | 39 | #include "GFilename.hpp" |
| 40 | +#include "GDaemon.hpp" | |
| 40 | 41 | |
| 41 | 42 | /* __ OpenMP section _____________________________________________________ */ |
| 42 | 43 | #ifdef _OPENMP |
| ... | ... | @@ -1190,6 +1191,9 @@ void GApplication::init_members(void) |
| 1190 | 1191 | m_cstart = double(clock()); |
| 1191 | 1192 | #endif |
| 1192 | 1193 | |
| 1194 | + // Start daemon | |
| 1195 | + start_daemon(); | |
| 1196 | + | |
| 1193 | 1197 | // Return |
| 1194 | 1198 | return; |
| 1195 | 1199 | } |
| ... | ... | @@ -1305,42 +1309,35 @@ void GApplication::set_log_filename(void) |
| 1305 | 1309 | ***************************************************************************/ |
| 1306 | 1310 | void GApplication::write_statistics(void) |
| 1307 | 1311 | { |
| 1308 | - // Initialise ASCII file name | |
| 1309 | - std::string filename; | |
| 1310 | - | |
| 1311 | - // Get ASCII file name. Make sure that ASCII file exists and that it | |
| 1312 | - // contains a header line | |
| 1313 | - uid_t uid = geteuid(); | |
| 1314 | - struct passwd* pw = getpwuid(uid); | |
| 1315 | - if (pw != NULL) { | |
| 1316 | - filename = std::string(pw->pw_dir) + "/.gamma/statistics.csv"; | |
| 1317 | - if (access(filename.c_str(), R_OK) != 0) { | |
| 1318 | - | |
| 1319 | - // OpenMP critical zone to write header in case that the file | |
| 1320 | - // does not yet exist | |
| 1321 | - #pragma omp critical(GApplication_write_statistics) | |
| 1322 | - { | |
| 1323 | - | |
| 1324 | - // Open statistics file, and in case of success, write | |
| 1325 | - // header line | |
| 1326 | - FILE* fptr = fopen(filename.c_str(), "w"); | |
| 1327 | - if (fptr != NULL) { | |
| 1328 | - fprintf(fptr, "Date," | |
| 1329 | - "GammaLib version," | |
| 1330 | - "Country," | |
| 1331 | - "Application," | |
| 1332 | - "Version," | |
| 1333 | - "Wall clock seconds," | |
| 1334 | - "CPU seconds," | |
| 1335 | - "g eCO2\n"); | |
| 1336 | - fclose(fptr); | |
| 1337 | - } | |
| 1312 | + // Get statistics ASCII file name | |
| 1313 | + GFilename filename = gammalib::gamma_filename("statistics.csv"); | |
| 1314 | + | |
| 1315 | + // Make sure that file exists and that it contains a header line | |
| 1316 | + if (access(filename.url().c_str(), R_OK) != 0) { | |
| 1338 | 1317 | |
| 1339 | - } // end of OMP critial zone | |
| 1318 | + // OpenMP critical zone to write header in case that the file does | |
| 1319 | + // not yet exist | |
| 1320 | + #pragma omp critical(GApplication_write_statistics) | |
| 1321 | + { | |
| 1340 | 1322 | |
| 1341 | - } // endif: no statistics file existed | |
| 1323 | + // Open statistics file, and in case of success, write header | |
| 1324 | + // line | |
| 1325 | + FILE* fptr = fopen(filename.url().c_str(), "w"); | |
| 1326 | + if (fptr != NULL) { | |
| 1327 | + fprintf(fptr, "Date," | |
| 1328 | + "GammaLib version," | |
| 1329 | + "Country," | |
| 1330 | + "Application," | |
| 1331 | + "Version," | |
| 1332 | + "Wall clock seconds," | |
| 1333 | + "CPU seconds," | |
| 1334 | + "g eCO2\n"); | |
| 1335 | + fclose(fptr); | |
| 1336 | + } | |
| 1342 | 1337 | |
| 1343 | - } // endif: managed to get user ID | |
| 1338 | + } // end of OMP critial zone | |
| 1339 | + | |
| 1340 | + } // endif: no statistics file existed | |
| 1344 | 1341 | |
| 1345 | 1342 | // OpenMP critical zone to write statitics |
| 1346 | 1343 | #pragma omp critical(GApplication_write_statistics) |
| ... | ... | @@ -1353,7 +1350,7 @@ void GApplication::write_statistics(void) |
| 1353 | 1350 | lock.l_start = 0; // No offset, lock entire file ... |
| 1354 | 1351 | lock.l_len = 0; // ... to the end |
| 1355 | 1352 | lock.l_pid = getpid(); // Current process ID |
| 1356 | - int fd = open(filename.c_str(), O_WRONLY); | |
| 1353 | + int fd = open(filename.url().c_str(), O_WRONLY); | |
| 1357 | 1354 | if (fd != -1) { |
| 1358 | 1355 | |
| 1359 | 1356 | // Lock file |
| ... | ... | @@ -1361,7 +1358,7 @@ void GApplication::write_statistics(void) |
| 1361 | 1358 | |
| 1362 | 1359 | // Open statistics file, and in case of success, write |
| 1363 | 1360 | // statistics |
| 1364 | - FILE* fptr = fopen(filename.c_str(), "a"); | |
| 1361 | + FILE* fptr = fopen(filename.url().c_str(), "a"); | |
| 1365 | 1362 | if (fptr != NULL) { |
| 1366 | 1363 | |
| 1367 | 1364 | // Get host country |
| ... | ... | @@ -1392,3 +1389,40 @@ void GApplication::write_statistics(void) |
| 1392 | 1389 | // Return |
| 1393 | 1390 | return; |
| 1394 | 1391 | } |
| 1392 | + | |
| 1393 | + | |
| 1394 | +/***********************************************************************//** | |
| 1395 | + * @brief Start GammaLib daemon | |
| 1396 | + * | |
| 1397 | + * Starts GammaLib daemon. The GammaLib daemon becomes an independent child | |
| 1398 | + * process, hence it will not affect the application that originally launched | |
| 1399 | + * the daemon. | |
| 1400 | + ***************************************************************************/ | |
| 1401 | +void GApplication::start_daemon(void) const | |
| 1402 | +{ | |
| 1403 | + // Initialise daemon | |
| 1404 | + GDaemon daemon; | |
| 1405 | + | |
| 1406 | + // If daemon is not alive then create instance | |
| 1407 | + if (!daemon.alive()) { | |
| 1408 | + | |
| 1409 | + // Create child process to start the daemon. Do nothing if child | |
| 1410 | + // process creation fails. | |
| 1411 | + int pid = fork(); | |
| 1412 | + if (pid >= 0) { | |
| 1413 | + | |
| 1414 | + // If we have a PID of 0 we are in the child process. In this | |
| 1415 | + // case we create and start the daemon ... | |
| 1416 | + if (pid == 0) { | |
| 1417 | + GDaemon daemon; | |
| 1418 | + daemon.start(); | |
| 1419 | + exit(0); | |
| 1420 | + } | |
| 1421 | + | |
| 1422 | + } // endif: child proces created | |
| 1423 | + | |
| 1424 | + } // endif: daemon | |
| 1425 | + | |
| 1426 | + // Return | |
| 1427 | + return; | |
| 1428 | +} | ... | ... |
src/support/GDaemon.cpp
0 → 100644
| 1 | +/*************************************************************************** | |
| 2 | + * GDaemon.cpp - Daemon class * | |
| 3 | + * ----------------------------------------------------------------------- * | |
| 4 | + * copyright (C) 2022 by Juergen Knoedlseder * | |
| 5 | + * ----------------------------------------------------------------------- * | |
| 6 | + * * | |
| 7 | + * This program is free software: you can redistribute it and/or modify * | |
| 8 | + * it under the terms of the GNU General Public License as published by * | |
| 9 | + * the Free Software Foundation, either version 3 of the License, or * | |
| 10 | + * (at your option) any later version. * | |
| 11 | + * * | |
| 12 | + * This program is distributed in the hope that it will be useful, * | |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| 15 | + * GNU General Public License for more details. * | |
| 16 | + * * | |
| 17 | + * You should have received a copy of the GNU General Public License * | |
| 18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. * | |
| 19 | + * * | |
| 20 | + ***************************************************************************/ | |
| 21 | +/** | |
| 22 | + * @file GDaemon.cpp | |
| 23 | + * @brief Daemon class implementation | |
| 24 | + * @author Juergen Knoedlseder | |
| 25 | + */ | |
| 26 | + | |
| 27 | +/* __ Includes ___________________________________________________________ */ | |
| 28 | +#ifdef HAVE_CONFIG_H | |
| 29 | +#include <config.h> | |
| 30 | +#endif | |
| 31 | +#include <cstdlib> // std::getenv() | |
| 32 | +#include <cstdio> // std::fopen() | |
| 33 | +#include <unistd.h> // sleep() | |
| 34 | +#include <fcntl.h> // for file locking | |
| 35 | +#include <signal.h> // kill() | |
| 36 | +#if defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) | |
| 37 | +#include <sys/prctl.h> // prctl(), PR_SET_NAME | |
| 38 | +#endif | |
| 39 | +#include "GDaemon.hpp" | |
| 40 | +#include "GCsv.hpp" | |
| 41 | +#include "GTime.hpp" | |
| 42 | +#include "GXml.hpp" | |
| 43 | +#include "GXmlNode.hpp" | |
| 44 | + | |
| 45 | +/* __ Method name definitions ____________________________________________ */ | |
| 46 | + | |
| 47 | +/* __ Macros _____________________________________________________________ */ | |
| 48 | + | |
| 49 | +/* __ Coding definitions _________________________________________________ */ | |
| 50 | + | |
| 51 | +/* __ Debug definitions __________________________________________________ */ | |
| 52 | + | |
| 53 | + | |
| 54 | +/*========================================================================== | |
| 55 | + = = | |
| 56 | + = Constructors/destructors = | |
| 57 | + = = | |
| 58 | + ==========================================================================*/ | |
| 59 | + | |
| 60 | +/***********************************************************************//** | |
| 61 | + * @brief Void constructor | |
| 62 | + ***************************************************************************/ | |
| 63 | +GDaemon::GDaemon(void) | |
| 64 | +{ | |
| 65 | + // Initialise class members | |
| 66 | + init_members(); | |
| 67 | + | |
| 68 | + // Return | |
| 69 | + return; | |
| 70 | +} | |
| 71 | + | |
| 72 | + | |
| 73 | +/***********************************************************************//** | |
| 74 | + * @brief Copy constructor | |
| 75 | + * | |
| 76 | + * @param[in] daemon Daemon. | |
| 77 | + ***************************************************************************/ | |
| 78 | +GDaemon::GDaemon(const GDaemon& daemon) | |
| 79 | +{ | |
| 80 | + // Initialise class members | |
| 81 | + init_members(); | |
| 82 | + | |
| 83 | + // Copy members | |
| 84 | + copy_members(daemon); | |
| 85 | + | |
| 86 | + // Return | |
| 87 | + return; | |
| 88 | +} | |
| 89 | + | |
| 90 | + | |
| 91 | +/***********************************************************************//** | |
| 92 | + * @brief Destructor | |
| 93 | + ***************************************************************************/ | |
| 94 | +GDaemon::~GDaemon(void) | |
| 95 | +{ | |
| 96 | + // Free members | |
| 97 | + free_members(); | |
| 98 | + | |
| 99 | + // Return | |
| 100 | + return; | |
| 101 | +} | |
| 102 | + | |
| 103 | + | |
| 104 | +/*========================================================================== | |
| 105 | + = = | |
| 106 | + = Operators = | |
| 107 | + = = | |
| 108 | + ==========================================================================*/ | |
| 109 | + | |
| 110 | +/***********************************************************************//** | |
| 111 | + * @brief Assignment operator | |
| 112 | + * | |
| 113 | + * @param[in] daemon Daemon. | |
| 114 | + * @return Daemon. | |
| 115 | + ***************************************************************************/ | |
| 116 | +GDaemon& GDaemon::operator=(const GDaemon& daemon) | |
| 117 | +{ | |
| 118 | + // Execute only if object is not identical | |
| 119 | + if (this != &daemon) { | |
| 120 | + | |
| 121 | + // Free members | |
| 122 | + free_members(); | |
| 123 | + | |
| 124 | + // Initialise private members | |
| 125 | + init_members(); | |
| 126 | + | |
| 127 | + // Copy members | |
| 128 | + copy_members(daemon); | |
| 129 | + | |
| 130 | + } // endif: object was not identical | |
| 131 | + | |
| 132 | + // Return this object | |
| 133 | + return *this; | |
| 134 | +} | |
| 135 | + | |
| 136 | + | |
| 137 | +/*========================================================================== | |
| 138 | + = = | |
| 139 | + = Public methods = | |
| 140 | + = = | |
| 141 | + ==========================================================================*/ | |
| 142 | + | |
| 143 | +/***********************************************************************//** | |
| 144 | + * @brief Clear Daemon | |
| 145 | + ***************************************************************************/ | |
| 146 | +void GDaemon::clear(void) | |
| 147 | +{ | |
| 148 | + // Free members | |
| 149 | + free_members(); | |
| 150 | + | |
| 151 | + // Initialise private members | |
| 152 | + init_members(); | |
| 153 | + | |
| 154 | + // Return | |
| 155 | + return; | |
| 156 | +} | |
| 157 | + | |
| 158 | + | |
| 159 | +/***********************************************************************//** | |
| 160 | + * @brief Clone Daemon | |
| 161 | + * | |
| 162 | + * @return Pointer to deep copy of Daemon. | |
| 163 | + ***************************************************************************/ | |
| 164 | +GDaemon* GDaemon::clone(void) const | |
| 165 | +{ | |
| 166 | + return new GDaemon(*this); | |
| 167 | +} | |
| 168 | + | |
| 169 | + | |
| 170 | +/***********************************************************************//** | |
| 171 | + * @brief Starts the daemon | |
| 172 | + * | |
| 173 | + * Starts up the daemon and entered the never-ending daemon event loop. | |
| 174 | + ***************************************************************************/ | |
| 175 | +void GDaemon::start(void) | |
| 176 | +{ | |
| 177 | + // Open logger | |
| 178 | + m_log.open(gammalib::gamma_filename("daemon.log"), true); | |
| 179 | + m_log.chatter((GChatter)m_chatter); | |
| 180 | + m_log.date(true); | |
| 181 | + | |
| 182 | + // Log start up of daemon | |
| 183 | + m_log << "GammaLib daemon start up" << std::endl; | |
| 184 | + | |
| 185 | + // Set daemon name | |
| 186 | + #if defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) | |
| 187 | + prctl(PR_SET_NAME, "gammalibd"); | |
| 188 | + #endif | |
| 189 | + | |
| 190 | + // Create lock file | |
| 191 | + create_lock_file(); | |
| 192 | + | |
| 193 | + // Main event handling loop | |
| 194 | + while (1) { | |
| 195 | + | |
| 196 | + // Update application statistics | |
| 197 | + update_statistics(); | |
| 198 | + | |
| 199 | + // Force logger flushing | |
| 200 | + m_log.flush(true); | |
| 201 | + | |
| 202 | + // Wait for some period | |
| 203 | + sleep(m_period); | |
| 204 | + | |
| 205 | + } // endwhile: main event loop | |
| 206 | + | |
| 207 | + // Delete lock file | |
| 208 | + delete_lock_file(); | |
| 209 | + | |
| 210 | + // Close log file | |
| 211 | + m_log.close(); | |
| 212 | + | |
| 213 | + // Return | |
| 214 | + return; | |
| 215 | +} | |
| 216 | + | |
| 217 | + | |
| 218 | +/***********************************************************************//** | |
| 219 | + * @brief Check if daemon is alive | |
| 220 | + * | |
| 221 | + * @return True if daemon is alive | |
| 222 | + * | |
| 223 | + * Checks if the daemon is alive. The daemon is alive if a lock file exists | |
| 224 | + * and if the process with the PID that is found in the lock file is alive. | |
| 225 | + * | |
| 226 | + * This method can be used by a client to check whether a daemon is alive. | |
| 227 | + ***************************************************************************/ | |
| 228 | +bool GDaemon::alive(void) const | |
| 229 | +{ | |
| 230 | + // Initialise flag | |
| 231 | + bool alive = false; | |
| 232 | + | |
| 233 | + // Get process ID in lock file | |
| 234 | + pid_t pid = lock_pid(); | |
| 235 | + | |
| 236 | + // If process ID is positive then check if process is still alive | |
| 237 | + if (pid > 0) { | |
| 238 | + | |
| 239 | + // Check if process is alive | |
| 240 | + if (0 == kill(pid, 0)) { | |
| 241 | + alive = true; | |
| 242 | + } | |
| 243 | + | |
| 244 | + } // endif: process ID was positive | |
| 245 | + | |
| 246 | + // Return flag | |
| 247 | + return alive; | |
| 248 | +} | |
| 249 | + | |
| 250 | + | |
| 251 | +/***********************************************************************//** | |
| 252 | + * @brief Print Daemon | |
| 253 | + * | |
| 254 | + * @param[in] chatter Chattiness. | |
| 255 | + * @return String containing Daemon information. | |
| 256 | + ***************************************************************************/ | |
| 257 | +std::string GDaemon::print(const GChatter& chatter) const | |
| 258 | +{ | |
| 259 | + // Initialise result string | |
| 260 | + std::string result; | |
| 261 | + | |
| 262 | + // Continue only if chatter is not silent | |
| 263 | + if (chatter != SILENT) { | |
| 264 | + | |
| 265 | + // Append header | |
| 266 | + result.append("=== GDaemon ==="); | |
| 267 | + | |
| 268 | + // Append information | |
| 269 | + result.append("\n"+gammalib::parformat("Process ID")); | |
| 270 | + result.append(gammalib::str(m_pid)); | |
| 271 | + result.append("\n"+gammalib::parformat("Wake up period (s)")); | |
| 272 | + result.append(gammalib::str(m_period)); | |
| 273 | + result.append("\n"+gammalib::parformat("Logger chatter level")); | |
| 274 | + result.append(gammalib::str(m_chatter)); | |
| 275 | + | |
| 276 | + } // endif: chatter was not silent | |
| 277 | + | |
| 278 | + // Return result | |
| 279 | + return result; | |
| 280 | +} | |
| 281 | + | |
| 282 | + | |
| 283 | +/*========================================================================== | |
| 284 | + = = | |
| 285 | + = Private methods = | |
| 286 | + = = | |
| 287 | + ==========================================================================*/ | |
| 288 | + | |
| 289 | +/***********************************************************************//** | |
| 290 | + * @brief Initialise class members | |
| 291 | + ***************************************************************************/ | |
| 292 | +void GDaemon::init_members(void) | |
| 293 | +{ | |
| 294 | + // Initialise members | |
| 295 | + m_pid = 0; //!< No process ID | |
| 296 | + m_period = 3600.0; //!< Wake-up daemon every hour | |
| 297 | + m_log.clear(); | |
| 298 | + m_chatter = NORMAL; //!< NORMAL chatter level | |
| 299 | + | |
| 300 | + // Return | |
| 301 | + return; | |
| 302 | +} | |
| 303 | + | |
| 304 | + | |
| 305 | +/***********************************************************************//** | |
| 306 | + * @brief Copy class members | |
| 307 | + * | |
| 308 | + * @param[in] daemon Daemon. | |
| 309 | + ***************************************************************************/ | |
| 310 | +void GDaemon::copy_members(const GDaemon& daemon) | |
| 311 | +{ | |
| 312 | + // Copy members | |
| 313 | + m_pid = daemon.m_pid; | |
| 314 | + m_period = daemon.m_period; | |
| 315 | + m_log = daemon.m_log; | |
| 316 | + m_chatter = daemon.m_chatter; | |
| 317 | + | |
| 318 | + // Return | |
| 319 | + return; | |
| 320 | +} | |
| 321 | + | |
| 322 | + | |
| 323 | +/***********************************************************************//** | |
| 324 | + * @brief Delete class members | |
| 325 | + ***************************************************************************/ | |
| 326 | +void GDaemon::free_members(void) | |
| 327 | +{ | |
| 328 | + // Delete lock file | |
| 329 | + delete_lock_file(); | |
| 330 | + | |
| 331 | + // Return | |
| 332 | + return; | |
| 333 | +} | |
| 334 | + | |
| 335 | + | |
| 336 | +/***********************************************************************//** | |
| 337 | + * @brief Create the lock file | |
| 338 | + * | |
| 339 | + * Creates the daemon lock file and write process ID into lock file. | |
| 340 | + ***************************************************************************/ | |
| 341 | +void GDaemon::create_lock_file(void) | |
| 342 | +{ | |
| 343 | + // Get lock filename | |
| 344 | + GFilename lockfile = lock_filename(); | |
| 345 | + | |
| 346 | + // Open lock file. Continue only if opening was successful. | |
| 347 | + FILE* fptr = fopen(lockfile.url().c_str(), "w"); | |
| 348 | + | |
| 349 | + // Successful? | |
| 350 | + if (fptr != NULL) { | |
| 351 | + | |
| 352 | + // Get process ID | |
| 353 | + m_pid = getpid(); | |
| 354 | + | |
| 355 | + // Write process ID into lockfile | |
| 356 | + fprintf(fptr, "%d\n", m_pid); | |
| 357 | + | |
| 358 | + // Close lockfile | |
| 359 | + fclose(fptr); | |
| 360 | + | |
| 361 | + // Log creation of lock file | |
| 362 | + m_log << "Created lock file \"" << lockfile.url(); | |
| 363 | + m_log << "\" for PID " << m_pid << std::endl; | |
| 364 | + | |
| 365 | + } // endif: Lock file opened successfully | |
| 366 | + | |
| 367 | + // Return | |
| 368 | + return; | |
| 369 | +} | |
| 370 | + | |
| 371 | + | |
| 372 | +/***********************************************************************//** | |
| 373 | + * @brief Delete daemon lock file | |
| 374 | + * | |
| 375 | + * Deletes the daemon lock file on disk if the process ID in the lock file | |
| 376 | + * corresponds to the process ID of the instance. | |
| 377 | + ***************************************************************************/ | |
| 378 | +void GDaemon::delete_lock_file(void) const | |
| 379 | +{ | |
| 380 | + // If process ID in lock file is the ID of the current process then | |
| 381 | + // delete the lock file | |
| 382 | + if (lock_pid() == getpid()) { | |
| 383 | + | |
| 384 | + // Get lock filename | |
| 385 | + GFilename lockfile = lock_filename(); | |
| 386 | + | |
| 387 | + // Delete lock file | |
| 388 | + std::remove(lockfile.url().c_str()); | |
| 389 | + | |
| 390 | + } | |
| 391 | + | |
| 392 | + // Return | |
| 393 | + return; | |
| 394 | +} | |
| 395 | + | |
| 396 | + | |
| 397 | +/***********************************************************************//** | |
| 398 | + * @brief Returns process ID in lock file | |
| 399 | + * | |
| 400 | + * @return Process ID in lock file. | |
| 401 | + * | |
| 402 | + * Returns the process ID that is found in the lock file. If no lock file | |
| 403 | + * exists the method returns 0. | |
| 404 | + ***************************************************************************/ | |
| 405 | +pid_t GDaemon::lock_pid(void) const | |
| 406 | +{ | |
| 407 | + // Initialis process ID | |
| 408 | + pid_t pid = 0; | |
| 409 | + | |
| 410 | + // Get lock filename | |
| 411 | + GFilename lockfile = lock_filename(); | |
| 412 | + | |
| 413 | + // Open lock file. Continue only if opening was successful. | |
| 414 | + FILE* fptr = fopen(lockfile.url().c_str(), "r"); | |
| 415 | + if (fptr != NULL) { | |
| 416 | + | |
| 417 | + // Allocate line buffer | |
| 418 | + const int n = 1000; | |
| 419 | + char line[n]; | |
| 420 | + | |
| 421 | + // Read line | |
| 422 | + fgets(line, n-1, fptr); | |
| 423 | + | |
| 424 | + // Close file | |
| 425 | + fclose(fptr); | |
| 426 | + | |
| 427 | + // Extract process ID | |
| 428 | + pid = gammalib::toint(std::string(line)); | |
| 429 | + | |
| 430 | + } // endif: lock file opened successfully | |
| 431 | + | |
| 432 | + // Return process ID | |
| 433 | + return pid; | |
| 434 | +} | |
| 435 | + | |
| 436 | + | |
| 437 | +/***********************************************************************//** | |
| 438 | + * @brief Update application statistics | |
| 439 | + * | |
| 440 | + * Update application statistics in the @p statistics.xml file by scanning | |
| 441 | + * the low-level file @p statistics.csv. | |
| 442 | + * | |
| 443 | + * @todo Delete low-level file if scan was successful. | |
| 444 | + ***************************************************************************/ | |
| 445 | +void GDaemon::update_statistics(void) | |
| 446 | +{ | |
| 447 | + // Set low-level statistics filename | |
| 448 | + GFilename filename = gammalib::gamma_filename("statistics.csv"); | |
| 449 | + | |
| 450 | + // Continue only if file exists | |
| 451 | + if (filename.exists()) { | |
| 452 | + | |
| 453 | + // OpenMP critical zone to write statitics | |
| 454 | + #pragma omp critical(GDaemon_update_statistics) | |
| 455 | + { | |
| 456 | + | |
| 457 | + // Get file lock. Continue only in case of success. | |
| 458 | + struct flock lock; | |
| 459 | + lock.l_type = F_RDLCK; // Want a read lock | |
| 460 | + lock.l_whence = SEEK_SET; // Want beginning of file | |
| 461 | + lock.l_start = 0; // No offset, lock entire file ... | |
| 462 | + lock.l_len = 0; // ... to the end | |
| 463 | + lock.l_pid = getpid(); // Current process ID | |
| 464 | + int fd = open(filename.url().c_str(), O_RDONLY); | |
| 465 | + if (fd != -1) { | |
| 466 | + | |
| 467 | + // Lock file | |
| 468 | + fcntl(fd, F_SETLKW, &lock); | |
| 469 | + | |
| 470 | + // Load CSV file | |
| 471 | + GCsv statistics(filename, ","); | |
| 472 | + | |
| 473 | + // Update high-level statistics | |
| 474 | + update_high_level_statistics(statistics); | |
| 475 | + | |
| 476 | + // Remove low-level statistics file | |
| 477 | + std::remove(filename.url().c_str()); | |
| 478 | + | |
| 479 | + // Unlock file | |
| 480 | + lock.l_type = F_UNLCK; | |
| 481 | + fcntl(fd, F_SETLK, &lock); | |
| 482 | + | |
| 483 | + // Close file | |
| 484 | + close(fd); | |
| 485 | + | |
| 486 | + } // endif: file locking successful | |
| 487 | + | |
| 488 | + } // end of OMP critial zone | |
| 489 | + | |
| 490 | + } // endif: there was a statistics ASCII file | |
| 491 | + | |
| 492 | + // Return | |
| 493 | + return; | |
| 494 | +} | |
| 495 | + | |
| 496 | + | |
| 497 | +/***********************************************************************//** | |
| 498 | + * @brief Update high-level statistics | |
| 499 | + * | |
| 500 | + * Update high-level statistics by scanning the low-level statistics file. | |
| 501 | + * The high-level statistics is written out as XML file. If no XML file | |
| 502 | + * exists a new one is created, otherwise an existing file is updated. | |
| 503 | + ***************************************************************************/ | |
| 504 | +void GDaemon::update_high_level_statistics(const GCsv& statistics) | |
| 505 | +{ | |
| 506 | + // Log updating of high-level statistics | |
| 507 | + m_log << "Update high-level statistics" << std::endl; | |
| 508 | + | |
| 509 | + // Set current time | |
| 510 | + GTime now; | |
| 511 | + now.now(); | |
| 512 | + | |
| 513 | + // Set high-level statistics filename | |
| 514 | + GFilename filename = gammalib::gamma_filename("statistics.xml"); | |
| 515 | + | |
| 516 | + // Initialise high-level statistics | |
| 517 | + GXml xml; | |
| 518 | + | |
| 519 | + // If a high-level statistics file exists already then load information | |
| 520 | + // from the file | |
| 521 | + if (filename.exists()) { | |
| 522 | + xml.load(filename); | |
| 523 | + } | |
| 524 | + | |
| 525 | + // ... otherwise create empty high-level statistics file | |
| 526 | + else { | |
| 527 | + | |
| 528 | + // Append base | |
| 529 | + GXmlNode* base = xml.append(GXmlElement("statistics title=\"High-level statistic\"")); | |
| 530 | + | |
| 531 | + // Append header | |
| 532 | + GXmlNode* header = base->append("header"); | |
| 533 | + GXmlNode* dates = header->append("dates"); | |
| 534 | + GXmlNode* d1 = dates->append("creation"); | |
| 535 | + d1->append(GXmlText(now.utc())); | |
| 536 | + GXmlNode* d2 = dates->append("modified"); | |
| 537 | + d2->append(GXmlText(now.utc())); | |
| 538 | + GXmlNode* d3 = dates->append("start"); | |
| 539 | + d3->append(GXmlText("")); | |
| 540 | + GXmlNode* d4 = dates->append("stop"); | |
| 541 | + d4->append(GXmlText("")); | |
| 542 | + | |
| 543 | + // Append data | |
| 544 | + GXmlNode* data = base->append("data"); | |
| 545 | + GXmlNode* daily = data->append("daily"); | |
| 546 | + | |
| 547 | + // Log creation of high-level statistics file | |
| 548 | + m_log << "Created high-level statistics XML file \""; | |
| 549 | + m_log << filename.url() << "\"" << std::endl; | |
| 550 | + | |
| 551 | + } // endelse: created empty high-level statistics file | |
| 552 | + | |
| 553 | + // Get some useful pointers | |
| 554 | + GXmlNode* base = xml.element("statistics",0); | |
| 555 | + GXmlNode* header = base->element("header",0); | |
| 556 | + GXmlNode* data = base->element("data",0); | |
| 557 | + GXmlNode* daily = data->element("daily",0); | |
| 558 | + | |
| 559 | + // Loop over statistics | |
| 560 | + for (int i = 0; i < statistics.nrows(); ++i) { | |
| 561 | + | |
| 562 | + // Skip header row | |
| 563 | + if (i == 0) { | |
| 564 | + continue; | |
| 565 | + } | |
| 566 | + | |
| 567 | + // Extract relevant attributes | |
| 568 | + std::string date = statistics.string(i,0).substr(0,10); | |
| 569 | + std::string tool = statistics.string(i,3); | |
| 570 | + std::string version = statistics.string(i,4); | |
| 571 | + double wall = statistics.real(i,5); | |
| 572 | + double cpu = statistics.real(i,6); | |
| 573 | + double gCO2e = statistics.real(i,7); | |
| 574 | + | |
| 575 | + // Skip tools without name | |
| 576 | + if (tool.empty()) { | |
| 577 | + continue; | |
| 578 | + } | |
| 579 | + | |
| 580 | + // Get pointer to relevant date element. If no date element exists | |
| 581 | + // that corresponds to the date of the statistics then append an | |
| 582 | + // element | |
| 583 | + GXmlNode* n_date = NULL; | |
| 584 | + int ndates = daily->elements("date"); | |
| 585 | + if (ndates == 0) { | |
| 586 | + n_date = daily->append("date"); | |
| 587 | + n_date->append(GXmlElement("value", date)); | |
| 588 | + } | |
| 589 | + else { | |
| 590 | + for (int k = 0; k < ndates; ++k) { | |
| 591 | + GXmlElement* n = daily->element("date", k); | |
| 592 | + if (daily->element("date", k)->element("value",0)->value() == date) { | |
| 593 | + n_date = n; | |
| 594 | + break; | |
| 595 | + } | |
| 596 | + } | |
| 597 | + if (n_date == NULL) { | |
| 598 | + n_date = daily->append("date"); | |
| 599 | + n_date->append(GXmlElement("value", date)); | |
| 600 | + } | |
| 601 | + } | |
| 602 | + | |
| 603 | + // Get pointer to "tools" node. If no such node exists then append | |
| 604 | + // one | |
| 605 | + GXmlNode* n_tools = NULL; | |
| 606 | + if (n_date->elements("tools") == 0) { | |
| 607 | + n_tools = n_date->append("tools"); | |
| 608 | + } | |
| 609 | + else { | |
| 610 | + n_tools = n_date->element("tools",0); | |
| 611 | + } | |
| 612 | + | |
| 613 | + // Get pointer to relevant tool element. If no tool element exists | |
| 614 | + // that corresponds to the relevant tool then append an element | |
| 615 | + GXmlElement* n_tool = NULL; | |
| 616 | + int ntools = n_tools->elements(); | |
| 617 | + if (ntools == 0) { | |
| 618 | + n_tool = n_tools->append(tool); | |
| 619 | + } | |
| 620 | + else { | |
| 621 | + for (int k = 0; k < ntools; ++k) { | |
| 622 | + GXmlElement* n = n_tools->element(k); | |
| 623 | + if (n->name() == tool) { | |
| 624 | + n_tool = n; | |
| 625 | + break; | |
| 626 | + } | |
| 627 | + } | |
| 628 | + if (n_tool == NULL) { | |
| 629 | + n_tool = n_tools->append(tool); | |
| 630 | + } | |
| 631 | + } | |
| 632 | + | |
| 633 | + // Set or update attributes | |
| 634 | + int calls = 1; | |
| 635 | + if (!n_tool->has_attribute("version")) { | |
| 636 | + n_tool->attribute("version", version); | |
| 637 | + } | |
| 638 | + if (n_tool->has_attribute("calls")) { | |
| 639 | + calls += gammalib::toint(n_tool->attribute("calls")); | |
| 640 | + } | |
| 641 | + if (n_tool->has_attribute("wall")) { | |
| 642 | + wall += gammalib::todouble(n_tool->attribute("wall")); | |
| 643 | + } | |
| 644 | + if (n_tool->has_attribute("cpu")) { | |
| 645 | + cpu += gammalib::todouble(n_tool->attribute("cpu")); | |
| 646 | + } | |
| 647 | + if (n_tool->has_attribute("gCO2e")) { | |
| 648 | + gCO2e += gammalib::todouble(n_tool->attribute("gCO2e")); | |
| 649 | + } | |
| 650 | + n_tool->attribute("calls", gammalib::str(calls)); | |
| 651 | + n_tool->attribute("wall", gammalib::str(wall)); | |
| 652 | + n_tool->attribute("cpu", gammalib::str(cpu)); | |
| 653 | + n_tool->attribute("gCO2e", gammalib::str(gCO2e)); | |
| 654 | + | |
| 655 | + } // endfor: looped over statistics | |
| 656 | + | |
| 657 | + // Update modified time in header | |
| 658 | + GXmlNode* modified = header->element("dates > modified"); | |
| 659 | + static_cast<GXmlText*>((*modified)[0])->text(now.utc()); | |
| 660 | + | |
| 661 | + // Update first and last time in header | |
| 662 | + std::string start; | |
| 663 | + std::string stop; | |
| 664 | + if (statistics.nrows() > 1) { | |
| 665 | + start = statistics.string(1,0); | |
| 666 | + stop = statistics.string(statistics.nrows()-1,0); | |
| 667 | + GXmlNode* n_start = header->element("dates > start"); | |
| 668 | + GXmlNode* n_stop = header->element("dates > stop"); | |
| 669 | + std::string current_start = static_cast<GXmlText*>((*n_start)[0])->text(); | |
| 670 | + std::string current_stop = static_cast<GXmlText*>((*n_stop)[0])->text(); | |
| 671 | + if (current_start.empty() || (start < current_start)) { | |
| 672 | + static_cast<GXmlText*>((*n_start)[0])->text(start); | |
| 673 | + } | |
| 674 | + if (current_stop.empty() || (stop > current_stop)) { | |
| 675 | + static_cast<GXmlText*>((*n_stop)[0])->text(stop); | |
| 676 | + } | |
| 677 | + } | |
| 678 | + | |
| 679 | + // Save high-level statistics file | |
| 680 | + xml.save(filename); | |
| 681 | + | |
| 682 | + // Return | |
| 683 | + return; | |
| 684 | +} | ... | ... |
src/support/GTools.cpp
| ... | ... | @@ -28,7 +28,7 @@ |
| 28 | 28 | #ifdef HAVE_CONFIG_H |
| 29 | 29 | #include <config.h> |
| 30 | 30 | #endif |
| 31 | -#include <unistd.h> // access() functions | |
| 31 | +#include <unistd.h> // geteuid(), access() functions | |
| 32 | 32 | #include <fcntl.h> // fcntl() functions |
| 33 | 33 | #include <sys/stat.h> |
| 34 | 34 | #include <sys/time.h> // timeval |
| ... | ... | @@ -37,7 +37,7 @@ |
| 37 | 37 | #include <sys/socket.h> // recv() function |
| 38 | 38 | #include <netinet/in.h> // struct sockaddr_in, struct sockaddr |
| 39 | 39 | #include <netdb.h> // struct hostent, gethostbyname |
| 40 | -#include <pwd.h> | |
| 40 | +#include <pwd.h> // getpwuid() | |
| 41 | 41 | #include <cmath> |
| 42 | 42 | #include <cfloat> |
| 43 | 43 | #include <cctype> |
| ... | ... | @@ -2331,3 +2331,43 @@ std::string gammalib::host_country(void) |
| 2331 | 2331 | // Return country |
| 2332 | 2332 | return country; |
| 2333 | 2333 | } |
| 2334 | + | |
| 2335 | + | |
| 2336 | +/***********************************************************************//** | |
| 2337 | + * @brief Returns filename in .gamma directory | |
| 2338 | + * | |
| 2339 | + * @param[in] name Name of file in .gamma directory. | |
| 2340 | + * @return Filename in .gamma directory. | |
| 2341 | + * | |
| 2342 | + * Returns the filename of the name of a file in the .gamma directory. | |
| 2343 | + ***************************************************************************/ | |
| 2344 | +GFilename gammalib::gamma_filename(const std::string& name) | |
| 2345 | +{ | |
| 2346 | + // Initialise lock filename | |
| 2347 | + GFilename filename; | |
| 2348 | + | |
| 2349 | + // First try accessing the user ID to get the home directory | |
| 2350 | + uid_t uid = geteuid(); | |
| 2351 | + struct passwd* pw = getpwuid(uid); | |
| 2352 | + if (pw != NULL) { | |
| 2353 | + filename = std::string(pw->pw_dir) + "/.gamma/" + name; | |
| 2354 | + } | |
| 2355 | + | |
| 2356 | + // ... otherwise try using the $HOME environment variable | |
| 2357 | + else { | |
| 2358 | + | |
| 2359 | + // First attempt fetching $HOME environment variable | |
| 2360 | + char* home_ptr = std::getenv("HOME"); | |
| 2361 | + if (home_ptr != NULL) { | |
| 2362 | + filename = std::string(home_ptr) + "/.gamma/" + name; | |
| 2363 | + } | |
| 2364 | + | |
| 2365 | + // ... otherwise use ~ as home directory | |
| 2366 | + else { | |
| 2367 | + filename = "~/.gamma/" + name; | |
| 2368 | + } | |
| 2369 | + } | |
| 2370 | + | |
| 2371 | + // Return filename | |
| 2372 | + return filename; | |
| 2373 | +} | ... | ... |
src/support/Makefile.am
| 1 | 1 | ############################################################################# |
| 2 | 2 | # Makefile.am for support module # |
| 3 | 3 | # ------------------------------------------------------------------------- # |
| 4 | -# Copyright (C) 2016-2021 Juergen Knoedlseder # | |
| 4 | +# Copyright (C) 2016-2022 Juergen Knoedlseder # | |
| 5 | 5 | # ------------------------------------------------------------------------- # |
| 6 | 6 | # # |
| 7 | 7 | # This program is free software: you can redistribute it and/or modify # |
| ... | ... | @@ -36,7 +36,9 @@ sources = GException.cpp \ |
| 36 | 36 | GUrl.cpp \ |
| 37 | 37 | GUrlFile.cpp \ |
| 38 | 38 | GUrlString.cpp \ |
| 39 | - GFilename.cpp | |
| 39 | + GFilename.cpp \ | |
| 40 | + GDaemon.cpp | |
| 41 | + | |
| 40 | 42 | |
| 41 | 43 | # Build libtool library |
| 42 | 44 | noinst_LTLIBRARIES = libsupport.la | ... | ... |