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 <nlohmann/json.hpp> 13 14 #include <array> 15 #include <string_view> 16 #include <vector> 17 18 namespace crow 19 { 20 namespace google_api 21 { 22 23 inline void 24 handleGoogleV1Get(const crow::Request& /*req*/, 25 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 26 { 27 asyncResp->res.jsonValue["@odata.type"] = 28 "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot"; 29 asyncResp->res.jsonValue["@odata.id"] = "/google/v1"; 30 asyncResp->res.jsonValue["Id"] = "Google Rest RootService"; 31 asyncResp->res.jsonValue["Name"] = "Google Service Root"; 32 asyncResp->res.jsonValue["Version"] = "1.0.0"; 33 asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] = 34 "/google/v1/RootOfTrustCollection"; 35 } 36 37 inline void handleRootOfTrustCollectionGet( 38 const crow::Request& /*req*/, 39 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 40 { 41 asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection"; 42 asyncResp->res.jsonValue["@odata.type"] = 43 "#RootOfTrustCollection.RootOfTrustCollection"; 44 const std::array<std::string_view, 1> interfaces{ 45 "xyz.openbmc_project.Control.Hoth"}; 46 redfish::collection_util::getCollectionMembers( 47 asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"), 48 interfaces, "/xyz/openbmc_project"); 49 } 50 51 // Helper struct to identify a resolved D-Bus object interface 52 struct ResolvedEntity 53 { 54 std::string id; 55 std::string service; 56 std::string object; 57 std::string interface; 58 }; 59 60 using ResolvedEntityHandler = std::function<void( 61 const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&, 62 const ResolvedEntity&)>; 63 64 inline void hothGetSubtreeCallback( 65 const std::string& command, 66 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 67 const std::string& rotId, const ResolvedEntityHandler& entityHandler, 68 const boost::system::error_code ec, 69 const dbus::utility::MapperGetSubTreeResponse& subtree) 70 { 71 if (ec) 72 { 73 redfish::messages::internalError(asyncResp->res); 74 return; 75 } 76 for (const auto& [path, services] : subtree) 77 { 78 sdbusplus::message::object_path objPath(path); 79 if (objPath.filename() != rotId || services.empty()) 80 { 81 continue; 82 } 83 84 ResolvedEntity resolvedEntity = { 85 .id = rotId, 86 .service = services[0].first, 87 .object = path, 88 .interface = "xyz.openbmc_project.Control.Hoth"}; 89 entityHandler(command, asyncResp, resolvedEntity); 90 return; 91 } 92 93 // Couldn't find an object with that name. return an error 94 redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId); 95 } 96 97 inline void resolveRoT(const std::string& command, 98 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 99 const std::string& rotId, 100 ResolvedEntityHandler&& entityHandler) 101 { 102 constexpr std::array<std::string_view, 1> hothIfaces = { 103 "xyz.openbmc_project.Control.Hoth"}; 104 dbus::utility::getSubTree( 105 "/xyz/openbmc_project", 0, hothIfaces, 106 [command, asyncResp, rotId, 107 entityHandler{std::forward<ResolvedEntityHandler>(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"] = crow::utility::urlFromPieces( 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