1 #pragma once 2 3 #include "app.hpp" 4 #include "async_resp.hpp" 5 #include "dbus_utility.hpp" 6 #include "error_messages.hpp" 7 #include "utils/collection.hpp" 8 #include "utils/hex_utils.hpp" 9 #include "utils/json_utils.hpp" 10 11 #include <boost/system/error_code.hpp> 12 #include <boost/url/format.hpp> 13 #include <nlohmann/json.hpp> 14 15 #include <array> 16 #include <string_view> 17 #include <vector> 18 19 namespace crow 20 { 21 namespace google_api 22 { 23 24 inline void 25 handleGoogleV1Get(const crow::Request& /*req*/, 26 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 27 { 28 asyncResp->res.jsonValue["@odata.type"] = 29 "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot"; 30 asyncResp->res.jsonValue["@odata.id"] = "/google/v1"; 31 asyncResp->res.jsonValue["Id"] = "Google Rest RootService"; 32 asyncResp->res.jsonValue["Name"] = "Google Service Root"; 33 asyncResp->res.jsonValue["Version"] = "1.0.0"; 34 asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] = 35 "/google/v1/RootOfTrustCollection"; 36 } 37 38 inline void handleRootOfTrustCollectionGet( 39 const crow::Request& /*req*/, 40 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 41 { 42 asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection"; 43 asyncResp->res.jsonValue["@odata.type"] = 44 "#RootOfTrustCollection.RootOfTrustCollection"; 45 const std::array<std::string_view, 1> interfaces{ 46 "xyz.openbmc_project.Control.Hoth"}; 47 redfish::collection_util::getCollectionMembers( 48 asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"), 49 interfaces, "/xyz/openbmc_project"); 50 } 51 52 // Helper struct to identify a resolved D-Bus object interface 53 struct ResolvedEntity 54 { 55 std::string id; 56 std::string service; 57 std::string object; 58 std::string interface; 59 }; 60 61 using ResolvedEntityHandler = std::function<void( 62 const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&, 63 const ResolvedEntity&)>; 64 65 inline void hothGetSubtreeCallback( 66 const std::string& command, 67 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 68 const std::string& rotId, const ResolvedEntityHandler& entityHandler, 69 const boost::system::error_code& ec, 70 const dbus::utility::MapperGetSubTreeResponse& subtree) 71 { 72 if (ec) 73 { 74 redfish::messages::internalError(asyncResp->res); 75 return; 76 } 77 for (const auto& [path, services] : subtree) 78 { 79 sdbusplus::message::object_path objPath(path); 80 if (objPath.filename() != rotId || services.empty()) 81 { 82 continue; 83 } 84 85 ResolvedEntity resolvedEntity = { 86 .id = rotId, 87 .service = services[0].first, 88 .object = path, 89 .interface = "xyz.openbmc_project.Control.Hoth"}; 90 entityHandler(command, asyncResp, resolvedEntity); 91 return; 92 } 93 94 // Couldn't find an object with that name. return an error 95 redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId); 96 } 97 98 inline void resolveRoT(const std::string& command, 99 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 100 const std::string& rotId, 101 ResolvedEntityHandler&& entityHandler) 102 { 103 constexpr std::array<std::string_view, 1> hothIfaces = { 104 "xyz.openbmc_project.Control.Hoth"}; 105 dbus::utility::getSubTree( 106 "/xyz/openbmc_project", 0, hothIfaces, 107 [command, asyncResp, rotId, entityHandler{std::move(entityHandler)}]( 108 const boost::system::error_code& ec, 109 const dbus::utility::MapperGetSubTreeResponse& subtree) { 110 hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec, 111 subtree); 112 }); 113 } 114 115 inline void populateRootOfTrustEntity( 116 const std::string& /*unused*/, 117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 118 const ResolvedEntity& resolvedEntity) 119 { 120 asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust"; 121 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 122 "/google/v1/RootOfTrustCollection/{}", resolvedEntity.id); 123 124 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 125 asyncResp->res.jsonValue["Id"] = resolvedEntity.id; 126 // Need to fix this later to a stabler property. 127 asyncResp->res.jsonValue["Name"] = resolvedEntity.id; 128 asyncResp->res.jsonValue["Description"] = "Google Root Of Trust"; 129 asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] = 130 "/google/v1/RootOfTrustCollection/" + resolvedEntity.id + 131 "/Actions/RootOfTrust.SendCommand"; 132 133 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 134 resolvedEntity.id; 135 asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] = 136 "Embedded"; 137 } 138 139 inline void 140 handleRootOfTrustGet(const crow::Request& /*req*/, 141 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 142 const std::string& param) 143 { 144 std::string emptyCommand; 145 resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity); 146 } 147 148 inline void 149 invocationCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 150 const boost::system::error_code& ec, 151 const std::vector<uint8_t>& responseBytes) 152 { 153 if (ec) 154 { 155 BMCWEB_LOG_ERROR("RootOfTrust.Actions.SendCommand failed: {}", 156 ec.message()); 157 redfish::messages::internalError(asyncResp->res); 158 return; 159 } 160 161 asyncResp->res.jsonValue["CommandResponse"] = 162 bytesToHexString(responseBytes); 163 } 164 165 inline void 166 invokeRoTCommand(const std::string& command, 167 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 168 const ResolvedEntity& resolvedEntity) 169 { 170 std::vector<uint8_t> bytes = hexStringToBytes(command); 171 if (bytes.empty()) 172 { 173 BMCWEB_LOG_DEBUG("Invalid command: {}", command); 174 redfish::messages::actionParameterValueTypeError(command, "Command", 175 "SendCommand"); 176 return; 177 } 178 179 crow::connections::systemBus->async_method_call( 180 [asyncResp{asyncResp}](const boost::system::error_code& ec, 181 const std::vector<uint8_t>& responseBytes) { 182 invocationCallback(asyncResp, ec, responseBytes); 183 }, 184 resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface, 185 "SendHostCommand", bytes); 186 } 187 188 inline void handleRoTSendCommandPost( 189 const crow::Request& request, 190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 191 const std::string& rotId) 192 { 193 std::string command; 194 if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command", 195 command)) 196 { 197 BMCWEB_LOG_DEBUG("Missing property Command."); 198 redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand", 199 "Command"); 200 return; 201 } 202 203 resolveRoT(command, asyncResp, rotId, invokeRoTCommand); 204 } 205 206 inline void requestRoutes(App& app) 207 { 208 BMCWEB_ROUTE(app, "/google/v1/") 209 .methods(boost::beast::http::verb::get)(handleGoogleV1Get); 210 211 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection") 212 .privileges({{"ConfigureManager"}}) 213 .methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet); 214 215 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>") 216 .privileges({{"ConfigureManager"}}) 217 .methods(boost::beast::http::verb::get)(handleRootOfTrustGet); 218 219 BMCWEB_ROUTE( 220 app, 221 "/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand") 222 .privileges({{"ConfigureManager"}}) 223 .methods(boost::beast::http::verb::post)(handleRoTSendCommandPost); 224 } 225 226 } // namespace google_api 227 } // namespace crow 228