1 #include "dump_monitor.hpp"
2 
3 #include "dump_utils.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 #include <regex>
8 
9 namespace openpower::dump
10 {
11 
12 constexpr auto dumpOutPath = "/var/lib/phosphor-debug-collector/opdump";
13 constexpr auto dumpStatusFailed =
14     "xyz.openbmc_project.Common.Progress.OperationStatus.Failed";
15 
executeCollectionScript(const sdbusplus::message::object_path & path,const PropertyMap & properties)16 void DumpMonitor::executeCollectionScript(
17     const sdbusplus::message::object_path& path, const PropertyMap& properties)
18 {
19     std::vector<std::string> args = {"opdreport"};
20     std::regex idFormat("^[a-fA-F0-9]{8}$");
21     auto dumpIdStr = path.filename();
22     if (!std::regex_match(dumpIdStr, idFormat))
23     {
24         lg2::error("Failed to extract dump id from path {PATH}", "PATH", path);
25         updateProgressStatus(path, dumpStatusFailed);
26         return;
27     }
28 
29     uint32_t dumpId = std::strtoul(dumpIdStr.c_str(), nullptr, 16);
30     int dumpType = getDumpTypeFromId(dumpId);
31     std::filesystem::path dumpPath =
32         std::filesystem::path(dumpOutPath) / dumpIdStr;
33 
34     // Add type, ID, and dump path to args
35     args.push_back("-t");
36     args.push_back(std::to_string(dumpType));
37     args.push_back("-i");
38     args.push_back(dumpIdStr);
39     args.push_back("-d");
40     args.push_back(dumpPath.string());
41 
42     // Optionally add ErrorLogId and FailingUnitId
43     auto errorLogIdIt = properties.find("ErrorLogId");
44     if (errorLogIdIt != properties.end())
45     {
46         uint32_t errorLogId = std::get<uint32_t>(errorLogIdIt->second);
47         args.push_back("-e");
48         args.push_back(std::to_string(errorLogId));
49     }
50 
51     auto failingUnitIdIt = properties.find("FailingUnitId");
52     if (failingUnitIdIt != properties.end())
53     {
54         uint32_t failingUnitId = std::get<uint32_t>(failingUnitIdIt->second);
55         args.push_back("-f");
56         args.push_back(std::to_string(failingUnitId));
57     }
58 
59     std::vector<char*> argv;
60     for (auto& arg : args)
61     {
62         argv.push_back(arg.data());
63     }
64 
65     argv.push_back(nullptr);
66     pid_t pid = fork();
67     if (pid == -1)
68     {
69         lg2::error("Failed to fork, cannot collect dump, {ERRRNO}", "ERRNO",
70                    errno);
71         updateProgressStatus(path, dumpStatusFailed);
72         exit(EXIT_FAILURE); // Exit explicitly with failure status
73     }
74     else if (pid == 0)
75     {
76         // Child process
77         execvp("opdreport", argv.data());
78         perror("execvp");   // execvp only returns on error
79         updateProgressStatus(path, dumpStatusFailed);
80         exit(EXIT_FAILURE); // Exit explicitly with failure status
81     }
82     else
83     {
84         // Parent process: wait for the child to terminate
85         int status;
86         waitpid(pid, &status, 0);
87         if (WIFEXITED(status))
88         {
89             int exit_status = WEXITSTATUS(status);
90 
91             if (exit_status != 0)
92             {
93                 // Handle failure
94                 lg2::error("Dump failed updating status {PATH}", "PATH", path);
95                 updateProgressStatus(path, dumpStatusFailed);
96             }
97         }
98     }
99 }
100 
handleDBusSignal(sdbusplus::message_t & msg)101 void DumpMonitor::handleDBusSignal(sdbusplus::message_t& msg)
102 {
103     sdbusplus::message::object_path objectPath;
104     InterfaceMap interfaces;
105 
106     msg.read(objectPath, interfaces);
107 
108     lg2::info("Signal received at {PATH}: ", "PATH", objectPath);
109 
110     // There can be new a entry created after the completion of the collection
111     // with completed status, so pick only entries with InProgress status.
112     if (isInProgress(interfaces))
113     {
114         for (const auto& interfaceName : monitoredInterfaces)
115         {
116             auto it = interfaces.find(interfaceName);
117             if (it != interfaces.end())
118             {
119                 lg2::info("An entry created, collecting a new dump {DUMP}",
120                           "DUMP", interfaceName);
121                 initiateDumpCollection(objectPath, interfaceName, it->second);
122             }
123         }
124     }
125 }
126 
updateProgressStatus(const std::string & path,const std::string & status)127 void DumpMonitor::updateProgressStatus(const std::string& path,
128                                        const std::string& status)
129 {
130     auto bus = sdbusplus::bus::new_default();
131     const std::string interfaceName = "xyz.openbmc_project.Common.Progress";
132     const std::string propertyName = "Status";
133     const std::string serviceName = "xyz.openbmc_project.Dump.Manager";
134 
135     try
136     {
137         auto statusVariant = std::variant<std::string>(status);
138         util::setProperty<std::variant<std::string>>(
139             interfaceName, propertyName, path, bus, statusVariant);
140         lg2::info("Status updated successfully to {STATUS} {PATH}", "STATUS",
141                   status, "PATH", path);
142     }
143     catch (const sdbusplus::exception_t& e)
144     {
145         lg2::error("Failed to update status {STATUS} {PATH} {ERROR}", "STATUS",
146                    status, "PATH", path, "ERROR", e);
147     }
148 }
149 
startMpReboot(const sdbusplus::message::object_path & objectPath)150 void DumpMonitor::startMpReboot(
151     const sdbusplus::message::object_path& objectPath)
152 {
153     constexpr auto systemdService = "org.freedesktop.systemd1";
154     constexpr auto systemdObjPath = "/org/freedesktop/systemd1";
155     constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
156     constexpr auto diagModeTarget = "obmc-host-crash@0.target";
157 
158     try
159     {
160         auto b = sdbusplus::bus::new_default();
161         auto method = b.new_method_call(systemdService, systemdObjPath,
162                                         systemdInterface, "StartUnit");
163         method.append(diagModeTarget); // unit to activate
164         method.append("replace");
165         b.call_noreply(method);
166     }
167     catch (const sdbusplus::exception_t& e)
168     {
169         lg2::error("Failed to start memory preserving reboot");
170         updateProgressStatus(objectPath, dumpStatusFailed);
171     }
172 }
173 
initiateDumpCollection(const std::string & path,const std::string & intf,const PropertyMap & properties)174 void DumpMonitor::initiateDumpCollection(const std::string& path,
175                                          const std::string& intf,
176                                          const PropertyMap& properties)
177 {
178     using namespace sdbusplus::common::xyz::openbmc_project::dump::entry;
179     if (intf == System::interface)
180     {
181         // Find the SystemImpact property, if this property is not
182         // available assume disruptive.
183         auto systemImpactIt = properties.find("SystemImpact");
184         if (systemImpactIt != properties.end() &&
185             std::get<std::string>(systemImpactIt->second) !=
186                 System::convertSystemImpactToString(
187                     System::SystemImpact::Disruptive))
188         {
189             lg2::info("Ignoring non-disruptive system dump {PATH}", "PATH",
190                       path);
191             return;
192         }
193         startMpReboot(path);
194     }
195     else
196     {
197         executeCollectionScript(path, properties);
198     }
199 }
200 
201 } // namespace openpower::dump
202