1 #include "hostname_manager.hpp"
2
3 #include "network_manager.hpp"
4
5 #include <phosphor-logging/elog-errors.hpp>
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <stdplus/pinned.hpp>
9 #include <xyz/openbmc_project/Common/error.hpp>
10
11 #include <filesystem>
12 #include <fstream>
13 #include <string>
14
15 namespace phosphor
16 {
17 namespace network
18 {
19
20 using namespace phosphor::logging;
21 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22
23 static constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
24 static constexpr const char* mapperObjPath =
25 "/xyz/openbmc_project/object_mapper";
26 static constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
27 static constexpr const char* inventoryRoot = "/xyz/openbmc_project/inventory";
28 static constexpr const char* bmcItemIntf =
29 "xyz.openbmc_project.Inventory.Item.Bmc";
30 static constexpr const char* assetIntf =
31 "xyz.openbmc_project.Inventory.Decorator.Asset";
32 static constexpr const char* networkItemIntf =
33 "xyz.openbmc_project.Inventory.Item.NetworkInterface";
34 static constexpr const char* propIntf = "org.freedesktop.DBus.Properties";
35 static constexpr const char* hostnamedBusName = "org.freedesktop.hostname1";
36 static constexpr const char* hostnamedObjPath = "/org/freedesktop/hostname1";
37 static constexpr const char* hostnamedIntf = "org.freedesktop.hostname1";
38
HostnameManager(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<Manager> manager)39 HostnameManager::HostnameManager(stdplus::PinnedRef<sdbusplus::bus_t> bus,
40 stdplus::PinnedRef<Manager> manager) :
41 bus(bus), manager(manager)
42 {}
43
initialize()44 void HostnameManager::initialize()
45 {
46 if (!isFirstBoot())
47 {
48 lg2::info("Hostname already set on previous boot, skipping");
49 return;
50 }
51
52 lg2::info("First boot detected, setting unique hostname");
53 setUniqueHostname();
54 markHostnameSet();
55 }
56
isFirstBoot() const57 bool HostnameManager::isFirstBoot() const
58 {
59 return !std::filesystem::exists(firstBootFile);
60 }
61
markHostnameSet()62 void HostnameManager::markHostnameSet()
63 {
64 try
65 {
66 // Create parent directories if they don't exist
67 std::filesystem::path firstBootFilePath(firstBootFile);
68 std::filesystem::create_directories(firstBootFilePath.parent_path());
69
70 std::ofstream file(firstBootFile);
71 if (!file)
72 {
73 lg2::error("Failed to create firstBoot file: {PATH}", "PATH",
74 firstBootFile);
75 }
76 }
77 catch (const std::exception& e)
78 {
79 lg2::error("Exception creating firstBoot file: {ERROR}", "ERROR", e);
80 }
81 }
82
getBmcSerialNumber()83 std::string HostnameManager::getBmcSerialNumber()
84 {
85 try
86 {
87 // Get BMC item path from inventory
88 auto method = bus.get().new_method_call(mapperBusName, mapperObjPath,
89 mapperIntf, "GetSubTree");
90 method.append(inventoryRoot, 0, std::vector<std::string>{bmcItemIntf});
91
92 auto reply = bus.get().call(method);
93
94 std::map<std::string, std::map<std::string, std::vector<std::string>>>
95 response;
96 reply.read(response);
97
98 if (response.empty())
99 {
100 lg2::warning("No BMC item found in inventory");
101 return "";
102 }
103
104 // Get the first BMC item path
105 const auto& bmcPath = response.begin()->first;
106 const auto& serviceMap = response.begin()->second;
107
108 if (serviceMap.empty())
109 {
110 lg2::warning("No service found for BMC item");
111 return "";
112 }
113
114 const auto& serviceName = serviceMap.begin()->first;
115
116 // Get SerialNumber property
117 auto propMethod = bus.get().new_method_call(
118 serviceName.c_str(), bmcPath.c_str(), propIntf, "Get");
119 propMethod.append(assetIntf, "SerialNumber");
120
121 auto propReply = bus.get().call(propMethod);
122 std::variant<std::string> serialNumber;
123 propReply.read(serialNumber);
124
125 std::string sn = std::get<std::string>(serialNumber);
126 if (sn.empty())
127 {
128 lg2::warning("BMC Serial Number is empty");
129 }
130 else
131 {
132 lg2::info("Retrieved BMC Serial Number: {SN}", "SN", sn);
133 }
134
135 return sn;
136 }
137 catch (const std::exception& e)
138 {
139 lg2::error("Failed to get BMC serial number: {ERROR}", "ERROR", e);
140 return "";
141 }
142 }
143
getMacAddress()144 std::string HostnameManager::getMacAddress()
145 {
146 try
147 {
148 auto method = bus.get().new_method_call(mapperBusName, mapperObjPath,
149 mapperIntf, "GetSubTree");
150 method.append(inventoryRoot, 0,
151 std::vector<std::string>{networkItemIntf});
152
153 auto reply = bus.get().call(method);
154
155 std::map<std::string, std::map<std::string, std::vector<std::string>>>
156 response;
157 reply.read(response);
158
159 if (response.empty())
160 {
161 lg2::warning("No network interface found in inventory");
162 return "";
163 }
164
165 // Get the first network interface path
166 const auto& netPath = response.begin()->first;
167 const auto& serviceMap = response.begin()->second;
168
169 if (serviceMap.empty())
170 {
171 lg2::warning("No service found for network interface");
172 return "";
173 }
174
175 const auto& serviceName = serviceMap.begin()->first;
176
177 // Get MACAddress property
178 auto propMethod = bus.get().new_method_call(
179 serviceName.c_str(), netPath.c_str(), propIntf, "Get");
180 propMethod.append(networkItemIntf, "MACAddress");
181
182 auto propReply = bus.get().call(propMethod);
183 std::variant<std::string> macAddress;
184 propReply.read(macAddress);
185
186 std::string mac = std::get<std::string>(macAddress);
187 if (mac.empty())
188 {
189 lg2::warning("MAC Address is empty");
190 }
191 else
192 {
193 lg2::info("Retrieved MAC Address: {MAC}", "MAC", mac);
194 }
195
196 return mac;
197 }
198 catch (const std::exception& e)
199 {
200 lg2::error("Failed to get MAC address: {ERROR}", "ERROR", e);
201 return "";
202 }
203 }
204
getCurrentHostname()205 std::string HostnameManager::getCurrentHostname()
206 {
207 try
208 {
209 auto method = bus.get().new_method_call(
210 hostnamedBusName, hostnamedObjPath, propIntf, "Get");
211 method.append(hostnamedIntf, "Hostname");
212
213 auto reply = bus.get().call(method);
214 std::variant<std::string> hostname;
215 reply.read(hostname);
216
217 return std::get<std::string>(hostname);
218 }
219 catch (const std::exception& e)
220 {
221 lg2::error("Failed to get current hostname: {ERROR}", "ERROR", e);
222 return "localhost";
223 }
224 }
225
setHostname(const std::string & hostname)226 bool HostnameManager::setHostname(const std::string& hostname)
227 {
228 try
229 {
230 auto method =
231 bus.get().new_method_call(hostnamedBusName, hostnamedObjPath,
232 hostnamedIntf, "SetStaticHostname");
233 method.append(hostname, false);
234
235 bus.get().call(method);
236 lg2::info("Successfully set hostname to: {HOSTNAME}", "HOSTNAME",
237 hostname);
238 return true;
239 }
240 catch (const std::exception& e)
241 {
242 lg2::error("Failed to set hostname to {HOSTNAME}: {ERROR}", "HOSTNAME",
243 hostname, "ERROR", e);
244 return false;
245 }
246 }
247
setUniqueHostname()248 void HostnameManager::setUniqueHostname()
249 {
250 std::string currentHostname = getCurrentHostname();
251 std::string uniqueSuffix;
252
253 // Try to get BMC serial number first
254 std::string serialNumber = getBmcSerialNumber();
255 if (!serialNumber.empty())
256 {
257 uniqueSuffix = serialNumber;
258 lg2::info("Using BMC Serial Number for unique hostname");
259 }
260 else
261 {
262 // Fallback to MAC address
263 lg2::warning(
264 "BMC Serial Number not available, falling back to MAC address");
265 std::string macAddress = getMacAddress();
266 if (!macAddress.empty())
267 {
268 uniqueSuffix = macAddress;
269 lg2::info("Using MAC Address for unique hostname");
270 }
271 else
272 {
273 lg2::error(
274 "Neither Serial Number nor MAC Address available, cannot set unique hostname");
275 return;
276 }
277 }
278
279 // Construct and set unique hostname
280 std::string newHostname = currentHostname + "-" + uniqueSuffix;
281
282 if (setHostname(newHostname))
283 {
284 lg2::info("Unique hostname set successfully: {HOSTNAME}", "HOSTNAME",
285 newHostname);
286 }
287 else
288 {
289 lg2::error("Failed to set unique hostname");
290 }
291 }
292
293 } // namespace network
294 } // namespace phosphor
295