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