| #include "libpldm/pldm_rde.h" |
| |
| #include "common/utils.hpp" |
| #include "helper/common.hpp" |
| #include "helper/discovery/base_discovery.hpp" |
| #include "helper/discovery/platform_discovery.hpp" |
| #include "helper/discovery/rde_discovery.hpp" |
| #include "helper/rde_operation/rde_operation.hpp" |
| |
| // libbej |
| #include "libbej/bej_common.h" |
| #include "libbej/bej_encoder_core.h" |
| |
| #include "libbej/bej_encoder_json.hpp" |
| |
| #include <locale.h> |
| #include <signal.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| |
| #include <boost/asio.hpp> |
| #include <boost/asio/io_context.hpp> |
| #include <helper/mctpsetup.hpp> |
| #include <libbej/bej_decoder_json.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/bus.hpp> |
| #include <sdbusplus/server.hpp> |
| #include <sdbusplus/unpack_properties.hpp> |
| #include <sdeventplus/event.hpp> |
| |
| #include <chrono> |
| #include <csignal> |
| #include <cstddef> |
| #include <iostream> |
| #include <unordered_map> |
| #include <vector> |
| |
| using namespace sdeventplus; |
| using namespace pldm::utils; |
| |
| using DbusVariant = std::variant<std::string, uint64_t, uint32_t, uint16_t, |
| uint8_t, sdbusplus::message::object_path>; |
| using ChangedPropertiesType = std::vector<std::pair<std::string, DbusVariant>>; |
| |
| constexpr uint8_t DEST_EID = 9; |
| // TODO(@harshtya): Change instance id to be dynamic |
| constexpr uint8_t INSTANCE_ID = 1; |
| |
| constexpr int SOCKET_TIMEOUT_SECONDS = 8; |
| |
| constexpr uint32_t annotationDictRid = 0xffffffff; |
| |
| int fd = -1; // MCTP Socket for RDE communication |
| |
| boost::asio::io_context io; |
| std::unordered_map<int, int> retryCounterMap; |
| |
| std::unordered_map<std::string, int> deviceNetIdMap; |
| std::unordered_map<std::string, |
| std::shared_ptr<sdbusplus::asio::dbus_interface>> |
| dbusIntfMap; |
| std::unordered_map<std::string, std::string> objectPathDeviceId; |
| |
| std::unordered_map<std::string, std::unordered_map<std::string, uint32_t>> |
| rdeResourceIdMap; |
| |
| struct CacheEntry |
| { |
| std::string response; |
| std::chrono::steady_clock::time_point timestamp; |
| }; |
| |
| std::unordered_map<std::string, std::unordered_map<std::string, CacheEntry>> |
| rdeResponseCacheMap; |
| |
| constexpr int CACHE_EXPIRATION_SECONDS = 300; |
| |
| static int getResourceIdForUri(std::string& uri, std::string& udevId, |
| uint32_t& resourceId) |
| { |
| if (rdeResourceIdMap.find(udevId) != rdeResourceIdMap.end()) |
| { |
| std::unordered_map<std::string, uint32_t>::iterator it = |
| rdeResourceIdMap[udevId].find(uri); |
| if (it != rdeResourceIdMap[udevId].end()) |
| { |
| resourceId = it->second; |
| if (DEBUG) |
| { |
| std::cout << "Resource id found: " << std::to_string(it->second) |
| << "\n"; |
| } |
| return 0; |
| } |
| } |
| |
| if ((uri.find("Cables") == std::string::npos) && |
| (uri.find("Systems") == std::string::npos)) |
| { |
| std::cerr << "Resource id not found for uri: " << uri << std::endl; |
| } |
| return 1; |
| } |
| |
| void cleanupAtExit() |
| { |
| // cleans up at daemon termination |
| fd = 0; |
| rdeResponseCacheMap.clear(); |
| rdeResourceIdMap.clear(); |
| deviceNetIdMap.clear(); |
| dbusIntfMap.clear(); |
| objectPathDeviceId.clear(); |
| retryCounterMap.clear(); |
| cleanupBaseDiscAtExit(); |
| cleanupRdeDiscAtExit(); |
| cleanupMctpAtExit(); |
| } |
| |
| void signalHandler(int signal) |
| { |
| std::cerr << "Received signal " << signal << ". Cleaning up...\n"; |
| cleanupAtExit(); |
| std::exit(signal); |
| } |
| |
| int initiateDiscovery(int fd, std::string udevId, int netId, uint8_t destEid, |
| uint8_t instanceId) |
| { |
| int rc; |
| |
| // Begin Base Discovery |
| std::cerr << "Initiating PLDM Discovery...\n"; |
| rc = performBaseDiscovery(udevId, fd, netId, destEid, instanceId); |
| if (rc) |
| { |
| std::cerr << "Failure in Base Discovery with error code: " << rc |
| << "\n"; |
| return rc; |
| } |
| |
| std::cerr << "Initializing RDE Discovery...\n"; |
| uint32_t negotiatedTransferSize = 0; |
| rc = performRdeDiscovery(udevId, fd, netId, destEid, instanceId, |
| negotiatedTransferSize); |
| |
| if (rc) |
| { |
| std::cerr << "Failure in Base Discovery with error code: " << rc |
| << "\n"; |
| return rc; |
| } |
| |
| std::unordered_map<std::string, uint32_t> resourceIdMap; |
| rdeResourceIdMap.emplace(std::make_pair(udevId, std::move(resourceIdMap))); |
| auto it = rdeResourceIdMap.find(udevId); |
| if (it == rdeResourceIdMap.end()) |
| { |
| std::cerr << "Failed to find Resource ID map for " << udevId |
| << " device" << std::endl; |
| return -1; |
| } |
| |
| std::cerr << "Initializing PDR Discovery...\n"; |
| rc = performPdrDiscovery(udevId, fd, netId, destEid, instanceId, |
| negotiatedTransferSize, it->second); |
| |
| if (rc) |
| { |
| std::cerr << "Failure in Base Discovery with error code: " << rc |
| << "\n"; |
| return rc; |
| } |
| |
| rc = performDictionaryDiscoveryForDevice(udevId, fd, destEid, instanceId, |
| it->second); |
| |
| if (rc) |
| { |
| std::cerr << "Failure in getting dictionaries with error code: " << rc |
| << "\n"; |
| return rc; |
| } |
| return 0; |
| } |
| |
| void getDeviceIdForRemovedDevice(const std::string& objPath, |
| std::string* deviceId) |
| { |
| auto it = objectPathDeviceId.find(objPath); |
| if (it != objectPathDeviceId.end()) |
| { |
| *deviceId = it->second; |
| } |
| } |
| |
| /** |
| * @brief generate operation id |
| */ |
| uint16_t generateOperationId() |
| { |
| // TODO(@harshtya): Create an operation id generator (Following the spec) |
| return 32770; |
| } |
| |
| /** |
| * @brief Retry counter tracker |
| */ |
| int getCurrentRetryCount(int requestId) |
| { |
| auto it = retryCounterMap.find(requestId); |
| if (it == retryCounterMap.end()) |
| { |
| retryCounterMap.insert({requestId, 1}); |
| return 1; |
| } |
| |
| int retryCount = it->second; |
| retryCounterMap.insert({requestId, retryCount + 1}); |
| return retryCount + 1; |
| } |
| |
| /** |
| * @brief Removes the query parameters as we do not support expand yet |
| */ |
| int removeQueryParams(std::string& uri, std::string_view delimeter) |
| { |
| std::string::size_type start = uri.find(delimeter); |
| int end = uri.length(); |
| if (start == std::string::npos) |
| { |
| if (DEBUG) |
| { |
| std::cerr << "No query parameters found\n"; |
| } |
| return 1; |
| } |
| uri.erase(start, end - start + 1); |
| return 0; |
| } |
| |
| /** |
| * @brief Remove any trailing forward slashes in URI |
| */ |
| void truncateTrailingFwSlash(std::string& uri) |
| { |
| if (uri.back() == '/') |
| { |
| uri.pop_back(); |
| } |
| } |
| |
| /** |
| * @brief Checks whether the POST request is an UPDATE request or ACTION |
| */ |
| bool isActionOperation(const std::string& uri, std::string& resourceUri) |
| { |
| std::string actionString = "/Actions"; |
| size_t index; |
| if ((index = uri.find(actionString)) != std::string::npos) |
| { |
| resourceUri = uri.substr(0, index); |
| return true; |
| } |
| return false; |
| } |
| |
| int getSchemaDictionary(void* context, uint32_t resourceId, |
| const uint8_t** dictionary) |
| { |
| int rc; |
| |
| if (context == nullptr) |
| { |
| std::cerr << " null context (deviceIdPtr) resourceId:" << resourceId |
| << "\n"; |
| return -1; |
| } |
| |
| const std::string* deviceIdPtr = static_cast<const std::string*>(context); |
| // (TODO) to get rid of this calculation once BMC removes the |
| // hardcoding of the resource ID mapping. |
| uint32_t ridSchema = resourceId >> 16 << 16; |
| uint32_t schemaDictLen = 0; |
| uint8_t* schemaDict = nullptr; |
| rc = getDictionaryForRidDev(*deviceIdPtr, ridSchema, &schemaDict, |
| &schemaDictLen); |
| if (rc) |
| { |
| std::cerr << "Unable to fetch schema dictionary\n"; |
| return rc; |
| } |
| *dictionary = (const uint8_t*)schemaDict; |
| return 0; |
| } |
| |
| /** |
| * Translate BEJ to string |
| */ |
| int translateBejToString(uint32_t resourceId, const std::string& deviceId, |
| std::string* json, uint8_t** payload, |
| uint32_t payloadLength) |
| { |
| int rc; |
| uint32_t annotationDictLen = 0; |
| uint8_t* annotationDict = nullptr; |
| |
| // get_dictioanry_for_rid from particular resource id |
| rc = getDictionaryForRidDev(deviceId, annotationDictRid, &annotationDict, |
| &annotationDictLen); |
| if (rc) |
| { |
| std::cerr << "Unable to fetch annotation dictionary\n"; |
| return rc; |
| } |
| uint32_t ridSchema = resourceId >> 16 << 16; |
| uint32_t schemaDictLen = 0; |
| uint8_t* schemaDict = nullptr; |
| rc = getDictionaryForRidDev(deviceId, ridSchema, &schemaDict, |
| &schemaDictLen); |
| if (rc) |
| { |
| std::cerr << "Unable to fetch schema dictionary\n"; |
| return rc; |
| } |
| |
| std::span<uint8_t> bejson = std::span{*payload, payloadLength}; |
| BejDictionaries bejDictionaries; |
| bejDictionaries.schemaDictionary = schemaDict; |
| bejDictionaries.schemaDictionarySize = schemaDictLen; |
| bejDictionaries.annotationDictionary = annotationDict; |
| bejDictionaries.annotationDictionarySize = annotationDictLen; |
| bejDictionaries.errorDictionary = nullptr; |
| bejDictionaries.errorDictionarySize = 0; |
| |
| struct BejNodeDecodeInfo nodeDecodeInfo = {}; |
| nodeDecodeInfo.context = (void*)&deviceId; |
| nodeDecodeInfo.getSchemaDictionary = &getSchemaDictionary; |
| |
| if (payloadLength > 0) |
| { |
| libbej::BejDecoderJson decoder; |
| decoder.decode(bejDictionaries, bejson, BEJ_DICTIONARY_START_AT_HEAD, |
| &nodeDecodeInfo); |
| *json = decoder.getOutput(); |
| } |
| else |
| { |
| *json = "{\"Status\": \"Completed\"}"; |
| } |
| return 0; |
| } |
| |
| /** |
| * @brief RDE Operation DBus method handler |
| */ |
| std::string |
| rdeOperation(boost::asio::yield_context yield, int requestId, |
| uint8_t operationType /* Change to std::string req_type */, |
| std::string uri, std::string udevid, |
| std::string reqPayloadJson) |
| { |
| struct pldm_rde_requester_context baseContext; |
| int rc = getRdeFreeContextForRdeDevice(udevid, &baseContext); |
| if (rc == RDE_NO_CONTEXT_FOUND) |
| { |
| std::cerr << "Unable to provide base context- Error in RDE discovery\n"; |
| return "Unsuccessful RDE operation with error code: " + |
| std::to_string(rc); |
| } |
| |
| // if base context is free to execute |
| if (baseContext.context_status == CONTEXT_BUSY || |
| baseContext.context_status == CONTEXT_CONTINUE) |
| { |
| auto timer = std::make_shared<boost::asio::steady_timer>(io); |
| timer->expires_from_now(std::chrono::milliseconds(20)); |
| timer->async_wait(yield); |
| std::cerr << "Timer expired. RDE Operation failed\n"; |
| |
| int currentRetry = getCurrentRetryCount(requestId); |
| if (currentRetry > MAX_RETRIES_FOR_REQUEST) |
| { |
| retryCounterMap.erase(requestId); |
| return "Max retries exceeded for request. Abandoning...\n"; |
| } |
| rdeOperation(yield, requestId, operationType, uri, udevid, |
| reqPayloadJson); |
| } |
| |
| // get manager |
| std::optional<struct pldm_rde_requester_manager*> managerOpt = |
| getManagerForRdeDevice(udevid); |
| |
| if (!managerOpt.has_value()) |
| { |
| std::cerr |
| << "Failed to get manager to perform read request for request id: " |
| << std::to_string(requestId) << ", request uri: " << uri |
| << ", UDEVID: " << udevid << "\n"; |
| retryCounterMap.erase(requestId); |
| return "Unsuccessful RDE operation with error code: " + |
| std::to_string(rc); |
| } |
| struct pldm_rde_requester_manager* manager = managerOpt.value(); |
| uint32_t resourceId = 0; |
| |
| std::string rawUri = uri; |
| // strip query params - until expand is supported |
| rc = removeQueryParams(uri, "?"); |
| if (DEBUG && !rc) |
| { |
| retryCounterMap.erase(requestId); |
| std::cerr << "URI after removing query params: " << uri << "\n"; |
| } |
| truncateTrailingFwSlash(uri); |
| |
| std::string resource_uri = uri; |
| bool isAction = isActionOperation(uri, resource_uri); |
| bool bypassCache = false; |
| |
| if (isAction) |
| { |
| if (int(operationType) != 8) |
| { |
| std::cerr << "Action operation with non-POST type: " |
| << std::to_string(operationType) << " for URI: " << rawUri |
| << "\n"; |
| retryCounterMap.erase(requestId); |
| return "Unsuccessful RDE operation: Action must be POST for URI: " + |
| rawUri + "\n"; |
| } |
| |
| rc = getResourceIdForUri(resource_uri, udevid, resourceId); |
| if (rc) |
| { |
| std::cerr << "Unable to resolve collection for RDE Action request " |
| << "with rawUri: " << rawUri << "\n"; |
| retryCounterMap.erase(requestId); |
| return "Unable to resolve collection for RDE Action request with rc: " + |
| std::to_string(rc) + "\n"; |
| } |
| |
| if (uri.find("FetchIdentifyState") != std::string::npos) |
| { |
| operationType = PLDM_RDE_OPERATION_READ; |
| bypassCache = true; |
| } |
| else |
| { |
| // TODO(@harshtya): Define 6 in PLDM_RDE_OPERATION_TYPES in libpldm |
| operationType = |
| PLDM_RDE_OPERATION_ACTION; // This is the operation type for |
| // ACTION request |
| } |
| } |
| else |
| { |
| rc = getResourceIdForUri(uri, udevid, resourceId); |
| if (rc) |
| { |
| if (int(operationType) != 8) |
| { |
| if ((uri.find("Cables") == std::string::npos) && |
| (uri.find("Systems") == std::string::npos)) |
| { |
| std::cerr |
| << "Unsuccessful RDE operation- no resource id found, rc: " + |
| std::to_string(rc) + " with URI: " + rawUri + |
| "\n"; |
| } |
| retryCounterMap.erase(requestId); |
| return "Unsuccessful RDE operation with error code: " + |
| std::to_string(rc) + " with URI: " + rawUri + "\n"; |
| } |
| else |
| { |
| // Create a resource request |
| std::cerr << "Operation create not supported: " << rawUri |
| << "\n"; |
| retryCounterMap.erase(requestId); |
| return "Create RDE operation not yet supported for " + rawUri + |
| "\n"; |
| } |
| } |
| } |
| |
| if (operationType == PLDM_RDE_OPERATION_READ && !bypassCache) |
| { |
| auto udevIt = rdeResponseCacheMap.find(udevid); |
| if (udevIt != rdeResponseCacheMap.end()) |
| { |
| auto uriIt = udevIt->second.find(rawUri); |
| if (uriIt != udevIt->second.end()) |
| { |
| auto now = std::chrono::steady_clock::now(); |
| auto duration = |
| std::chrono::duration<double>(now - uriIt->second.timestamp) |
| .count(); |
| if (DEBUG) |
| { |
| std::cerr << "Cache duration for URI: " << rawUri << " is " |
| << duration << " seconds\n"; |
| } |
| if (duration <= CACHE_EXPIRATION_SECONDS) |
| { |
| if (DEBUG) |
| { |
| std::cerr |
| << "Serving response from cache for URI: " << rawUri |
| << "\n"; |
| } |
| retryCounterMap.erase(requestId); |
| return uriIt->second.response; |
| } |
| else |
| { |
| // Cache expired, let's proceed to fetch new one |
| if (DEBUG) |
| { |
| std::cerr << "Cache expired for URI: " << rawUri |
| << "\n"; |
| } |
| udevIt->second.erase(uriIt); |
| } |
| } |
| } |
| } |
| |
| uint16_t operation_id = generateOperationId(); |
| |
| for (int retry = 0; retry < MAX_RETRIES_MCTP_SOCK_FAILURE; retry++) |
| { |
| rc = executeRdeOperation(fd, DEST_EID, INSTANCE_ID, rawUri, uri, udevid, |
| operation_id, operationType, requestId, |
| manager, &baseContext, resourceId, |
| reqPayloadJson); |
| if (rc) |
| { |
| std::cerr << "Request execution failed: ReqID: " |
| << std::to_string(requestId) << ", URI: " << uri << ", " |
| << "operation type: " << std::to_string(operationType) |
| << " with retry counter: " << (retry + 1) << std::endl; |
| retryCounterMap.erase(requestId); |
| cleanupRequestId(requestId, &baseContext); |
| |
| if (retry == (MAX_RETRIES_MCTP_SOCK_FAILURE - 1)) |
| { |
| std::cerr << "All retries exhausted for rde execute: " << uri |
| << std::endl; |
| return "Unsuccessful RDE operation with error code: " + |
| std::to_string(rc); |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| uint8_t* payload; |
| uint32_t payloadLength; |
| rc = getResponseForRequestId(requestId, &payload, &payloadLength); |
| if (rc) |
| { |
| std::cerr << "Unable to fetch response from response map with rc: " |
| << rc << " for URI: " << rawUri; |
| retryCounterMap.erase(requestId); |
| cleanupRequestId(requestId, &baseContext); |
| return "Unsuccessful RDE operation with error code: " + |
| std::to_string(rc); |
| } |
| |
| std::string jsonResp; |
| rc = translateBejToString(resourceId, udevid, &jsonResp, &payload, |
| payloadLength); |
| if (rc) |
| { |
| std::cerr << "Unable to translate BEJ for URI: " << rawUri << "\n"; |
| jsonResp = "Unsuccessful RDE operation- failed to translate bej"; |
| } |
| else if (operationType == PLDM_RDE_OPERATION_READ && !bypassCache) |
| { |
| CacheEntry entry; |
| entry.response = jsonResp; |
| entry.timestamp = std::chrono::steady_clock::now(); |
| rdeResponseCacheMap[udevid][rawUri] = entry; |
| // Add a cap for max entries per device here to prevent unbounded memory |
| // growth. |
| if (rdeResponseCacheMap[udevid].size() >= 100) |
| { |
| rdeResponseCacheMap[udevid].clear(); |
| } |
| } |
| cleanupRequestId(requestId, &baseContext); |
| retryCounterMap.erase(requestId); |
| return jsonResp; |
| } |
| |
| void pldmSetup(int& fd, sdbusplus::asio::object_server& objectServer, |
| std::string changedObject, std::string& port, |
| std::string& udevid, std::string& vendorId, |
| std::string& prefixPath) |
| { |
| int netId = setupOnePort(port, udevid); // MCTP Setup |
| if (netId == -1) |
| { |
| std::cerr << "Mctp failed udevId: " << udevid << ", " << port << "\n"; |
| return; |
| } |
| deviceNetIdMap.insert({udevid, netId}); |
| int rc = 0; |
| for (int retryCounter = 0; retryCounter < MAX_RETRIES_MCTP_SOCK_FAILURE; |
| retryCounter++) |
| { |
| rc = initiateDiscovery(fd, udevid, netId, DEST_EID, INSTANCE_ID); |
| |
| if (rc) |
| { |
| std::cerr << "PLDM/RDE Discovery failed for device: " << port |
| << ", udev: " << udevid |
| << "with retry counter:" << (retryCounter + 1) |
| << std::endl; |
| cleanupDictionaries(udevid); |
| if (retryCounter == (MAX_RETRIES_MCTP_SOCK_FAILURE - 1)) |
| { |
| std::cerr << "All retries exhausted for discovery for " |
| << " udev: " << udevid << std::endl; |
| return; |
| } |
| } |
| else |
| { |
| break; |
| } |
| std::cerr << "Retrying discovery again due to failure" << std::endl; |
| } |
| |
| if (rc) |
| { |
| std::cerr << "PLDM/RDE Discovery failed for device: " << port |
| << ", udev: " << udevid << "after all retries" << std::endl; |
| return; |
| } |
| |
| std::string objectPath = prefixPath + udevid; |
| if (DEBUG) |
| { |
| std::cerr << "New object path created: " << objectPath << "\n"; |
| } |
| |
| std::shared_ptr<sdbusplus::asio::dbus_interface> iface = |
| objectServer.add_interface(objectPath, "xyz.openbmc_project.RdeDevice"); |
| iface->register_property("VID", vendorId, |
| sdbusplus::asio::PropertyPermission::readOnly); |
| iface->register_property("USBPORT", port, |
| sdbusplus::asio::PropertyPermission::readOnly); |
| iface->register_property("UDEVID", udevid, |
| sdbusplus::asio::PropertyPermission::readOnly); |
| |
| iface->register_method("execute_rde", std::move(rdeOperation)); |
| |
| iface->initialize(); |
| dbusIntfMap.insert({std::string(changedObject), iface}); |
| objectPathDeviceId.insert({std::string(changedObject), udevid}); |
| } |
| |
| int triggerRdeReactor(int fd) |
| { |
| /** |
| * RDE Reactor is responsible to react to a new RDE device whenever Entity |
| * Manager adds a new object path in its service tree that has the interface |
| * "xyz.openbmc_project.Configuration.RdeSatelliteController" |
| * |
| * The detector code for detecting a RDE Device resides in Entity Manager |
| */ |
| boost::asio::io_context io; |
| std::shared_ptr<sdbusplus::asio::connection> systemBus = |
| std::make_shared<sdbusplus::asio::connection>(io); |
| sdbusplus::asio::object_server objectServer(systemBus, true); |
| objectServer.add_manager("/xyz/openbmc_project/rde_devices"); |
| std::string prefixPath = "/xyz/openbmc_project/rde_devices/"; |
| |
| std::cerr << "Beginning RDE reactor...\n"; |
| |
| // Find the all the object paths that implement interface |
| // xyz.openbmc_project.Configuration.RdeSatelliteController |
| // Then, create rde_device dbus object |
| using propertyMap = |
| std::vector<std::pair<std::string, std::vector<std::string>>>; |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Configuration.RdeSatelliteController"}; |
| using DBusProperties = std::vector<std::pair<std::string, DbusVariant>>; |
| |
| systemBus->async_method_call_timed( |
| [&fd, &systemBus, &objectServer, &prefixPath]( |
| const boost::system::error_code& ec, |
| const std::vector<std::pair<std::string, propertyMap>>& subtree) { |
| if (ec) |
| { |
| std::cerr << "D-Bus error: " << ec << ", " << ec.message() << " \n"; |
| return; |
| } |
| if (subtree.empty()) |
| { |
| std::cerr << "No instances found \n"; |
| return; |
| } |
| for (const auto& [objPath, services] : subtree) |
| { |
| for (const auto& service : services) |
| { |
| systemBus->async_method_call_timed( |
| [&fd, &objectServer, &prefixPath, |
| objPath](const boost::system::error_code& ec2, |
| const DBusProperties& properties) { |
| if (ec2) |
| { |
| std::cerr << "DBUS response error on GetAll: " |
| << ec2.message() << " \n"; |
| return; |
| } |
| std::string vendorId; |
| std::string udevid; |
| std::string port; |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| [](const sdbusplus::UnpackErrorReason reason, |
| const std::string& property) { |
| std::cerr << " Error unpacking the property " |
| << property << " reason " |
| << static_cast<int>(reason) << " \n"; |
| }, |
| properties, "VID", vendorId, "USBPORT", port, "UDEVID", |
| udevid); |
| if (success) |
| { |
| if (vendorId.empty() || port.empty() || udevid.empty()) |
| { |
| std::cerr |
| << "Matcher does not have required properties\n"; |
| return; |
| } |
| // Do PLDM Setup |
| pldmSetup(fd, objectServer, std::string(objPath), port, |
| udevid, vendorId, prefixPath); |
| } |
| else |
| { |
| std::cerr << "error in parsing properties \n"; |
| } |
| }, |
| service.first, objPath, "org.freedesktop.DBus.Properties", |
| "GetAll", /*timeout in usec*/ 90000000, |
| "xyz.openbmc_project.Configuration.RdeSatelliteController"); |
| } |
| } |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetSubTree", /*timeout*/ 90000000, |
| "/xyz/openbmc_project/inventory", 0, interfaces); |
| |
| auto matchAdd = std::make_unique<sdbusplus::bus::match_t>( |
| *systemBus, sdbusplus::bus::match::rules::interfacesAdded(), |
| [&objectServer, &prefixPath, &systemBus, |
| &fd](sdbusplus::message_t& reply) { |
| sdbusplus::message::object_path changedObject; |
| reply.read(changedObject); |
| std::vector<std::pair<std::string, ChangedPropertiesType>> |
| changedInterfaces; |
| reply.read(changedInterfaces); |
| |
| std::string vendorId; |
| std::string udevid; |
| std::string port; |
| for (const auto& [changedInterface, changedProps] : changedInterfaces) |
| { |
| if (changedInterface != |
| "xyz.openbmc_project.Configuration.RdeSatelliteController") |
| { |
| continue; |
| } |
| |
| if (DEBUG) |
| { |
| std::cerr << "DEBUG: Changed object path: " |
| << std::string(changedObject) << "\n"; |
| } |
| |
| for (auto& [key, value] : changedProps) |
| { |
| if (key == "VID") |
| { |
| vendorId = std::get<std::string>(value); |
| } |
| else if (key == "USBPORT") |
| { |
| port = std::get<std::string>(value); |
| } |
| else if (key == "UDEVID") |
| { |
| udevid = std::get<std::string>(value); |
| } |
| } |
| if (vendorId.empty() || port.empty() || udevid.empty()) |
| { |
| std::cerr << "Matcher does not have required properties\n"; |
| return; |
| } |
| |
| pldmSetup(fd, objectServer, std::string(changedObject), port, |
| udevid, vendorId, prefixPath); |
| } |
| }); |
| |
| // Remove object path if the device is removed |
| auto matchRemove = std::make_unique<sdbusplus::bus::match_t>( |
| *systemBus, sdbusplus::bus::match::rules::interfacesRemoved(), |
| [&objectServer](sdbusplus::message_t& reply) { |
| sdbusplus::message::object_path changedObject; |
| std::vector<std::string> interfacesRemoved; |
| reply.read(changedObject, interfacesRemoved); |
| |
| auto it = dbusIntfMap.find(std::string(changedObject)); |
| if (it != dbusIntfMap.end()) |
| { |
| std::shared_ptr<sdbusplus::asio::dbus_interface> iface = it->second; |
| auto removed = objectServer.remove_interface(iface); |
| std::cout << "Removed RDE Device from tree: " << removed << "\n"; |
| dbusIntfMap.erase(std::string(changedObject)); |
| std::string deviceId; |
| getDeviceIdForRemovedDevice(std::string(changedObject), &deviceId); |
| |
| if (!deviceId.empty()) |
| { |
| cleanupDictionaries(deviceId); |
| cleanupMctpLink(deviceId); |
| // Remove the resource ID maps for the rde device |
| rdeResourceIdMap.erase(deviceId); |
| rdeResponseCacheMap.erase(deviceId); |
| } |
| // TODO (@harshtya): clean up the manager for this object path |
| // TODO (@harshtya): clean up the dictionaries |
| } |
| }); |
| |
| systemBus->request_name("xyz.openbmc_project.rdeoperation"); |
| io.run(); |
| return 0; |
| } |
| |
| void setSocketTimeout(int fd, int seconds, int milliseconds) |
| { |
| struct timeval tv; |
| tv.tv_sec = seconds; |
| tv.tv_usec = milliseconds; |
| setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv); |
| } |
| |
| int main() |
| { |
| int rc = 0; |
| std::atexit(cleanupAtExit); |
| std::signal(SIGTERM, signalHandler); |
| std::signal(SIGINT, signalHandler); |
| |
| std::cerr << "Successfully started the RDE Daemon \n"; |
| std::cerr << "rde_expand_enabled: " << rde_expand_enabled << "\n"; |
| fd = socket(AF_MCTP, SOCK_DGRAM, 0); |
| if (-1 == fd) |
| { |
| std::cerr << "Created socket Failed\n"; |
| return fd; |
| } |
| |
| std::cerr << "Socket Created with ID: " << fd << ". Setting timeouts...\n"; |
| setSocketTimeout(fd, /*seconds=*/SOCKET_TIMEOUT_SECONDS, |
| /*milliseconds=*/0); |
| socklen_t optlen; |
| int currentSendbuffSize; |
| optlen = sizeof(currentSendbuffSize); |
| |
| int res = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, ¤tSendbuffSize, |
| &optlen); |
| if (res == -1) |
| { |
| std::cerr << "Error in obtaining the default send buffer size, Error: " |
| << strerror(errno) << std::endl; |
| return -1; |
| } |
| |
| rc = triggerRdeReactor(fd); |
| if (rc) |
| { |
| std::cerr << "Error in RDE reactor \n"; |
| return rc; |
| } |
| |
| std::cerr << "RDE Reactor stopped...\n"; |
| // Daemon loop -- Code never reaches here if the reactor is running fine-- |
| auto event = Event::get_default(); |
| rc = event.loop(); |
| if (rc) |
| { |
| exit(EXIT_FAILURE); |
| } |
| exit(EXIT_SUCCESS); |
| } |