xref: /openbmc/phosphor-fan-presence/hwmon_ffdc.cpp (revision fbf4703f3de7fbdbd8388e946bd71c3b760b174c)
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