1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 
18 #include "data_interface.hpp"
19 
20 #include <fstream>
21 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
22 
23 namespace openpower
24 {
25 namespace pels
26 {
27 
28 namespace service_name
29 {
30 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
31 } // namespace service_name
32 
33 namespace object_path
34 {
35 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
36 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
37 constexpr auto baseInv = "/xyz/openbmc_project/inventory";
38 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
39 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
40 constexpr auto hostState = "/xyz/openbmc_project/state/host0";
41 constexpr auto pldm = "/xyz/openbmc_project/pldm";
42 constexpr auto enableHostPELs =
43     "/xyz/openbmc_project/logging/send_event_logs_to_host";
44 } // namespace object_path
45 
46 namespace interface
47 {
48 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
49 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
50 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
51 constexpr auto osStatus = "xyz.openbmc_project.State.OperatingSystem.Status";
52 constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
53 constexpr auto enable = "xyz.openbmc_project.Object.Enable";
54 constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
55 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
56 constexpr auto hostState = "xyz.openbmc_project.State.Host";
57 constexpr auto invMotherboard =
58     "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
59 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
60 } // namespace interface
61 
62 using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
63 using sdbusplus::exception::SdBusError;
64 
65 DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
66 {
67     readBMCFWVersion();
68     readServerFWVersion();
69     readBMCFWVersionID();
70     readMotherboardCCIN();
71 
72     // Watch both the Model and SN properties on the system's Asset iface
73     _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
74         bus, object_path::systemInv, interface::invAsset, *this,
75         [this](const auto& properties) {
76             auto model = properties.find("Model");
77             if (model != properties.end())
78             {
79                 this->_machineTypeModel = std::get<std::string>(model->second);
80             }
81 
82             auto sn = properties.find("SerialNumber");
83             if (sn != properties.end())
84             {
85                 this->_machineSerialNumber = std::get<std::string>(sn->second);
86             }
87         }));
88 
89     // Watch the OperatingSystemState property
90     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
91         bus, object_path::hostState, interface::osStatus,
92         "OperatingSystemState", *this, [this](const auto& value) {
93             auto status =
94                 Status::convertOSStatusFromString(std::get<std::string>(value));
95 
96             if ((status == Status::OSStatus::BootComplete) ||
97                 (status == Status::OSStatus::Standby))
98             {
99                 setHostUp(true);
100             }
101             else
102             {
103                 setHostUp(false);
104             }
105         }));
106 
107     // Watch the host PEL enable property
108     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
109         bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
110         [this](const auto& value) {
111             this->_sendPELsToHost = std::get<bool>(value);
112         }));
113 
114     // Watch the BMCState property
115     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
116         bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
117         *this, [this](const auto& value) {
118             this->_bmcState = std::get<std::string>(value);
119         }));
120 
121     // Watch the chassis current and requested power state properties
122     _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
123         bus, object_path::chassisState, interface::chassisState, *this,
124         [this](const auto& properties) {
125             auto state = properties.find("CurrentPowerState");
126             if (state != properties.end())
127             {
128                 this->_chassisState = std::get<std::string>(state->second);
129             }
130 
131             auto trans = properties.find("RequestedPowerTransition");
132             if (trans != properties.end())
133             {
134                 this->_chassisTransition = std::get<std::string>(trans->second);
135             }
136         }));
137 
138     // Watch the CurrentHostState property
139     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
140         bus, object_path::hostState, interface::hostState, "CurrentHostState",
141         *this, [this](const auto& value) {
142             this->_hostState = std::get<std::string>(value);
143         }));
144 }
145 
146 DBusPropertyMap
147     DataInterface::getAllProperties(const std::string& service,
148                                     const std::string& objectPath,
149                                     const std::string& interface) const
150 {
151     DBusPropertyMap properties;
152 
153     auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
154                                        interface::dbusProperty, "GetAll");
155     method.append(interface);
156     auto reply = _bus.call(method);
157 
158     reply.read(properties);
159 
160     return properties;
161 }
162 
163 void DataInterface::getProperty(const std::string& service,
164                                 const std::string& objectPath,
165                                 const std::string& interface,
166                                 const std::string& property,
167                                 DBusValue& value) const
168 {
169 
170     auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
171                                        interface::dbusProperty, "Get");
172     method.append(interface, property);
173     auto reply = _bus.call(method);
174 
175     reply.read(value);
176 }
177 
178 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
179 {
180 
181     auto method = _bus.new_method_call(
182         service_name::objectMapper, object_path::objectMapper,
183         interface::objectMapper, "GetSubTreePaths");
184 
185     method.append(std::string{"/"}, 0, interfaces);
186 
187     auto reply = _bus.call(method);
188 
189     DBusPathList paths;
190     reply.read(paths);
191 
192     return paths;
193 }
194 
195 DBusService DataInterface::getService(const std::string& objectPath,
196                                       const std::string& interface) const
197 {
198     auto method = _bus.new_method_call(service_name::objectMapper,
199                                        object_path::objectMapper,
200                                        interface::objectMapper, "GetObject");
201 
202     method.append(objectPath, std::vector<std::string>({interface}));
203 
204     auto reply = _bus.call(method);
205 
206     std::map<DBusService, DBusInterfaceList> response;
207     reply.read(response);
208 
209     if (!response.empty())
210     {
211         return response.begin()->first;
212     }
213 
214     return std::string{};
215 }
216 
217 /**
218  * @brief Return a value found in the /etc/os-release file
219  *
220  * @param[in] key - The key name, like "VERSION"
221  *
222  * @return std::optional<std::string> - The value
223  */
224 std::optional<std::string> getOSReleaseValue(const std::string& key)
225 {
226     std::ifstream versionFile{BMC_VERSION_FILE};
227     std::string line;
228     std::string keyPattern{key + '='};
229 
230     while (std::getline(versionFile, line))
231     {
232         if (line.find(keyPattern) != std::string::npos)
233         {
234             auto pos = line.find_first_of('"') + 1;
235             auto value = line.substr(pos, line.find_last_of('"') - pos);
236             return value;
237         }
238     }
239 
240     return std::nullopt;
241 }
242 
243 void DataInterface::readBMCFWVersion()
244 {
245     _bmcFWVersion = getOSReleaseValue("VERSION").value_or("");
246 }
247 
248 void DataInterface::readServerFWVersion()
249 {
250     // Not available yet
251 }
252 
253 void DataInterface::readBMCFWVersionID()
254 {
255     _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or("");
256 }
257 
258 void DataInterface::readMotherboardCCIN()
259 {
260     try
261     {
262         // First, try to find the motherboard
263         auto motherboards = getPaths({interface::invMotherboard});
264         if (motherboards.empty())
265         {
266             throw std::runtime_error("No motherboards yet");
267         }
268 
269         // Found it, so now get the CCIN
270         _properties.emplace_back(
271             std::make_unique<PropertyWatcher<DataInterface>>(
272                 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
273                 *this,
274                 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
275     }
276     catch (const std::exception& e)
277     {
278         // No motherboard in the inventory yet - watch for it
279         _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
280             _bus, match_rules::interfacesAdded(object_path::baseInv),
281             std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
282                       std::placeholders::_1));
283     }
284 }
285 
286 void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
287 {
288     sdbusplus::message::object_path path;
289     DBusInterfaceMap interfaces;
290 
291     msg.read(path, interfaces);
292 
293     // This is watching the whole inventory, so check if it's what we want
294     if (interfaces.find(interface::invMotherboard) == interfaces.end())
295     {
296         return;
297     }
298 
299     // Done watching for any new inventory interfaces
300     _inventoryIfacesAddedMatch.reset();
301 
302     // Watch the motherboard CCIN, using the service from this signal
303     // for the initial property read.
304     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
305         _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
306         [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
307 }
308 
309 } // namespace pels
310 } // namespace openpower
311