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