From d206b437e3fb79a89205a9fbe748b017d312ddfa Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Thu, 27 Aug 2020 11:45:42 -0700 Subject: Revert http::Request::socket() callback Details on why this revert is needed are here. https://lists.ozlabs.org/pipermail/openbmc/2020-August/022478.html Appu and Ravi still have not commented. It should be noted, this also causes a memory leak in http connection, where connections refuse to be freed, because of a bad usage of shared_from_this. This code wasn't very well thought through, and needs rearchitected to not break the unit testability of bmcweb, nor cause memory leaks. https://github.com/openbmc/bmcweb/blob/218bd4746130aac22366968c8c9a34a929e45a3d/http/http_connection.h#L351 Is the memory leak in question. Specifically, this reverts: The /attachment download in LogServices. This needs reimplemented properly, but is an OEM property, so it shouldn't be a big deal to revert, and shouldn't break our redfish compliance. The IpAddress property in SessionService. I have no idea why this was injected, and it's functionally incorrect. IpAddresses are not related to a session, and IP addresses can change over the course of a session, so this property is already broken as written. I suspect the author really wanted RedfishEvent type logging, but that was too complex, so they half implemented this. Redfish SSE properties. This needs to be reimplemented similar to the patchset here: https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/13948 Where the ownership of the HTTP connection does not leave the http framework. As written, the SSE implementation causes ownership issues, as there's no clear delineation of the ownership between HttpConnection and the SSE framework. Tested: On current master, running this command: wget -O- --no-http-keep-alive --no-check-certificate https://{bmc hostname}:18080/redfish/v1 Which should download the service root, then immediately close and destroy the connection, prints: (2020-08-28 16:55:24) [DEBUG "routing.h":1258] Matched rule '/redfish/v1/' 2 / 4 (2020-08-28 16:55:24) [DEBUG "http_response.h":130] calling completion handler (2020-08-28 16:55:24) [DEBUG "http_response.h":133] completion handler was valid (2020-08-28 16:55:24) [INFO "http_connection.h":429] Response: 0x1e1ee28 /redfish/v1 200 keepalive=0 (2020-08-28 16:55:24) [DEBUG "timer_queue.h":48] timer add inside: 0x1d3d1a8 7 (2020-08-28 16:55:24) [DEBUG "http_connection.h":751] 0x1e1ee28 timer added: 0x1d3d1a8 7 (2020-08-28 16:55:24) [DEBUG "http_connection.h":655] 0x1e1ee28 doWrite (2020-08-28 16:55:24) [DEBUG "http_connection.h":663] 0x1e1ee28 async_write 1555 bytes (2020-08-28 16:55:24) [DEBUG "http_connection.h":697] 0x1e1ee28 timer cancelled: 0x1d3d1a8 7 (2020-08-28 16:55:24) [DEBUG "http_connection.h":676] 0x1e1ee28 from write(1) Then stops. Note, that the connection was not destroyed, and has leaked. Once this patchset is added, the connection closes and destroys properly, and doesn't leak, so it prints the above, but also prints. (2020-08-28 16:27:10) [DEBUG "http_connection.h":305] 0x1d15c90 Connection closed, total 1 Ran Redfish service validator. Saw one unrelated failure due to UUID, all other things pass. Signed-off-by: Ed Tanous Change-Id: I18686037bf58f20389d31facc0d77020274d38a1 --- http/http_connection.h | 4 - include/dump_offload.hpp | 299 ---------------------------------- redfish-core/include/redfish.hpp | 3 - redfish-core/lib/event_service.hpp | 107 ------------ redfish-core/lib/log_services.hpp | 70 -------- redfish-core/lib/redfish_sessions.hpp | 7 - 6 files changed, 490 deletions(-) delete mode 100644 include/dump_offload.hpp diff --git a/http/http_connection.h b/http/http_connection.h index 109a272253..7781ca60a7 100644 --- a/http/http_connection.h +++ b/http/http_connection.h @@ -348,10 +348,6 @@ class Connection : if (!isInvalidRequest) { - req->socket = [self = shared_from_this()]() -> Adaptor& { - return self->socket(); - }; - res.completeRequestHandler = [] {}; res.isAliveHelper = [this]() -> bool { return isAlive(); }; diff --git a/include/dump_offload.hpp b/include/dump_offload.hpp deleted file mode 100644 index bc885cf573..0000000000 --- a/include/dump_offload.hpp +++ /dev/null @@ -1,299 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -#include -#include - -namespace crow -{ -namespace obmc_dump -{ - -inline void handleDumpOffloadUrl(const crow::Request& req, crow::Response& res, - const std::string& entryId); -inline void resetHandler(); - -// The max network block device buffer size is 128kb plus 16bytes -// for the message header -static constexpr auto nbdBufferSize = 131088; - -/** class Handler - * handles data transfer between nbd-client and nbd-server. - * This handler invokes nbd-proxy and reads data from socket - * and writes on to nbd-client and vice-versa - */ -class Handler : public std::enable_shared_from_this -{ - public: - Handler(const std::string& mediaIn, boost::asio::io_context& ios, - const std::string& entryIDIn) : - pipeOut(ios), - pipeIn(ios), media(mediaIn), entryID(entryIDIn), doingWrite(false), - negotiationDone(false), writeonnbd(false), - outputBuffer(std::make_unique< - boost::beast::flat_static_buffer>()), - inputBuffer( - std::make_unique>()) - {} - - ~Handler() - {} - - /** - * @brief Invokes InitiateOffload method of dump manager which - * directs pldm to start writing on the nbd device. - * - * @return void - */ - void initiateOffloadOnNbdDevice() - { - crow::connections::systemBus->async_method_call( - [this, - self(shared_from_this())](const boost::system::error_code ec) { - if (ec) - { - BMCWEB_LOG_ERROR << "DBUS response error: " << ec; - resetBuffers(); - resetHandler(); - return; - } - }, - "xyz.openbmc_project.Dump.Manager", - "/xyz/openbmc_project/dump/entry/" + entryID, - "xyz.openbmc_project.Dump.Entry", "InitiateOffload", "/dev/nbd1"); - } - - /** - * @brief Kills nbd-proxy - * - * @return void - */ - void doClose() - { - int rc = kill(proxy.id(), SIGTERM); - if (rc) - { - return; - } - proxy.wait(); - } - - /** - * @brief Starts nbd-proxy - * - * @return void - */ - void connect() - { - std::error_code ec; - proxy = boost::process::child("/usr/sbin/nbd-proxy", media, - boost::process::std_out > pipeOut, - boost::process::std_in < pipeIn, ec); - if (ec) - { - BMCWEB_LOG_ERROR << "Couldn't connect to nbd-proxy: " - << ec.message(); - resetHandler(); - return; - } - doRead(); - } - - /** - * @brief Wait for data on tcp socket from nbd-server. - * - * @return void - */ - void waitForMessageOnSocket() - { - - std::size_t bytes = inputBuffer->capacity() - inputBuffer->size(); - - (*stream).async_read_some( - inputBuffer->prepare(bytes), - [this, - self(shared_from_this())](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - if (ec) - { - BMCWEB_LOG_DEBUG << "Error while reading on socket"; - doClose(); - resetBuffers(); - resetHandler(); - return; - } - - inputBuffer->commit(bytes_transferred); - doWrite(); - }); - } - - /** - * @brief Writes data on input pipe of nbd-client. - * - * @return void - */ - void doWrite() - { - - if (doingWrite) - { - BMCWEB_LOG_DEBUG << "Already writing. Bailing out"; - return; - } - - if (inputBuffer->size() == 0) - { - BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out"; - return; - } - - doingWrite = true; - boost::asio::async_write( - pipeIn, inputBuffer->data(), - [this, self(shared_from_this())](const boost::beast::error_code& ec, - std::size_t bytesWritten) { - if (ec) - { - BMCWEB_LOG_DEBUG << "VM socket port closed"; - doClose(); - resetBuffers(); - resetHandler(); - return; - } - - doingWrite = false; - - if (negotiationDone == false) - { - // "gDf" is NBD reply magic - std::string reply_magic("gDf"); - std::string reply_string( - static_cast(inputBuffer->data().data()), - bytesWritten); - std::size_t found = reply_string.find(reply_magic); - if (found != std::string::npos) - { - negotiationDone = true; - writeonnbd = true; - } - } - - inputBuffer->consume(bytesWritten); - waitForMessageOnSocket(); - if (writeonnbd) - { - // NBD Negotiation Complete!!!!. Notify Dump manager to - // start dumping the actual data over NBD device - initiateOffloadOnNbdDevice(); - writeonnbd = false; - } - }); - } - - /** - * @brief Reads data on output pipe of nbd-client and write on - * tcp socket. - * - * @return void - */ - void doRead() - { - std::size_t bytes = outputBuffer->capacity() - outputBuffer->size(); - - pipeOut.async_read_some( - outputBuffer->prepare(bytes), - [this, self(shared_from_this())]( - const boost::system::error_code& ec, std::size_t bytesRead) { - if (ec) - { - BMCWEB_LOG_ERROR << "Couldn't read from VM port: " << ec; - doClose(); - resetBuffers(); - resetHandler(); - return; - } - - outputBuffer->commit(bytesRead); - - boost::asio::async_write( - *stream, outputBuffer->data(), - [this](const boost::system::error_code& ec2, - std::size_t bytes_transferred) { - if (ec2) - { - BMCWEB_LOG_DEBUG << "Error while writing on socket"; - doClose(); - resetBuffers(); - resetHandler(); - return; - } - - outputBuffer->consume(bytes_transferred); - doRead(); - }); - }); - } - - /** - * @brief Resets input and output buffers. - * @return void - */ - void resetBuffers() - { - this->inputBuffer->clear(); - this->outputBuffer->clear(); - } - - boost::process::async_pipe pipeOut; - boost::process::async_pipe pipeIn; - boost::process::child proxy; - std::string media; - std::string entryID; - bool doingWrite; - bool negotiationDone; - bool writeonnbd; - std::unique_ptr> - outputBuffer; - std::unique_ptr> - inputBuffer; - std::shared_ptr stream; -}; - -static std::shared_ptr handler; -inline void resetHandler() -{ - - handler.reset(); -} -inline void handleDumpOffloadUrl(const crow::Request& req, crow::Response& res, - const std::string& entryId) -{ - - // Run only one instance of Handler, one dump offload can happen at a time - if (handler != nullptr) - { - BMCWEB_LOG_ERROR << "Handler already running"; - res.result(boost::beast::http::status::service_unavailable); - res.jsonValue["Description"] = "Service is already being used"; - res.end(); - return; - } - - const char* media = "1"; - boost::asio::io_context* io_con = req.ioService; - - handler = std::make_shared(media, *io_con, entryId); - handler->stream = - std::make_shared(std::move(req.socket())); - handler->connect(); - handler->waitForMessageOnSocket(); -} -} // namespace obmc_dump -} // namespace crow diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 18a0353e49..8a2b972ed3 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -108,14 +108,12 @@ class RedfishService nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); - nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); - nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); #endif @@ -199,7 +197,6 @@ class RedfishService nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); - nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index 7d17ceacb2..e1c06ec3f2 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -59,8 +59,6 @@ class EventService : public Node {"@odata.type", "#EventService.v1_5_0.EventService"}, {"Id", "EventService"}, {"Name", "Event Service"}, - {"ServerSentEventUri", - "/redfish/v1/EventService/Subscriptions/SSE"}, {"Subscriptions", {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}}, {"Actions", @@ -447,111 +445,6 @@ class EventDestinationCollection : public Node } }; -class EventServiceSSE : public Node -{ - public: - EventServiceSSE(App& app) : - Node(app, "/redfish/v1/EventService/Subscriptions/SSE/") - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"ConfigureManager"}}}, - {boost::beast::http::verb::head, {{"ConfigureManager"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector&) override - { - if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= - maxNoOfSubscriptions) - { - messages::eventSubscriptionLimitExceeded(res); - res.end(); - return; - } - - std::shared_ptr sseConn = - std::make_shared(std::move(req.socket())); - std::shared_ptr subValue = - std::make_shared(sseConn); - - // GET on this URI means, Its SSE subscriptionType. - subValue->subscriptionType = "SSE"; - - // Default values - subValue->protocol = "Redfish"; - subValue->retryPolicy = "TerminateAfterRetries"; - - boost::urls::url_view::params_type::iterator it = - req.urlParams.find("$filter"); - if (it == req.urlParams.end()) - { - subValue->eventFormatType = "Event"; - } - - else - { - std::string filters = it->value(); - // Reading from query params. - bool status = readSSEQueryParams( - filters, subValue->eventFormatType, subValue->registryMsgIds, - subValue->registryPrefixes, subValue->metricReportDefinitions); - - if (!status) - { - messages::invalidObject(res, filters); - return; - } - - if (!subValue->eventFormatType.empty()) - { - if (std::find(supportedEvtFormatTypes.begin(), - supportedEvtFormatTypes.end(), - subValue->eventFormatType) == - supportedEvtFormatTypes.end()) - { - messages::propertyValueNotInList( - res, subValue->eventFormatType, "EventFormatType"); - return; - } - } - else - { - // If nothing specified, using default "Event" - subValue->eventFormatType = "Event"; - } - - if (!subValue->registryPrefixes.empty()) - { - for (const std::string& it : subValue->registryPrefixes) - { - if (std::find(supportedRegPrefixes.begin(), - supportedRegPrefixes.end(), - it) == supportedRegPrefixes.end()) - { - messages::propertyValueNotInList(res, it, - "RegistryPrefixes"); - return; - } - } - } - } - - std::string id = - EventServiceManager::getInstance().addSubscription(subValue, false); - if (id.empty()) - { - messages::internalError(res); - res.end(); - return; - } - } -}; - class EventDestination : public Node { public: diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index c96a297d3a..1cda61c182 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -533,20 +532,12 @@ inline void getDumpEntryCollection(std::shared_ptr& asyncResp, { thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] = "Manager"; - thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] = - "/redfish/v1/Managers/bmc/LogServices/Dump/" - "attachment/" + - entryID; } else if (dumpType == "System") { thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] = "OEM"; thisEntry["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] = "System"; - thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] = - "/redfish/v1/Systems/system/LogServices/Dump/" - "attachment/" + - entryID; } } asyncResp->res.jsonValue["Members@odata.count"] = @@ -2164,36 +2155,6 @@ class BMCDumpCreate : public Node } }; -class BMCDumpEntryDownload : public Node -{ - public: - BMCDumpEntryDownload(App& app) : - Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/attachment//", - std::string()) - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector& params) override - { - if (params.size() != 1) - { - messages::internalError(res); - return; - } - const std::string& entryID = params[0]; - crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID); - } -}; - class BMCDumpClear : public Node { public: @@ -2366,37 +2327,6 @@ class SystemDumpCreate : public Node } }; -class SystemDumpEntryDownload : public Node -{ - public: - SystemDumpEntryDownload(App& app) : - Node(app, - "/redfish/v1/Systems/system/LogServices/Dump/attachment//", - std::string()) - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector& params) override - { - if (params.size() != 1) - { - messages::internalError(res); - return; - } - const std::string& entryID = params[0]; - crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID); - } -}; - class SystemDumpClear : public Node { public: diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp index 1d8e3c4fcc..fbbffcb22b 100644 --- a/redfish-core/lib/redfish_sessions.hpp +++ b/redfish-core/lib/redfish_sessions.hpp @@ -227,13 +227,6 @@ class SessionCollection : public Node } #endif -#ifdef BMCWEB_ENABLE_SSL - clientIp = - req.socket().next_layer().remote_endpoint().address().to_string(); -#else - clientIp = req.socket().remote_endpoint().address().to_string(); -#endif - // User is authenticated - create session std::shared_ptr session = persistent_data::SessionStore::getInstance().generateUserSession( -- cgit v1.2.3