API
 
Loading...
Searching...
No Matches
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
33namespace MagAOX
34{
35namespace app
36{
37
38/** MagAO-X application to read and report system statistics
39 *
40 */
41class 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
52protected:
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
87public:
88
89 /// Default c'tor.
90 sysMonitor();
91
92 /// D'tor, declared and defined for 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 */
270protected:
271 std::string m_chronySourceMac;
272 std::string m_chronySourceIP;
273 std::string m_chronySynch;
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
288public:
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
324public:
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
344
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
363inline 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
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]));
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 {
471 return -1;
472 }
473
475 return 0;
476}
477
479{
480
481 m_coreTemps.clear();
483 if (rvCPUTemp >= 0)
484 {
486 }
487
488 if (rvCPUTemp >= 0)
489 {
490 if (rvCPUTemp == 1)
491 {
493 }
494 else if (rvCPUTemp == 2)
495 {
497 }
498
500 }
501 else
502 {
503 log<software_error>({ __FILE__, __LINE__,"Could not log values for CPU core temps." });
504 }
505
506 m_coreLoads.clear();
508
509 if (rvCPULoad >= 0)
510 {
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();
522
523 if (rvDiskTemp >= 0)
524 {
526 }
527
528 if (rvDiskTemp >= 0)
529 {
530 if (rvDiskTemp == 1)
531 {
533 }
534 else if (rvDiskTemp == 2)
535 {
537 }
538
540 }
541 else
542 {
543 log<software_error>({ __FILE__, __LINE__,"Could not log values for drive temps." });
544 }
545
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 {
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
595int sysMonitor::findCPUTemperatures(std::vector<float>& temps)
596{
597 std::vector<std::string> commandList{ "sensors" };
598
599 std::vector<std::string> commandOutput, commandError;
600
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;
620 {
621 temps.push_back(tempVal);
622 rv = 0;
623 }
624 }
625 return rv;
626}
627
629 const std::string& line
630 )
631{
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
786int 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
810int 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
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
848int 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;
879 return 0;
880}
881
882int sysMonitor::findDiskTemperature( std::vector<std::string>& hdd_names,
883 std::vector<float>& hdd_temps
884 )
885{
886 std::vector<std::string> commandList{ "hddtemp" };
887
888 std::vector<std::string> commandOutput, commandError;
889
891 {
892 if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
893 else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
894 }
895
896 if (commandError.size() > 0)
897 {
898 for (size_t n = 0; n < commandError.size(); ++n)
899 {
900 if(commandError[n].find("doesn't have a temperature sensor") != std::string::npos)
901 {
902 continue;
903 }
904
905 log<software_error>({ __FILE__, __LINE__, "hddtemp stderr: " + commandError[n] });
906 }
907 }
908
909 int rv = -1;
910 for (auto line : commandOutput)
911 {
912 std::string driveName;
913 float tempVal;
914 if (parseDiskTemperature(driveName, tempVal, line) == 0)
915 {
916 hdd_names.push_back(driveName);
917 hdd_temps.push_back(tempVal);
918 rv = 0;
919 }
920 }
921 return rv;
922
923 //return 0;
924}
925
926int sysMonitor::parseDiskTemperature( std::string& driveName,
927 float& hdd_temp,
928 const std::string& line
929 )
930{
931 float tempValue;
932 if (line.length() <= 6)
933 {
934 driveName = "";
935 hdd_temp = -999;
936 return -1;
937 }
938
939 size_t sp = line.find(':', 0);
940 driveName = line.substr(5, sp - 5);
941
942 std::istringstream iss(line);
943 std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
944
945 for (auto temp_s : tokens)
946 {
947 try
948 {
949 if (isdigit(temp_s.at(0)) && temp_s.substr(temp_s.length() - 1, 1) == "C")
950 {
951 temp_s.pop_back();
952 temp_s.pop_back();
953 try
954 {
955 tempValue = std::stof(temp_s);
956 }
957 catch (const std::invalid_argument& e)
958 {
959 log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive temperatures." });
960 hdd_temp = -999;
961 driveName = "";
962 return -1;
963 }
965 if (m_warningDiskTemp == 0)
966 {
968 }
969 if (m_criticalDiskTemp == 0)
970 {
972 }
973 return 0;
974 }
975 }
976 catch (const std::out_of_range& e)
977 {
978 hdd_temp = -999;
979 driveName = "";
980 return -1;
981 }
982 }
983
984 hdd_temp = -999;
985 driveName = "";
986 return -1;
987}
988
989int sysMonitor::criticalDiskTemperature(std::vector<float>& v)
990{
991 int rv = 0;
992 for (auto it : v)
993 {
994 float temp = it;
995 if (temp >= m_warningDiskTemp && temp < m_criticalDiskTemp)
996 {
997 std::cout << "Warning temperature for Disk" << std::endl;
998 if (rv < 2)
999 {
1000 rv = 1;
1001 }
1002 }
1003 else if (temp >= m_criticalDiskTemp)
1004 {
1005 std::cout << "Critical temperature for Disk " << std::endl;
1006 rv = 2;
1007 }
1008 }
1009 return rv;
1010}
1011
1012int sysMonitor::findDiskUsage(float& rootUsage, float& dataUsage, float& bootUsage)
1013{
1014 std::vector<std::string> commandList{ "df" };
1015
1016 std::vector<std::string> commandOutput, commandError;
1017
1019 {
1020 if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
1021 else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
1022 }
1023
1024 if (commandError.size() > 0)
1025 {
1026 for (size_t n = 0; n < commandError.size(); ++n)
1027 {
1028 log<software_error>({ __FILE__, __LINE__, "df stderr: " + commandError[n] });
1029 }
1030 }
1031
1032 int rv = -1;
1033 for (auto line : commandOutput)
1034 {
1036 if (rvDiskUsage == 0)
1037 {
1038 rv = 0;
1039 }
1040 }
1041 return rv;
1042}
1043
1044int sysMonitor::parseDiskUsage(std::string line, float& rootUsage, float& dataUsage, float& bootUsage)
1045{
1046 if (line.length() <= 1)
1047 {
1048 return -1;
1049 }
1050
1051 std::istringstream iss(line);
1052 std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
1053
1054 try {
1055 if (tokens.at(5).compare("/") == 0)
1056 {
1057 tokens.at(4).pop_back();
1058 try
1059 {
1060 rootUsage = std::stof(tokens.at(4)) / 100;
1061 return 0;
1062 }
1063 catch (const std::invalid_argument& e)
1064 {
1065 log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
1066 return -1;
1067 }
1068 }
1069 else if (tokens.at(5).compare("/data") == 0)
1070 {
1071 tokens.at(4).pop_back();
1072 try
1073 {
1074 dataUsage = std::stof(tokens.at(4)) / 100;
1075 return 0;
1076 }
1077 catch (const std::invalid_argument& e)
1078 {
1079 log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
1080 return -1;
1081 }
1082 }
1083 else if (tokens.at(5).compare("/boot") == 0)
1084 {
1085 tokens.at(4).pop_back();
1086 try
1087 {
1088 bootUsage = std::stof(tokens.at(4)) / 100;
1089 return 0;
1090 }
1091 catch (const std::invalid_argument& e)
1092 {
1093 log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing drive usage." });
1094 return -1;
1095 }
1096 }
1097 }
1098 catch (const std::out_of_range& e) {
1099 return -1;
1100 }
1101 return -1;
1102}
1103
1104int sysMonitor::findRamUsage(float& ramUsage)
1105{
1106 std::vector<std::string> commandList{ "free", "-m" };
1107
1108 std::vector<std::string> commandOutput, commandError;
1109
1111 {
1112 if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
1113 else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
1114 }
1115
1116 if (commandError.size() > 0)
1117 {
1118 for (size_t n = 0; n < commandError.size(); ++n)
1119 {
1120 log<software_error>({ __FILE__, __LINE__, "free stderr: " + commandError[n] });
1121 }
1122 }
1123
1124 for (auto line : commandOutput)
1125 {
1126 if (parseRamUsage(line, ramUsage) == 0)
1127 {
1128 return 0;
1129 }
1130 }
1131 return -1;
1132}
1133
1134int sysMonitor::parseRamUsage(std::string line, float& ramUsage)
1135{
1136 if (line.length() <= 1)
1137 {
1138 return -1;
1139 }
1140 std::istringstream iss(line);
1141 std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},std::istream_iterator<std::string>{} };
1142 try
1143 {
1144 if (tokens.at(0).compare("Mem:") != 0)
1145 {
1146 return -1;
1147 }
1148 ramUsage = std::stof(tokens.at(2)) / std::stof(tokens.at(1));
1149 if (ramUsage > 1 || ramUsage == 0)
1150 {
1151 ramUsage = -1;
1152 return -1;
1153 }
1154 return 0;
1155 }
1156 catch (const std::invalid_argument& e)
1157 {
1158 log<software_error>({ __FILE__, __LINE__,"Invalid read occured when parsing RAM usage." });
1159 return -1;
1160 }
1161 catch (const std::out_of_range& e) {
1162 return -1;
1163 }
1164}
1165
1167{
1168 std::vector<std::string> commandList{ "chronyc", "-c", "tracking" };
1169
1170 std::vector<std::string> commandOutput, commandError;
1171
1173 {
1174 if (commandOutput.size() < 1) return log<software_error, -1>({ __FILE__, __LINE__ });
1175 else return log<software_error, -1>({ __FILE__, __LINE__, commandOutput[0] });
1176 }
1177
1178 if (commandError.size() > 0)
1179 {
1180 for (size_t n = 0; n < commandError.size(); ++n)
1181 {
1182 log<software_error>({ __FILE__, __LINE__, "chronyc stderr: " + commandError[n] });
1183 }
1184 }
1185
1186 if (commandOutput.size() < 1)
1187 {
1188 log<software_error>({ __FILE__,__LINE__, "no response from chronyc -c" });
1189 return -1;
1190 }
1191
1192 std::vector<std::string> results;
1193 mx::ioutils::parseStringVector(results, commandOutput[0], ',');
1194
1195 if (results.size() < 1)
1196 {
1197 log<software_error>({ __FILE__,__LINE__, "wrong number of fields from chronyc -c" });
1198 return -1;
1199 }
1200
1201 static std::string last_mac;
1202 static std::string last_ip;
1205 if (m_chronySourceMac == "7F7F0101" || m_chronySourceIP == "127.0.0.1")
1206 {
1207 m_chronySynch = "NO";
1208 log<text_log>("chrony is not synchronized", logPrio::LOG_WARNING);
1209 }
1210 else
1211 {
1212 m_chronySynch = "YES";
1213 }
1214
1216 {
1217 log<text_log>("chrony is synchronizing to " + m_chronySourceMac + " / " + m_chronySourceIP);
1220 }
1221
1222
1223
1224 m_chronySystemTime = std::stod(results[4]);
1225 m_chronyLastOffset = std::stod(results[5]);
1226 m_chronyRMSOffset = std::stod(results[6]);
1227 m_chronyFreq = std::stod(results[7]);
1228 m_chronyResidFreq = std::stod(results[8]);
1229 m_chronySkew = std::stod(results[9]);
1230 m_chronyRootDelay = std::stod(results[10]);
1231 m_chronyRootDispersion = std::stod(results[11]);
1232 m_chronyUpdateInt = std::stod(results[12]);
1233 m_chronyLeap = results[13];
1234
1237
1238 return 0;
1239}
1240
1242{
1243 float min, max, mean;
1244
1245 if (m_coreLoads.size() > 0)
1246 {
1247 min = m_coreLoads[0];
1248 max = m_coreLoads[0];
1249 mean = m_coreLoads[0];
1250 for (size_t n = 1; n < m_coreLoads.size(); ++n)
1251 {
1252 if (m_coreLoads[n] < min) min = m_coreLoads[n];
1253 if (m_coreLoads[n] > max) max = m_coreLoads[n];
1254 mean += m_coreLoads[n];
1255 }
1256 mean /= m_coreLoads.size();
1257
1258 updateIfChanged<float>(m_indiP_core_loads, { "min","max","mean" }, { min,max,mean });
1259 }
1260
1261
1262 if (m_coreTemps.size() > 0)
1263 {
1264 min = m_coreTemps[0];
1265 max = m_coreTemps[0];
1266 mean = m_coreTemps[0];
1267 for (size_t n = 1; n < m_coreTemps.size(); ++n)
1268 {
1269 if (m_coreTemps[n] < min) min = m_coreTemps[n];
1270 if (m_coreTemps[n] > max) max = m_coreTemps[n];
1271 mean += m_coreTemps[n];
1272 }
1273 mean /= m_coreTemps.size();
1274
1275 updateIfChanged<float>(m_indiP_core_temps, { "min","max","mean" }, { min,max,mean });
1276 }
1277
1279
1280 updateIfChanged<float>(m_indiP_usage, { "root_usage","boot_usage","data_usage","ram_usage" }, { m_rootUsage,m_bootUsage,m_dataUsage,m_ramUsage });
1281
1282
1285
1286 updateIfChanged<double>(m_indiP_chronyStats, { "system_time", "last_offset", "rms_offset" }, { m_chronySystemTime, m_chronyLastOffset, m_chronyRMSOffset });
1287
1288 if (m_setLatency)
1289 {
1290 updateSwitchIfChanged(m_indiP_setlat, "toggle", pcf::IndiElement::On, INDI_OK);
1291 }
1292 else
1293 {
1294 updateSwitchIfChanged(m_indiP_setlat, "toggle", pcf::IndiElement::Off, INDI_IDLE);
1295 }
1296
1297 return 0;
1298}
1299
1300inline
1302{
1303 s->setlatThreadExec();
1304}
1305
1306inline
1308{
1310
1311 //Wait fpr the thread starter to finish initializing this thread.
1312 while (m_setlatThreadInit == true && m_shutdown == 0)
1313 {
1314 sleep(1);
1315 }
1316
1317 int fd = 0;
1318 while (m_shutdown == 0)
1319 {
1320 if (m_setLatency)
1321 {
1322 if (fd <= 0)
1323 {
1324 elevatedPrivileges ep(this);
1325
1326 for (size_t cpu = 0; cpu < m_coreLoads.size(); ++cpu) ///\todo this needs error checks
1327 {
1328 std::string cpuFile = "/sys/devices/system/cpu/cpu";
1329 cpuFile += std::to_string(cpu);
1330 cpuFile += "/cpufreq/scaling_governor";
1331 int wfd = open(cpuFile.c_str(), O_WRONLY);
1332 ssize_t perfsz = sizeof("performance");
1333 ssize_t wrtsz = write(wfd, "performance", perfsz);
1334 if(wrtsz != perfsz)
1335 {
1336 log<software_error>({ __FILE__,__LINE__,"error setting performance governor for CPU " + std::to_string(cpu) });
1337 }
1338 close(wfd);
1339 }
1340 log<text_log>("set governor to performance", logPrio::LOG_NOTICE);
1341
1342 fd = open("/dev/cpu_dma_latency", O_WRONLY);
1343
1344 if (fd <= 0) log<software_error>({ __FILE__,__LINE__,"error opening cpu_dma_latency" });
1345 else
1346 {
1347 int l = 0;
1348 if (write(fd, &l, sizeof(l)) != sizeof(l))
1349 {
1350 log<software_error>({ __FILE__,__LINE__,"error writing to cpu_dma_latency" });
1351 }
1352 else
1353 {
1354 log<text_log>("set latency to 0", logPrio::LOG_NOTICE);
1355 }
1356 }
1357
1358
1359 }
1360 }
1361 else
1362 {
1363 if (fd != 0)
1364 {
1365 close(fd);
1366 fd = 0;
1367 log<text_log>("restored CPU latency to default", logPrio::LOG_NOTICE);
1368
1369 elevatedPrivileges ep(this);
1370 for (size_t cpu = 0; cpu < m_coreLoads.size(); ++cpu) ///\todo this needs error checks
1371 {
1372 std::string cpuFile = "/sys/devices/system/cpu/cpu";
1373 cpuFile += std::to_string(cpu);
1374 cpuFile += "/cpufreq/scaling_governor";
1375 int wfd = open(cpuFile.c_str(), O_WRONLY);
1376 ssize_t pwrsz = sizeof("powersave");
1377 ssize_t wrtsz = write(wfd, "powersave", pwrsz);
1378 if(wrtsz != pwrsz)
1379 {
1380 log<software_error>({ __FILE__,__LINE__,"error setting powersave governor for CPU " + std::to_string(cpu) });
1381 }
1382
1383 close(wfd);
1384 }
1385 log<text_log>("set governor to powersave", logPrio::LOG_NOTICE);
1386 }
1387 }
1388
1389 sleep(1);
1390 }
1391
1392 if (fd) close(fd);
1393
1394}
1395
1396INDI_NEWCALLBACK_DEFN(sysMonitor, m_indiP_setlat)(const pcf::IndiProperty& ipRecv)
1397{
1398 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
1420inline
1431
1433{
1434 return recordCoreLoads(true);
1435}
1436
1438{
1439 return recordCoreTemps(true);
1440}
1441
1443{
1444 return recordDriveTemps(true);
1445}
1446
1448{
1449 return recordUsage(true);
1450}
1451
1453{
1454 return recordChronyStatus(true);
1455}
1456
1458{
1459 return recordChronyStats(true);
1460}
1461
1463{
1464 static std::vector<float> old_coreLoads;
1465
1466 if (old_coreLoads.size() != m_coreLoads.size())
1467 {
1468 old_coreLoads.resize(m_coreLoads.size(), -1e30);
1469 }
1470
1471 bool write = false;
1472
1473 for (size_t n = 0; n < m_coreLoads.size(); ++n)
1474 {
1475 if (m_coreLoads[n] != old_coreLoads[n]) write = true;
1476 }
1477
1478 if (force || write)
1479 {
1481
1482 for (size_t n = 0; n < m_coreLoads.size(); ++n)
1483 {
1485 }
1486 }
1487
1488 return 0;
1489}
1490
1492{
1493 static std::vector<float> old_coreTemps;
1494
1495 if (old_coreTemps.size() != m_coreTemps.size())
1496 {
1497 old_coreTemps.resize(m_coreTemps.size(), -1e30);
1498 }
1499
1500 bool write = false;
1501
1502 for (size_t n = 0; n < m_coreTemps.size(); ++n)
1503 {
1504 if (m_coreTemps[n] != old_coreTemps[n]) write = true;
1505 }
1506
1507 if (force || write)
1508 {
1510 for (size_t n = 0; n < m_coreTemps.size(); ++n)
1511 {
1513 }
1514 }
1515
1516 return 0;
1517}
1518
1520{
1521 static std::vector<std::string> old_diskNames;
1522 static std::vector<float> old_diskTemps;
1523
1524 if (old_diskTemps.size() != m_diskTemps.size() || old_diskNames.size() != m_diskNames.size())
1525 {
1526 old_diskNames.resize(m_diskNames.size());
1527 old_diskTemps.resize(m_diskTemps.size(), -1e30);
1528 }
1529
1530 bool write = false;
1531
1532 for (size_t n = 0; n < m_diskTemps.size(); ++n)
1533 {
1534 if (m_diskTemps[n] != old_diskTemps[n] || m_diskNames[n] != old_diskNames[n]) write = true;
1535 }
1536
1537 if (force || write)
1538 {
1540 for (size_t n = 0; n < m_diskTemps.size(); ++n)
1541 {
1544 }
1545 }
1546
1547 return 0;
1548}
1549
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
1558 {
1560
1565 }
1566
1567 return 0;
1568}
1569
1590
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
1608 {
1609
1612
1622
1623 }
1624
1625 return 0;
1626}
1627
1628} //namespace app
1629} //namespace MagAOX
1630
1631#endif //sysMonitor_hpp
Internal class to manage setuid privilege escalation with RAII.
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.
stateCodes::stateCodeT state()
Get the current state code.
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.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
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.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
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.
int m_criticalDiskTemp
User defined critical temperature for drives.
std::string m_chronySourceMac
pcf::IndiProperty m_indiP_core_temps
Indi variable for reporting CPU core temperature(s)
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
void setlatThreadExec()
Execute the frame grabber main loop.
float m_bootUsage
Disk usage in /boot path as a value out of 100.
int parseDiskTemperature(std::string &driveName, float &temp, const std::string &line)
Parses string from system call to find drive temperatures.
int recordChronyStats(bool force=false)
float m_rootUsage
Disk usage in root path as a value out of 100.
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.
int recordChronyStatus(bool force=false)
std::vector< std::string > m_diskNames
vector of names of the hard disks returned by hdd_temp
virtual void setupConfig()
Setup the user-defined warning and critical values for core and drive temperatures.
int parseCPUTemperatures(float &temp, const std::string &line)
Parses string from system call to find CPU temperatures.
~sysMonitor() noexcept
D'tor, declared and defined for noexcept.
virtual int appLogic()
Implementation of reading and logging each of the measured statistics.
int parseCPULoads(float &, const std::string &)
Parses string from system call to find CPU usage loads.
std::thread m_setlatThread
A separate thread for the actual setting of low latency.
pcf::IndiProperty m_setlatThreadProp
The property to hold the setlat thread details.
int m_warningDiskTemp
User defined warning temperature for drives.
pid_t m_setlatThreadID
Set latency thread ID.
pcf::IndiProperty m_indiP_setlat
std::vector< float > m_coreLoads
List of current core load(s)
int findCPUTemperatures(std::vector< float > &)
Finds all CPU core temperatures.
std::vector< float > m_coreTemps
List of current core temperature(s)
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.
int findDiskTemperature(std::vector< std::string > &hdd_names, std::vector< float > &hdd_temps)
Finds all drive temperatures.
INDI_NEWCALLBACK_DECL(sysMonitor, m_indiP_setlat)
int m_criticalCoreTemp
User defined critical temperature for CPU cores.
virtual int appStartup()
Registers all new Indi properties for each of the reported values to publish.
bool m_setlatThreadInit
Synchronizer to ensure set lat thread initializes before doing dangerous things.
pcf::IndiProperty m_indiP_drive_temps
Indi variable for reporting drive temperature(s)
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.
int m_warningCoreTemp
User defined warning temperature for CPU cores.
float m_dataUsage
Disk usage in /data path as a value out of 100.
int criticalCoreTemperature(std::vector< float > &)
Checks if any core temperatures are warning or critical levels.
virtual int appShutdown()
Do any needed shutdown tasks; currently nothing in this app.
std::vector< std::string > m_diskNameList
vector of names of the hard disks to monitor
std::vector< float > m_diskTemps
vector of current disk temperature(s)
pcf::IndiProperty m_indiP_usage
Indi variable for reporting drive usage of all paths.
int criticalDiskTemperature(std::vector< float > &)
Checks if any drive temperatures are warning or critical levels.
float m_ramUsage
RAM usage as a decimal value between 0 and 1.
int m_setlatThreadPrio
Priority of the set latency thread, should normally be > 00.
pcf::IndiProperty m_indiP_core_loads
Indi variable for reporting CPU core loads.
sysMonitor()
Default c'tor.
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
int findCPULoads(std::vector< float > &)
Finds all CPU core usage loads.
int parseRamUsage(std::string, float &)
Parses string from system call to find RAM usage.
int recordUsage(bool force=false)
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
@ READY
The device is ready for operation, but is not operating.
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.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:27
#define INDI_OK
Definition indiUtils.hpp:28
const pcf::IndiProperty & ipRecv
Definition dm.hpp:26
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ALERT
This should only be used if some action is required by operators to keep the system safe.
A device base class which saves telemetry.
Definition telemeter.hpp:69
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
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.