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