| #include "server-conf.hpp" | 
 |  | 
 | #include "utils.hpp" | 
 | #include "xyz/openbmc_project/Common/error.hpp" | 
 |  | 
 | #include <fstream> | 
 | #include <phosphor-logging/elog.hpp> | 
 | #if __has_include("../../usr/include/phosphor-logging/elog-errors.hpp") | 
 | #include "../../usr/include/phosphor-logging/elog-errors.hpp" | 
 | #else | 
 | #include <phosphor-logging/elog-errors.hpp> | 
 | #endif | 
 | #include <arpa/inet.h> | 
 | #include <netdb.h> | 
 |  | 
 | #include <optional> | 
 | #include <string> | 
 |  | 
 | namespace phosphor | 
 | { | 
 | namespace rsyslog_config | 
 | { | 
 |  | 
 | namespace utils = phosphor::rsyslog_utils; | 
 | using namespace phosphor::logging; | 
 | using namespace sdbusplus::xyz::openbmc_project::Common::Error; | 
 |  | 
 | namespace internal | 
 | { | 
 |  | 
 | bool isIPv6Address(const std::string& addr) | 
 | { | 
 |     struct in6_addr result; | 
 |     return inet_pton(AF_INET6, addr.c_str(), &result) == 1; | 
 | } | 
 |  | 
 | std::optional<std::pair<std::string, uint32_t>> parseConfig(std::istream& ss) | 
 | { | 
 |     std::string line; | 
 |     std::getline(ss, line); | 
 |  | 
 |     //"*.* @@<address>:<port>" or | 
 |     //"*.* @@[<ipv6-address>:<port>" | 
 |     constexpr auto start = 6; // Skip "*.* @@" | 
 |     std::string serverAddress; | 
 |     std::string serverPort; | 
 |  | 
 |     // Ignore if line is commented | 
 |     if (!line.empty() && '#' != line.at(0)) | 
 |     { | 
 |         // Check if there is "[]", and make IPv6 address from it | 
 |         auto posColonLeft = line.find('['); | 
 |         auto posColonRight = line.find(']'); | 
 |         if (posColonLeft != std::string::npos || | 
 |             posColonRight != std::string::npos) | 
 |         { | 
 |             // It contains [ or ], so it should be an IPv6 address | 
 |             if (posColonLeft == std::string::npos || | 
 |                 posColonRight == std::string::npos) | 
 |             { | 
 |                 // There either '[' or ']', invalid config | 
 |                 return {}; | 
 |             } | 
 |             if (line.size() < posColonRight + 2 || | 
 |                 line.at(posColonRight + 1) != ':') | 
 |             { | 
 |                 // There is no ':', or no more content after ':', invalid config | 
 |                 return {}; | 
 |             } | 
 |             serverAddress = | 
 |                 line.substr(posColonLeft + 1, posColonRight - posColonLeft - 1); | 
 |             serverPort = line.substr(posColonRight + 2); | 
 |         } | 
 |         else | 
 |         { | 
 |             auto pos = line.find(':'); | 
 |             if (pos == std::string::npos) | 
 |             { | 
 |                 // There is no ':', invalid config | 
 |                 return {}; | 
 |             } | 
 |             serverAddress = line.substr(start, pos - start); | 
 |             serverPort = line.substr(pos + 1); | 
 |         } | 
 |     } | 
 |     if (serverAddress.empty() || serverPort.empty()) | 
 |     { | 
 |         return {}; | 
 |     } | 
 |     try | 
 |     { | 
 |         uint32_t port = std::stoul(serverPort); | 
 |         return std::make_pair(std::move(serverAddress), port); | 
 |     } | 
 |     catch (const std::exception& ex) | 
 |     { | 
 |         log<level::ERR>("Invalid config", entry("ERR=%s", ex.what())); | 
 |         return {}; | 
 |     } | 
 | } | 
 |  | 
 | } // namespace internal | 
 |  | 
 | std::string Server::address(std::string value) | 
 | { | 
 |     using Argument = xyz::openbmc_project::Common::InvalidArgument; | 
 |     std::string result{}; | 
 |  | 
 |     try | 
 |     { | 
 |         auto serverAddress = address(); | 
 |         if (serverAddress == value) | 
 |         { | 
 |             return serverAddress; | 
 |         } | 
 |  | 
 |         if (!value.empty() && !addressValid(value)) | 
 |         { | 
 |             elog<InvalidArgument>(Argument::ARGUMENT_NAME("Address"), | 
 |                                   Argument::ARGUMENT_VALUE(value.c_str())); | 
 |         } | 
 |  | 
 |         writeConfig(value, port(), configFilePath.c_str()); | 
 |         result = std::move(NetworkClient::address(value)); | 
 |     } | 
 |     catch (const InvalidArgument& e) | 
 |     { | 
 |         throw; | 
 |     } | 
 |     catch (const InternalFailure& e) | 
 |     { | 
 |         throw; | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         log<level::ERR>(e.what()); | 
 |         elog<InternalFailure>(); | 
 |     } | 
 |  | 
 |     return result; | 
 | } | 
 |  | 
 | uint16_t Server::port(uint16_t value) | 
 | { | 
 |     uint16_t result{}; | 
 |  | 
 |     try | 
 |     { | 
 |         auto serverPort = port(); | 
 |         if (serverPort == value) | 
 |         { | 
 |             return serverPort; | 
 |         } | 
 |  | 
 |         writeConfig(address(), value, configFilePath.c_str()); | 
 |         result = NetworkClient::port(value); | 
 |     } | 
 |     catch (const InternalFailure& e) | 
 |     { | 
 |         throw; | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         log<level::ERR>(e.what()); | 
 |         elog<InternalFailure>(); | 
 |     } | 
 |  | 
 |     return result; | 
 | } | 
 |  | 
 | void Server::writeConfig(const std::string& serverAddress, uint16_t serverPort, | 
 |                          const char* filePath) | 
 | { | 
 |     std::fstream stream(filePath, std::fstream::out); | 
 |  | 
 |     if (serverPort && !serverAddress.empty()) | 
 |     { | 
 |         // write '*.* @@<remote-host>:<port>' | 
 |         if (internal::isIPv6Address(serverAddress)) | 
 |         { | 
 |             stream << "*.* @@[" << serverAddress << "]:" << serverPort; | 
 |         } | 
 |         else | 
 |         { | 
 |             stream << "*.* @@" << serverAddress << ":" << serverPort; | 
 |         } | 
 |     } | 
 |     else // this is a disable request | 
 |     { | 
 |         // write '*.* ~' - this causes rsyslog to discard all messages | 
 |         stream << "*.* ~"; | 
 |     } | 
 |  | 
 |     restart(); | 
 | } | 
 |  | 
 | bool Server::addressValid(const std::string& address) | 
 | { | 
 |     addrinfo hints{}; | 
 |     addrinfo* res = nullptr; | 
 |     hints.ai_family = AF_UNSPEC; | 
 |     hints.ai_socktype = SOCK_STREAM; | 
 |     hints.ai_flags |= AI_CANONNAME; | 
 |  | 
 |     auto result = getaddrinfo(address.c_str(), nullptr, &hints, &res); | 
 |     if (result) | 
 |     { | 
 |         log<level::ERR>("bad address", entry("ADDRESS=%s", address.c_str()), | 
 |                         entry("ERRNO=%d", result)); | 
 |         return false; | 
 |     } | 
 |  | 
 |     freeaddrinfo(res); | 
 |     return true; | 
 | } | 
 |  | 
 | void Server::restore(const char* filePath) | 
 | { | 
 |     std::fstream stream(filePath, std::fstream::in); | 
 |  | 
 |     auto ret = internal::parseConfig(stream); | 
 |     if (ret) | 
 |     { | 
 |         NetworkClient::address(ret->first); | 
 |         NetworkClient::port(ret->second); | 
 |     } | 
 | } | 
 |  | 
 | void Server::restart() | 
 | { | 
 |     utils::restart(); | 
 | } | 
 |  | 
 | } // namespace rsyslog_config | 
 | } // namespace phosphor |