/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "channel_mgmt.hpp" #include "apphandler.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace ipmi { namespace variant_ns = sdbusplus::message::variant_ns; using namespace phosphor::logging; static constexpr const char* channelAccessDefaultFilename = "/usr/share/ipmi-providers/channel_access.json"; static constexpr const char* channelConfigDefaultFilename = "/usr/share/ipmi-providers/channel_config.json"; static constexpr const char* channelNvDataFilename = "/var/lib/ipmi/channel_access_nv.json"; static constexpr const char* channelVolatileDataFilename = "/run/ipmi/channel_access_volatile.json"; // TODO: Get the service name dynamically.. static constexpr const char* networkIntfServiceName = "xyz.openbmc_project.Network"; static constexpr const char* networkIntfObjectBasePath = "/xyz/openbmc_project/network"; static constexpr const char* networkChConfigIntfName = "xyz.openbmc_project.Channel.ChannelAccess"; static constexpr const char* privilegePropertyString = "MaxPrivilege"; static constexpr const char* dBusPropertiesInterface = "org.freedesktop.DBus.Properties"; static constexpr const char* propertiesChangedSignal = "PropertiesChanged"; // STRING DEFINES: Should sync with key's in JSON static constexpr const char* nameString = "name"; static constexpr const char* isValidString = "is_valid"; static constexpr const char* activeSessionsString = "active_sessions"; static constexpr const char* maxTransferSizeString = "max_transfer_size"; static constexpr const char* channelInfoString = "channel_info"; static constexpr const char* mediumTypeString = "medium_type"; static constexpr const char* protocolTypeString = "protocol_type"; static constexpr const char* sessionSupportedString = "session_supported"; static constexpr const char* isIpmiString = "is_ipmi"; static constexpr const char* authTypeSupportedString = "auth_type_supported"; static constexpr const char* accessModeString = "access_mode"; static constexpr const char* userAuthDisabledString = "user_auth_disabled"; static constexpr const char* perMsgAuthDisabledString = "per_msg_auth_disabled"; static constexpr const char* alertingDisabledString = "alerting_disabled"; static constexpr const char* privLimitString = "priv_limit"; static constexpr const char* authTypeEnabledString = "auth_type_enabled"; // Default values static constexpr const char* defaultChannelName = "RESERVED"; static constexpr const uint8_t defaultMediumType = static_cast(EChannelMediumType::reserved); static constexpr const uint8_t defaultProtocolType = static_cast(EChannelProtocolType::reserved); static constexpr const uint8_t defaultSessionSupported = static_cast(EChannelSessSupported::none); static constexpr const uint8_t defaultAuthType = static_cast(EAuthType::none); static constexpr const bool defaultIsIpmiState = false; static constexpr size_t smallChannelSize = 64; std::unique_ptr chPropertiesSignal __attribute__((init_priority(101))); // String mappings use in JSON config file static std::unordered_map mediumTypeMap = { {"reserved", EChannelMediumType::reserved}, {"ipmb", EChannelMediumType::ipmb}, {"icmb-v1.0", EChannelMediumType::icmbV10}, {"icmb-v0.9", EChannelMediumType::icmbV09}, {"lan-802.3", EChannelMediumType::lan8032}, {"serial", EChannelMediumType::serial}, {"other-lan", EChannelMediumType::otherLan}, {"pci-smbus", EChannelMediumType::pciSmbus}, {"smbus-v1.0", EChannelMediumType::smbusV11}, {"smbus-v2.0", EChannelMediumType::smbusV20}, {"usb-1x", EChannelMediumType::usbV1x}, {"usb-2x", EChannelMediumType::usbV2x}, {"system-interface", EChannelMediumType::systemInterface}, {"oem", EChannelMediumType::oem}, {"unknown", EChannelMediumType::unknown}}; static std::unordered_map interfaceMap = { {interfaceKCS, "SMS"}, {interfaceLAN1, "eth0"}, {interfaceUnknown, "unknown"}}; static std::unordered_map protocolTypeMap = { {"na", EChannelProtocolType::na}, {"ipmb-1.0", EChannelProtocolType::ipmbV10}, {"icmb-2.0", EChannelProtocolType::icmbV11}, {"reserved", EChannelProtocolType::reserved}, {"ipmi-smbus", EChannelProtocolType::ipmiSmbus}, {"kcs", EChannelProtocolType::kcs}, {"smic", EChannelProtocolType::smic}, {"bt-10", EChannelProtocolType::bt10}, {"bt-15", EChannelProtocolType::bt15}, {"tmode", EChannelProtocolType::tMode}, {"oem", EChannelProtocolType::oem}}; static std::array accessModeList = { "disabled", "pre-boot", "always_available", "shared"}; static std::array sessionSupportList = { "session-less", "single-session", "multi-session", "session-based"}; static std::array privList = { "priv-reserved", "priv-callback", "priv-user", "priv-operator", "priv-admin", "priv-oem"}; std::string ChannelConfig::getChannelName(const int chNum) { if (!isValidChannel(chNum)) { log("Invalid channel number.", entry("ChannelID:%d", chNum)); throw std::invalid_argument("Invalid channel number"); } return channelData[chNum].chName; } int ChannelConfig::convertToChannelNumberFromChannelName( const std::string& chName) { for (const auto& it : channelData) { if (it.chName == chName) { return it.chID; } } log("Invalid channel name.", entry("Channel:%s", chName.c_str())); throw std::invalid_argument("Invalid channel name"); return -1; } std::string ChannelConfig::getChannelNameFromPath(const std::string& path) { std::size_t pos = path.find(networkIntfObjectBasePath); if (pos == std::string::npos) { log("Invalid interface path.", entry("PATH:%s", path.c_str())); throw std::invalid_argument("Invalid interface path"); } std::string chName = path.substr(pos + strlen(networkIntfObjectBasePath) + 1); return chName; } void ChannelConfig::processChAccessPropChange( const std::string& path, const DbusChObjProperties& chProperties) { // Get interface name from path. ex: '/xyz/openbmc_project/network/eth0' std::string chName; try { chName = getChannelNameFromPath(path); } catch (const std::invalid_argument& e) { log("Exception: ", entry("MSG: %s", e.what())); return; } // Get the MaxPrivilege property value from the signal std::string intfPrivStr; std::string propName; for (const auto& prop : chProperties) { if (prop.first == privilegePropertyString) { propName = privilegePropertyString; intfPrivStr = variant_ns::get(prop.second); break; } } if (propName != privilegePropertyString) { log("Unknown signal caught."); return; } if (intfPrivStr.empty()) { log("Invalid privilege string.", entry("INTF:%s", chName.c_str())); return; } uint8_t intfPriv = 0; int chNum; try { intfPriv = static_cast(convertToPrivLimitIndex(intfPrivStr)); chNum = convertToChannelNumberFromChannelName(chName); } catch (const std::invalid_argument& e) { log("Exception: ", entry("MSG: %s", e.what())); return; } boost::interprocess::scoped_lock channelLock{*channelMutex}; // skip updating the values, if this property change originated from IPMI. if (signalFlag & (1 << chNum)) { signalFlag &= ~(1 << chNum); log("Request originated from IPMI so ignoring signal"); return; } // Update both volatile & Non-volatile, if there is mismatch. // as property change other than IPMI, has to update both volatile & // non-volatile data. checkAndReloadVolatileData(); checkAndReloadNVData(); if (channelData[chNum].chAccess.chNonVolatileData.privLimit != intfPriv) { // Update NV data channelData[chNum].chAccess.chNonVolatileData.privLimit = intfPriv; if (writeChannelPersistData() != 0) { log("Failed to update the persist data file"); return; } // Update Volatile data if (channelData[chNum].chAccess.chVolatileData.privLimit != intfPriv) { channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv; if (writeChannelVolatileData() != 0) { log("Failed to update the volatile data file"); return; } } } return; } ChannelConfig& getChannelConfigObject() { static ChannelConfig channelConfig; return channelConfig; } ChannelConfig::~ChannelConfig() { if (signalHndlrObjectState) { chPropertiesSignal.reset(); sigHndlrLock.unlock(); } } ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection()) { std::ofstream mutexCleanUpFile; mutexCleanUpFile.open(ipmiChMutexCleanupLockFile, std::ofstream::out | std::ofstream::app); if (!mutexCleanUpFile.good()) { log("Unable to open mutex cleanup file"); return; } mutexCleanUpFile.close(); mutexCleanupLock = boost::interprocess::file_lock(ipmiChMutexCleanupLockFile); if (mutexCleanupLock.try_lock()) { boost::interprocess::named_recursive_mutex::remove(ipmiChannelMutex); channelMutex = std::make_unique( boost::interprocess::open_or_create, ipmiChannelMutex); mutexCleanupLock.lock_sharable(); } else { mutexCleanupLock.lock_sharable(); channelMutex = std::make_unique( boost::interprocess::open_or_create, ipmiChannelMutex); } initChannelPersistData(); sigHndlrLock = boost::interprocess::file_lock(channelNvDataFilename); // Register it for single object and single process either netipimd / // host-ipmid if (chPropertiesSignal == nullptr && sigHndlrLock.try_lock()) { log("Registering channel signal handler."); chPropertiesSignal = std::make_unique( bus, sdbusplus::bus::match::rules::path_namespace( networkIntfObjectBasePath) + sdbusplus::bus::match::rules::type::signal() + sdbusplus::bus::match::rules::member(propertiesChangedSignal) + sdbusplus::bus::match::rules::interface( dBusPropertiesInterface) + sdbusplus::bus::match::rules::argN(0, networkChConfigIntfName), [&](sdbusplus::message::message& msg) { DbusChObjProperties props; std::string iface; std::string path = msg.get_path(); msg.read(iface, props); processChAccessPropChange(path, props); }); signalHndlrObjectState = true; } } bool ChannelConfig::isValidChannel(const uint8_t chNum) { if (chNum > maxIpmiChannels) { log("Invalid channel ID - Out of range"); return false; } if (channelData[chNum].isChValid == false) { log("Channel is not valid"); } return channelData[chNum].isChValid; } EChannelSessSupported ChannelConfig::getChannelSessionSupport(const uint8_t chNum) { EChannelSessSupported chSessSupport = (EChannelSessSupported)channelData[chNum].chInfo.sessionSupported; return chSessSupport; } bool ChannelConfig::isValidAuthType(const uint8_t chNum, const EAuthType& authType) { if ((authType < EAuthType::md2) || (authType > EAuthType::oem)) { log("Invalid authentication type"); return false; } uint8_t authTypeSupported = channelData[chNum].chInfo.authTypeSupported; if (!(authTypeSupported & (1 << static_cast(authType)))) { log("Authentication type is not supported."); return false; } return true; } std::string ChannelConfig::getChannelName(const uint8_t chNum) { return channelData[chNum].chName; } int ChannelConfig::getChannelActiveSessions(const uint8_t chNum) { // TODO: TEMPORARY FIX // Channels active session count is managed separately // by monitoring channel session which includes LAN and // RAKP layer changes. This will be updated, once the // authentication part is implemented. return channelData[chNum].activeSessCount; } size_t ChannelConfig::getChannelMaxTransferSize(uint8_t chNum) { return channelData[chNum].maxTransferSize; } ipmi_ret_t ChannelConfig::getChannelInfo(const uint8_t chNum, ChannelInfo& chInfo) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } std::copy_n(reinterpret_cast(&channelData[chNum].chInfo), sizeof(channelData[chNum].chInfo), reinterpret_cast(&chInfo)); return IPMI_CC_OK; } ipmi_ret_t ChannelConfig::getChannelAccessData(const uint8_t chNum, ChannelAccess& chAccessData) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log("Session-less channel doesn't have access data."); return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; } if (checkAndReloadVolatileData() != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } std::copy_n( reinterpret_cast(&channelData[chNum].chAccess.chVolatileData), sizeof(channelData[chNum].chAccess.chVolatileData), reinterpret_cast(&chAccessData)); return IPMI_CC_OK; } ipmi_ret_t ChannelConfig::setChannelAccessData(const uint8_t chNum, const ChannelAccess& chAccessData, const uint8_t setFlag) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log("Session-less channel doesn't have access data."); return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; } if (((setFlag & setAccessMode) && (!isValidAccessMode(chAccessData.accessMode))) || ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))) { log("Invalid access mode / privilege limit specified"); return IPMI_CC_INVALID_FIELD_REQUEST; } boost::interprocess::scoped_lock channelLock{*channelMutex}; if (checkAndReloadVolatileData() != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } if (setFlag & setAccessMode) { channelData[chNum].chAccess.chVolatileData.accessMode = chAccessData.accessMode; } if (setFlag & setUserAuthEnabled) { channelData[chNum].chAccess.chVolatileData.userAuthDisabled = chAccessData.userAuthDisabled; } if (setFlag & setMsgAuthEnabled) { channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled = chAccessData.perMsgAuthDisabled; } if (setFlag & setAlertingEnabled) { channelData[chNum].chAccess.chVolatileData.alertingDisabled = chAccessData.alertingDisabled; } if (setFlag & setPrivLimit) { channelData[chNum].chAccess.chVolatileData.privLimit = chAccessData.privLimit; } // Write Volatile data to file if (writeChannelVolatileData() != 0) { log("Failed to update the channel volatile data"); return IPMI_CC_UNSPECIFIED_ERROR; } return IPMI_CC_OK; } ipmi_ret_t ChannelConfig::getChannelAccessPersistData(const uint8_t chNum, ChannelAccess& chAccessData) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log("Session-less channel doesn't have access data."); return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; } if (checkAndReloadNVData() != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } std::copy_n(reinterpret_cast( &channelData[chNum].chAccess.chNonVolatileData), sizeof(channelData[chNum].chAccess.chNonVolatileData), reinterpret_cast(&chAccessData)); return IPMI_CC_OK; } ipmi_ret_t ChannelConfig::setChannelAccessPersistData( const uint8_t chNum, const ChannelAccess& chAccessData, const uint8_t setFlag) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log("Session-less channel doesn't have access data."); return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; } if (((setFlag & setAccessMode) && (!isValidAccessMode(chAccessData.accessMode))) || ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))) { log("Invalid access mode / privilege limit specified"); return IPMI_CC_INVALID_FIELD_REQUEST; } boost::interprocess::scoped_lock channelLock{*channelMutex}; if (checkAndReloadNVData() != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } if (setFlag & setAccessMode) { channelData[chNum].chAccess.chNonVolatileData.accessMode = chAccessData.accessMode; } if (setFlag & setUserAuthEnabled) { channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled = chAccessData.userAuthDisabled; } if (setFlag & setMsgAuthEnabled) { channelData[chNum].chAccess.chNonVolatileData.perMsgAuthDisabled = chAccessData.perMsgAuthDisabled; } if (setFlag & setAlertingEnabled) { channelData[chNum].chAccess.chNonVolatileData.alertingDisabled = chAccessData.alertingDisabled; } if (setFlag & setPrivLimit) { // Send Update to network channel config interfaces over dbus std::string privStr = convertToPrivLimitString(chAccessData.privLimit); std::string networkIntfObj = std::string(networkIntfObjectBasePath) + "/" + channelData[chNum].chName; try { if (0 != setDbusProperty(networkIntfServiceName, networkIntfObj, networkChConfigIntfName, privilegePropertyString, privStr)) { log( "Network interface does not exist", entry("INTERFACE:%s", channelData[chNum].chName.c_str())); return IPMI_CC_UNSPECIFIED_ERROR; } } catch (const sdbusplus::exception::SdBusError& e) { log("Exception: Network interface does not exist"); return IPMI_CC_INVALID_FIELD_REQUEST; } signalFlag |= (1 << chNum); channelData[chNum].chAccess.chNonVolatileData.privLimit = chAccessData.privLimit; } // Write persistent data to file if (writeChannelPersistData() != 0) { log("Failed to update the presist data file"); return IPMI_CC_UNSPECIFIED_ERROR; } return IPMI_CC_OK; } ipmi_ret_t ChannelConfig::getChannelAuthTypeSupported(const uint8_t chNum, uint8_t& authTypeSupported) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } authTypeSupported = channelData[chNum].chInfo.authTypeSupported; return IPMI_CC_OK; } ipmi_ret_t ChannelConfig::getChannelEnabledAuthType(const uint8_t chNum, const uint8_t priv, EAuthType& authType) { if (!isValidChannel(chNum)) { log("Invalid channel"); return IPMI_CC_INVALID_FIELD_REQUEST; } if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log("Sessionless channel doesn't have access data."); return IPMI_CC_INVALID_FIELD_REQUEST; } if (!isValidPrivLimit(priv)) { log("Invalid privilege specified."); return IPMI_CC_INVALID_FIELD_REQUEST; } // TODO: Hardcoded for now. Need to implement. authType = EAuthType::none; return IPMI_CC_OK; } std::time_t ChannelConfig::getUpdatedFileTime(const std::string& fileName) { struct stat fileStat; if (stat(fileName.c_str(), &fileStat) != 0) { log("Error in getting last updated time stamp"); return -EIO; } return fileStat.st_mtime; } EChannelAccessMode ChannelConfig::convertToAccessModeIndex(const std::string& mode) { auto iter = std::find(accessModeList.begin(), accessModeList.end(), mode); if (iter == accessModeList.end()) { log("Invalid access mode.", entry("MODE_STR=%s", mode.c_str())); throw std::invalid_argument("Invalid access mode."); } return static_cast( std::distance(accessModeList.begin(), iter)); } std::string ChannelConfig::convertToAccessModeString(const uint8_t value) { if (accessModeList.size() <= value) { log("Invalid access mode.", entry("MODE_IDX=%d", value)); throw std::invalid_argument("Invalid access mode."); } return accessModeList.at(value); } CommandPrivilege ChannelConfig::convertToPrivLimitIndex(const std::string& value) { auto iter = std::find(privList.begin(), privList.end(), value); if (iter == privList.end()) { log("Invalid privilege.", entry("PRIV_STR=%s", value.c_str())); throw std::invalid_argument("Invalid privilege."); } return static_cast(std::distance(privList.begin(), iter)); } std::string ChannelConfig::convertToPrivLimitString(const uint8_t value) { if (privList.size() <= value) { log("Invalid privilege.", entry("PRIV_IDX=%d", value)); throw std::invalid_argument("Invalid privilege."); } return privList.at(value); } EChannelSessSupported ChannelConfig::convertToSessionSupportIndex(const std::string& value) { auto iter = std::find(sessionSupportList.begin(), sessionSupportList.end(), value); if (iter == sessionSupportList.end()) { log("Invalid session supported.", entry("SESS_STR=%s", value.c_str())); throw std::invalid_argument("Invalid session supported."); } return static_cast( std::distance(sessionSupportList.begin(), iter)); } EChannelMediumType ChannelConfig::convertToMediumTypeIndex(const std::string& value) { std::unordered_map::iterator it = mediumTypeMap.find(value); if (it == mediumTypeMap.end()) { log("Invalid medium type.", entry("MEDIUM_STR=%s", value.c_str())); throw std::invalid_argument("Invalid medium type."); } return static_cast(it->second); } EChannelProtocolType ChannelConfig::convertToProtocolTypeIndex(const std::string& value) { std::unordered_map::iterator it = protocolTypeMap.find(value); if (it == protocolTypeMap.end()) { log("Invalid protocol type.", entry("PROTO_STR=%s", value.c_str())); throw std::invalid_argument("Invalid protocol type."); } return static_cast(it->second); } uint8_t ChannelConfig::convertToChannelIndexNumber(const uint8_t chNum) { // TODO: There is limitation in current design. we cannot detect exact // LAN interface(eth0 or eth1) so Implementation may be updated // when there is any design update to figure out all the interfaces // independently based on the message. static uint8_t curChannel = 0xFF; if (curChannel == 0xFF) { auto it = interfaceMap.find(getInterfaceIndex()); if (it == interfaceMap.end()) { log("Invalid Interface type ", entry("InterfaceIndex: %d", getInterfaceIndex())); throw std::invalid_argument("Invalid interface type."); } for (auto& channel : channelData) { std::string& interfaceName = it->second; if (channel.chName == interfaceName) { curChannel = channel.chID; break; } } } return ((chNum == currentChNum) ? curChannel : chNum); } Json ChannelConfig::readJsonFile(const std::string& configFile) { std::ifstream jsonFile(configFile); if (!jsonFile.good()) { log("JSON file not found"); return nullptr; } Json data = nullptr; try { data = Json::parse(jsonFile, nullptr, false); } catch (Json::parse_error& e) { log("Corrupted channel config.", entry("MSG: %s", e.what())); throw std::runtime_error("Corrupted channel config file"); } return data; } int ChannelConfig::writeJsonFile(const std::string& configFile, const Json& jsonData) { std::ofstream jsonFile(configFile); if (!jsonFile.good()) { log("JSON file not found"); return -EIO; } // Write JSON to file jsonFile << jsonData; jsonFile.flush(); return 0; } void ChannelConfig::setDefaultChannelConfig(const uint8_t chNum, const std::string& chName) { channelData[chNum].chName = chName; channelData[chNum].chID = chNum; channelData[chNum].isChValid = false; channelData[chNum].activeSessCount = 0; channelData[chNum].chInfo.mediumType = defaultMediumType; channelData[chNum].chInfo.protocolType = defaultProtocolType; channelData[chNum].chInfo.sessionSupported = defaultSessionSupported; channelData[chNum].chInfo.isIpmi = defaultIsIpmiState; channelData[chNum].chInfo.authTypeSupported = defaultAuthType; } int ChannelConfig::loadChannelConfig() { boost::interprocess::scoped_lock channelLock{*channelMutex}; Json data = readJsonFile(channelConfigDefaultFilename); if (data.empty()) { log("Error in opening IPMI Channel data file"); return -EIO; } channelData.fill(ChannelProperties{}); for (int chNum = 0; chNum < maxIpmiChannels; chNum++) { try { std::string chKey = std::to_string(chNum); Json jsonChData = data[chKey].get(); if (jsonChData.is_null()) { log( "Channel not configured so loading default.", entry("CHANNEL_NUM:%d", chNum)); // If user didn't want to configure specific channel (say // reserved channel), then load that index with default values. setDefaultChannelConfig(chNum, defaultChannelName); continue; } Json jsonChInfo = jsonChData[channelInfoString].get(); if (jsonChInfo.is_null()) { log("Invalid/corrupted channel config file"); return -EBADMSG; } ChannelProperties& chData = channelData[chNum]; chData.chName = jsonChData[nameString].get(); chData.chID = chNum; chData.isChValid = jsonChData[isValidString].get(); chData.activeSessCount = jsonChData.value(activeSessionsString, 0); chData.maxTransferSize = jsonChData.value(maxTransferSizeString, smallChannelSize); std::string medTypeStr = jsonChInfo[mediumTypeString].get(); chData.chInfo.mediumType = static_cast(convertToMediumTypeIndex(medTypeStr)); std::string protoTypeStr = jsonChInfo[protocolTypeString].get(); chData.chInfo.protocolType = static_cast(convertToProtocolTypeIndex(protoTypeStr)); std::string sessStr = jsonChInfo[sessionSupportedString].get(); chData.chInfo.sessionSupported = static_cast(convertToSessionSupportIndex(sessStr)); chData.chInfo.isIpmi = jsonChInfo[isIpmiString].get(); chData.chInfo.authTypeSupported = defaultAuthType; } catch (const Json::exception& e) { log("Json Exception caught.", entry("MSG:%s", e.what())); return -EBADMSG; } catch (const std::invalid_argument& e) { log("Corrupted config.", entry("MSG:%s", e.what())); return -EBADMSG; } } return 0; } int ChannelConfig::readChannelVolatileData() { boost::interprocess::scoped_lock channelLock{*channelMutex}; Json data = readJsonFile(channelVolatileDataFilename); if (data == nullptr) { log("Error in opening IPMI Channel data file"); return -EIO; } try { // Fill in global structure for (auto it = data.begin(); it != data.end(); ++it) { std::string chKey = it.key(); uint8_t chNum = std::stoi(chKey, nullptr, 10); if ((chNum < 0) || (chNum > maxIpmiChannels)) { log( "Invalid channel access entry in config file"); throw std::out_of_range("Out of range - channel number"); } Json jsonChData = it.value(); if (!jsonChData.is_null()) { std::string accModeStr = jsonChData[accessModeString].get(); channelData[chNum].chAccess.chVolatileData.accessMode = static_cast(convertToAccessModeIndex(accModeStr)); channelData[chNum].chAccess.chVolatileData.userAuthDisabled = jsonChData[userAuthDisabledString].get(); channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled = jsonChData[perMsgAuthDisabledString].get(); channelData[chNum].chAccess.chVolatileData.alertingDisabled = jsonChData[alertingDisabledString].get(); std::string privStr = jsonChData[privLimitString].get(); channelData[chNum].chAccess.chVolatileData.privLimit = static_cast(convertToPrivLimitIndex(privStr)); } else { log( "Invalid/corrupted volatile channel access file", entry("FILE: %s", channelVolatileDataFilename)); throw std::runtime_error( "Corrupted volatile channel access file"); } } } catch (const Json::exception& e) { log("Json Exception caught.", entry("MSG:%s", e.what())); throw std::runtime_error("Corrupted volatile channel access file"); } catch (const std::invalid_argument& e) { log("Corrupted config.", entry("MSG:%s", e.what())); throw std::runtime_error("Corrupted volatile channel access file"); } // Update the timestamp voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename); return 0; } int ChannelConfig::readChannelPersistData() { boost::interprocess::scoped_lock channelLock{*channelMutex}; Json data = readJsonFile(channelNvDataFilename); if (data == nullptr) { log("Error in opening IPMI Channel data file"); return -EIO; } try { // Fill in global structure for (auto it = data.begin(); it != data.end(); ++it) { std::string chKey = it.key(); uint8_t chNum = std::stoi(chKey, nullptr, 10); if ((chNum < 0) || (chNum > maxIpmiChannels)) { log( "Invalid channel access entry in config file"); throw std::out_of_range("Out of range - channel number"); } Json jsonChData = it.value(); if (!jsonChData.is_null()) { std::string accModeStr = jsonChData[accessModeString].get(); channelData[chNum].chAccess.chNonVolatileData.accessMode = static_cast(convertToAccessModeIndex(accModeStr)); channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled = jsonChData[userAuthDisabledString].get(); channelData[chNum] .chAccess.chNonVolatileData.perMsgAuthDisabled = jsonChData[perMsgAuthDisabledString].get(); channelData[chNum].chAccess.chNonVolatileData.alertingDisabled = jsonChData[alertingDisabledString].get(); std::string privStr = jsonChData[privLimitString].get(); channelData[chNum].chAccess.chNonVolatileData.privLimit = static_cast(convertToPrivLimitIndex(privStr)); } else { log("Invalid/corrupted nv channel access file", entry("FILE:%s", channelNvDataFilename)); throw std::runtime_error("Corrupted nv channel access file"); } } } catch (const Json::exception& e) { log("Json Exception caught.", entry("MSG:%s", e.what())); throw std::runtime_error("Corrupted nv channel access file"); } catch (const std::invalid_argument& e) { log("Corrupted config.", entry("MSG: %s", e.what())); throw std::runtime_error("Corrupted nv channel access file"); } // Update the timestamp nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename); return 0; } int ChannelConfig::writeChannelVolatileData() { boost::interprocess::scoped_lock channelLock{*channelMutex}; Json outData; try { for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++) { if (getChannelSessionSupport(chNum) != EChannelSessSupported::none) { Json jsonObj; std::string chKey = std::to_string(chNum); std::string accModeStr = convertToAccessModeString( channelData[chNum].chAccess.chVolatileData.accessMode); jsonObj[accessModeString] = accModeStr; jsonObj[userAuthDisabledString] = channelData[chNum].chAccess.chVolatileData.userAuthDisabled; jsonObj[perMsgAuthDisabledString] = channelData[chNum] .chAccess.chVolatileData.perMsgAuthDisabled; jsonObj[alertingDisabledString] = channelData[chNum].chAccess.chVolatileData.alertingDisabled; std::string privStr = convertToPrivLimitString( channelData[chNum].chAccess.chVolatileData.privLimit); jsonObj[privLimitString] = privStr; outData[chKey] = jsonObj; } } } catch (const std::invalid_argument& e) { log("Corrupted config.", entry("MSG: %s", e.what())); return -EINVAL; } if (writeJsonFile(channelVolatileDataFilename, outData) != 0) { log("Error in write JSON data to file"); return -EIO; } // Update the timestamp voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename); return 0; } int ChannelConfig::writeChannelPersistData() { boost::interprocess::scoped_lock channelLock{*channelMutex}; Json outData; try { for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++) { if (getChannelSessionSupport(chNum) != EChannelSessSupported::none) { Json jsonObj; std::string chKey = std::to_string(chNum); std::string accModeStr = convertToAccessModeString( channelData[chNum].chAccess.chNonVolatileData.accessMode); jsonObj[accessModeString] = accModeStr; jsonObj[userAuthDisabledString] = channelData[chNum] .chAccess.chNonVolatileData.userAuthDisabled; jsonObj[perMsgAuthDisabledString] = channelData[chNum] .chAccess.chNonVolatileData.perMsgAuthDisabled; jsonObj[alertingDisabledString] = channelData[chNum] .chAccess.chNonVolatileData.alertingDisabled; std::string privStr = convertToPrivLimitString( channelData[chNum].chAccess.chNonVolatileData.privLimit); jsonObj[privLimitString] = privStr; outData[chKey] = jsonObj; } } } catch (const std::invalid_argument& e) { log("Corrupted config.", entry("MSG: %s", e.what())); return -EINVAL; } if (writeJsonFile(channelNvDataFilename, outData) != 0) { log("Error in write JSON data to file"); return -EIO; } // Update the timestamp nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename); return 0; } int ChannelConfig::checkAndReloadNVData() { std::time_t updateTime = getUpdatedFileTime(channelNvDataFilename); int ret = 0; if (updateTime != nvFileLastUpdatedTime || updateTime == -EIO) { try { ret = readChannelPersistData(); } catch (const std::exception& e) { log("Exception caught in readChannelPersistData.", entry("MSG=%s", e.what())); ret = -EIO; } } return ret; } int ChannelConfig::checkAndReloadVolatileData() { std::time_t updateTime = getUpdatedFileTime(channelVolatileDataFilename); int ret = 0; if (updateTime != voltFileLastUpdatedTime || updateTime == -EIO) { try { ret = readChannelVolatileData(); } catch (const std::exception& e) { log("Exception caught in readChannelVolatileData.", entry("MSG=%s", e.what())); ret = -EIO; } } return ret; } int ChannelConfig::setDbusProperty(const std::string& service, const std::string& objPath, const std::string& interface, const std::string& property, const DbusVariant& value) { try { auto method = bus.new_method_call(service.c_str(), objPath.c_str(), "org.freedesktop.DBus.Properties", "Set"); method.append(interface, property, value); auto reply = bus.call(method); } catch (const sdbusplus::exception::SdBusError& e) { log("set-property failed", entry("SERVICE:%s", service.c_str()), entry("OBJPATH:%s", objPath.c_str()), entry("INTERFACE:%s", interface.c_str()), entry("PROP:%s", property.c_str())); return -EIO; } return 0; } int ChannelConfig::getDbusProperty(const std::string& service, const std::string& objPath, const std::string& interface, const std::string& property, DbusVariant& value) { try { auto method = bus.new_method_call(service.c_str(), objPath.c_str(), "org.freedesktop.DBus.Properties", "Get"); method.append(interface, property); auto reply = bus.call(method); reply.read(value); } catch (const sdbusplus::exception::SdBusError& e) { log("get-property failed", entry("SERVICE:%s", service.c_str()), entry("OBJPATH:%s", objPath.c_str()), entry("INTERFACE:%s", interface.c_str()), entry("PROP:%s", property.c_str())); return -EIO; } return 0; } int ChannelConfig::syncNetworkChannelConfig() { boost::interprocess::scoped_lock channelLock{*channelMutex}; bool isUpdated = false; for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++) { if (getChannelSessionSupport(chNum) != EChannelSessSupported::none) { std::string intfPrivStr; try { std::string networkIntfObj = std::string(networkIntfObjectBasePath) + "/" + channelData[chNum].chName; DbusVariant variant; if (0 != getDbusProperty(networkIntfServiceName, networkIntfObj, networkChConfigIntfName, privilegePropertyString, variant)) { log("Network interface does not exist", entry("INTERFACE:%s", channelData[chNum].chName.c_str())); continue; } intfPrivStr = variant_ns::get(variant); } catch (const variant_ns::bad_variant_access& e) { log( "exception: Network interface does not exist"); continue; } catch (const sdbusplus::exception::SdBusError& e) { log( "exception: Network interface does not exist"); continue; } uint8_t intfPriv = static_cast(convertToPrivLimitIndex(intfPrivStr)); if (channelData[chNum].chAccess.chNonVolatileData.privLimit != intfPriv) { isUpdated = true; channelData[chNum].chAccess.chNonVolatileData.privLimit = intfPriv; channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv; } } } if (isUpdated) { // Write persistent data to file if (writeChannelPersistData() != 0) { log("Failed to update the persistent data file"); return -EIO; } // Write Volatile data to file if (writeChannelVolatileData() != 0) { log("Failed to update the channel volatile data"); return -EIO; } } return 0; } void ChannelConfig::initChannelPersistData() { /* Always read the channel config */ if (loadChannelConfig() != 0) { log("Failed to read channel config file"); throw std::ios_base::failure("Failed to load channel configuration"); } /* Populate the channel persist data */ if (readChannelPersistData() != 0) { // Copy default NV data to RW location std::experimental::filesystem::copy_file(channelAccessDefaultFilename, channelNvDataFilename); // Load the channel access NV data if (readChannelPersistData() != 0) { log("Failed to read channel access NV data"); throw std::ios_base::failure( "Failed to read channel access NV configuration"); } } // First check the volatile data file // If not present, load the default values if (readChannelVolatileData() != 0) { // Copy default volatile data to temporary location // NV file(channelNvDataFilename) must have created by now. std::experimental::filesystem::copy_file(channelNvDataFilename, channelVolatileDataFilename); // Load the channel access volatile data if (readChannelVolatileData() != 0) { log("Failed to read channel access volatile data"); throw std::ios_base::failure( "Failed to read channel access volatile configuration"); } } // Synchronize the channel config(priv) with network channel // configuration(priv) over dbus if (syncNetworkChannelConfig() != 0) { log( "Failed to synchronize data with network channel config over dbus"); throw std::ios_base::failure( "Failed to synchronize data with network channel config over dbus"); } log("Successfully completed channel data initialization."); return; } } // namespace ipmi