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