LCOV - code coverage report
Current view: top level - apps/sysMonitor - sysMonitor.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 19.8 % 650 129
Test Date: 2026-01-03 21:03:39 Functions: 22.5 % 40 9

            Line data    Source code
       1              : /** \file sysMonitor.hpp
       2              :   * \brief The MagAO-X sysMonitor app main program which provides functions to read and report system statistics
       3              :   * \author Chris Bohlman (cbohlman@pm.me)
       4              :   *
       5              :   * To view logdump files: logdump -f sysMonitor
       6              :   *
       7              :   * To view sysMonitor with cursesIndi:
       8              :   * 1. /opt/MagAOX/bin/xindiserver -n xindiserverMaths
       9              :   * 2. /opt/MagAOX/bin/sysMonitor -n sysMonitor
      10              :   * 3. /opt/MagAOX/bin/cursesINDI
      11              :   *
      12              :   * \ingroup sysMonitor_files
      13              :   *
      14              :   * History:
      15              :   * - 2018-08-10 created by CJB
      16              :   */
      17              : #ifndef sysMonitor_hpp
      18              : #define sysMonitor_hpp
      19              : 
      20              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      21              : #include "../../magaox_git_version.h"
      22              : 
      23              : #include <iostream>
      24              : #include <string>
      25              : #include <fstream>
      26              : #include <vector>
      27              : #include <sstream>
      28              : #include <algorithm>
      29              : #include <iterator>
      30              : #include <sys/wait.h>
      31              : 
      32              : 
      33              : namespace MagAOX
      34              : {
      35              : namespace app
      36              : {
      37              : 
      38              : /** MagAO-X application to read and report system statistics
      39              :   *
      40              :   */
      41              : class sysMonitor : public MagAOXApp<>, public dev::telemeter<sysMonitor>
      42              : {
      43              : 
      44              :    friend class dev::telemeter<sysMonitor>;
      45              : 
      46              :    enum class sysType
      47              :    {
      48              :       Intel,
      49              :       AMD
      50              :    };
      51              : 
      52              : protected:
      53              : 
      54              :    sysType m_sysType{ sysType::Intel };
      55              : 
      56              :    int m_warningCoreTemp = 0;   ///< User defined warning temperature for CPU cores
      57              :    int m_criticalCoreTemp = 0;   ///< User defined critical temperature for CPU cores
      58              :    int m_warningDiskTemp = 0;   ///< User defined warning temperature for drives
      59              :    int m_criticalDiskTemp = 0;   ///< User defined critical temperature for drives
      60              : 
      61              :    pcf::IndiProperty m_indiP_core_loads;   ///< Indi variable for reporting CPU core loads
      62              :    pcf::IndiProperty m_indiP_core_temps;   ///< Indi variable for reporting CPU core temperature(s)
      63              :    pcf::IndiProperty m_indiP_drive_temps;   ///< Indi variable for reporting drive temperature(s)
      64              :    pcf::IndiProperty m_indiP_usage;   ///< Indi variable for reporting drive usage of all paths
      65              : 
      66              :    std::vector<float> m_coreTemps;   ///< List of current core temperature(s)
      67              :    std::vector<float> m_coreLoads;   ///< List of current core load(s)
      68              : 
      69              :    std::vector<std::string> m_diskNameList; ///< vector of names of the hard disks to monitor
      70              :    std::vector<std::string> m_diskNames; ///< vector of names of the hard disks returned by hdd_temp
      71              :    std::vector<float> m_diskTemps;        ///< vector of current disk temperature(s)
      72              : 
      73              :    float m_rootUsage = 0;   ///< Disk usage in root path as a value out of 100
      74              :    float m_dataUsage = 0;   ///< Disk usage in /data path as a value out of 100
      75              :    float m_bootUsage = 0;   ///< Disk usage in /boot path as a value out of 100
      76              :    float m_ramUsage = 0;   ///< RAM usage as a decimal value between 0 and 1
      77              : 
      78              :    /// Updates Indi property values of all system statistics
      79              :    /** This includes updating values for core loads, core temps, drive temps, / usage, /boot usage, /data usage, and RAM usage
      80              :      * Unsure if this method can fail in any way, as of now always returns 0
      81              :      *
      82              :      * \TODO: Check to see if any method called in here can fail
      83              :      * \returns 0 on completion
      84              :      */
      85              :    int updateVals();
      86              : 
      87              : public:
      88              : 
      89              :    /// Default c'tor.
      90              :    sysMonitor();
      91              : 
      92              :    /// D'tor, declared and defined for noexcept.
      93           33 :    ~sysMonitor() noexcept
      94           33 :    {
      95           33 :    }
      96              : 
      97              :    /// Setup the user-defined warning and critical values for core and drive temperatures
      98              :    virtual void setupConfig();
      99              : 
     100              :    /// Load the warning and critical temperature values for core and drive temperatures
     101              :    virtual void loadConfig();
     102              : 
     103              :    /// Registers all new Indi properties for each of the reported values to publish
     104              :    virtual int appStartup();
     105              : 
     106              :    /// Implementation of reading and logging each of the measured statistics
     107              :    virtual int appLogic();
     108              : 
     109              :    /// Do any needed shutdown tasks; currently nothing in this app
     110              :    virtual int appShutdown();
     111              : 
     112              : 
     113              :    /// Finds all CPU core temperatures
     114              :    /** Makes system call and then parses result to add temperatures to vector of values
     115              :      *
     116              :      * \returns -1 on error with system command or output reading
     117              :      * \returns 0 on successful completion otherwise
     118              :      */
     119              :    int findCPUTemperatures(std::vector<float>&  /**< [out] the vector of measured CPU core temperatures*/);
     120              : 
     121              : 
     122              :    /// Parses string from system call to find CPU temperatures
     123              :    /** When a valid string is read in, the value from that string is stored.
     124              :      * This calls the appropriate function based on m_sysType.
     125              :      *
     126              :      * \returns -1 on invalid string being read in
     127              :      * \returns 0 on completion and storing of value
     128              :      */
     129              :    int parseCPUTemperatures( float& temp,            /// [out] the return value from the string
     130              :                              const std::string& line /// [in] the string to be parsed
     131              :                            );
     132              : 
     133              :    /// Parses string from system call to find CPU temperatures on an Intel system
     134              :    /** When a valid string is read in, the value from that string is stored
     135              :      *
     136              :      * \returns -1 on invalid string being read in
     137              :      * \returns 0 on completion and storing of value
     138              :      */
     139              :    int parseCPUTemperaturesIntel( float& temp,            /// [out] the return value from the string
     140              :                                   const std::string& line /// [in] the string to be parsed
     141              :                                 );
     142              : 
     143              :    /// Parses string from system call to find CPU temperatures on an AMD system
     144              :    /** When a valid string is read in, the value from that string is stored
     145              :      *
     146              :      * \returns -1 on invalid string being read in
     147              :      * \returns 0 on completion and storing of value
     148              :      */
     149              :    int parseCPUTemperaturesAMD( float& temp,            /// [out] the return value from the string
     150              :                                 const std::string& line /// [in] the string to be parsed
     151              :                               );
     152              : 
     153              : 
     154              :    /// Checks if any core temperatures are warning or critical levels
     155              :    /** Warning and critical temperatures are either user-defined or generated based on initial core temperature values
     156              :      *
     157              :      * \returns 1 if a temperature value is at the warning level
     158              :      * \returns 2 if a temperature value is at critical level
     159              :      * \returns 0 otherwise (all temperatures are considered normal)
     160              :      */
     161              :    int criticalCoreTemperature( std::vector<float>&   /**< [in] the vector of temperature values to be checked*/);
     162              : 
     163              :    /// Finds all CPU core usage loads
     164              :    /** Makes system call and then parses result to add usage loads to vector of values
     165              :      *
     166              :      * \returns -1 on error with system command or output file reading
     167              :      * \returns 0 on completion
     168              :      */
     169              :    int findCPULoads( std::vector<float>&   /**< [out] the vector of measured CPU usages*/);
     170              : 
     171              : 
     172              :    /// Parses string from system call to find CPU usage loads
     173              :    /** When a valid string is read in, the value from that string is stored
     174              :      *
     175              :      * \returns -1 on invalid string being read in
     176              :      * \returns 0 on completion and storing of value
     177              :      */
     178              :    int parseCPULoads( float &,            ///< [out] the return value from the string
     179              :                       const std::string & ///< [in] the string to be parsed
     180              :                     );
     181              : 
     182              : 
     183              :    /// Finds all drive temperatures
     184              :    /** Makes 'hddtemp' system call and then parses result to add temperatures to vector of values
     185              :      * For hard drive temp utility:
     186              :      * `wget http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/h/hddtemp-0.3-0.31.beta15.el7.x86_64.rpm`
     187              :      * `su`
     188              :      * `rpm -Uvh hddtemp-0.3-0.31.beta15.el7.x86_64.rpm`
     189              :      * Check install with rpm -q -a | grep -i hddtemp
     190              :      *
     191              :      * \returns -1 on error with system command or output reading
     192              :      * \returns 0 on successful completion otherwise
     193              :      */
     194              :    int findDiskTemperature( std::vector<std::string>& hdd_names, ///< [out] the names of the drives reported by hddtemp
     195              :                             std::vector<float>& hdd_temps        ///< [out] the vector of measured drive temperatures
     196              :                           );
     197              : 
     198              : 
     199              :    /// Parses string from system call to find drive temperatures
     200              :    /** When a valid string is read in, the drive name and value from that string is stored
     201              :      *
     202              :      * \returns -1 on invalid string being read in
     203              :      * \returns 0 on completion and storing of value
     204              :      */
     205              :    int parseDiskTemperature( std::string& driveName, ///< [out] the name of the drive
     206              :                              float& temp,            ///< [out] the return value from the string
     207              :                              const std::string& line ///< [in] the string to be parsed
     208              :                            );
     209              : 
     210              : 
     211              :    /// Checks if any drive temperatures are warning or critical levels
     212              :    /** Warning and critical temperatures are either user-defined or generated based on initial drive temperature values
     213              :      *
     214              :      * \returns 1 if a temperature value is at the warning level
     215              :      * \returns 2 if a temperature value is at critical level
     216              :      * \returns 0 otherwise (all temperatures are considered normal)
     217              :      */
     218              :    int criticalDiskTemperature(std::vector<float>&   /**< [in] the vector of temperature values to be checked*/);
     219              : 
     220              : 
     221              :    /// Finds usages of space for following directory paths: /; /data; /boot
     222              :    /** These usage values are stored as integer values between 0 and 100 (e.g. value of 39 means directory is 39% full)
     223              :      * If directory is not found, space usage value will remain 0
     224              :      * \TODO: What about multiple drives? What does this do?
     225              :      *
     226              :      * \returns -1 on error with system command or output reading
     227              :      * \returns 0 if at least one of the return values is found
     228              :      */
     229              :    int findDiskUsage( float&,   /**< [out] the return value for usage in root path*/
     230              :                       float&,   /**< [out] the return value for usage in /data path*/
     231              :                       float&    /**< [out] the return value for usage in /boot path*/
     232              :                     );
     233              : 
     234              : 
     235              :    /// Parses string from system call to find drive usage space
     236              :    /** When a valid string is read in, the value from that string is stored
     237              :      *
     238              :      * \returns -1 on invalid string being read in
     239              :      * \returns 0 on completion and storing of value
     240              :      */
     241              :    int parseDiskUsage( std::string,   /**< [in] the string to be parsed*/
     242              :                        float&,   /**< [out] the return value for usage in root path*/
     243              :                        float&,   /**< [out] the return value for usage in /data path*/
     244              :                        float&    /**< [out] the return value for usage in /boot path*/
     245              :                      );
     246              : 
     247              : 
     248              :    /// Finds current RAM usage
     249              :    /** This usage value is stored as a decimal value between 0 and 1 (e.g. value of 0.39 means RAM usage is 39%)
     250              :      *
     251              :      * \returns -1 on error with system command or output reading
     252              :      * \returns 0 on completion
     253              :      */
     254              :    int findRamUsage(float&    /**< [out] the return value for current RAM usage*/);
     255              : 
     256              : 
     257              :    /// Parses string from system call to find RAM usage
     258              :    /** When a valid string is read in, the value from that string is stored
     259              :     *
     260              :     * \returns -1 on invalid string being read in
     261              :     * \returns 0 on completion and storing of value
     262              :     */
     263              :    int parseRamUsage( std::string,   /**< [in] the string to be parsed*/
     264              :                       float&    /**< [out] the return value for current RAM usage*/
     265              :                     );
     266              : 
     267              :    /** \name Chrony Status
     268              :      * @{
     269              :      */
     270              : protected:
     271              :    std::string m_chronySourceMac;
     272              :    std::string m_chronySourceIP;
     273              :    std::string m_chronySynch;
     274              :    double m_chronySystemTime{ 0 };
     275              :    double m_chronyLastOffset{ 0 };
     276              :    double m_chronyRMSOffset{ 0 };
     277              :    double m_chronyFreq{ 0 };
     278              :    double m_chronyResidFreq{ 0 };
     279              :    double m_chronySkew{ 0 };
     280              :    double m_chronyRootDelay{ 0 };
     281              :    double m_chronyRootDispersion{ 0 };
     282              :    double m_chronyUpdateInt{ 0 };
     283              :    std::string m_chronyLeap;
     284              : 
     285              :    pcf::IndiProperty m_indiP_chronyStatus;
     286              :    pcf::IndiProperty m_indiP_chronyStats;
     287              : 
     288              : public:
     289              :    /// Finds current chronyd status
     290              :    /** Uses the chrony tracking command
     291              :      *
     292              :      * \returns -1 on error
     293              :      * \returns 0 on success
     294              :      */
     295              :    int findChronyStatus();
     296              : 
     297              :    ///@}
     298              : 
     299              :    /** \name Set Latency
     300              :      * This thread spins up the cpus to minimize latency when requested
     301              :      *
     302              :      * @{
     303              :      */
     304              :    bool m_setLatency{ false };
     305              : 
     306              :    int m_setlatThreadPrio{ 0 }; ///< Priority of the set latency thread, should normally be > 00.
     307              : 
     308              :    std::thread m_setlatThread; ///< A separate thread for the actual setting of low latency
     309              : 
     310              :    bool m_setlatThreadInit{ true }; ///< Synchronizer to ensure set lat thread initializes before doing dangerous things.
     311              : 
     312              :    pid_t m_setlatThreadID{ 0 }; ///< Set latency thread ID.
     313              : 
     314              :    pcf::IndiProperty m_setlatThreadProp; ///< The property to hold the setlat thread details.
     315              : 
     316              :    ///Thread starter, called by threadStart on thread construction.  Calls setlatThreadExec.
     317              :    static void setlatThreadStart(sysMonitor* s /**< [in] a pointer to a sysMonitor instance (normally this) */);
     318              : 
     319              :    /// Execute the frame grabber main loop.
     320              :    void setlatThreadExec();
     321              : 
     322              :    pcf::IndiProperty m_indiP_setlat;
     323              : 
     324              : public:
     325            0 :    INDI_NEWCALLBACK_DECL(sysMonitor, m_indiP_setlat);
     326              : 
     327              :    ///@}
     328              : 
     329              :    /** \name Telemeter Interface
     330              :      *
     331              :      * @{
     332              :      */
     333              :    int checkRecordTimes();
     334              : 
     335              :    int recordTelem(const telem_coreloads*);
     336              : 
     337              :    int recordTelem(const telem_coretemps*);
     338              : 
     339              :    int recordTelem(const telem_drivetemps*);
     340              : 
     341              :    int recordTelem(const telem_usage*);
     342              : 
     343              :    int recordTelem(const telem_chrony_status*);
     344              : 
     345              :    int recordTelem(const telem_chrony_stats*);
     346              : 
     347              :    int recordCoreLoads(bool force = false);
     348              : 
     349              :    int recordCoreTemps(bool force = false);
     350              : 
     351              :    int recordDriveTemps(bool force = false);
     352              : 
     353              :    int recordUsage(bool force = false);
     354              : 
     355              :    int recordChronyStatus(bool force = false);
     356              : 
     357              :    int recordChronyStats(bool force = false);
     358              : 
     359              :    ///@}
     360              : 
     361              : };
     362              : 
     363           99 : inline sysMonitor::sysMonitor() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     364              : {
     365              :    //m_loopPause = 100000; //Set default to 1 milli-second due to mpstat averaging time of 1 sec.
     366           33 :    return;
     367            0 : }
     368              : 
     369            0 : void sysMonitor::setupConfig()
     370              : {
     371            0 :    config.add("sysType", "", "sysType", argType::Required, "", "sysType", false, "string", "The system type, Intel (default) or AMD");
     372            0 :    config.add("diskNames", "", "diskNames", argType::Required, "", "diskNames", false, "vector<string>", "The names (/dev/sdX) of the drives to monitor");
     373            0 :    config.add("warningCoreTemp", "", "warningCoreTemp", argType::Required, "", "warningCoreTemp", false, "int", "The warning temperature for CPU cores.");
     374            0 :    config.add("criticalCoreTemp", "", "criticalCoreTemp", argType::Required, "", "criticalCoreTemp", false, "int", "The critical temperature for CPU cores.");
     375            0 :    config.add("warningDiskTemp", "", "warningDiskTemp", argType::Required, "", "warningDiskTemp", false, "int", "The warning temperature for the disk.");
     376            0 :    config.add("criticalDiskTemp", "", "criticalDiskTemp", argType::Required, "", "criticalDiskTemp", false, "int", "The critical temperature for disk.");
     377              : 
     378            0 :    dev::telemeter<sysMonitor>::setupConfig(config);
     379            0 : }
     380              : 
     381            0 : void sysMonitor::loadConfig()
     382              : {
     383            0 :    std::string st;
     384              :    //Configure for default, such that logs are correct after config
     385            0 :    if (m_sysType == sysType::Intel)
     386              :    {
     387            0 :       st = "Intel";
     388              :    }
     389            0 :    else if (m_sysType == sysType::AMD)
     390              :    {
     391            0 :       st = "AMD";
     392              :    }
     393            0 :    config(st, "sysType");
     394            0 :    if (st == "Intel")
     395              :    {
     396            0 :       m_sysType = sysType::Intel;
     397              :    }
     398            0 :    else if (st == "AMD")
     399              :    {
     400            0 :       m_sysType = sysType::AMD;
     401              :    }
     402              :    else
     403              :    {
     404            0 :       log<software_critical>({ __FILE__, __LINE__, "Invalid system type specified." });
     405            0 :       m_shutdown = 1;
     406              :    }
     407              : 
     408            0 :    config(m_diskNameList, "diskNames");
     409            0 :    config(m_warningCoreTemp, "warningCoreTemp");
     410            0 :    config(m_criticalCoreTemp, "criticalCoreTemp");
     411            0 :    config(m_warningDiskTemp, "warningDiskTemp");
     412            0 :    config(m_criticalDiskTemp, "criticalDiskTemp");
     413              : 
     414            0 :    dev::telemeter<sysMonitor>::loadConfig(config);
     415            0 : }
     416              : 
     417            0 : int sysMonitor::appStartup()
     418              : {
     419              : 
     420            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_core_temps, "core_temps", pcf::IndiProperty::Number);
     421            0 :    m_indiP_core_temps.add(pcf::IndiElement("max"));
     422            0 :    m_indiP_core_temps.add(pcf::IndiElement("min"));
     423            0 :    m_indiP_core_temps.add(pcf::IndiElement("mean"));
     424              : 
     425            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_core_loads, "core_loads", pcf::IndiProperty::Number);
     426            0 :    m_indiP_core_loads.add(pcf::IndiElement("max"));
     427            0 :    m_indiP_core_loads.add(pcf::IndiElement("min"));
     428            0 :    m_indiP_core_loads.add(pcf::IndiElement("mean"));
     429              : 
     430            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_drive_temps, "drive_temps", pcf::IndiProperty::Number);
     431            0 :    findDiskTemperature(m_diskNames, m_diskTemps);
     432            0 :    for (unsigned int i = 0; i < m_diskTemps.size(); i++)
     433              :    {
     434            0 :       m_indiP_drive_temps.add(pcf::IndiElement(m_diskNames[i]));
     435            0 :       m_indiP_drive_temps[m_diskNames[i]].set<double>(m_diskTemps[i]);
     436              :    }
     437              : 
     438            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_usage, "resource_use", pcf::IndiProperty::Number);
     439            0 :    m_indiP_usage.add(pcf::IndiElement("root_usage"));
     440            0 :    m_indiP_usage.add(pcf::IndiElement("boot_usage"));
     441            0 :    m_indiP_usage.add(pcf::IndiElement("data_usage"));
     442            0 :    m_indiP_usage.add(pcf::IndiElement("ram_usage"));
     443              : 
     444            0 :    m_indiP_usage["root_usage"].set<double>(0.0);
     445            0 :    m_indiP_usage["boot_usage"].set<double>(0.0);
     446            0 :    m_indiP_usage["data_usage"].set<double>(0.0);
     447            0 :    m_indiP_usage["ram_usage"].set<double>(0.0);
     448              : 
     449              : 
     450              : 
     451            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_chronyStatus, "chrony_status", pcf::IndiProperty::Text);
     452            0 :    m_indiP_chronyStatus.add(pcf::IndiElement("synch"));
     453            0 :    m_indiP_chronyStatus.add(pcf::IndiElement("source"));
     454              : 
     455            0 :    REG_INDI_NEWPROP_NOCB(m_indiP_chronyStats, "chrony_stats", pcf::IndiProperty::Number);
     456            0 :    m_indiP_chronyStats.add(pcf::IndiElement("system_time"));
     457            0 :    m_indiP_chronyStats.add(pcf::IndiElement("last_offset"));
     458            0 :    m_indiP_chronyStats.add(pcf::IndiElement("rms_offset"));
     459              : 
     460            0 :    createStandardIndiToggleSw(m_indiP_setlat, "set_latency");
     461            0 :    registerIndiPropertyNew(m_indiP_setlat, INDI_NEWCALLBACK(m_indiP_setlat));
     462              : 
     463            0 :    if (dev::telemeter<sysMonitor>::appStartup() < 0)
     464              :    {
     465            0 :       return log<software_error, -1>({ __FILE__,__LINE__ });
     466              :    }
     467              : 
     468            0 :    if (threadStart(m_setlatThread, m_setlatThreadInit, m_setlatThreadID, m_setlatThreadProp, m_setlatThreadPrio, "", "set_latency", this, setlatThreadStart) < 0)
     469              :    {
     470            0 :       log<software_critical>({ __FILE__, __LINE__ });
     471            0 :       return -1;
     472              :    }
     473              : 
     474            0 :    state(stateCodes::READY);
     475            0 :    return 0;
     476              : }
     477              : 
     478            0 : int sysMonitor::appLogic()
     479              : {
     480              : 
     481            0 :    m_coreTemps.clear();
     482            0 :    int rvCPUTemp = findCPUTemperatures(m_coreTemps);
     483            0 :    if (rvCPUTemp >= 0)
     484              :    {
     485            0 :       rvCPUTemp = criticalCoreTemperature(m_coreTemps);
     486              :    }
     487              : 
     488            0 :    if (rvCPUTemp >= 0)
     489              :    {
     490            0 :       if (rvCPUTemp == 1)
     491              :       {
     492            0 :          log<telem_coretemps>(m_coreTemps, logPrio::LOG_WARNING);
     493              :       }
     494            0 :       else if (rvCPUTemp == 2)
     495              :       {
     496            0 :          log<telem_coretemps>(m_coreTemps, logPrio::LOG_ALERT);
     497              :       }
     498              : 
     499            0 :       recordCoreTemps();
     500              :    }
     501              :    else
     502              :    {
     503            0 :       log<software_error>({ __FILE__, __LINE__,"Could not log values for CPU core temps." });
     504              :    }
     505              : 
     506            0 :    m_coreLoads.clear();
     507            0 :    int rvCPULoad = findCPULoads(m_coreLoads);
     508              : 
     509            0 :    if (rvCPULoad >= 0)
     510              :    {
     511            0 :       recordCoreLoads();
     512              :    }
     513              :    else
     514              :    {
     515            0 :       log<software_error>({ __FILE__, __LINE__,"Could not log values for CPU core loads." });
     516              :    }
     517              : 
     518              : 
     519            0 :    m_diskNames.clear();
     520            0 :    m_diskTemps.clear();
     521            0 :    int rvDiskTemp = findDiskTemperature(m_diskNames, m_diskTemps);
     522              : 
     523            0 :    if (rvDiskTemp >= 0)
     524              :    {
     525            0 :       rvDiskTemp = criticalDiskTemperature(m_diskTemps);
     526              :    }
     527              : 
     528            0 :    if (rvDiskTemp >= 0)
     529              :    {
     530            0 :       if (rvDiskTemp == 1)
     531              :       {
     532            0 :          log<telem_drivetemps>({ m_diskNames, m_diskTemps }, logPrio::LOG_WARNING);
     533              :       }
     534            0 :       else if (rvDiskTemp == 2)
     535              :       {
     536            0 :          log<telem_drivetemps>({ m_diskNames, m_diskTemps }, logPrio::LOG_ALERT);
     537              :       }
     538              : 
     539            0 :       recordDriveTemps();
     540              :    }
     541              :    else
     542              :    {
     543            0 :       log<software_error>({ __FILE__, __LINE__,"Could not log values for drive temps." });
     544              :    }
     545              : 
     546            0 :    int rvDiskUsage = findDiskUsage(m_rootUsage, m_dataUsage, m_bootUsage);
     547            0 :    int rvRamUsage = findRamUsage(m_ramUsage);
     548              : 
     549              : 
     550            0 :    if (rvDiskUsage >= 0 && rvRamUsage >= 0)
     551              :    {
     552            0 :       recordUsage();
     553              :    }
     554              :    else
     555              :    {
     556            0 :       log<software_error>({ __FILE__, __LINE__,"Could not log values for usage." });
     557              :    }
     558              : 
     559              : 
     560            0 :    if (findChronyStatus() == 0)
     561              :    {
     562              :    }
     563              :    else
     564              :    {
     565            0 :       log<software_error>({ __FILE__, __LINE__,"Could not get chronyd status." });
     566              :    }
     567              : 
     568            0 :    if (telemeter<sysMonitor>::appLogic() < 0)
     569              :    {
     570            0 :       log<software_error>({ __FILE__, __LINE__ });
     571            0 :       return 0;
     572              :    }
     573              : 
     574            0 :    updateVals();
     575              : 
     576            0 :    return 0;
     577              : }
     578              : 
     579            0 : int sysMonitor::appShutdown()
     580              : {
     581              :    try
     582              :    {
     583            0 :       if (m_setlatThread.joinable())
     584              :       {
     585            0 :          m_setlatThread.join();
     586              :       }
     587              :    }
     588            0 :    catch (...) {}
     589              : 
     590            0 :    dev::telemeter<sysMonitor>::appShutdown();
     591              : 
     592            0 :    return 0;
     593              : }
     594              : 
     595            0 : int sysMonitor::findCPUTemperatures(std::vector<float>& temps)
     596              : {
     597            0 :    std::vector<std::string> commandList{ "sensors" };
     598              : 
     599            0 :    std::vector<std::string> commandOutput, commandError;
     600              : 
     601            0 :    if (sys::runCommand(commandOutput, commandError, commandList) < 0)
     602              :    {
     603            0 :       if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
     604            0 :       else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
     605              :    }
     606              : 
     607            0 :    if (commandError.size() > 0)
     608              :    {
     609            0 :       for (size_t n = 0; n < commandError.size(); ++n)
     610              :       {
     611            0 :          log<software_error>({ __FILE__, __LINE__, "sensors stderr: " + commandError[n] });
     612              :       }
     613              :    }
     614              : 
     615            0 :    int rv = -1;
     616            0 :    for (size_t n = 0; n < commandOutput.size(); ++n)
     617              :    {
     618              :       float tempVal;
     619            0 :       if (parseCPUTemperatures(tempVal, commandOutput[n]) == 0)
     620              :       {
     621            0 :          temps.push_back(tempVal);
     622            0 :          rv = 0;
     623              :       }
     624              :    }
     625            0 :    return rv;
     626            0 : }
     627              : 
     628            7 : int sysMonitor::parseCPUTemperatures( float& temp,
     629              :                                       const std::string& line
     630              :                                     )
     631              : {
     632            7 :    if (m_sysType == sysType::Intel)
     633              :    {
     634            7 :       return parseCPUTemperaturesIntel(temp, line);
     635              :    }
     636            0 :    else if (m_sysType == sysType::AMD)
     637              :    {
     638            0 :       return parseCPUTemperaturesAMD(temp, line);
     639              :    }
     640              :    else
     641              :    {
     642            0 :       log<software_error>({ __FILE__, __LINE__, "invalid system type" });
     643            0 :       return -1;
     644              :    }
     645              : 
     646              : }
     647              : 
     648              : 
     649            7 : int sysMonitor::parseCPUTemperaturesIntel( float& temp,
     650              :                                            const std::string& line
     651              :                                          )
     652              : {
     653            7 :    if (line.length() <= 1)
     654              :    {
     655            1 :       temp = -999;
     656            1 :       return -1;
     657              :    }
     658              : 
     659            6 :    std::string str = line.substr(0, 5);
     660            6 :    if (str.compare("Core ") == 0)
     661              :    {
     662            4 :       size_t st = line.find(':', 0);
     663            4 :       if (st == std::string::npos)
     664              :       {
     665            0 :          log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
     666            0 :          temp = -999;
     667            0 :          return -1;
     668              :       }
     669              : 
     670            4 :       ++st;
     671              : 
     672            4 :       size_t ed = line.find('C', st);
     673            4 :       if (ed == std::string::npos)
     674              :       {
     675            1 :          log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
     676            1 :          temp = -999;
     677            1 :          return -1;
     678              :       }
     679              : 
     680            3 :       --ed;
     681              : 
     682            3 :       std::string temp_str = line.substr(st, ed - st);
     683              : 
     684              :       try
     685              :       {
     686            3 :          temp = std::stof(temp_str);
     687              :       }
     688            0 :       catch (const std::invalid_argument& e)
     689              :       {
     690            0 :          log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
     691            0 :          temp = -999;
     692            0 :          return -1;
     693            0 :       }
     694              : 
     695            3 :       if (m_warningCoreTemp == 0)
     696              :       {
     697            3 :          std::istringstream iss(line);
     698            9 :          std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
     699              :          try
     700              :          {
     701            3 :             tokens.at(5).pop_back();
     702            3 :             tokens.at(5).pop_back();
     703            3 :             tokens.at(5).pop_back();
     704            3 :             tokens.at(5).pop_back();
     705            3 :             tokens.at(5).erase(0, 1);
     706            3 :             m_warningCoreTemp = std::stof(tokens.at(5));
     707              :          }
     708            0 :          catch (const std::invalid_argument& e)
     709              :          {
     710            0 :             log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing warning CPU temperatures." });
     711            0 :             return -1;
     712            0 :          }
     713            3 :       }
     714            3 :       if (m_criticalCoreTemp == 0)
     715              :       {
     716            3 :          std::istringstream iss(line);
     717            9 :          std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
     718              :          try
     719              :          {
     720            3 :             tokens.at(8).pop_back();
     721            3 :             tokens.at(8).pop_back();
     722            3 :             tokens.at(8).pop_back();
     723            3 :             tokens.at(8).pop_back();
     724            3 :             tokens.at(8).erase(0, 1);
     725            3 :             m_criticalCoreTemp = std::stof(tokens.at(8));
     726              :          }
     727            0 :          catch (const std::invalid_argument& e)
     728              :          {
     729            0 :             log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing critical CPU temperatures." });
     730            0 :             temp = -999;
     731            0 :             return -1;
     732            0 :          }
     733            3 :       }
     734            3 :       return 0;
     735            3 :    }
     736              :    else
     737              :    {
     738            2 :       temp = -999;
     739            2 :       return -1;
     740              :    }
     741              : 
     742            6 : }
     743              : 
     744            0 : int sysMonitor::parseCPUTemperaturesAMD( float& temp,
     745              :                                          const std::string& line
     746              :                                        )
     747              : {
     748            0 :    if (line.length() <= 1)
     749              :    {
     750            0 :       temp = -999;
     751            0 :       return -1;
     752              :    }
     753              : 
     754            0 :    std::string str = line.substr(0, 6);
     755            0 :    if (str.compare("Tctl: ") == 0)
     756              :    {
     757            0 :       size_t ed = line.find('C', 0);
     758            0 :       if (ed == std::string::npos)
     759              :       {
     760            0 :          log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
     761            0 :          temp = -999;
     762            0 :          return -1;
     763              :       }
     764              : 
     765            0 :       str = line.substr(7, ((ed - 1) - 7)); //ed-1 to eat degree.
     766              : 
     767              :       try
     768              :       {
     769            0 :          temp = std::stof(str);
     770              :       }
     771            0 :       catch (...)
     772              :       {
     773            0 :          log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
     774            0 :          temp = -999;
     775            0 :          return -1;
     776            0 :       }
     777            0 :       return 0;
     778              :    }
     779              :    else
     780              :    {
     781            0 :       temp = -999;
     782            0 :       return -1;
     783              :    }
     784            0 : }
     785              : 
     786            0 : int sysMonitor::criticalCoreTemperature(std::vector<float>& v)
     787              : {
     788            0 :    int coreNum = 0, rv = 0;
     789            0 :    for (auto it : v)
     790              :    {
     791            0 :       float temp = it;
     792            0 :       if (temp >= m_warningCoreTemp && temp < m_criticalCoreTemp)
     793              :       {
     794            0 :          std::cout << "Warning temperature for Core " << coreNum << std::endl;
     795            0 :          if (rv < 2)
     796              :          {
     797            0 :             rv = 1;
     798              :          }
     799              :       }
     800            0 :       else if (temp >= m_criticalCoreTemp)
     801              :       {
     802            0 :          std::cout << "Critical temperature for Core " << coreNum << std::endl;
     803            0 :          rv = 2;
     804              :       }
     805            0 :       ++coreNum;
     806              :    }
     807            0 :    return rv;
     808              : }
     809              : 
     810            0 : int sysMonitor::findCPULoads(std::vector<float>& loads)
     811              : {
     812            0 :    std::vector<std::string> commandList{ "mpstat", "-P", "ALL", "1", "1" };
     813            0 :    std::vector<std::string> commandOutput, commandError;
     814              : 
     815            0 :    if (sys::runCommand(commandOutput, commandError, commandList) < 0)
     816              :    {
     817            0 :       if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
     818            0 :       else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
     819              :    }
     820              : 
     821            0 :    if (commandError.size() > 0)
     822              :    {
     823            0 :       for (size_t n = 0; n < commandError.size(); ++n)
     824              :       {
     825            0 :          log<software_error>({ __FILE__, __LINE__, "mpstat stderr: " + commandError[n] });
     826              :       }
     827              :    }
     828              : 
     829            0 :    int rv = -1;
     830              :    // If output lines are less than 5 (with one CPU, guarenteed output is 5)
     831            0 :    if (commandOutput.size() < 5)
     832              :    {
     833            0 :       return log<software_error, -1>({ __FILE__, __LINE__, "not enough lines returned by mpstat" });
     834              :    }
     835              :    //start iterating at fourth line
     836            0 :    for (auto line = commandOutput.begin() + 4; line != commandOutput.end(); line++)
     837              :    {
     838              :       float loadVal;
     839            0 :       if (parseCPULoads(loadVal, *line) == 0)
     840              :       {
     841            0 :          loads.push_back(loadVal);
     842            0 :          rv = 0;
     843              :       }
     844              :    }
     845            0 :    return rv;
     846            0 : }
     847              : 
     848            6 : int sysMonitor::parseCPULoads( float & loadVal,
     849              :                                const std::string & line
     850              :                              )
     851              : {
     852            6 :    if (line.length() <= 1)
     853              :    {
     854            1 :       log<software_error>({ __FILE__, __LINE__,"zero length line in parseCPULoads." });
     855            1 :       return -1;
     856              :    }
     857            5 :    std::istringstream iss(line);
     858              : 
     859            5 :    std::vector<std::string> tokens(std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{});
     860            5 :    if (tokens.size() < 8) return 1;
     861              : 
     862              :    float cpu_load;
     863              :    try
     864              :    {
     865            5 :       cpu_load = 100.0 - std::stof(tokens.at(tokens.size() - 1));
     866              :    }
     867            2 :    catch (const std::invalid_argument& e)
     868              :    {
     869            2 :       log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU core usage." });
     870            2 :       return -1;
     871            2 :    }
     872            0 :    catch (const std::out_of_range& e)
     873              :    {
     874            0 :       log<software_error>({ __FILE__, __LINE__,"Out of range exception in parseCPULoads." });
     875            0 :       return -1;
     876            0 :    }
     877            3 :    cpu_load /= 100;
     878            3 :    loadVal = cpu_load;
     879            3 :    return 0;
     880            5 : }
     881              : 
     882            0 : int sysMonitor::findDiskTemperature( std::vector<std::string>& hdd_names,
     883              :                                      std::vector<float>& hdd_temps
     884              :                                    )
     885              : {
     886            0 :    std::vector<std::string> commandList{ "hddtemp" };
     887              : 
     888            0 :    std::vector<std::string> commandOutput, commandError;
     889              : 
     890            0 :    if (sys::runCommand(commandOutput, commandError, commandList) < 0)
     891              :    {
     892            0 :       if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
     893            0 :       else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
     894              :    }
     895              : 
     896            0 :    if (commandError.size() > 0)
     897              :    {
     898            0 :       for (size_t n = 0; n < commandError.size(); ++n)
     899              :       {
     900            0 :          if(commandError[n].find("doesn't have a temperature sensor") != std::string::npos)
     901              :          {
     902            0 :             continue;
     903              :          }
     904              : 
     905            0 :          log<software_error>({ __FILE__, __LINE__, "hddtemp stderr: " + commandError[n] });
     906              :       }
     907              :    }
     908              : 
     909            0 :    int rv = -1;
     910            0 :    for (auto line : commandOutput)
     911              :    {
     912            0 :       std::string driveName;
     913              :       float tempVal;
     914            0 :       if (parseDiskTemperature(driveName, tempVal, line) == 0)
     915              :       {
     916            0 :          hdd_names.push_back(driveName);
     917            0 :          hdd_temps.push_back(tempVal);
     918            0 :          rv = 0;
     919              :       }
     920            0 :    }
     921            0 :    return rv;
     922              : 
     923              :    //return 0;
     924            0 : }
     925              : 
     926            6 : int sysMonitor::parseDiskTemperature( std::string& driveName,
     927              :                                       float& hdd_temp,
     928              :                                       const std::string& line
     929              :                                     )
     930              : {
     931              :    float tempValue;
     932            6 :    if (line.length() <= 6)
     933              :    {
     934            1 :       driveName = "";
     935            1 :       hdd_temp = -999;
     936            1 :       return -1;
     937              :    }
     938              : 
     939            5 :    size_t sp = line.find(':', 0);
     940            5 :    driveName = line.substr(5, sp - 5);
     941              : 
     942            5 :    std::istringstream iss(line);
     943           15 :    std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
     944              : 
     945           28 :    for (auto temp_s : tokens)
     946              :    {
     947              :       try
     948              :       {
     949           26 :          if (isdigit(temp_s.at(0)) && temp_s.substr(temp_s.length() - 1, 1) == "C")
     950              :          {
     951            3 :             temp_s.pop_back();
     952            3 :             temp_s.pop_back();
     953              :             try
     954              :             {
     955            3 :                tempValue = std::stof(temp_s);
     956              :             }
     957            0 :             catch (const std::invalid_argument& e)
     958              :             {
     959            0 :                log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive temperatures." });
     960            0 :                hdd_temp = -999;
     961            0 :                driveName = "";
     962            0 :                return -1;
     963            0 :             }
     964            3 :             hdd_temp = tempValue;
     965            3 :             if (m_warningDiskTemp == 0)
     966              :             {
     967            3 :                m_warningDiskTemp = tempValue + (.1 * tempValue);
     968              :             }
     969            3 :             if (m_criticalDiskTemp == 0)
     970              :             {
     971            3 :                m_criticalDiskTemp = tempValue + (.2 * tempValue);
     972              :             }
     973            3 :             return 0;
     974              :          }
     975              :       }
     976            0 :       catch (const std::out_of_range& e)
     977              :       {
     978            0 :          hdd_temp = -999;
     979            0 :          driveName = "";
     980            0 :          return -1;
     981            0 :       }
     982           26 :    }
     983              : 
     984            2 :    hdd_temp = -999;
     985            2 :    driveName = "";
     986            2 :    return -1;
     987            5 : }
     988              : 
     989            0 : int sysMonitor::criticalDiskTemperature(std::vector<float>& v)
     990              : {
     991            0 :    int rv = 0;
     992            0 :    for (auto it : v)
     993              :    {
     994            0 :       float temp = it;
     995            0 :       if (temp >= m_warningDiskTemp && temp < m_criticalDiskTemp)
     996              :       {
     997            0 :          std::cout << "Warning temperature for Disk" << std::endl;
     998            0 :          if (rv < 2)
     999              :          {
    1000            0 :             rv = 1;
    1001              :          }
    1002              :       }
    1003            0 :       else if (temp >= m_criticalDiskTemp)
    1004              :       {
    1005            0 :          std::cout << "Critical temperature for Disk " << std::endl;
    1006            0 :          rv = 2;
    1007              :       }
    1008              :    }
    1009            0 :    return rv;
    1010              : }
    1011              : 
    1012            0 : int sysMonitor::findDiskUsage(float& rootUsage, float& dataUsage, float& bootUsage)
    1013              : {
    1014            0 :    std::vector<std::string> commandList{ "df" };
    1015              : 
    1016            0 :    std::vector<std::string> commandOutput, commandError;
    1017              : 
    1018            0 :    if (sys::runCommand(commandOutput, commandError, commandList) < 0)
    1019              :    {
    1020            0 :       if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
    1021            0 :       else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
    1022              :    }
    1023              : 
    1024            0 :    if (commandError.size() > 0)
    1025              :    {
    1026            0 :       for (size_t n = 0; n < commandError.size(); ++n)
    1027              :       {
    1028            0 :          log<software_error>({ __FILE__, __LINE__, "df stderr: " + commandError[n] });
    1029              :       }
    1030              :    }
    1031              : 
    1032            0 :    int rv = -1;
    1033            0 :    for (auto line : commandOutput)
    1034              :    {
    1035            0 :       int rvDiskUsage = parseDiskUsage(line, rootUsage, dataUsage, bootUsage);
    1036            0 :       if (rvDiskUsage == 0)
    1037              :       {
    1038            0 :          rv = 0;
    1039              :       }
    1040            0 :    }
    1041            0 :    return rv;
    1042            0 : }
    1043              : 
    1044            6 : int sysMonitor::parseDiskUsage(std::string line, float& rootUsage, float& dataUsage, float& bootUsage)
    1045              : {
    1046            6 :    if (line.length() <= 1)
    1047              :    {
    1048            1 :       return -1;
    1049              :    }
    1050              : 
    1051            5 :    std::istringstream iss(line);
    1052           15 :    std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
    1053              : 
    1054              :    try {
    1055            5 :       if (tokens.at(5).compare("/") == 0)
    1056              :       {
    1057            2 :          tokens.at(4).pop_back();
    1058              :          try
    1059              :          {
    1060            2 :             rootUsage = std::stof(tokens.at(4)) / 100;
    1061            1 :             return 0;
    1062              :          }
    1063            1 :          catch (const std::invalid_argument& e)
    1064              :          {
    1065            1 :             log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
    1066            1 :             return -1;
    1067            1 :          }
    1068              :       }
    1069            3 :       else if (tokens.at(5).compare("/data") == 0)
    1070              :       {
    1071            1 :          tokens.at(4).pop_back();
    1072              :          try
    1073              :          {
    1074            1 :             dataUsage = std::stof(tokens.at(4)) / 100;
    1075            1 :             return 0;
    1076              :          }
    1077            0 :          catch (const std::invalid_argument& e)
    1078              :          {
    1079            0 :             log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
    1080            0 :             return -1;
    1081            0 :          }
    1082              :       }
    1083            2 :       else if (tokens.at(5).compare("/boot") == 0)
    1084              :       {
    1085            1 :          tokens.at(4).pop_back();
    1086              :          try
    1087              :          {
    1088            1 :             bootUsage = std::stof(tokens.at(4)) / 100;
    1089            1 :             return 0;
    1090              :          }
    1091            0 :          catch (const std::invalid_argument& e)
    1092              :          {
    1093            0 :             log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
    1094            0 :             return -1;
    1095            0 :          }
    1096              :       }
    1097              :    }
    1098            0 :    catch (const std::out_of_range& e) {
    1099            0 :       return -1;
    1100            0 :    }
    1101            1 :    return -1;
    1102            5 : }
    1103              : 
    1104            0 : int sysMonitor::findRamUsage(float& ramUsage)
    1105              : {
    1106            0 :    std::vector<std::string> commandList{ "free", "-m" };
    1107              : 
    1108            0 :    std::vector<std::string> commandOutput, commandError;
    1109              : 
    1110            0 :    if (sys::runCommand(commandOutput, commandError, commandList) < 0)
    1111              :    {
    1112            0 :       if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
    1113            0 :       else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
    1114              :    }
    1115              : 
    1116            0 :    if (commandError.size() > 0)
    1117              :    {
    1118            0 :       for (size_t n = 0; n < commandError.size(); ++n)
    1119              :       {
    1120            0 :          log<software_error>({ __FILE__, __LINE__, "free stderr: " + commandError[n] });
    1121              :       }
    1122              :    }
    1123              : 
    1124            0 :    for (auto line : commandOutput)
    1125              :    {
    1126            0 :       if (parseRamUsage(line, ramUsage) == 0)
    1127              :       {
    1128            0 :          return 0;
    1129              :       }
    1130            0 :    }
    1131            0 :    return -1;
    1132            0 : }
    1133              : 
    1134            5 : int sysMonitor::parseRamUsage(std::string line, float& ramUsage)
    1135              : {
    1136            5 :    if (line.length() <= 1)
    1137              :    {
    1138            1 :       return -1;
    1139              :    }
    1140            4 :    std::istringstream iss(line);
    1141           12 :    std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
    1142              :    try
    1143              :    {
    1144            4 :       if (tokens.at(0).compare("Mem:") != 0)
    1145              :       {
    1146            1 :          return -1;
    1147              :       }
    1148            3 :       ramUsage = std::stof(tokens.at(2)) / std::stof(tokens.at(1));
    1149            3 :       if (ramUsage > 1 || ramUsage == 0)
    1150              :       {
    1151            1 :          ramUsage = -1;
    1152            1 :          return -1;
    1153              :       }
    1154            2 :       return 0;
    1155              :    }
    1156            0 :    catch (const std::invalid_argument& e)
    1157              :    {
    1158            0 :       log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing RAM usage." });
    1159            0 :       return -1;
    1160            0 :    }
    1161            0 :    catch (const std::out_of_range& e) {
    1162            0 :       return -1;
    1163            0 :    }
    1164            4 : }
    1165              : 
    1166            0 : int sysMonitor::findChronyStatus()
    1167              : {
    1168            0 :    std::vector<std::string> commandList{ "chronyc", "-c", "tracking" };
    1169              : 
    1170            0 :    std::vector<std::string> commandOutput, commandError;
    1171              : 
    1172            0 :    if (sys::runCommand(commandOutput, commandError, commandList) < 0)
    1173              :    {
    1174            0 :       if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
    1175            0 :       else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
    1176              :    }
    1177              : 
    1178            0 :    if (commandError.size() > 0)
    1179              :    {
    1180            0 :       for (size_t n = 0; n < commandError.size(); ++n)
    1181              :       {
    1182            0 :          log<software_error>({ __FILE__, __LINE__, "chronyc stderr: " + commandError[n] });
    1183              :       }
    1184              :    }
    1185              : 
    1186            0 :    if (commandOutput.size() < 1)
    1187              :    {
    1188            0 :       log<software_error>({ __FILE__,__LINE__, "no response from chronyc -c" });
    1189            0 :       return -1;
    1190              :    }
    1191              : 
    1192            0 :    std::vector<std::string> results;
    1193            0 :    mx::ioutils::parseStringVector(results, commandOutput[0], ',');
    1194              : 
    1195            0 :    if (results.size() < 1)
    1196              :    {
    1197            0 :       log<software_error>({ __FILE__,__LINE__, "wrong number of fields from chronyc -c" });
    1198            0 :       return -1;
    1199              :    }
    1200              : 
    1201            0 :    static std::string last_mac;
    1202            0 :    static std::string last_ip;
    1203            0 :    m_chronySourceMac = results[0];
    1204            0 :    m_chronySourceIP = results[1];
    1205            0 :    if (m_chronySourceMac == "7F7F0101" || m_chronySourceIP == "127.0.0.1")
    1206              :    {
    1207            0 :       m_chronySynch = "NO";
    1208            0 :       log<text_log>("chrony is not synchronized", logPrio::LOG_WARNING);
    1209              :    }
    1210              :    else
    1211              :    {
    1212            0 :       m_chronySynch = "YES";
    1213              :    }
    1214              : 
    1215            0 :    if (last_mac != m_chronySourceMac || last_ip != m_chronySourceIP)
    1216              :    {
    1217            0 :       log<text_log>("chrony is synchronizing to " + m_chronySourceMac + " / " + m_chronySourceIP);
    1218            0 :       last_mac = m_chronySourceMac;
    1219            0 :       last_ip = m_chronySourceIP;
    1220              :    }
    1221              : 
    1222              : 
    1223              : 
    1224            0 :    m_chronySystemTime = std::stod(results[4]);
    1225            0 :    m_chronyLastOffset = std::stod(results[5]);
    1226            0 :    m_chronyRMSOffset = std::stod(results[6]);
    1227            0 :    m_chronyFreq = std::stod(results[7]);
    1228            0 :    m_chronyResidFreq = std::stod(results[8]);
    1229            0 :    m_chronySkew = std::stod(results[9]);
    1230            0 :    m_chronyRootDelay = std::stod(results[10]);
    1231            0 :    m_chronyRootDispersion = std::stod(results[11]);
    1232            0 :    m_chronyUpdateInt = std::stod(results[12]);
    1233            0 :    m_chronyLeap = results[13];
    1234              : 
    1235            0 :    recordChronyStatus();
    1236            0 :    recordChronyStats();
    1237              : 
    1238            0 :    return 0;
    1239            0 : }
    1240              : 
    1241            0 : int sysMonitor::updateVals()
    1242              : {
    1243              :    float min, max, mean;
    1244              : 
    1245            0 :    if (m_coreLoads.size() > 0)
    1246              :    {
    1247            0 :       min = m_coreLoads[0];
    1248            0 :       max = m_coreLoads[0];
    1249            0 :       mean = m_coreLoads[0];
    1250            0 :       for (size_t n = 1; n < m_coreLoads.size(); ++n)
    1251              :       {
    1252            0 :          if (m_coreLoads[n] < min) min = m_coreLoads[n];
    1253            0 :          if (m_coreLoads[n] > max) max = m_coreLoads[n];
    1254            0 :          mean += m_coreLoads[n];
    1255              :       }
    1256            0 :       mean /= m_coreLoads.size();
    1257              : 
    1258            0 :       updateIfChanged<float>(m_indiP_core_loads, { "min","max","mean" }, { min,max,mean });
    1259              :    }
    1260              : 
    1261              : 
    1262            0 :    if (m_coreTemps.size() > 0)
    1263              :    {
    1264            0 :       min = m_coreTemps[0];
    1265            0 :       max = m_coreTemps[0];
    1266            0 :       mean = m_coreTemps[0];
    1267            0 :       for (size_t n = 1; n < m_coreTemps.size(); ++n)
    1268              :       {
    1269            0 :          if (m_coreTemps[n] < min) min = m_coreTemps[n];
    1270            0 :          if (m_coreTemps[n] > max) max = m_coreTemps[n];
    1271            0 :          mean += m_coreTemps[n];
    1272              :       }
    1273            0 :       mean /= m_coreTemps.size();
    1274              : 
    1275            0 :       updateIfChanged<float>(m_indiP_core_temps, { "min","max","mean" }, { min,max,mean });
    1276              :    }
    1277              : 
    1278            0 :    updateIfChanged(m_indiP_drive_temps, m_diskNames, m_diskTemps);
    1279              : 
    1280            0 :    updateIfChanged<float>(m_indiP_usage, { "root_usage","boot_usage","data_usage","ram_usage" }, { m_rootUsage,m_bootUsage,m_dataUsage,m_ramUsage });
    1281              : 
    1282              : 
    1283            0 :    updateIfChanged(m_indiP_chronyStatus, "synch", m_chronySynch);
    1284            0 :    updateIfChanged(m_indiP_chronyStatus, "source", m_chronySourceIP);
    1285              : 
    1286            0 :    updateIfChanged<double>(m_indiP_chronyStats, { "system_time", "last_offset", "rms_offset" }, { m_chronySystemTime, m_chronyLastOffset, m_chronyRMSOffset });
    1287              : 
    1288            0 :    if (m_setLatency)
    1289              :    {
    1290            0 :       updateSwitchIfChanged(m_indiP_setlat, "toggle", pcf::IndiElement::On, INDI_OK);
    1291              :    }
    1292              :    else
    1293              :    {
    1294            0 :       updateSwitchIfChanged(m_indiP_setlat, "toggle", pcf::IndiElement::Off, INDI_IDLE);
    1295              :    }
    1296              : 
    1297            0 :    return 0;
    1298              : }
    1299              : 
    1300              : inline
    1301            0 : void sysMonitor::setlatThreadStart(sysMonitor* s)
    1302              : {
    1303            0 :    s->setlatThreadExec();
    1304            0 : }
    1305              : 
    1306              : inline
    1307            0 : void sysMonitor::setlatThreadExec()
    1308              : {
    1309            0 :    m_setlatThreadID = syscall(SYS_gettid);
    1310              : 
    1311              :    //Wait fpr the thread starter to finish initializing this thread.
    1312            0 :    while (m_setlatThreadInit == true && m_shutdown == 0)
    1313              :    {
    1314            0 :       sleep(1);
    1315              :    }
    1316              : 
    1317            0 :    int fd = 0;
    1318            0 :    while (m_shutdown == 0)
    1319              :    {
    1320            0 :       if (m_setLatency)
    1321              :       {
    1322            0 :          if (fd <= 0)
    1323              :          {
    1324            0 :             elevatedPrivileges ep(this);
    1325              : 
    1326            0 :             for (size_t cpu = 0; cpu < m_coreLoads.size(); ++cpu) ///\todo this needs error checks
    1327              :             {
    1328            0 :                std::string cpuFile = "/sys/devices/system/cpu/cpu";
    1329            0 :                cpuFile += std::to_string(cpu);
    1330            0 :                cpuFile += "/cpufreq/scaling_governor";
    1331            0 :                int wfd = open(cpuFile.c_str(), O_WRONLY);
    1332            0 :                ssize_t perfsz = sizeof("performance");
    1333            0 :                ssize_t wrtsz = write(wfd, "performance", perfsz);
    1334            0 :                if(wrtsz != perfsz)
    1335              :                {
    1336            0 :                   log<software_error>({ __FILE__,__LINE__,"error setting performance governor for CPU " + std::to_string(cpu) });
    1337              :                }
    1338            0 :                close(wfd);
    1339            0 :             }
    1340            0 :             log<text_log>("set governor to performance", logPrio::LOG_NOTICE);
    1341              : 
    1342            0 :             fd = open("/dev/cpu_dma_latency", O_WRONLY);
    1343              : 
    1344            0 :             if (fd <= 0) log<software_error>({ __FILE__,__LINE__,"error opening cpu_dma_latency" });
    1345              :             else
    1346              :             {
    1347            0 :                int l = 0;
    1348            0 :                if (write(fd, &l, sizeof(l)) != sizeof(l))
    1349              :                {
    1350            0 :                   log<software_error>({ __FILE__,__LINE__,"error writing to cpu_dma_latency" });
    1351              :                }
    1352              :                else
    1353              :                {
    1354            0 :                   log<text_log>("set latency to 0", logPrio::LOG_NOTICE);
    1355              :                }
    1356              :             }
    1357              : 
    1358              : 
    1359            0 :          }
    1360              :       }
    1361              :       else
    1362              :       {
    1363            0 :          if (fd != 0)
    1364              :          {
    1365            0 :             close(fd);
    1366            0 :             fd = 0;
    1367            0 :             log<text_log>("restored CPU latency to default", logPrio::LOG_NOTICE);
    1368              : 
    1369            0 :             elevatedPrivileges ep(this);
    1370            0 :             for (size_t cpu = 0; cpu < m_coreLoads.size(); ++cpu) ///\todo this needs error checks
    1371              :             {
    1372            0 :                std::string cpuFile = "/sys/devices/system/cpu/cpu";
    1373            0 :                cpuFile += std::to_string(cpu);
    1374            0 :                cpuFile += "/cpufreq/scaling_governor";
    1375            0 :                int wfd = open(cpuFile.c_str(), O_WRONLY);
    1376            0 :                ssize_t pwrsz = sizeof("powersave");
    1377            0 :                ssize_t wrtsz = write(wfd, "powersave", pwrsz);
    1378            0 :                if(wrtsz != pwrsz)
    1379              :                {
    1380            0 :                   log<software_error>({ __FILE__,__LINE__,"error setting powersave governor for CPU " + std::to_string(cpu) });
    1381              :                }
    1382              : 
    1383            0 :                close(wfd);
    1384            0 :             }
    1385            0 :             log<text_log>("set governor to powersave", logPrio::LOG_NOTICE);
    1386            0 :          }
    1387              :       }
    1388              : 
    1389            0 :       sleep(1);
    1390              :    }
    1391              : 
    1392            0 :    if (fd) close(fd);
    1393              : 
    1394            0 : }
    1395              : 
    1396            3 : INDI_NEWCALLBACK_DEFN(sysMonitor, m_indiP_setlat)(const pcf::IndiProperty& ipRecv)
    1397              : {
    1398            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_setlat, ipRecv);
    1399              : 
    1400              :    if (ipRecv.getName() != m_indiP_setlat.getName())
    1401              :    {
    1402              :       log<software_error>({ __FILE__,__LINE__, "wrong INDI property received." });
    1403              :       return -1;
    1404              :    }
    1405              : 
    1406              :    if (!ipRecv.find("toggle")) return 0;
    1407              : 
    1408              :    if (ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
    1409              :    {
    1410              :       m_setLatency = false;
    1411              :    }
    1412              : 
    1413              :    if (ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
    1414              :    {
    1415              :       m_setLatency = true;
    1416              :    }
    1417              :    return 0;
    1418              : }
    1419              : 
    1420              : inline
    1421            0 : int sysMonitor::checkRecordTimes()
    1422              : {
    1423            0 :    return telemeter<sysMonitor>::checkRecordTimes( telem_coreloads(),
    1424              :                                                    telem_coretemps(),
    1425              :                                                    telem_drivetemps(),
    1426              :                                                    telem_usage(),
    1427              :                                                    telem_chrony_status(),
    1428              :                                                    telem_chrony_stats()
    1429            0 :                                                  );
    1430              : }
    1431              : 
    1432            0 : int sysMonitor::recordTelem(const telem_coreloads*)
    1433              : {
    1434            0 :    return recordCoreLoads(true);
    1435              : }
    1436              : 
    1437            0 : int sysMonitor::recordTelem(const telem_coretemps*)
    1438              : {
    1439            0 :    return recordCoreTemps(true);
    1440              : }
    1441              : 
    1442            0 : int sysMonitor::recordTelem(const telem_drivetemps*)
    1443              : {
    1444            0 :    return recordDriveTemps(true);
    1445              : }
    1446              : 
    1447            0 : int sysMonitor::recordTelem(const telem_usage*)
    1448              : {
    1449            0 :    return recordUsage(true);
    1450              : }
    1451              : 
    1452            0 : int sysMonitor::recordTelem(const telem_chrony_status*)
    1453              : {
    1454            0 :    return recordChronyStatus(true);
    1455              : }
    1456              : 
    1457            0 : int sysMonitor::recordTelem(const telem_chrony_stats*)
    1458              : {
    1459            0 :    return recordChronyStats(true);
    1460              : }
    1461              : 
    1462            0 : int sysMonitor::recordCoreLoads(bool force)
    1463              : {
    1464            0 :    static std::vector<float> old_coreLoads;
    1465              : 
    1466            0 :    if (old_coreLoads.size() != m_coreLoads.size())
    1467              :    {
    1468            0 :       old_coreLoads.resize(m_coreLoads.size(), -1e30);
    1469              :    }
    1470              : 
    1471            0 :    bool write = false;
    1472              : 
    1473            0 :    for (size_t n = 0; n < m_coreLoads.size(); ++n)
    1474              :    {
    1475            0 :       if (m_coreLoads[n] != old_coreLoads[n]) write = true;
    1476              :    }
    1477              : 
    1478            0 :    if (force || write)
    1479              :    {
    1480            0 :       telem<telem_coreloads>(m_coreLoads);
    1481              : 
    1482            0 :       for (size_t n = 0; n < m_coreLoads.size(); ++n)
    1483              :       {
    1484            0 :          old_coreLoads[n] = m_coreLoads[n];
    1485              :       }
    1486              :    }
    1487              : 
    1488            0 :    return 0;
    1489              : }
    1490              : 
    1491            0 : int sysMonitor::recordCoreTemps(bool force)
    1492              : {
    1493            0 :    static std::vector<float> old_coreTemps;
    1494              : 
    1495            0 :    if (old_coreTemps.size() != m_coreTemps.size())
    1496              :    {
    1497            0 :       old_coreTemps.resize(m_coreTemps.size(), -1e30);
    1498              :    }
    1499              : 
    1500            0 :    bool write = false;
    1501              : 
    1502            0 :    for (size_t n = 0; n < m_coreTemps.size(); ++n)
    1503              :    {
    1504            0 :       if (m_coreTemps[n] != old_coreTemps[n]) write = true;
    1505              :    }
    1506              : 
    1507            0 :    if (force || write)
    1508              :    {
    1509            0 :       telem<telem_coretemps>(m_coreTemps);
    1510            0 :       for (size_t n = 0; n < m_coreTemps.size(); ++n)
    1511              :       {
    1512            0 :          old_coreTemps[n] = m_coreTemps[n];
    1513              :       }
    1514              :    }
    1515              : 
    1516            0 :    return 0;
    1517              : }
    1518              : 
    1519            0 : int sysMonitor::recordDriveTemps(bool force)
    1520              : {
    1521            0 :    static std::vector<std::string> old_diskNames;
    1522            0 :    static std::vector<float> old_diskTemps;
    1523              : 
    1524            0 :    if (old_diskTemps.size() != m_diskTemps.size() || old_diskNames.size() != m_diskNames.size())
    1525              :    {
    1526            0 :       old_diskNames.resize(m_diskNames.size());
    1527            0 :       old_diskTemps.resize(m_diskTemps.size(), -1e30);
    1528              :    }
    1529              : 
    1530            0 :    bool write = false;
    1531              : 
    1532            0 :    for (size_t n = 0; n < m_diskTemps.size(); ++n)
    1533              :    {
    1534            0 :       if (m_diskTemps[n] != old_diskTemps[n] || m_diskNames[n] != old_diskNames[n]) write = true;
    1535              :    }
    1536              : 
    1537            0 :    if (force || write)
    1538              :    {
    1539            0 :       telem<telem_drivetemps>({ m_diskNames, m_diskTemps });
    1540            0 :       for (size_t n = 0; n < m_diskTemps.size(); ++n)
    1541              :       {
    1542            0 :          old_diskNames[n] = m_diskNames[n];
    1543            0 :          old_diskTemps[n] = m_diskTemps[n];
    1544              :       }
    1545              :    }
    1546              : 
    1547            0 :    return 0;
    1548              : }
    1549              : 
    1550            0 : int sysMonitor::recordUsage(bool force)
    1551              : {
    1552              :    static float old_ramUsage = 0;
    1553              :    static float old_bootUsage = 0;
    1554              :    static float old_rootUsage = 0;
    1555              :    static float old_dataUsage = 0;
    1556              : 
    1557            0 :    if (old_ramUsage != m_ramUsage || old_bootUsage != m_bootUsage || old_rootUsage != m_rootUsage || old_dataUsage != m_dataUsage || force)
    1558              :    {
    1559            0 :       telem<telem_usage>({ m_ramUsage, m_bootUsage, m_rootUsage, m_dataUsage });
    1560              : 
    1561            0 :       old_ramUsage = m_ramUsage;
    1562            0 :       old_bootUsage = m_bootUsage;
    1563            0 :       old_rootUsage = m_rootUsage;
    1564            0 :       old_dataUsage = m_dataUsage;
    1565              :    }
    1566              : 
    1567            0 :    return 0;
    1568              : }
    1569              : 
    1570            0 : int sysMonitor::recordChronyStatus(bool force)
    1571              : {
    1572            0 :    static std::string old_chronySourceMac;
    1573            0 :    static std::string old_chronySourceIP;
    1574            0 :    static std::string old_chronySynch;
    1575            0 :    static std::string old_chronyLeap;
    1576              : 
    1577              : 
    1578            0 :    if (old_chronySourceMac != m_chronySourceMac || old_chronySourceIP != m_chronySourceIP || old_chronySynch != m_chronySynch || old_chronyLeap != m_chronyLeap || force)
    1579              :    {
    1580            0 :       telem<telem_chrony_status>({ m_chronySourceMac, m_chronySourceIP, m_chronySynch, m_chronyLeap });
    1581              : 
    1582            0 :       old_chronySourceMac = m_chronySourceMac;
    1583            0 :       old_chronySourceIP = m_chronySourceIP;
    1584            0 :       old_chronySynch = m_chronySynch;
    1585            0 :       old_chronyLeap = m_chronyLeap;
    1586              :    }
    1587              : 
    1588            0 :    return 0;
    1589              : }
    1590              : 
    1591            0 : int sysMonitor::recordChronyStats(bool force)
    1592              : {
    1593              :    static double old_chronySystemTime = 1e50; //to force an update the first time no matter what
    1594              :    static double old_chronyLastOffset = 0;
    1595              :    static double old_chronyRMSOffset = 0;
    1596              :    static double old_chronyFreq = 0;
    1597              :    static double old_chronyResidFreq = 0;
    1598              :    static double old_chronySkew = 0;
    1599              :    static double old_chronyRootDelay = 0;
    1600              :    static double old_chronyRootDispersion = 0;
    1601              :    static double old_chronyUpdateInt = 0;
    1602              : 
    1603            0 :    if(old_chronySystemTime == m_chronySystemTime || old_chronyLastOffset == m_chronyLastOffset ||
    1604            0 :       old_chronyRMSOffset == m_chronyRMSOffset || old_chronyFreq == m_chronyFreq ||
    1605            0 :       old_chronyResidFreq == m_chronyResidFreq || old_chronySkew == m_chronySkew ||
    1606            0 :       old_chronyRootDelay == m_chronyRootDelay || old_chronyRootDispersion == m_chronyRootDispersion ||
    1607            0 :       old_chronyUpdateInt == m_chronyUpdateInt || force)
    1608              :    {
    1609              : 
    1610            0 :       telem<telem_chrony_stats>({ m_chronySystemTime, m_chronyLastOffset, m_chronyRMSOffset, m_chronyFreq, m_chronyResidFreq, m_chronySkew,
    1611              :                                      m_chronyRootDelay, m_chronyRootDispersion, m_chronyUpdateInt });
    1612              : 
    1613            0 :       old_chronySystemTime = m_chronySystemTime;
    1614            0 :       old_chronyLastOffset = m_chronyLastOffset;
    1615            0 :       old_chronyRMSOffset = m_chronyRMSOffset;
    1616            0 :       old_chronyFreq = m_chronyFreq;
    1617            0 :       old_chronyResidFreq = m_chronyResidFreq;
    1618            0 :       old_chronySkew = m_chronySkew;
    1619            0 :       old_chronyRootDelay = m_chronyRootDelay;
    1620            0 :       old_chronyRootDispersion = m_chronyRootDispersion;
    1621            0 :       old_chronyUpdateInt = m_chronyUpdateInt;
    1622              : 
    1623              :    }
    1624              : 
    1625            0 :    return 0;
    1626              : }
    1627              : 
    1628              : } //namespace app
    1629              : } //namespace MagAOX
    1630              : 
    1631              : #endif //sysMonitor_hpp
        

Generated by: LCOV version 2.0-1