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