API
sysMonitor.hpp
Go to the documentation of this file.
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 
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  ~sysMonitor() noexcept
94  {
95  }
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 };
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:
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 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  return;
367 }
368 
370 {
371  config.add("sysType", "", "sysType", argType::Required, "", "sysType", false, "string", "The system type, Intel (default) or AMD");
372  config.add("diskNames", "", "diskNames", argType::Required, "", "diskNames", false, "vector<string>", "The names (/dev/sdX) of the drives to monitor");
373  config.add("warningCoreTemp", "", "warningCoreTemp", argType::Required, "", "warningCoreTemp", false, "int", "The warning temperature for CPU cores.");
374  config.add("criticalCoreTemp", "", "criticalCoreTemp", argType::Required, "", "criticalCoreTemp", false, "int", "The critical temperature for CPU cores.");
375  config.add("warningDiskTemp", "", "warningDiskTemp", argType::Required, "", "warningDiskTemp", false, "int", "The warning temperature for the disk.");
376  config.add("criticalDiskTemp", "", "criticalDiskTemp", argType::Required, "", "criticalDiskTemp", false, "int", "The critical temperature for disk.");
377 
379 }
380 
382 {
383  std::string st;
384  //Configure for default, such that logs are correct after config
385  if (m_sysType == sysType::Intel)
386  {
387  st = "Intel";
388  }
389  else if (m_sysType == sysType::AMD)
390  {
391  st = "AMD";
392  }
393  config(st, "sysType");
394  if (st == "Intel")
395  {
397  }
398  else if (st == "AMD")
399  {
401  }
402  else
403  {
404  log<software_critical>({ __FILE__, __LINE__, "Invalid system type specified." });
405  m_shutdown = 1;
406  }
407 
408  config(m_diskNameList, "diskNames");
409  config(m_warningCoreTemp, "warningCoreTemp");
410  config(m_criticalCoreTemp, "criticalCoreTemp");
411  config(m_warningDiskTemp, "warningDiskTemp");
412  config(m_criticalDiskTemp, "criticalDiskTemp");
413 
415 }
416 
418 {
419 
420  REG_INDI_NEWPROP_NOCB(m_indiP_core_temps, "core_temps", pcf::IndiProperty::Number);
421  m_indiP_core_temps.add(pcf::IndiElement("max"));
422  m_indiP_core_temps.add(pcf::IndiElement("min"));
423  m_indiP_core_temps.add(pcf::IndiElement("mean"));
424 
425  REG_INDI_NEWPROP_NOCB(m_indiP_core_loads, "core_loads", pcf::IndiProperty::Number);
426  m_indiP_core_loads.add(pcf::IndiElement("max"));
427  m_indiP_core_loads.add(pcf::IndiElement("min"));
428  m_indiP_core_loads.add(pcf::IndiElement("mean"));
429 
430  REG_INDI_NEWPROP_NOCB(m_indiP_drive_temps, "drive_temps", pcf::IndiProperty::Number);
432  for (unsigned int i = 0; i < m_diskTemps.size(); i++)
433  {
434  m_indiP_drive_temps.add(pcf::IndiElement(m_diskNames[i]));
435  m_indiP_drive_temps[m_diskNames[i]].set<double>(m_diskTemps[i]);
436  }
437 
438  REG_INDI_NEWPROP_NOCB(m_indiP_usage, "resource_use", pcf::IndiProperty::Number);
439  m_indiP_usage.add(pcf::IndiElement("root_usage"));
440  m_indiP_usage.add(pcf::IndiElement("boot_usage"));
441  m_indiP_usage.add(pcf::IndiElement("data_usage"));
442  m_indiP_usage.add(pcf::IndiElement("ram_usage"));
443 
444  m_indiP_usage["root_usage"].set<double>(0.0);
445  m_indiP_usage["boot_usage"].set<double>(0.0);
446  m_indiP_usage["data_usage"].set<double>(0.0);
447  m_indiP_usage["ram_usage"].set<double>(0.0);
448 
449 
450 
451  REG_INDI_NEWPROP_NOCB(m_indiP_chronyStatus, "chrony_status", pcf::IndiProperty::Text);
452  m_indiP_chronyStatus.add(pcf::IndiElement("synch"));
453  m_indiP_chronyStatus.add(pcf::IndiElement("source"));
454 
455  REG_INDI_NEWPROP_NOCB(m_indiP_chronyStats, "chrony_stats", pcf::IndiProperty::Number);
456  m_indiP_chronyStats.add(pcf::IndiElement("system_time"));
457  m_indiP_chronyStats.add(pcf::IndiElement("last_offset"));
458  m_indiP_chronyStats.add(pcf::IndiElement("rms_offset"));
459 
462 
464  {
465  return log<software_error, -1>({ __FILE__,__LINE__ });
466  }
467 
469  {
470  log<software_critical>({ __FILE__, __LINE__ });
471  return -1;
472  }
473 
475  return 0;
476 }
477 
479 {
480 
481  m_coreTemps.clear();
482  int rvCPUTemp = findCPUTemperatures(m_coreTemps);
483  if (rvCPUTemp >= 0)
484  {
486  }
487 
488  if (rvCPUTemp >= 0)
489  {
490  if (rvCPUTemp == 1)
491  {
492  log<telem_coretemps>(m_coreTemps, logPrio::LOG_WARNING);
493  }
494  else if (rvCPUTemp == 2)
495  {
496  log<telem_coretemps>(m_coreTemps, logPrio::LOG_ALERT);
497  }
498 
499  recordCoreTemps();
500  }
501  else
502  {
503  log<software_error>({ __FILE__, __LINE__,"Could not log values for CPU core temps." });
504  }
505 
506  m_coreLoads.clear();
507  int rvCPULoad = findCPULoads(m_coreLoads);
508 
509  if (rvCPULoad >= 0)
510  {
511  recordCoreLoads();
512  }
513  else
514  {
515  log<software_error>({ __FILE__, __LINE__,"Could not log values for CPU core loads." });
516  }
517 
518 
519  m_diskNames.clear();
520  m_diskTemps.clear();
521  int rvDiskTemp = findDiskTemperature(m_diskNames, m_diskTemps);
522 
523  if (rvDiskTemp >= 0)
524  {
525  rvDiskTemp = criticalDiskTemperature(m_diskTemps);
526  }
527 
528  if (rvDiskTemp >= 0)
529  {
530  if (rvDiskTemp == 1)
531  {
532  log<telem_drivetemps>({ m_diskNames, m_diskTemps }, logPrio::LOG_WARNING);
533  }
534  else if (rvDiskTemp == 2)
535  {
536  log<telem_drivetemps>({ m_diskNames, m_diskTemps }, logPrio::LOG_ALERT);
537  }
538 
540  }
541  else
542  {
543  log<software_error>({ __FILE__, __LINE__,"Could not log values for drive temps." });
544  }
545 
546  int rvDiskUsage = findDiskUsage(m_rootUsage, m_dataUsage, m_bootUsage);
547  int rvRamUsage = findRamUsage(m_ramUsage);
548 
549 
550  if (rvDiskUsage >= 0 && rvRamUsage >= 0)
551  {
552  recordUsage();
553  }
554  else
555  {
556  log<software_error>({ __FILE__, __LINE__,"Could not log values for usage." });
557  }
558 
559 
560  if (findChronyStatus() == 0)
561  {
562  }
563  else
564  {
565  log<software_error>({ __FILE__, __LINE__,"Could not get chronyd status." });
566  }
567 
569  {
570  log<software_error>({ __FILE__, __LINE__ });
571  return 0;
572  }
573 
574  updateVals();
575 
576  return 0;
577 }
578 
580 {
581  try
582  {
583  if (m_setlatThread.joinable())
584  {
585  m_setlatThread.join();
586  }
587  }
588  catch (...) {}
589 
591 
592  return 0;
593 }
594 
595 int sysMonitor::findCPUTemperatures(std::vector<float>& temps)
596 {
597  std::vector<std::string> commandList{ "sensors" };
598 
599  std::vector<std::string> commandOutput, commandError;
600 
601  if (sys::runCommand(commandOutput, commandError, commandList) < 0)
602  {
603  if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
604  else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
605  }
606 
607  if (commandError.size() > 0)
608  {
609  for (size_t n = 0; n < commandError.size(); ++n)
610  {
611  log<software_error>({ __FILE__, __LINE__, "sensors stderr: " + commandError[n] });
612  }
613  }
614 
615  int rv = -1;
616  for (size_t n = 0; n < commandOutput.size(); ++n)
617  {
618  float tempVal;
619  if (parseCPUTemperatures(tempVal, commandOutput[n]) == 0)
620  {
621  temps.push_back(tempVal);
622  rv = 0;
623  }
624  }
625  return rv;
626 }
627 
629  const std::string& line
630  )
631 {
632  if (m_sysType == sysType::Intel)
633  {
634  return parseCPUTemperaturesIntel(temp, line);
635  }
636  else if (m_sysType == sysType::AMD)
637  {
638  return parseCPUTemperaturesAMD(temp, line);
639  }
640  else
641  {
642  log<software_error>({ __FILE__, __LINE__, "invalid system type" });
643  return -1;
644  }
645 
646 }
647 
648 
650  const std::string& line
651  )
652 {
653  if (line.length() <= 1)
654  {
655  temp = -999;
656  return -1;
657  }
658 
659  std::string str = line.substr(0, 5);
660  if (str.compare("Core ") == 0)
661  {
662  size_t st = line.find(':', 0);
663  if (st == std::string::npos)
664  {
665  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
666  temp = -999;
667  return -1;
668  }
669 
670  ++st;
671 
672  size_t ed = line.find('C', st);
673  if (ed == std::string::npos)
674  {
675  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
676  temp = -999;
677  return -1;
678  }
679 
680  --ed;
681 
682  std::string temp_str = line.substr(st, ed - st);
683 
684  try
685  {
686  temp = std::stof(temp_str);
687  }
688  catch (const std::invalid_argument& e)
689  {
690  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
691  temp = -999;
692  return -1;
693  }
694 
695  if (m_warningCoreTemp == 0)
696  {
697  std::istringstream iss(line);
698  std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
699  try
700  {
701  tokens.at(5).pop_back();
702  tokens.at(5).pop_back();
703  tokens.at(5).pop_back();
704  tokens.at(5).pop_back();
705  tokens.at(5).erase(0, 1);
706  m_warningCoreTemp = std::stof(tokens.at(5));
707  }
708  catch (const std::invalid_argument& e)
709  {
710  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing warning CPU temperatures." });
711  return -1;
712  }
713  }
714  if (m_criticalCoreTemp == 0)
715  {
716  std::istringstream iss(line);
717  std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
718  try
719  {
720  tokens.at(8).pop_back();
721  tokens.at(8).pop_back();
722  tokens.at(8).pop_back();
723  tokens.at(8).pop_back();
724  tokens.at(8).erase(0, 1);
725  m_criticalCoreTemp = std::stof(tokens.at(8));
726  }
727  catch (const std::invalid_argument& e)
728  {
729  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing critical CPU temperatures." });
730  temp = -999;
731  return -1;
732  }
733  }
734  return 0;
735  }
736  else
737  {
738  temp = -999;
739  return -1;
740  }
741 
742 }
743 
745  const std::string& line
746  )
747 {
748  if (line.length() <= 1)
749  {
750  temp = -999;
751  return -1;
752  }
753 
754  std::string str = line.substr(0, 6);
755  if (str.compare("Tctl: ") == 0)
756  {
757  size_t ed = line.find('C', 0);
758  if (ed == std::string::npos)
759  {
760  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
761  temp = -999;
762  return -1;
763  }
764 
765  str = line.substr(7, ((ed - 1) - 7)); //ed-1 to eat degree.
766 
767  try
768  {
769  temp = std::stof(str);
770  }
771  catch (...)
772  {
773  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU temperatures." });
774  temp = -999;
775  return -1;
776  }
777  return 0;
778  }
779  else
780  {
781  temp = -999;
782  return -1;
783  }
784 }
785 
786 int sysMonitor::criticalCoreTemperature(std::vector<float>& v)
787 {
788  int coreNum = 0, rv = 0;
789  for (auto it : v)
790  {
791  float temp = it;
792  if (temp >= m_warningCoreTemp && temp < m_criticalCoreTemp)
793  {
794  std::cout << "Warning temperature for Core " << coreNum << std::endl;
795  if (rv < 2)
796  {
797  rv = 1;
798  }
799  }
800  else if (temp >= m_criticalCoreTemp)
801  {
802  std::cout << "Critical temperature for Core " << coreNum << std::endl;
803  rv = 2;
804  }
805  ++coreNum;
806  }
807  return rv;
808 }
809 
810 int sysMonitor::findCPULoads(std::vector<float>& loads)
811 {
812  std::vector<std::string> commandList{ "mpstat", "-P", "ALL", "1", "1" };
813  std::vector<std::string> commandOutput, commandError;
814 
815  if (sys::runCommand(commandOutput, commandError, commandList) < 0)
816  {
817  if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
818  else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
819  }
820 
821  if (commandError.size() > 0)
822  {
823  for (size_t n = 0; n < commandError.size(); ++n)
824  {
825  log<software_error>({ __FILE__, __LINE__, "mpstat stderr: " + commandError[n] });
826  }
827  }
828 
829  int rv = -1;
830  // If output lines are less than 5 (with one CPU, guarenteed output is 5)
831  if (commandOutput.size() < 5)
832  {
833  return log<software_error, -1>({ __FILE__, __LINE__, "not enough lines returned by mpstat" });
834  }
835  //start iterating at fourth line
836  for (auto line = commandOutput.begin() + 4; line != commandOutput.end(); line++)
837  {
838  float loadVal;
839  if (parseCPULoads(loadVal, *line) == 0)
840  {
841  loads.push_back(loadVal);
842  rv = 0;
843  }
844  }
845  return rv;
846 }
847 
848 int sysMonitor::parseCPULoads( float & loadVal,
849  const std::string & line
850  )
851 {
852  if (line.length() <= 1)
853  {
854  log<software_error>({ __FILE__, __LINE__,"zero length line in parseCPULoads." });
855  return -1;
856  }
857  std::istringstream iss(line);
858 
859  std::vector<std::string> tokens(std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{});
860  if (tokens.size() < 8) return 1;
861 
862  float cpu_load;
863  try
864  {
865  cpu_load = 100.0 - std::stof(tokens.at(tokens.size() - 1));
866  }
867  catch (const std::invalid_argument& e)
868  {
869  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing CPU core usage." });
870  return -1;
871  }
872  catch (const std::out_of_range& e)
873  {
874  log<software_error>({ __FILE__, __LINE__,"Out of range exception in parseCPULoads." });
875  return -1;
876  }
877  cpu_load /= 100;
878  loadVal = cpu_load;
879  return 0;
880 }
881 
882 int sysMonitor::findDiskTemperature( std::vector<std::string>& hdd_names,
883  std::vector<float>& hdd_temps
884  )
885 {
886  std::vector<std::string> commandList{ "hddtemp" };
887  for (size_t n = 0;n < m_diskNameList.size();++n)
888  {
889  commandList.push_back(m_diskNameList[n]);
890  }
891 
892  std::vector<std::string> commandOutput, commandError;
893 
894  if (sys::runCommand(commandOutput, commandError, commandList) < 0)
895  {
896  if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
897  else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
898  }
899 
900  if (commandError.size() > 0)
901  {
902  for (size_t n = 0; n < commandError.size(); ++n)
903  {
904  log<software_error>({ __FILE__, __LINE__, "hddtemp stderr: " + commandError[n] });
905  }
906  }
907 
908  int rv = -1;
909  for (auto line : commandOutput)
910  {
911  std::string driveName;
912  float tempVal;
913  if (parseDiskTemperature(driveName, tempVal, line) == 0)
914  {
915  hdd_names.push_back(driveName);
916  hdd_temps.push_back(tempVal);
917  rv = 0;
918  }
919  }
920  return rv;
921 
922  //return 0;
923 }
924 
925 int sysMonitor::parseDiskTemperature( std::string& driveName,
926  float& hdd_temp,
927  const std::string& line
928  )
929 {
930  float tempValue;
931  if (line.length() <= 6)
932  {
933  driveName = "";
934  hdd_temp = -999;
935  return -1;
936  }
937 
938  size_t sp = line.find(':', 0);
939  driveName = line.substr(5, sp - 5);
940 
941  std::istringstream iss(line);
942  std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
943 
944  for (auto temp_s : tokens)
945  {
946  try
947  {
948  if (isdigit(temp_s.at(0)) && temp_s.substr(temp_s.length() - 1, 1) == "C")
949  {
950  temp_s.pop_back();
951  temp_s.pop_back();
952  try
953  {
954  tempValue = std::stof(temp_s);
955  }
956  catch (const std::invalid_argument& e)
957  {
958  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive temperatures." });
959  hdd_temp = -999;
960  driveName = "";
961  return -1;
962  }
963  hdd_temp = tempValue;
964  if (m_warningDiskTemp == 0)
965  {
966  m_warningDiskTemp = tempValue + (.1 * tempValue);
967  }
968  if (m_criticalDiskTemp == 0)
969  {
970  m_criticalDiskTemp = tempValue + (.2 * tempValue);
971  }
972  return 0;
973  }
974  }
975  catch (const std::out_of_range& e)
976  {
977  hdd_temp = -999;
978  driveName = "";
979  return -1;
980  }
981  }
982 
983  hdd_temp = -999;
984  driveName = "";
985  return -1;
986 }
987 
988 int sysMonitor::criticalDiskTemperature(std::vector<float>& v)
989 {
990  int rv = 0;
991  for (auto it : v)
992  {
993  float temp = it;
994  if (temp >= m_warningDiskTemp && temp < m_criticalDiskTemp)
995  {
996  std::cout << "Warning temperature for Disk" << std::endl;
997  if (rv < 2)
998  {
999  rv = 1;
1000  }
1001  }
1002  else if (temp >= m_criticalDiskTemp)
1003  {
1004  std::cout << "Critical temperature for Disk " << std::endl;
1005  rv = 2;
1006  }
1007  }
1008  return rv;
1009 }
1010 
1011 int sysMonitor::findDiskUsage(float& rootUsage, float& dataUsage, float& bootUsage)
1012 {
1013  std::vector<std::string> commandList{ "df" };
1014 
1015  std::vector<std::string> commandOutput, commandError;
1016 
1017  if (sys::runCommand(commandOutput, commandError, commandList) < 0)
1018  {
1019  if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
1020  else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
1021  }
1022 
1023  if (commandError.size() > 0)
1024  {
1025  for (size_t n = 0; n < commandError.size(); ++n)
1026  {
1027  log<software_error>({ __FILE__, __LINE__, "df stderr: " + commandError[n] });
1028  }
1029  }
1030 
1031  int rv = -1;
1032  for (auto line : commandOutput)
1033  {
1034  int rvDiskUsage = parseDiskUsage(line, rootUsage, dataUsage, bootUsage);
1035  if (rvDiskUsage == 0)
1036  {
1037  rv = 0;
1038  }
1039  }
1040  return rv;
1041 }
1042 
1043 int sysMonitor::parseDiskUsage(std::string line, float& rootUsage, float& dataUsage, float& bootUsage)
1044 {
1045  if (line.length() <= 1)
1046  {
1047  return -1;
1048  }
1049 
1050  std::istringstream iss(line);
1051  std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
1052 
1053  try {
1054  if (tokens.at(5).compare("/") == 0)
1055  {
1056  tokens.at(4).pop_back();
1057  try
1058  {
1059  rootUsage = std::stof(tokens.at(4)) / 100;
1060  return 0;
1061  }
1062  catch (const std::invalid_argument& e)
1063  {
1064  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
1065  return -1;
1066  }
1067  }
1068  else if (tokens.at(5).compare("/data") == 0)
1069  {
1070  tokens.at(4).pop_back();
1071  try
1072  {
1073  dataUsage = std::stof(tokens.at(4)) / 100;
1074  return 0;
1075  }
1076  catch (const std::invalid_argument& e)
1077  {
1078  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
1079  return -1;
1080  }
1081  }
1082  else if (tokens.at(5).compare("/boot") == 0)
1083  {
1084  tokens.at(4).pop_back();
1085  try
1086  {
1087  bootUsage = std::stof(tokens.at(4)) / 100;
1088  return 0;
1089  }
1090  catch (const std::invalid_argument& e)
1091  {
1092  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
1093  return -1;
1094  }
1095  }
1096  }
1097  catch (const std::out_of_range& e) {
1098  return -1;
1099  }
1100  return -1;
1101 }
1102 
1103 int sysMonitor::findRamUsage(float& ramUsage)
1104 {
1105  std::vector<std::string> commandList{ "free", "-m" };
1106 
1107  std::vector<std::string> commandOutput, commandError;
1108 
1109  if (sys::runCommand(commandOutput, commandError, commandList) < 0)
1110  {
1111  if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
1112  else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
1113  }
1114 
1115  if (commandError.size() > 0)
1116  {
1117  for (size_t n = 0; n < commandError.size(); ++n)
1118  {
1119  log<software_error>({ __FILE__, __LINE__, "free stderr: " + commandError[n] });
1120  }
1121  }
1122 
1123  for (auto line : commandOutput)
1124  {
1125  if (parseRamUsage(line, ramUsage) == 0)
1126  {
1127  return 0;
1128  }
1129  }
1130  return -1;
1131 }
1132 
1133 int sysMonitor::parseRamUsage(std::string line, float& ramUsage)
1134 {
1135  if (line.length() <= 1)
1136  {
1137  return -1;
1138  }
1139  std::istringstream iss(line);
1140  std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
1141  try
1142  {
1143  if (tokens.at(0).compare("Mem:") != 0)
1144  {
1145  return -1;
1146  }
1147  ramUsage = std::stof(tokens.at(2)) / std::stof(tokens.at(1));
1148  if (ramUsage > 1 || ramUsage == 0)
1149  {
1150  ramUsage = -1;
1151  return -1;
1152  }
1153  return 0;
1154  }
1155  catch (const std::invalid_argument& e)
1156  {
1157  log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing RAM usage." });
1158  return -1;
1159  }
1160  catch (const std::out_of_range& e) {
1161  return -1;
1162  }
1163 }
1164 
1166 {
1167  std::vector<std::string> commandList{ "chronyc", "-c", "tracking" };
1168 
1169  std::vector<std::string> commandOutput, commandError;
1170 
1171  if (sys::runCommand(commandOutput, commandError, commandList) < 0)
1172  {
1173  if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
1174  else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
1175  }
1176 
1177  if (commandError.size() > 0)
1178  {
1179  for (size_t n = 0; n < commandError.size(); ++n)
1180  {
1181  log<software_error>({ __FILE__, __LINE__, "chronyc stderr: " + commandError[n] });
1182  }
1183  }
1184 
1185  if (commandOutput.size() < 1)
1186  {
1187  log<software_error>({ __FILE__,__LINE__, "no response from chronyc -c" });
1188  return -1;
1189  }
1190 
1191  std::vector<std::string> results;
1192  mx::ioutils::parseStringVector(results, commandOutput[0], ',');
1193 
1194  if (results.size() < 1)
1195  {
1196  log<software_error>({ __FILE__,__LINE__, "wrong number of fields from chronyc -c" });
1197  return -1;
1198  }
1199 
1200  static std::string last_mac;
1201  static std::string last_ip;
1202  m_chronySourceMac = results[0];
1203  m_chronySourceIP = results[1];
1204  if (m_chronySourceMac == "7F7F0101" || m_chronySourceIP == "127.0.0.1")
1205  {
1206  m_chronySynch = "NO";
1207  log<text_log>("chrony is not synchronized", logPrio::LOG_WARNING);
1208  }
1209  else
1210  {
1211  m_chronySynch = "YES";
1212  }
1213 
1214  if (last_mac != m_chronySourceMac || last_ip != m_chronySourceIP)
1215  {
1216  log<text_log>("chrony is synchronizing to " + m_chronySourceMac + " / " + m_chronySourceIP);
1217  last_mac = m_chronySourceMac;
1218  last_ip = m_chronySourceIP;
1219  }
1220 
1221 
1222 
1223  m_chronySystemTime = std::stod(results[4]);
1224  m_chronyLastOffset = std::stod(results[5]);
1225  m_chronyRMSOffset = std::stod(results[6]);
1226  m_chronyFreq = std::stod(results[7]);
1227  m_chronyResidFreq = std::stod(results[8]);
1228  m_chronySkew = std::stod(results[9]);
1229  m_chronyRootDelay = std::stod(results[10]);
1230  m_chronyRootDispersion = std::stod(results[11]);
1231  m_chronyUpdateInt = std::stod(results[12]);
1232  m_chronyLeap = results[13];
1233 
1236 
1237  return 0;
1238 }
1239 
1241 {
1242  float min, max, mean;
1243 
1244  if (m_coreLoads.size() > 0)
1245  {
1246  min = m_coreLoads[0];
1247  max = m_coreLoads[0];
1248  mean = m_coreLoads[0];
1249  for (size_t n = 1; n < m_coreLoads.size(); ++n)
1250  {
1251  if (m_coreLoads[n] < min) min = m_coreLoads[n];
1252  if (m_coreLoads[n] > max) max = m_coreLoads[n];
1253  mean += m_coreLoads[n];
1254  }
1255  mean /= m_coreLoads.size();
1256 
1257  updateIfChanged<float>(m_indiP_core_loads, { "min","max","mean" }, { min,max,mean });
1258  }
1259 
1260 
1261  if (m_coreTemps.size() > 0)
1262  {
1263  min = m_coreTemps[0];
1264  max = m_coreTemps[0];
1265  mean = m_coreTemps[0];
1266  for (size_t n = 1; n < m_coreTemps.size(); ++n)
1267  {
1268  if (m_coreTemps[n] < min) min = m_coreTemps[n];
1269  if (m_coreTemps[n] > max) max = m_coreTemps[n];
1270  mean += m_coreTemps[n];
1271  }
1272  mean /= m_coreTemps.size();
1273 
1274  updateIfChanged<float>(m_indiP_core_temps, { "min","max","mean" }, { min,max,mean });
1275  }
1276 
1278 
1279  updateIfChanged<float>(m_indiP_usage, { "root_usage","boot_usage","data_usage","ram_usage" }, { m_rootUsage,m_bootUsage,m_dataUsage,m_ramUsage });
1280 
1281 
1284 
1285  updateIfChanged<double>(m_indiP_chronyStats, { "system_time", "last_offset", "rms_offset" }, { m_chronySystemTime, m_chronyLastOffset, m_chronyRMSOffset });
1286 
1287  if (m_setLatency)
1288  {
1289  updateSwitchIfChanged(m_indiP_setlat, "toggle", pcf::IndiElement::On, INDI_OK);
1290  }
1291  else
1292  {
1293  updateSwitchIfChanged(m_indiP_setlat, "toggle", pcf::IndiElement::Off, INDI_IDLE);
1294  }
1295 
1296  return 0;
1297 }
1298 
1299 inline
1301 {
1302  s->setlatThreadExec();
1303 }
1304 
1305 inline
1307 {
1308  m_setlatThreadID = syscall(SYS_gettid);
1309 
1310  //Wait fpr the thread starter to finish initializing this thread.
1311  while (m_setlatThreadInit == true && m_shutdown == 0)
1312  {
1313  sleep(1);
1314  }
1315 
1316  int fd = 0;
1317  while (m_shutdown == 0)
1318  {
1319  if (m_setLatency)
1320  {
1321  if (fd <= 0)
1322  {
1323  elevatedPrivileges ep(this);
1324 
1325  for (size_t cpu = 0; cpu < m_coreLoads.size(); ++cpu) ///\todo this needs error checks
1326  {
1327  std::string cpuFile = "/sys/devices/system/cpu/cpu";
1328  cpuFile += std::to_string(cpu);
1329  cpuFile += "/cpufreq/scaling_governor";
1330  int wfd = open(cpuFile.c_str(), O_WRONLY);
1331  ssize_t perfsz = sizeof("performance");
1332  ssize_t wrtsz = write(wfd, "performance", perfsz);
1333  if(wrtsz != perfsz)
1334  {
1335  log<software_error>({ __FILE__,__LINE__,"error setting performance governor for CPU " + std::to_string(cpu) });
1336  }
1337  close(wfd);
1338  }
1339  log<text_log>("set governor to performance", logPrio::LOG_NOTICE);
1340 
1341  fd = open("/dev/cpu_dma_latency", O_WRONLY);
1342 
1343  if (fd <= 0) log<software_error>({ __FILE__,__LINE__,"error opening cpu_dma_latency" });
1344  else
1345  {
1346  int l = 0;
1347  if (write(fd, &l, sizeof(l)) != sizeof(l))
1348  {
1349  log<software_error>({ __FILE__,__LINE__,"error writing to cpu_dma_latency" });
1350  }
1351  else
1352  {
1353  log<text_log>("set latency to 0", logPrio::LOG_NOTICE);
1354  }
1355  }
1356 
1357 
1358  }
1359  }
1360  else
1361  {
1362  if (fd != 0)
1363  {
1364  close(fd);
1365  fd = 0;
1366  log<text_log>("restored CPU latency to default", logPrio::LOG_NOTICE);
1367 
1368  elevatedPrivileges ep(this);
1369  for (size_t cpu = 0; cpu < m_coreLoads.size(); ++cpu) ///\todo this needs error checks
1370  {
1371  std::string cpuFile = "/sys/devices/system/cpu/cpu";
1372  cpuFile += std::to_string(cpu);
1373  cpuFile += "/cpufreq/scaling_governor";
1374  int wfd = open(cpuFile.c_str(), O_WRONLY);
1375  ssize_t pwrsz = sizeof("powersave");
1376  ssize_t wrtsz = write(wfd, "powersave", pwrsz);
1377  if(wrtsz != pwrsz)
1378  {
1379  log<software_error>({ __FILE__,__LINE__,"error setting powersave governor for CPU " + std::to_string(cpu) });
1380  }
1381 
1382  close(wfd);
1383  }
1384  log<text_log>("set governor to powersave", logPrio::LOG_NOTICE);
1385  }
1386  }
1387 
1388  sleep(1);
1389  }
1390 
1391  if (fd) close(fd);
1392 
1393 }
1394 
1395 INDI_NEWCALLBACK_DEFN(sysMonitor, m_indiP_setlat)(const pcf::IndiProperty& ipRecv)
1396 {
1397  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_setlat, ipRecv);
1398 
1399  if (ipRecv.getName() != m_indiP_setlat.getName())
1400  {
1401  log<software_error>({ __FILE__,__LINE__, "wrong INDI property received." });
1402  return -1;
1403  }
1404 
1405  if (!ipRecv.find("toggle")) return 0;
1406 
1407  if (ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
1408  {
1409  m_setLatency = false;
1410  }
1411 
1412  if (ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
1413  {
1414  m_setLatency = true;
1415  }
1416  return 0;
1417 }
1418 
1419 inline
1421 {
1423  telem_coretemps(),
1424  telem_drivetemps(),
1425  telem_usage(),
1428  );
1429 }
1430 
1432 {
1433  return recordCoreLoads(true);
1434 }
1435 
1437 {
1438  return recordCoreTemps(true);
1439 }
1440 
1442 {
1443  return recordDriveTemps(true);
1444 }
1445 
1447 {
1448  return recordUsage(true);
1449 }
1450 
1452 {
1453  return recordChronyStatus(true);
1454 }
1455 
1457 {
1458  return recordChronyStats(true);
1459 }
1460 
1462 {
1463  static std::vector<float> old_coreLoads;
1464 
1465  if (old_coreLoads.size() != m_coreLoads.size())
1466  {
1467  old_coreLoads.resize(m_coreLoads.size(), -1e30);
1468  }
1469 
1470  bool write = false;
1471 
1472  for (size_t n = 0; n < m_coreLoads.size(); ++n)
1473  {
1474  if (m_coreLoads[n] != old_coreLoads[n]) write = true;
1475  }
1476 
1477  if (force || write)
1478  {
1479  telem<telem_coreloads>(m_coreLoads);
1480 
1481  for (size_t n = 0; n < m_coreLoads.size(); ++n)
1482  {
1483  old_coreLoads[n] = m_coreLoads[n];
1484  }
1485  }
1486 
1487  return 0;
1488 }
1489 
1491 {
1492  static std::vector<float> old_coreTemps;
1493 
1494  if (old_coreTemps.size() != m_coreTemps.size())
1495  {
1496  old_coreTemps.resize(m_coreTemps.size(), -1e30);
1497  }
1498 
1499  bool write = false;
1500 
1501  for (size_t n = 0; n < m_coreTemps.size(); ++n)
1502  {
1503  if (m_coreTemps[n] != old_coreTemps[n]) write = true;
1504  }
1505 
1506  if (force || write)
1507  {
1508  telem<telem_coretemps>(m_coreTemps);
1509  for (size_t n = 0; n < m_coreTemps.size(); ++n)
1510  {
1511  old_coreTemps[n] = m_coreTemps[n];
1512  }
1513  }
1514 
1515  return 0;
1516 }
1517 
1519 {
1520  static std::vector<std::string> old_diskNames;
1521  static std::vector<float> old_diskTemps;
1522 
1523  if (old_diskTemps.size() != m_diskTemps.size() || old_diskNames.size() != m_diskNames.size())
1524  {
1525  old_diskNames.resize(m_diskNames.size());
1526  old_diskTemps.resize(m_diskTemps.size(), -1e30);
1527  }
1528 
1529  bool write = false;
1530 
1531  for (size_t n = 0; n < m_diskTemps.size(); ++n)
1532  {
1533  if (m_diskTemps[n] != old_diskTemps[n] || m_diskNames[n] != old_diskNames[n]) write = true;
1534  }
1535 
1536  if (force || write)
1537  {
1538  telem<telem_drivetemps>({ m_diskNames, m_diskTemps });
1539  for (size_t n = 0; n < m_diskTemps.size(); ++n)
1540  {
1541  old_diskNames[n] = m_diskNames[n];
1542  old_diskTemps[n] = m_diskTemps[n];
1543  }
1544  }
1545 
1546  return 0;
1547 }
1548 
1550 {
1551  static float old_ramUsage = 0;
1552  static float old_bootUsage = 0;
1553  static float old_rootUsage = 0;
1554  static float old_dataUsage = 0;
1555 
1556  if (old_ramUsage != m_ramUsage || old_bootUsage != m_bootUsage || old_rootUsage != m_rootUsage || old_dataUsage != m_dataUsage || force)
1557  {
1558  telem<telem_usage>({ m_ramUsage, m_bootUsage, m_rootUsage, m_dataUsage });
1559 
1560  old_ramUsage = m_ramUsage;
1561  old_bootUsage = m_bootUsage;
1562  old_rootUsage = m_rootUsage;
1563  old_dataUsage = m_dataUsage;
1564  }
1565 
1566  return 0;
1567 }
1568 
1570 {
1571  static std::string old_chronySourceMac;
1572  static std::string old_chronySourceIP;
1573  static std::string old_chronySynch;
1574  static std::string old_chronyLeap;
1575 
1576 
1577  if (old_chronySourceMac != m_chronySourceMac || old_chronySourceIP != m_chronySourceIP || old_chronySynch != m_chronySynch || old_chronyLeap != m_chronyLeap || force)
1578  {
1579  telem<telem_chrony_status>({ m_chronySourceMac, m_chronySourceIP, m_chronySynch, m_chronyLeap });
1580 
1581  old_chronySourceMac = m_chronySourceMac;
1582  old_chronySourceIP = m_chronySourceIP;
1583  old_chronySynch = m_chronySynch;
1584  old_chronyLeap = m_chronyLeap;
1585  }
1586 
1587  return 0;
1588 }
1589 
1591 {
1592  static double old_chronySystemTime = 1e50; //to force an update the first time no matter what
1593  static double old_chronyLastOffset = 0;
1594  static double old_chronyRMSOffset = 0;
1595  static double old_chronyFreq = 0;
1596  static double old_chronyResidFreq = 0;
1597  static double old_chronySkew = 0;
1598  static double old_chronyRootDelay = 0;
1599  static double old_chronyRootDispersion = 0;
1600  static double old_chronyUpdateInt = 0;
1601 
1602  if(old_chronySystemTime == m_chronySystemTime || old_chronyLastOffset == m_chronyLastOffset ||
1603  old_chronyRMSOffset == m_chronyRMSOffset || old_chronyFreq == m_chronyFreq ||
1604  old_chronyResidFreq == m_chronyResidFreq || old_chronySkew == m_chronySkew ||
1605  old_chronyRootDelay == m_chronyRootDelay || old_chronyRootDispersion == m_chronyRootDispersion ||
1606  old_chronyUpdateInt == m_chronyUpdateInt || force)
1607  {
1608 
1611 
1612  old_chronySystemTime = m_chronySystemTime;
1613  old_chronyLastOffset = m_chronyLastOffset;
1614  old_chronyRMSOffset = m_chronyRMSOffset;
1615  old_chronyFreq = m_chronyFreq;
1616  old_chronyResidFreq = m_chronyResidFreq;
1617  old_chronySkew = m_chronySkew;
1618  old_chronyRootDelay = m_chronyRootDelay;
1619  old_chronyRootDispersion = m_chronyRootDispersion;
1620  old_chronyUpdateInt = m_chronyUpdateInt;
1621 
1622  }
1623 
1624  return 0;
1625 }
1626 
1627 } //namespace app
1628 } //namespace MagAOX
1629 
1630 #endif //sysMonitor_hpp
Internal class to manage setuid privilege escalation with RAII.
Definition: MagAOXApp.hpp:324
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:3120
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
Definition: MagAOXApp.hpp:2543
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:100
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:3144
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
Definition: MagAOXApp.hpp:2157
int m_criticalDiskTemp
User defined critical temperature for drives.
Definition: sysMonitor.hpp:59
std::string m_chronySourceMac
Definition: sysMonitor.hpp:271
pcf::IndiProperty m_indiP_core_temps
Indi variable for reporting CPU core temperature(s)
Definition: sysMonitor.hpp:62
int findRamUsage(float &)
Finds current RAM usage.
int findDiskUsage(float &, float &, float &)
Finds usages of space for following directory paths: /; /data; /boot.
pcf::IndiProperty m_indiP_chronyStatus
Definition: sysMonitor.hpp:285
void setlatThreadExec()
Execute the frame grabber main loop.
float m_bootUsage
Disk usage in /boot path as a value out of 100.
Definition: sysMonitor.hpp:75
int parseDiskTemperature(std::string &driveName, float &temp, const std::string &line)
Parses string from system call to find drive temperatures.
Definition: sysMonitor.hpp:925
int recordChronyStats(bool force=false)
float m_rootUsage
Disk usage in root path as a value out of 100.
Definition: sysMonitor.hpp:73
int recordDriveTemps(bool force=false)
int recordTelem(const telem_coreloads *)
int parseCPUTemperaturesAMD(float &temp, const std::string &line)
Parses string from system call to find CPU temperatures on an AMD system.
Definition: sysMonitor.hpp:744
int recordChronyStatus(bool force=false)
std::vector< std::string > m_diskNames
vector of names of the hard disks returned by hdd_temp
Definition: sysMonitor.hpp:70
virtual void setupConfig()
Setup the user-defined warning and critical values for core and drive temperatures.
Definition: sysMonitor.hpp:369
int parseCPUTemperatures(float &temp, const std::string &line)
Parses string from system call to find CPU temperatures.
Definition: sysMonitor.hpp:628
~sysMonitor() noexcept
D'tor, declared and defined for noexcept.
Definition: sysMonitor.hpp:93
virtual int appLogic()
Implementation of reading and logging each of the measured statistics.
Definition: sysMonitor.hpp:478
int parseCPULoads(float &, const std::string &)
Parses string from system call to find CPU usage loads.
Definition: sysMonitor.hpp:848
std::thread m_setlatThread
A separate thread for the actual setting of low latency.
Definition: sysMonitor.hpp:308
pcf::IndiProperty m_setlatThreadProp
The property to hold the setlat thread details.
Definition: sysMonitor.hpp:314
int m_warningDiskTemp
User defined warning temperature for drives.
Definition: sysMonitor.hpp:58
pid_t m_setlatThreadID
Set latency thread ID.
Definition: sysMonitor.hpp:312
pcf::IndiProperty m_indiP_setlat
Definition: sysMonitor.hpp:322
std::vector< float > m_coreLoads
List of current core load(s)
Definition: sysMonitor.hpp:67
int findCPUTemperatures(std::vector< float > &)
Finds all CPU core temperatures.
Definition: sysMonitor.hpp:595
std::vector< float > m_coreTemps
List of current core temperature(s)
Definition: sysMonitor.hpp:66
int recordCoreLoads(bool force=false)
int updateVals()
Updates Indi property values of all system statistics.
virtual void loadConfig()
Load the warning and critical temperature values for core and drive temperatures.
Definition: sysMonitor.hpp:381
int findDiskTemperature(std::vector< std::string > &hdd_names, std::vector< float > &hdd_temps)
Finds all drive temperatures.
Definition: sysMonitor.hpp:882
INDI_NEWCALLBACK_DECL(sysMonitor, m_indiP_setlat)
int m_criticalCoreTemp
User defined critical temperature for CPU cores.
Definition: sysMonitor.hpp:57
virtual int appStartup()
Registers all new Indi properties for each of the reported values to publish.
Definition: sysMonitor.hpp:417
bool m_setlatThreadInit
Synchronizer to ensure set lat thread initializes before doing dangerous things.
Definition: sysMonitor.hpp:310
pcf::IndiProperty m_indiP_drive_temps
Indi variable for reporting drive temperature(s)
Definition: sysMonitor.hpp:63
int recordCoreTemps(bool force=false)
static void setlatThreadStart(sysMonitor *s)
Thread starter, called by threadStart on thread construction. Calls setlatThreadExec.
int parseCPUTemperaturesIntel(float &temp, const std::string &line)
Parses string from system call to find CPU temperatures on an Intel system.
Definition: sysMonitor.hpp:649
int m_warningCoreTemp
User defined warning temperature for CPU cores.
Definition: sysMonitor.hpp:56
float m_dataUsage
Disk usage in /data path as a value out of 100.
Definition: sysMonitor.hpp:74
int criticalCoreTemperature(std::vector< float > &)
Checks if any core temperatures are warning or critical levels.
Definition: sysMonitor.hpp:786
virtual int appShutdown()
Do any needed shutdown tasks; currently nothing in this app.
Definition: sysMonitor.hpp:579
std::vector< std::string > m_diskNameList
vector of names of the hard disks to monitor
Definition: sysMonitor.hpp:69
std::vector< float > m_diskTemps
vector of current disk temperature(s)
Definition: sysMonitor.hpp:71
pcf::IndiProperty m_indiP_usage
Indi variable for reporting drive usage of all paths.
Definition: sysMonitor.hpp:64
int criticalDiskTemperature(std::vector< float > &)
Checks if any drive temperatures are warning or critical levels.
Definition: sysMonitor.hpp:988
float m_ramUsage
RAM usage as a decimal value between 0 and 1.
Definition: sysMonitor.hpp:76
int m_setlatThreadPrio
Priority of the set latency thread, should normally be > 00.
Definition: sysMonitor.hpp:306
std::string m_chronySourceIP
Definition: sysMonitor.hpp:272
pcf::IndiProperty m_indiP_core_loads
Indi variable for reporting CPU core loads.
Definition: sysMonitor.hpp:61
sysMonitor()
Default c'tor.
Definition: sysMonitor.hpp:363
int findChronyStatus()
Finds current chronyd status.
int parseDiskUsage(std::string, float &, float &, float &)
Parses string from system call to find drive usage space.
pcf::IndiProperty m_indiP_chronyStats
Definition: sysMonitor.hpp:286
int findCPULoads(std::vector< float > &)
Finds all CPU core usage loads.
Definition: sysMonitor.hpp:810
int parseRamUsage(std::string, float &)
Parses string from system call to find RAM usage.
int recordUsage(bool force=false)
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
Definition: indiMacros.hpp:248
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:208
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:56
int runCommand(std::vector< std::string > &commandOutput, std::vector< std::string > &commandStderr, std::vector< std::string > &commandList)
Runs a command (with parameters) passed in using fork/exec.
Definition: runCommand.cpp:22
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cout()
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_ALERT
This should only be used if some action is required by operators to keep the system safe.
Definition: logPriority.hpp:34
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
A device base class which saves telemetry.
Definition: telemeter.hpp:69
int appShutdown()
Perform telemeter application shutdown.
Definition: telemeter.hpp:274
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:223
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:268
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:211
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:281
Software ERR log entry.
Log entry recording the statistics from chrony.
Log entry recording the status of chrony.
Log entry recording CPU loads.
Log entry recording CPU temperatures.
Log entry recording hdd temperatures.
Log entry recording hdd temperatures.
Definition: telem_usage.hpp:26