1 #include "server-conf.hpp" 2 3 #include "utils.hpp" 4 #include "xyz/openbmc_project/Common/error.hpp" 5 6 #include <fstream> 7 #include <phosphor-logging/elog.hpp> 8 #if __has_include("../../usr/include/phosphor-logging/elog-errors.hpp") 9 #include "../../usr/include/phosphor-logging/elog-errors.hpp" 10 #else 11 #include <phosphor-logging/elog-errors.hpp> 12 #endif 13 #include <arpa/inet.h> 14 #include <netdb.h> 15 16 #include <optional> 17 #include <string> 18 19 namespace phosphor 20 { 21 namespace rsyslog_config 22 { 23 24 namespace utils = phosphor::rsyslog_utils; 25 using namespace phosphor::logging; 26 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 27 28 namespace internal 29 { 30 31 bool isIPv6Address(const std::string& addr) 32 { 33 struct in6_addr result; 34 return inet_pton(AF_INET6, addr.c_str(), &result) == 1; 35 } 36 37 std::optional<std::pair<std::string, uint32_t>> parseConfig(std::istream& ss) 38 { 39 std::string line; 40 std::getline(ss, line); 41 42 //"*.* @@<address>:<port>" or 43 //"*.* @@[<ipv6-address>:<port>" 44 constexpr auto start = 6; // Skip "*.* @@" 45 std::string serverAddress; 46 std::string serverPort; 47 48 // Ignore if line is commented 49 if (!line.empty() && '#' != line.at(0)) 50 { 51 // Check if there is "[]", and make IPv6 address from it 52 auto posColonLeft = line.find('['); 53 auto posColonRight = line.find(']'); 54 if (posColonLeft != std::string::npos || 55 posColonRight != std::string::npos) 56 { 57 // It contains [ or ], so it should be an IPv6 address 58 if (posColonLeft == std::string::npos || 59 posColonRight == std::string::npos) 60 { 61 // There either '[' or ']', invalid config 62 return {}; 63 } 64 if (line.size() < posColonRight + 2 || 65 line.at(posColonRight + 1) != ':') 66 { 67 // There is no ':', or no more content after ':', invalid config 68 return {}; 69 } 70 serverAddress = 71 line.substr(posColonLeft + 1, posColonRight - posColonLeft - 1); 72 serverPort = line.substr(posColonRight + 2); 73 } 74 else 75 { 76 auto pos = line.find(':'); 77 if (pos == std::string::npos) 78 { 79 // There is no ':', invalid config 80 return {}; 81 } 82 serverAddress = line.substr(start, pos - start); 83 serverPort = line.substr(pos + 1); 84 } 85 } 86 if (serverAddress.empty() || serverPort.empty()) 87 { 88 return {}; 89 } 90 try 91 { 92 uint32_t port = std::stoul(serverPort); 93 return std::make_pair(std::move(serverAddress), port); 94 } 95 catch (const std::exception& ex) 96 { 97 log<level::ERR>("Invalid config", entry("ERR=%s", ex.what())); 98 return {}; 99 } 100 } 101 102 } // namespace internal 103 104 std::string Server::address(std::string value) 105 { 106 using Argument = xyz::openbmc_project::Common::InvalidArgument; 107 std::string result{}; 108 109 try 110 { 111 auto serverAddress = address(); 112 if (serverAddress == value) 113 { 114 return serverAddress; 115 } 116 117 if (!value.empty() && !addressValid(value)) 118 { 119 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Address"), 120 Argument::ARGUMENT_VALUE(value.c_str())); 121 } 122 123 writeConfig(value, port(), configFilePath.c_str()); 124 result = NetworkClient::address(value); 125 } 126 catch (const InvalidArgument& e) 127 { 128 throw; 129 } 130 catch (const InternalFailure& e) 131 { 132 throw; 133 } 134 catch (const std::exception& e) 135 { 136 log<level::ERR>(e.what()); 137 elog<InternalFailure>(); 138 } 139 140 return result; 141 } 142 143 uint16_t Server::port(uint16_t value) 144 { 145 uint16_t result{}; 146 147 try 148 { 149 auto serverPort = port(); 150 if (serverPort == value) 151 { 152 return serverPort; 153 } 154 155 writeConfig(address(), value, configFilePath.c_str()); 156 result = NetworkClient::port(value); 157 } 158 catch (const InternalFailure& e) 159 { 160 throw; 161 } 162 catch (const std::exception& e) 163 { 164 log<level::ERR>(e.what()); 165 elog<InternalFailure>(); 166 } 167 168 return result; 169 } 170 171 void Server::writeConfig(const std::string& serverAddress, uint16_t serverPort, 172 const char* filePath) 173 { 174 std::fstream stream(filePath, std::fstream::out); 175 176 if (serverPort && !serverAddress.empty()) 177 { 178 // write '*.* @@<remote-host>:<port>' 179 if (internal::isIPv6Address(serverAddress)) 180 { 181 stream << "*.* @@[" << serverAddress << "]:" << serverPort; 182 } 183 else 184 { 185 stream << "*.* @@" << serverAddress << ":" << serverPort; 186 } 187 } 188 else // this is a disable request 189 { 190 // write '*.* ~' - this causes rsyslog to discard all messages 191 stream << "*.* ~"; 192 } 193 194 restart(); 195 } 196 197 bool Server::addressValid(const std::string& address) 198 { 199 addrinfo hints{}; 200 addrinfo* res = nullptr; 201 hints.ai_family = AF_UNSPEC; 202 hints.ai_socktype = SOCK_STREAM; 203 hints.ai_flags |= AI_CANONNAME; 204 205 auto result = getaddrinfo(address.c_str(), nullptr, &hints, &res); 206 if (result) 207 { 208 log<level::ERR>("bad address", entry("ADDRESS=%s", address.c_str()), 209 entry("ERRNO=%d", result)); 210 return false; 211 } 212 213 freeaddrinfo(res); 214 return true; 215 } 216 217 void Server::restore(const char* filePath) 218 { 219 std::fstream stream(filePath, std::fstream::in); 220 221 auto ret = internal::parseConfig(stream); 222 if (ret) 223 { 224 NetworkClient::address(ret->first); 225 NetworkClient::port(ret->second); 226 } 227 } 228 229 void Server::restart() 230 { 231 utils::restart(); 232 } 233 234 } // namespace rsyslog_config 235 } // namespace phosphor 236