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