1 #include "config.h"
2 
3 #include "dump_manager_system.hpp"
4 
5 #include "dump_utils.hpp"
6 #include "op_dump_consts.hpp"
7 #include "op_dump_util.hpp"
8 #include "system_dump_entry.hpp"
9 #include "xyz/openbmc_project/Common/error.hpp"
10 
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/elog.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 
15 namespace openpower
16 {
17 namespace dump
18 {
19 namespace system
20 {
21 
22 using namespace phosphor::logging;
23 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
24 
notify(uint32_t dumpId,uint64_t size)25 void Manager::notify(uint32_t dumpId, uint64_t size)
26 {
27     // Get the timestamp
28     uint64_t timeStamp =
29         std::chrono::duration_cast<std::chrono::microseconds>(
30             std::chrono::system_clock::now().time_since_epoch())
31             .count();
32 
33     // A system dump can be created due to a fault in the server or by a user
34     // request. A system dump by fault is first reported here, but for a
35     // user-requested dump, an entry will be created first with an invalid
36     // source id. Since only one system dump creation is allowed at a time, if
37     // there's an entry with an invalid sourceId, we will update that entry.
38     openpower::dump::system::Entry* upEntry = nullptr;
39     for (auto& entry : entries)
40     {
41         openpower::dump::system::Entry* sysEntry =
42             dynamic_cast<openpower::dump::system::Entry*>(entry.second.get());
43 
44         // If there's already a completed entry with the input source id and
45         // size, ignore this notification
46         if ((sysEntry->sourceDumpId() == dumpId) && (sysEntry->size() == size))
47         {
48             if (sysEntry->status() ==
49                 phosphor::dump::OperationStatus::Completed)
50             {
51                 lg2::info(
52                     "System dump entry with source dump id:{SOURCE_ID} and "
53                     "size: {SIZE} is already present with entry id:{ID}",
54                     "SOURCE_ID", dumpId, "SIZE", size, "ID",
55                     sysEntry->getDumpId());
56                 return;
57             }
58             else
59             {
60                 lg2::error("A duplicate notification for an incomplete dump "
61                            "dump id: {SOURCE_ID} entry id: {ID}",
62                            "SOURCE_D", dumpId, "ID", sysEntry->getDumpId());
63                 upEntry = sysEntry;
64                 break;
65             }
66         }
67         else if (sysEntry->sourceDumpId() == dumpId)
68         {
69             // If the dump id is the same but the size is different, then this
70             // is a new dump. So, delete the stale entry and prepare to create a
71             // new one.
72             lg2::info("A previous dump entry found with same source id: "
73                       "{SOURCE_ID}, deleting it, entry id: {DUMP_ID}",
74                       "SOURCE_ID", dumpId, "DUMP_ID", sysEntry->getDumpId());
75             sysEntry->delete_();
76             // No 'break' here, as we need to continue checking other entries.
77         }
78 
79         // Save the first entry with INVALID_SOURCE_ID, but continue in the loop
80         // to ensure the new entry is not a duplicate.
81         if ((sysEntry->sourceDumpId() == INVALID_SOURCE_ID) &&
82             (upEntry == nullptr))
83         {
84             upEntry = sysEntry;
85         }
86     }
87 
88     if (upEntry != nullptr)
89     {
90         lg2::info(
91             "System Dump Notify: Updating dumpId:{ID} Source Id:{SOURCE_ID} "
92             "Size:{SIZE}",
93             "ID", upEntry->getDumpId(), "SOURCE_ID", dumpId, "SIZE", size);
94         upEntry->update(timeStamp, size, dumpId);
95         return;
96     }
97 
98     // Get the id
99     auto id = lastEntryId + 1;
100     auto idString = std::to_string(id);
101     auto objPath = std::filesystem::path(baseEntryPath) / idString;
102 
103     // TODO: Get the originator Id, Type from the persisted file.
104     // For now replacing it with null
105     try
106     {
107         lg2::info("System Dump Notify: creating new dump "
108                   "entry dumpId:{ID} Source Id:{SOURCE_ID} Size:{SIZE}",
109                   "ID", id, "SOURCE_ID", dumpId, "SIZE", size);
110         entries.insert(std::make_pair(
111             id, std::make_unique<system::Entry>(
112                     bus, objPath.c_str(), id, timeStamp, size, dumpId,
113                     phosphor::dump::OperationStatus::Completed, std::string(),
114                     originatorTypes::Internal, *this)));
115     }
116     catch (const std::invalid_argument& e)
117     {
118         lg2::error(
119             "Error in creating system dump entry, errormsg: {ERROR}, "
120             "OBJECTPATH: {OBJECT_PATH}, ID: {ID}, TIMESTAMP: {TIMESTAMP}, "
121             "SIZE: {SIZE}, SOURCEID: {SOURCE_ID}",
122             "ERROR", e, "OBJECT_PATH", objPath, "ID", id, "TIMESTAMP",
123             timeStamp, "SIZE", size, "SOURCE_ID", dumpId);
124         report<InternalFailure>();
125         return;
126     }
127     lastEntryId++;
128     return;
129 }
130 
131 sdbusplus::message::object_path
createDump(phosphor::dump::DumpCreateParams params)132     Manager::createDump(phosphor::dump::DumpCreateParams params)
133 {
134     constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
135     constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
136     constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
137     constexpr auto DIAG_MOD_TARGET = "obmc-host-crash@0.target";
138 
139     if (params.size() > CREATE_DUMP_MAX_PARAMS)
140     {
141         lg2::warning(
142             "System dump accepts not more than 2 additional parameters");
143     }
144     using Unavailable =
145         sdbusplus::xyz::openbmc_project::Common::Error::Unavailable;
146 
147     if (openpower::dump::util::isSystemDumpInProgress(bus))
148     {
149         lg2::error("Another dump in progress or available to offload");
150         elog<Unavailable>();
151     }
152 
153     using NotAllowed =
154         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
155     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
156 
157     auto isHostRunning = false;
158     phosphor::dump::HostState hostState;
159     try
160     {
161         isHostRunning = phosphor::dump::isHostRunning();
162         hostState = phosphor::dump::getHostState();
163     }
164     catch (const std::exception& e)
165     {
166         lg2::error(
167             "System state cannot be determined, system dump is not allowed: "
168             "{ERROR}",
169             "ERROR", e);
170         return std::string();
171     }
172     bool isHostQuiesced = hostState == phosphor::dump::HostState::Quiesced;
173     bool isHostTransitioningToOff =
174         hostState == phosphor::dump::HostState::TransitioningToOff;
175     // Allow creating system dump only when the host is up or quiesced
176     // starting to power off
177     if (!isHostRunning && !isHostQuiesced && !isHostTransitioningToOff)
178     {
179         lg2::error("System dump can be initiated only when the host is up "
180                    "or quiesced or starting to poweroff");
181         elog<NotAllowed>(Reason(
182             "System dump can be initiated only when the host is up "
183             "or quiesced or starting to poweroff"));
184         return std::string();
185     }
186 
187     // Get the originator id and type from params
188     std::string originatorId;
189     originatorTypes originatorType;
190 
191     phosphor::dump::extractOriginatorProperties(params, originatorId,
192                                                 originatorType);
193 
194     auto b = sdbusplus::bus::new_default();
195     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
196                                       SYSTEMD_INTERFACE, "StartUnit");
197     method.append(DIAG_MOD_TARGET); // unit to activate
198     method.append("replace");
199     bus.call_noreply(method);
200 
201     auto id = lastEntryId + 1;
202     auto idString = std::to_string(id);
203     auto objPath = std::filesystem::path(baseEntryPath) / idString;
204     uint64_t timeStamp =
205         std::chrono::duration_cast<std::chrono::microseconds>(
206             std::chrono::system_clock::now().time_since_epoch())
207             .count();
208 
209     try
210     {
211         entries.insert(std::make_pair(
212             id, std::make_unique<system::Entry>(
213                     bus, objPath.c_str(), id, timeStamp, 0, INVALID_SOURCE_ID,
214                     phosphor::dump::OperationStatus::InProgress, originatorId,
215                     originatorType, *this)));
216     }
217     catch (const std::invalid_argument& e)
218     {
219         lg2::error("Error in creating system dump entry, errormsg: {ERROR}, "
220                    "OBJECTPATH: {OBJECT_PATH}, ID: {ID}",
221                    "ERROR", e, "OBJECT_PATH", objPath, "ID", id);
222         elog<InternalFailure>();
223         return std::string();
224     }
225     lastEntryId++;
226     return objPath.string();
227 }
228 
229 } // namespace system
230 } // namespace dump
231 } // namespace openpower
232