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, 108 entityHandler{std::forward<ResolvedEntityHandler>(entityHandler)}]( 109 const boost::system::error_code& ec, 110 const dbus::utility::MapperGetSubTreeResponse& subtree) { 111 hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec, 112 subtree); 113 }); 114 } 115 116 inline void populateRootOfTrustEntity( 117 const std::string& /*unused*/, 118 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 119 const ResolvedEntity& resolvedEntity) 120 { 121 asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust"; 122 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 123 "/google/v1/RootOfTrustCollection/{}", resolvedEntity.id); 124 125 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 126 asyncResp->res.jsonValue["Id"] = resolvedEntity.id; 127 // Need to fix this later to a stabler property. 128 asyncResp->res.jsonValue["Name"] = resolvedEntity.id; 129 asyncResp->res.jsonValue["Description"] = "Google Root Of Trust"; 130 asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] = 131 "/google/v1/RootOfTrustCollection/" + resolvedEntity.id + 132 "/Actions/RootOfTrust.SendCommand"; 133 134 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 135 resolvedEntity.id; 136 asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] = 137 "Embedded"; 138 } 139 140 inline void 141 handleRootOfTrustGet(const crow::Request& /*req*/, 142 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 143 const std::string& param) 144 { 145 std::string emptyCommand; 146 resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity); 147 } 148 149 inline void 150 invocationCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 151 const boost::system::error_code& ec, 152 const std::vector<uint8_t>& responseBytes) 153 { 154 if (ec) 155 { 156 BMCWEB_LOG_ERROR("RootOfTrust.Actions.SendCommand failed: {}", 157 ec.message()); 158 redfish::messages::internalError(asyncResp->res); 159 return; 160 } 161 162 asyncResp->res.jsonValue["CommandResponse"] = 163 bytesToHexString(responseBytes); 164 } 165 166 inline void 167 invokeRoTCommand(const std::string& command, 168 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 169 const ResolvedEntity& resolvedEntity) 170 { 171 std::vector<uint8_t> bytes = hexStringToBytes(command); 172 if (bytes.empty()) 173 { 174 BMCWEB_LOG_DEBUG("Invalid command: {}", command); 175 redfish::messages::actionParameterValueTypeError(command, "Command", 176 "SendCommand"); 177 return; 178 } 179 180 crow::connections::systemBus->async_method_call( 181 [asyncResp{asyncResp}](const boost::system::error_code& ec, 182 const std::vector<uint8_t>& responseBytes) { 183 invocationCallback(asyncResp, ec, responseBytes); 184 }, 185 resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface, 186 "SendHostCommand", bytes); 187 } 188 189 inline void handleRoTSendCommandPost( 190 const crow::Request& request, 191 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 192 const std::string& rotId) 193 { 194 std::string command; 195 if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command", 196 command)) 197 { 198 BMCWEB_LOG_DEBUG("Missing property Command."); 199 redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand", 200 "Command"); 201 return; 202 } 203 204 resolveRoT(command, asyncResp, rotId, invokeRoTCommand); 205 } 206 207 inline void requestRoutes(App& app) 208 { 209 BMCWEB_ROUTE(app, "/google/v1/") 210 .methods(boost::beast::http::verb::get)(handleGoogleV1Get); 211 212 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection") 213 .privileges({{"ConfigureManager"}}) 214 .methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet); 215 216 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>") 217 .privileges({{"ConfigureManager"}}) 218 .methods(boost::beast::http::verb::get)(handleRootOfTrustGet); 219 220 BMCWEB_ROUTE( 221 app, 222 "/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand") 223 .privileges({{"ConfigureManager"}}) 224 .methods(boost::beast::http::verb::post)(handleRoTSendCommandPost); 225 } 226 227 } // namespace google_api 228 } // namespace crow 229