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