1 #include <phosphor-logging/lg2.hpp>
2 #include <sdbusplus/bus.hpp>
3 #include <sdbusplus/bus/match.hpp>
4 #include <watchdog_dbus.hpp>
5 #include <watchdog_handler.hpp>
6 #include <watchdog_logging.hpp>
7 
8 namespace watchdog
9 {
10 namespace dump
11 {
12 
13 /**
14  * @brief Callback for dump request properties change signal monitor
15  *
16  * @param msg - dbus message from the dbus match infrastructure
17  * @param path - the object path we are monitoring
18  * @param progressStatus - dump progress status
19  * @return Always non-zero indicating no error, no cascading callbacks
20  */
21 uint dumpStatusChanged(sdbusplus::message_t& msg, std::string path,
22                        DumpProgressStatus& progressStatus)
23 {
24     // reply (msg) will be a property change message
25     std::string interface;
26     std::map<std::string, std::variant<std::string, uint8_t>> property;
27     msg.read(interface, property);
28 
29     // looking for property Status changes
30     std::string propertyType = "Status";
31     auto dumpStatus = property.find(propertyType);
32 
33     if (dumpStatus != property.end())
34     {
35         const std::string* status =
36             std::get_if<std::string>(&(dumpStatus->second));
37 
38         if ((nullptr != status) && ("xyz.openbmc_project.Common.Progress."
39                                     "OperationStatus.InProgress" != *status))
40         {
41             // dump is not in InProgress state, trace some info and change in
42             // progress status
43             lg2::info("{PATH}", "PATH", path);
44             lg2::info("{STATUS}", "STATUS", *status);
45 
46             if ("xyz.openbmc_project.Common.Progress.OperationStatus."
47                 "Completed" == *status)
48             {
49                 // Dump completed successfully
50                 progressStatus = DumpProgressStatus::Completed;
51             }
52             else
53             {
54                 // Dump Failed
55                 progressStatus = DumpProgressStatus::Failed;
56             }
57         }
58     }
59 
60     return 1; // non-negative return code for successful callback
61 }
62 
63 /**
64  * @brief Register a callback for dump progress status changes
65  *
66  * @param path - the object path of the dump to monitor
67  * @param timeout - timeout - timeout interval in seconds
68  */
69 void monitorDump(const std::string& path, const uint32_t timeout)
70 {
71     // callback will update progressStatus
72     DumpProgressStatus progressStatus = DumpProgressStatus::InProgress;
73 
74     // setup the signal match rules and callback
75     std::string matchInterface = "xyz.openbmc_project.Common.Progress";
76     auto bus = sdbusplus::bus::new_system();
77 
78     std::unique_ptr<sdbusplus::bus::match_t> match =
79         std::make_unique<sdbusplus::bus::match_t>(
80             bus,
81             sdbusplus::bus::match::rules::propertiesChanged(
82                 path.c_str(), matchInterface.c_str()),
83             [&](auto& msg) {
84                 return dumpStatusChanged(msg, path, progressStatus);
85             });
86 
87     // wait for dump status to be completed (complete == true)
88     // or until timeout interval
89 
90     bool timedOut = false;
91     uint32_t secondsCount = 0;
92     while ((DumpProgressStatus::InProgress == progressStatus) && !timedOut)
93     {
94         bus.wait(std::chrono::seconds(1));
95         bus.process_discard();
96 
97         if (++secondsCount == timeout)
98         {
99             timedOut = true;
100         }
101     }
102 
103     if (timedOut)
104     {
105         lg2::error("Dump progress status did not change to "
106                    "complete within the timeout interval, exiting...");
107     }
108     else if (DumpProgressStatus::Completed == progressStatus)
109     {
110         lg2::info("dump collection completed");
111     }
112     else
113     {
114         lg2::info("dump collection failed");
115     }
116 }
117 
118 void requestDump(const DumpParameters& dumpParameters)
119 {
120     constexpr auto path = "/xyz/openbmc_project/dump/system";
121     constexpr auto interface = "xyz.openbmc_project.Dump.Create";
122     constexpr auto function = "CreateDump";
123 
124     sdbusplus::message_t method;
125 
126     if (0 == dbusMethod(path, interface, function, method))
127     {
128         try
129         {
130             // dbus call arguments
131             std::map<std::string, std::variant<std::string, uint64_t>>
132                 createParams;
133             createParams["com.ibm.Dump.Create.CreateParameters.ErrorLogId"] =
134                 uint64_t(dumpParameters.logId);
135             if (DumpType::Hostboot == dumpParameters.dumpType)
136             {
137                 lg2::info("hostboot dump requested");
138                 createParams["com.ibm.Dump.Create.CreateParameters.DumpType"] =
139                     "com.ibm.Dump.Create.DumpType.Hostboot";
140             }
141             else if (DumpType::SBE == dumpParameters.dumpType)
142             {
143                 lg2::info("SBE dump requested");
144                 createParams["com.ibm.Dump.Create.CreateParameters.DumpType"] =
145                     "com.ibm.Dump.Create.DumpType.SBE";
146                 createParams
147                     ["com.ibm.Dump.Create.CreateParameters.FailingUnitId"] =
148                         dumpParameters.unitId;
149             }
150 
151             method.append(createParams);
152 
153             // using system dbus
154             auto bus = sdbusplus::bus::new_system();
155             auto response = bus.call(method);
156 
157             // reply will be type dbus::ObjectPath
158             sdbusplus::message::object_path reply;
159             response.read(reply);
160 
161             // monitor dump progress
162             monitorDump(reply, dumpParameters.timeout);
163         }
164         catch (const sdbusplus::exception_t& e)
165         {
166             constexpr auto ERROR_DUMP_DISABLED =
167                 "xyz.openbmc_project.Dump.Create.Error.Disabled";
168             if (e.name() == ERROR_DUMP_DISABLED)
169             {
170                 // Dump is disabled, Skip the dump collection.
171                 lg2::info("Dump is disabled on({UNIT}), "
172                           "skipping dump collection",
173                           "UNIT", dumpParameters.unitId);
174             }
175             else
176             {
177                 lg2::error("D-Bus call createDump exception OBJPATH={OBJPATH}, "
178                            "INTERFACE={INTERFACE}, EXCEPTION={ERROR}",
179                            "OBJPATH", path, "INTERFACE", interface, "ERROR", e);
180             }
181         }
182     }
183 }
184 
185 } // namespace dump
186 } // namespace watchdog
187