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