1 #include "hwmon_ffdc.hpp" 2 3 #include "logging.hpp" 4 5 #include <array> 6 #include <filesystem> 7 #include <format> 8 #include <fstream> 9 #include <string> 10 #include <vector> 11 12 namespace phosphor::fan::monitor 13 { 14 15 namespace util 16 { 17 18 namespace fs = std::filesystem; 19 20 inline std::vector<std::string> executeCommand(const std::string& command) 21 { 22 std::vector<std::string> output; 23 std::array<char, 128> buffer; 24 25 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), 26 pclose); 27 if (!pipe) 28 { 29 getLogger().log( 30 std::format("popen() failed when running command: {}", command)); 31 return output; 32 } 33 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) 34 { 35 output.emplace_back(buffer.data()); 36 } 37 38 return output; 39 } 40 41 std::vector<std::string> getHwmonNameFFDC() 42 { 43 const fs::path hwmonBaseDir{"/sys/class/hwmon"}; 44 std::vector<std::string> hwmonNames; 45 46 if (!fs::exists(hwmonBaseDir)) 47 { 48 getLogger().log(std::format("Hwmon base directory {} doesn't exist", 49 hwmonBaseDir.native())); 50 return hwmonNames; 51 } 52 53 try 54 { 55 for (const auto& path : fs::directory_iterator(hwmonBaseDir)) 56 { 57 if (!path.is_directory()) 58 { 59 continue; 60 } 61 62 auto nameFile = path.path() / "name"; 63 if (fs::exists(nameFile)) 64 { 65 std::ifstream f{nameFile}; 66 if (f.good()) 67 { 68 std::string name; 69 f >> name; 70 hwmonNames.push_back(name); 71 } 72 } 73 } 74 } 75 catch (const std::exception& e) 76 { 77 getLogger().log( 78 std::format("Error traversing hwmon directories: {}", e.what())); 79 } 80 81 return hwmonNames; 82 } 83 84 std::vector<std::string> getDmesgFFDC() 85 { 86 std::vector<std::string> output; 87 auto dmesgOutput = executeCommand("dmesg"); 88 89 // Only pull in dmesg lines with interesting keywords. 90 // One example is: 91 // [ 16.390603] max31785: probe of 7-0052 failed with error -110 92 // using ' probe' to avoid 'modprobe' 93 std::vector<std::string> matches{" probe", "failed"}; 94 95 for (const auto& line : dmesgOutput) 96 { 97 for (const auto& m : matches) 98 { 99 if (line.find(m) != std::string::npos) 100 { 101 output.push_back(line); 102 if (output.back().back() == '\n') 103 { 104 output.back().pop_back(); 105 } 106 break; 107 } 108 } 109 } 110 111 return output; 112 } 113 114 } // namespace util 115 116 nlohmann::json collectHwmonFFDC() 117 { 118 nlohmann::json ffdc; 119 120 auto hwmonNames = util::getHwmonNameFFDC(); 121 if (!hwmonNames.empty()) 122 { 123 ffdc["hwmonNames"] = std::move(hwmonNames); 124 } 125 126 auto dmesg = util::getDmesgFFDC(); 127 if (!dmesg.empty()) 128 { 129 ffdc["dmesg"] = std::move(dmesg); 130 } 131 132 return ffdc; 133 } 134 135 } // namespace phosphor::fan::monitor 136