1 #include "config.h"
2
3 #include "util.hpp"
4
5 #include "config_parser.hpp"
6 #include "types.hpp"
7
8 #include <sys/wait.h>
9
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <stdplus/numeric/str.hpp>
13 #include <stdplus/str/buf.hpp>
14 #include <stdplus/str/cat.hpp>
15 #include <xyz/openbmc_project/Common/error.hpp>
16
17 #include <cctype>
18 #include <string>
19 #include <string_view>
20
21 namespace phosphor
22 {
23 namespace network
24 {
25
26 using std::literals::string_view_literals::operator""sv;
27 using namespace phosphor::logging;
28 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
29
30 namespace internal
31 {
32
executeCommandinChildProcess(stdplus::zstring_view path,char ** args)33 void executeCommandinChildProcess(stdplus::zstring_view path, char** args)
34 {
35 using namespace std::string_literals;
36 pid_t pid = fork();
37
38 if (pid == 0)
39 {
40 execv(path.c_str(), args);
41 exit(255);
42 }
43 else if (pid < 0)
44 {
45 auto error = errno;
46 lg2::error("Error occurred during fork: {ERRNO}", "ERRNO", error);
47 elog<InternalFailure>();
48 }
49 else if (pid > 0)
50 {
51 int status;
52 while (waitpid(pid, &status, 0) == -1)
53 {
54 if (errno != EINTR)
55 {
56 status = -1;
57 break;
58 }
59 }
60
61 if (status < 0)
62 {
63 stdplus::StrBuf buf;
64 stdplus::strAppend(buf, "`"sv, path, "`"sv);
65 for (size_t i = 0; args[i] != nullptr; ++i)
66 {
67 stdplus::strAppend(buf, " `"sv, args[i], "`"sv);
68 }
69 buf.push_back('\0');
70 lg2::error("Unable to execute the command {CMD}: {STATUS}", "CMD",
71 buf.data(), "STATUS", status);
72 elog<InternalFailure>();
73 }
74 }
75 }
76
77 /** @brief Get ignored interfaces from environment */
getIgnoredInterfacesEnv()78 std::string_view getIgnoredInterfacesEnv()
79 {
80 auto r = std::getenv("IGNORED_INTERFACES");
81 if (r == nullptr)
82 {
83 return "";
84 }
85 return r;
86 }
87
88 /** @brief Parse the comma separated interface names */
89 std::unordered_set<std::string_view>
parseInterfaces(std::string_view interfaces)90 parseInterfaces(std::string_view interfaces)
91 {
92 std::unordered_set<std::string_view> result;
93 while (true)
94 {
95 auto sep = interfaces.find(',');
96 auto interface = interfaces.substr(0, sep);
97 while (!interface.empty() && std::isspace(interface.front()))
98 {
99 interface.remove_prefix(1);
100 }
101 while (!interface.empty() && std::isspace(interface.back()))
102 {
103 interface.remove_suffix(1);
104 }
105 if (!interface.empty())
106 {
107 result.insert(interface);
108 }
109 if (sep == interfaces.npos)
110 {
111 break;
112 }
113 interfaces = interfaces.substr(sep + 1);
114 }
115 return result;
116 }
117
118 /** @brief Get the ignored interfaces */
getIgnoredInterfaces()119 const std::unordered_set<std::string_view>& getIgnoredInterfaces()
120 {
121 static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
122 return ignoredInterfaces;
123 }
124
125 } // namespace internal
126
interfaceToUbootEthAddr(std::string_view intf)127 std::optional<std::string> interfaceToUbootEthAddr(std::string_view intf)
128 {
129 constexpr auto pfx = "eth"sv;
130 if (!intf.starts_with(pfx))
131 {
132 return std::nullopt;
133 }
134 intf.remove_prefix(pfx.size());
135 unsigned idx;
136 try
137 {
138 idx = stdplus::StrToInt<10, unsigned>{}(intf);
139 }
140 catch (...)
141 {
142 return std::nullopt;
143 }
144 if (idx == 0)
145 {
146 return "ethaddr";
147 }
148 stdplus::ToStrHandle<stdplus::IntToStr<10, unsigned>> tsh;
149 return stdplus::strCat("eth"sv, tsh(idx), "addr"sv);
150 }
151
systemdParseDHCP(std::string_view str)152 static std::optional<DHCPVal> systemdParseDHCP(std::string_view str)
153 {
154 if (config::icaseeq(str, "ipv4"))
155 {
156 return DHCPVal{.v4 = true, .v6 = false};
157 }
158 if (config::icaseeq(str, "ipv6"))
159 {
160 return DHCPVal{.v4 = false, .v6 = true};
161 }
162 if (auto b = config::parseBool(str); b)
163 {
164 return DHCPVal{.v4 = *b, .v6 = *b};
165 }
166 return std::nullopt;
167 }
168
systemdParseLast(const config::Parser & config,std::string_view section,std::string_view key,auto && fun)169 inline auto systemdParseLast(const config::Parser& config,
170 std::string_view section, std::string_view key,
171 auto&& fun)
172 {
173 if (!config.getFileExists())
174 {}
175 else if (auto str = config.map.getLastValueString(section, key);
176 str == nullptr)
177 {
178 lg2::notice(
179 "Unable to get the value of {CFG_SEC}[{CFG_KEY}] from {CFG_FILE}",
180 "CFG_SEC", section, "CFG_KEY", key, "CFG_FILE",
181 config.getFilename());
182 }
183 else if (auto val = fun(*str); !val)
184 {
185 lg2::notice(
186 "Invalid value of {CFG_SEC}[{CFG_KEY}] from {CFG_FILE}: {CFG_VAL}",
187 "CFG_SEC", section, "CFG_KEY", key, "CFG_FILE",
188 config.getFilename(), "CFG_VAL", *str);
189 }
190 else
191 {
192 return val;
193 }
194 return decltype(fun(std::string_view{}))(std::nullopt);
195 }
196
getIPv6AcceptRA(const config::Parser & config)197 bool getIPv6AcceptRA(const config::Parser& config)
198 {
199 #ifdef ENABLE_IPV6_ACCEPT_RA
200 constexpr bool def = true;
201 #else
202 constexpr bool def = false;
203 #endif
204 return systemdParseLast(config, "Network", "IPv6AcceptRA",
205 config::parseBool)
206 .value_or(def);
207 }
208
getDHCPValue(const config::Parser & config)209 DHCPVal getDHCPValue(const config::Parser& config)
210 {
211 return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP)
212 .value_or(DHCPVal{.v4 = true, .v6 = true});
213 }
214
getDHCPProp(const config::Parser & config,DHCPType dhcpType,std::string_view key)215 bool getDHCPProp(const config::Parser& config, DHCPType dhcpType,
216 std::string_view key)
217 {
218 std::string_view type = (dhcpType == DHCPType::v4) ? "DHCPv4" : "DHCPv6";
219
220 if (!config.map.contains(type))
221 {
222 type = "DHCP";
223 }
224
225 return systemdParseLast(config, type, key, config::parseBool)
226 .value_or(true);
227 }
228
229 } // namespace network
230 } // namespace phosphor
231