1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2018 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu // http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu
17d7be555eSGeorge Liu #include "ChassisIntrusionSensor.hpp"
18d7be555eSGeorge Liu #include "Utils.hpp"
19d7be555eSGeorge Liu
20d7be555eSGeorge Liu #include <boost/asio/error.hpp>
21d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
22d7be555eSGeorge Liu #include <boost/asio/steady_timer.hpp>
23d7be555eSGeorge Liu #include <boost/container/flat_map.hpp>
24d7be555eSGeorge Liu #include <phosphor-logging/lg2.hpp>
25d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
26d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
27d7be555eSGeorge Liu #include <sdbusplus/bus.hpp>
28d7be555eSGeorge Liu #include <sdbusplus/bus/match.hpp>
29d7be555eSGeorge Liu #include <sdbusplus/message.hpp>
30d7be555eSGeorge Liu
31d7be555eSGeorge Liu #include <array>
32d7be555eSGeorge Liu #include <charconv>
33d7be555eSGeorge Liu #include <chrono>
34d7be555eSGeorge Liu #include <cstdint>
35d7be555eSGeorge Liu #include <ctime>
36d7be555eSGeorge Liu #include <exception>
37d7be555eSGeorge Liu #include <filesystem>
38d7be555eSGeorge Liu #include <fstream>
39d7be555eSGeorge Liu #include <functional>
40d7be555eSGeorge Liu #include <map>
41d7be555eSGeorge Liu #include <memory>
42d7be555eSGeorge Liu #include <string>
43d7be555eSGeorge Liu #include <system_error>
44d7be555eSGeorge Liu #include <utility>
45d7be555eSGeorge Liu #include <variant>
46d7be555eSGeorge Liu #include <vector>
47d7be555eSGeorge Liu
48d7be555eSGeorge Liu static constexpr bool debug = false;
49d7be555eSGeorge Liu
50d7be555eSGeorge Liu static constexpr const char* sensorType = "ChassisIntrusionSensor";
51d7be555eSGeorge Liu static constexpr const char* nicType = "NIC";
52d7be555eSGeorge Liu static constexpr auto nicTypes{std::to_array<const char*>({nicType})};
53d7be555eSGeorge Liu
54d7be555eSGeorge Liu static const std::map<std::string, std::string> compatibleHwmonNames = {
55d7be555eSGeorge Liu {"Aspeed2600_Hwmon", "intrusion0_alarm"}
56d7be555eSGeorge Liu // Add compatible strings here for new hwmon intrusion detection
57d7be555eSGeorge Liu // drivers that have different hwmon names but would also like to
58d7be555eSGeorge Liu // use the available Hwmon class.
59d7be555eSGeorge Liu };
60d7be555eSGeorge Liu
createSensorsFromConfig(boost::asio::io_context & io,sdbusplus::asio::object_server & objServer,const std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,std::shared_ptr<ChassisIntrusionSensor> & pSensor)61d7be555eSGeorge Liu static void createSensorsFromConfig(
62d7be555eSGeorge Liu boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
63d7be555eSGeorge Liu const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
64d7be555eSGeorge Liu std::shared_ptr<ChassisIntrusionSensor>& pSensor)
65d7be555eSGeorge Liu {
66d7be555eSGeorge Liu // find matched configuration according to sensor type
67d7be555eSGeorge Liu ManagedObjectType sensorConfigurations;
68d7be555eSGeorge Liu bool useCache = false;
69d7be555eSGeorge Liu
70d7be555eSGeorge Liu if (!getSensorConfiguration(sensorType, dbusConnection,
71d7be555eSGeorge Liu sensorConfigurations, useCache))
72d7be555eSGeorge Liu {
73*e9a1c9c0SGeorge Liu lg2::error("error communicating to entity manager");
74d7be555eSGeorge Liu return;
75d7be555eSGeorge Liu }
76d7be555eSGeorge Liu
77d7be555eSGeorge Liu const SensorData* sensorData = nullptr;
78d7be555eSGeorge Liu const std::pair<std::string, SensorBaseConfigMap>* baseConfiguration =
79d7be555eSGeorge Liu nullptr;
80d7be555eSGeorge Liu
81d7be555eSGeorge Liu for (const auto& [path, cfgData] : sensorConfigurations)
82d7be555eSGeorge Liu {
83d7be555eSGeorge Liu baseConfiguration = nullptr;
84d7be555eSGeorge Liu sensorData = &cfgData;
85d7be555eSGeorge Liu
86d7be555eSGeorge Liu // match sensor type
87d7be555eSGeorge Liu auto sensorBase = sensorData->find(configInterfaceName(sensorType));
88d7be555eSGeorge Liu if (sensorBase == sensorData->end())
89d7be555eSGeorge Liu {
90*e9a1c9c0SGeorge Liu lg2::error("error finding base configuration");
91d7be555eSGeorge Liu continue;
92d7be555eSGeorge Liu }
93d7be555eSGeorge Liu
94d7be555eSGeorge Liu baseConfiguration = &(*sensorBase);
95d7be555eSGeorge Liu
96d7be555eSGeorge Liu // Rearm defaults to "Automatic" mode
97d7be555eSGeorge Liu bool autoRearm = true;
98d7be555eSGeorge Liu auto findRearm = baseConfiguration->second.find("Rearm");
99d7be555eSGeorge Liu if (findRearm != baseConfiguration->second.end())
100d7be555eSGeorge Liu {
101d7be555eSGeorge Liu std::string rearmStr = std::get<std::string>(findRearm->second);
102d7be555eSGeorge Liu if (rearmStr != "Automatic" && rearmStr != "Manual")
103d7be555eSGeorge Liu {
104*e9a1c9c0SGeorge Liu lg2::error("Wrong input for Rearm parameter");
105d7be555eSGeorge Liu continue;
106d7be555eSGeorge Liu }
107d7be555eSGeorge Liu autoRearm = (rearmStr == "Automatic");
108d7be555eSGeorge Liu }
109d7be555eSGeorge Liu
110d7be555eSGeorge Liu // judge class, "Gpio", "Hwmon" or "I2C"
111d7be555eSGeorge Liu auto findClass = baseConfiguration->second.find("Class");
112d7be555eSGeorge Liu if (findClass != baseConfiguration->second.end())
113d7be555eSGeorge Liu {
114d7be555eSGeorge Liu auto classString = std::get<std::string>(findClass->second);
115d7be555eSGeorge Liu if (classString == "Gpio")
116d7be555eSGeorge Liu {
117d7be555eSGeorge Liu auto findGpioPolarity =
118d7be555eSGeorge Liu baseConfiguration->second.find("GpioPolarity");
119d7be555eSGeorge Liu
120d7be555eSGeorge Liu if (findGpioPolarity == baseConfiguration->second.end())
121d7be555eSGeorge Liu {
122*e9a1c9c0SGeorge Liu lg2::error("error finding gpio polarity in configuration");
123d7be555eSGeorge Liu continue;
124d7be555eSGeorge Liu }
125d7be555eSGeorge Liu
126d7be555eSGeorge Liu try
127d7be555eSGeorge Liu {
128d7be555eSGeorge Liu bool gpioInverted =
129d7be555eSGeorge Liu (std::get<std::string>(findGpioPolarity->second) ==
130d7be555eSGeorge Liu "Low");
131d7be555eSGeorge Liu pSensor = std::make_shared<ChassisIntrusionGpioSensor>(
132d7be555eSGeorge Liu autoRearm, io, objServer, gpioInverted);
133d7be555eSGeorge Liu pSensor->start();
134d7be555eSGeorge Liu if (debug)
135d7be555eSGeorge Liu {
136*e9a1c9c0SGeorge Liu lg2::info(
137*e9a1c9c0SGeorge Liu "find chassis intrusion sensor polarity inverted flag is '{GPIO_INVERTED}'",
138*e9a1c9c0SGeorge Liu "GPIO_INVERTED", gpioInverted);
139d7be555eSGeorge Liu }
140d7be555eSGeorge Liu return;
141d7be555eSGeorge Liu }
142d7be555eSGeorge Liu catch (const std::bad_variant_access& e)
143d7be555eSGeorge Liu {
144*e9a1c9c0SGeorge Liu lg2::error("invalid value for gpio info in config.");
145d7be555eSGeorge Liu continue;
146d7be555eSGeorge Liu }
147d7be555eSGeorge Liu catch (const std::exception& e)
148d7be555eSGeorge Liu {
149*e9a1c9c0SGeorge Liu lg2::error(
150*e9a1c9c0SGeorge Liu "error creating chassis intrusion gpio sensor: '{ERROR}'",
151*e9a1c9c0SGeorge Liu "ERROR", e);
152d7be555eSGeorge Liu continue;
153d7be555eSGeorge Liu }
154d7be555eSGeorge Liu }
155d7be555eSGeorge Liu // If class string contains Hwmon string
156d7be555eSGeorge Liu else if (classString.find("Hwmon") != std::string::npos)
157d7be555eSGeorge Liu {
158d7be555eSGeorge Liu std::string hwmonName;
159d7be555eSGeorge Liu std::map<std::string, std::string>::const_iterator
160d7be555eSGeorge Liu compatIterator = compatibleHwmonNames.find(classString);
161d7be555eSGeorge Liu
162d7be555eSGeorge Liu if (compatIterator == compatibleHwmonNames.end())
163d7be555eSGeorge Liu {
164*e9a1c9c0SGeorge Liu lg2::error("Hwmon Class string is not supported");
165d7be555eSGeorge Liu continue;
166d7be555eSGeorge Liu }
167d7be555eSGeorge Liu
168d7be555eSGeorge Liu hwmonName = compatIterator->second;
169d7be555eSGeorge Liu
170d7be555eSGeorge Liu try
171d7be555eSGeorge Liu {
172d7be555eSGeorge Liu pSensor = std::make_shared<ChassisIntrusionHwmonSensor>(
173d7be555eSGeorge Liu autoRearm, io, objServer, hwmonName);
174d7be555eSGeorge Liu pSensor->start();
175d7be555eSGeorge Liu return;
176d7be555eSGeorge Liu }
177d7be555eSGeorge Liu catch (const std::exception& e)
178d7be555eSGeorge Liu {
179*e9a1c9c0SGeorge Liu lg2::error(
180*e9a1c9c0SGeorge Liu "error creating chassis intrusion hwmon sensor: '{ERROR}'",
181*e9a1c9c0SGeorge Liu "ERROR", e);
182d7be555eSGeorge Liu continue;
183d7be555eSGeorge Liu }
184d7be555eSGeorge Liu }
185d7be555eSGeorge Liu else
186d7be555eSGeorge Liu {
187d7be555eSGeorge Liu auto findBus = baseConfiguration->second.find("Bus");
188d7be555eSGeorge Liu auto findAddress = baseConfiguration->second.find("Address");
189d7be555eSGeorge Liu if (findBus == baseConfiguration->second.end() ||
190d7be555eSGeorge Liu findAddress == baseConfiguration->second.end())
191d7be555eSGeorge Liu {
192*e9a1c9c0SGeorge Liu lg2::error("error finding bus or address in configuration");
193d7be555eSGeorge Liu continue;
194d7be555eSGeorge Liu }
195d7be555eSGeorge Liu try
196d7be555eSGeorge Liu {
197d7be555eSGeorge Liu int busId = std::get<uint64_t>(findBus->second);
198d7be555eSGeorge Liu int slaveAddr = std::get<uint64_t>(findAddress->second);
199d7be555eSGeorge Liu pSensor = std::make_shared<ChassisIntrusionPchSensor>(
200d7be555eSGeorge Liu autoRearm, io, objServer, busId, slaveAddr);
201d7be555eSGeorge Liu pSensor->start();
202d7be555eSGeorge Liu if (debug)
203d7be555eSGeorge Liu {
204*e9a1c9c0SGeorge Liu lg2::info(
205*e9a1c9c0SGeorge Liu "find matched bus '{BUS}', matched slave addr '{ADDR}'",
206*e9a1c9c0SGeorge Liu "BUS", busId, "ADDR", slaveAddr);
207d7be555eSGeorge Liu }
208d7be555eSGeorge Liu return;
209d7be555eSGeorge Liu }
210d7be555eSGeorge Liu catch (const std::bad_variant_access& e)
211d7be555eSGeorge Liu {
212*e9a1c9c0SGeorge Liu lg2::error("invalid value for bus or address in config.");
213d7be555eSGeorge Liu continue;
214d7be555eSGeorge Liu }
215d7be555eSGeorge Liu catch (const std::exception& e)
216d7be555eSGeorge Liu {
217*e9a1c9c0SGeorge Liu lg2::error(
218*e9a1c9c0SGeorge Liu "error creating chassis intrusion pch sensor: '{ERROR}'",
219*e9a1c9c0SGeorge Liu "ERROR", e);
220d7be555eSGeorge Liu continue;
221d7be555eSGeorge Liu }
222d7be555eSGeorge Liu }
223d7be555eSGeorge Liu }
224d7be555eSGeorge Liu }
225d7be555eSGeorge Liu
226*e9a1c9c0SGeorge Liu lg2::error("Can't find matched I2C, GPIO or Hwmon configuration");
227d7be555eSGeorge Liu
228d7be555eSGeorge Liu // Make sure nothing runs when there's failure in configuration for the
229d7be555eSGeorge Liu // sensor after rescan
230d7be555eSGeorge Liu if (pSensor)
231d7be555eSGeorge Liu {
232*e9a1c9c0SGeorge Liu lg2::error("Reset the occupied sensor pointer");
233d7be555eSGeorge Liu pSensor = nullptr;
234d7be555eSGeorge Liu }
235d7be555eSGeorge Liu }
236d7be555eSGeorge Liu
237d7be555eSGeorge Liu static constexpr bool debugLanLeash = false;
238d7be555eSGeorge Liu boost::container::flat_map<int, bool> lanStatusMap;
239d7be555eSGeorge Liu boost::container::flat_map<int, std::string> lanInfoMap;
240d7be555eSGeorge Liu boost::container::flat_map<std::string, int> pathSuffixMap;
241d7be555eSGeorge Liu
getNicNameInfo(const std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)242d7be555eSGeorge Liu static void getNicNameInfo(
243d7be555eSGeorge Liu const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
244d7be555eSGeorge Liu {
245d7be555eSGeorge Liu auto getter = std::make_shared<GetSensorConfiguration>(
246d7be555eSGeorge Liu dbusConnection, [](const ManagedObjectType& sensorConfigurations) {
247d7be555eSGeorge Liu // Get NIC name and save to map
248d7be555eSGeorge Liu lanInfoMap.clear();
249d7be555eSGeorge Liu for (const auto& [path, cfgData] : sensorConfigurations)
250d7be555eSGeorge Liu {
251d7be555eSGeorge Liu const std::pair<std::string, SensorBaseConfigMap>*
252d7be555eSGeorge Liu baseConfiguration = nullptr;
253d7be555eSGeorge Liu
254d7be555eSGeorge Liu // find base configuration
255d7be555eSGeorge Liu auto sensorBase = cfgData.find(configInterfaceName(nicType));
256d7be555eSGeorge Liu if (sensorBase == cfgData.end())
257d7be555eSGeorge Liu {
258d7be555eSGeorge Liu continue;
259d7be555eSGeorge Liu }
260d7be555eSGeorge Liu baseConfiguration = &(*sensorBase);
261d7be555eSGeorge Liu
262d7be555eSGeorge Liu auto findEthIndex = baseConfiguration->second.find("EthIndex");
263d7be555eSGeorge Liu auto findName = baseConfiguration->second.find("Name");
264d7be555eSGeorge Liu
265d7be555eSGeorge Liu if (findEthIndex != baseConfiguration->second.end() &&
266d7be555eSGeorge Liu findName != baseConfiguration->second.end())
267d7be555eSGeorge Liu {
268d7be555eSGeorge Liu const auto* pEthIndex =
269d7be555eSGeorge Liu std::get_if<uint64_t>(&findEthIndex->second);
270d7be555eSGeorge Liu const auto* pName =
271d7be555eSGeorge Liu std::get_if<std::string>(&findName->second);
272d7be555eSGeorge Liu if (pEthIndex != nullptr && pName != nullptr)
273d7be555eSGeorge Liu {
274d7be555eSGeorge Liu lanInfoMap[*pEthIndex] = *pName;
275d7be555eSGeorge Liu if (debugLanLeash)
276d7be555eSGeorge Liu {
277*e9a1c9c0SGeorge Liu lg2::info("find name of eth{ETH_INDEX} is '{NAME}'",
278*e9a1c9c0SGeorge Liu "ETH_INDEX", *pEthIndex, "NAME", *pName);
279d7be555eSGeorge Liu }
280d7be555eSGeorge Liu }
281d7be555eSGeorge Liu }
282d7be555eSGeorge Liu }
283d7be555eSGeorge Liu
284d7be555eSGeorge Liu if (lanInfoMap.empty())
285d7be555eSGeorge Liu {
286*e9a1c9c0SGeorge Liu lg2::error("can't find matched NIC name.");
287d7be555eSGeorge Liu }
288d7be555eSGeorge Liu });
289d7be555eSGeorge Liu
290d7be555eSGeorge Liu getter->getConfiguration(
291d7be555eSGeorge Liu std::vector<std::string>{nicTypes.begin(), nicTypes.end()});
292d7be555eSGeorge Liu }
293d7be555eSGeorge Liu
processLanStatusChange(sdbusplus::message_t & message)294d7be555eSGeorge Liu static void processLanStatusChange(sdbusplus::message_t& message)
295d7be555eSGeorge Liu {
296d7be555eSGeorge Liu const std::string& pathName = message.get_path();
297d7be555eSGeorge Liu std::string interfaceName;
298d7be555eSGeorge Liu SensorBaseConfigMap properties;
299d7be555eSGeorge Liu message.read(interfaceName, properties);
300d7be555eSGeorge Liu
301d7be555eSGeorge Liu auto findStateProperty = properties.find("OperationalState");
302d7be555eSGeorge Liu if (findStateProperty == properties.end())
303d7be555eSGeorge Liu {
304d7be555eSGeorge Liu return;
305d7be555eSGeorge Liu }
306d7be555eSGeorge Liu std::string* pState =
307d7be555eSGeorge Liu std::get_if<std::string>(&(findStateProperty->second));
308d7be555eSGeorge Liu if (pState == nullptr)
309d7be555eSGeorge Liu {
310*e9a1c9c0SGeorge Liu lg2::error("invalid OperationalState");
311d7be555eSGeorge Liu return;
312d7be555eSGeorge Liu }
313d7be555eSGeorge Liu
314d7be555eSGeorge Liu bool newLanConnected = (*pState == "routable" || *pState == "carrier" ||
315d7be555eSGeorge Liu *pState == "degraded");
316d7be555eSGeorge Liu
317d7be555eSGeorge Liu // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0
318d7be555eSGeorge Liu size_t pos = pathName.find("/_");
319d7be555eSGeorge Liu if (pos == std::string::npos || pathName.length() <= pos + 2)
320d7be555eSGeorge Liu {
321*e9a1c9c0SGeorge Liu lg2::error("unexpected path name '{NAME}'", "NAME", pathName);
322d7be555eSGeorge Liu return;
323d7be555eSGeorge Liu }
324d7be555eSGeorge Liu std::string suffixStr = pathName.substr(pos + 2);
325d7be555eSGeorge Liu
326d7be555eSGeorge Liu auto findEthNum = pathSuffixMap.find(suffixStr);
327d7be555eSGeorge Liu if (findEthNum == pathSuffixMap.end())
328d7be555eSGeorge Liu {
329*e9a1c9c0SGeorge Liu lg2::error("unexpected eth for suffixStr '{SUFFIX}'", "SUFFIX",
330*e9a1c9c0SGeorge Liu suffixStr);
331d7be555eSGeorge Liu return;
332d7be555eSGeorge Liu }
333d7be555eSGeorge Liu int ethNum = findEthNum->second;
334d7be555eSGeorge Liu
335d7be555eSGeorge Liu // get lan status from map
336d7be555eSGeorge Liu auto findLanStatus = lanStatusMap.find(ethNum);
337d7be555eSGeorge Liu if (findLanStatus == lanStatusMap.end())
338d7be555eSGeorge Liu {
339*e9a1c9c0SGeorge Liu lg2::error("unexpected eth{ETH_INDEX} is lanStatusMap", "ETH_INDEX",
340*e9a1c9c0SGeorge Liu ethNum);
341d7be555eSGeorge Liu return;
342d7be555eSGeorge Liu }
343d7be555eSGeorge Liu bool oldLanConnected = findLanStatus->second;
344d7be555eSGeorge Liu
345d7be555eSGeorge Liu // get lan info from map
346d7be555eSGeorge Liu std::string lanInfo;
347d7be555eSGeorge Liu if (!lanInfoMap.empty())
348d7be555eSGeorge Liu {
349d7be555eSGeorge Liu auto findLanInfo = lanInfoMap.find(ethNum);
350d7be555eSGeorge Liu if (findLanInfo == lanInfoMap.end())
351d7be555eSGeorge Liu {
352*e9a1c9c0SGeorge Liu lg2::error("unexpected eth{ETH_INDEX} is lanInfoMap", "ETH_INDEX",
353*e9a1c9c0SGeorge Liu ethNum);
354d7be555eSGeorge Liu }
355d7be555eSGeorge Liu else
356d7be555eSGeorge Liu {
357d7be555eSGeorge Liu lanInfo = "(" + findLanInfo->second + ")";
358d7be555eSGeorge Liu }
359d7be555eSGeorge Liu }
360d7be555eSGeorge Liu
361d7be555eSGeorge Liu if (debugLanLeash)
362d7be555eSGeorge Liu {
363*e9a1c9c0SGeorge Liu lg2::info(
364*e9a1c9c0SGeorge Liu "ethNum = {ETH_INDEX}, state = {LAN_STATUS}, oldLanConnected = {OLD_LAN_CONNECTED}, "
365*e9a1c9c0SGeorge Liu "newLanConnected = {NEW_LAN_CONNECTED}",
366*e9a1c9c0SGeorge Liu "ETH_INDEX", ethNum, "LAN_STATUS", *pState, "OLD_LAN_CONNECTED",
367*e9a1c9c0SGeorge Liu (oldLanConnected ? "true" : "false"), "NEW_LAN_CONNECTED",
368*e9a1c9c0SGeorge Liu (newLanConnected ? "true" : "false"));
369d7be555eSGeorge Liu }
370d7be555eSGeorge Liu
371d7be555eSGeorge Liu if (oldLanConnected != newLanConnected)
372d7be555eSGeorge Liu {
373d7be555eSGeorge Liu std::string strEthNum = "eth" + std::to_string(ethNum) + lanInfo;
374d7be555eSGeorge Liu const auto* strState = newLanConnected ? "connected" : "lost";
375d7be555eSGeorge Liu const auto* strMsgId =
376d7be555eSGeorge Liu newLanConnected ? "OpenBMC.0.1.LanRegained" : "OpenBMC.0.1.LanLost";
377d7be555eSGeorge Liu
378*e9a1c9c0SGeorge Liu lg2::info("'{ETH_INFO}' LAN leash '{LAN_STATUS}'", "ETH_INFO",
379*e9a1c9c0SGeorge Liu strEthNum, "LAN_STATUS", strState, "REDFISH_MESSAGE_ID",
380*e9a1c9c0SGeorge Liu strMsgId, "REDFISH_MESSAGE_ARGS", strEthNum);
381d7be555eSGeorge Liu
382d7be555eSGeorge Liu lanStatusMap[ethNum] = newLanConnected;
383d7be555eSGeorge Liu }
384d7be555eSGeorge Liu }
385d7be555eSGeorge Liu
386d7be555eSGeorge Liu /** @brief Initialize the lan status.
387d7be555eSGeorge Liu *
388d7be555eSGeorge Liu * @return true on success and false on failure
389d7be555eSGeorge Liu */
initializeLanStatus(const std::shared_ptr<sdbusplus::asio::connection> & conn)390d7be555eSGeorge Liu static bool initializeLanStatus(
391d7be555eSGeorge Liu const std::shared_ptr<sdbusplus::asio::connection>& conn)
392d7be555eSGeorge Liu {
393d7be555eSGeorge Liu // init lan port name from configuration
394d7be555eSGeorge Liu getNicNameInfo(conn);
395d7be555eSGeorge Liu
396d7be555eSGeorge Liu // get eth info from sysfs
3972e466967SEd Tanous std::vector<std::filesystem::path> files;
3982e466967SEd Tanous if (!findFiles(std::filesystem::path("/sys/class/net/"),
3992e466967SEd Tanous R"(eth\d+/ifindex)", files))
400d7be555eSGeorge Liu {
401*e9a1c9c0SGeorge Liu lg2::error("No eth in system");
402d7be555eSGeorge Liu return false;
403d7be555eSGeorge Liu }
404d7be555eSGeorge Liu
405d7be555eSGeorge Liu // iterate through all found eth files, and save ifindex
4062e466967SEd Tanous for (const std::filesystem::path& fileName : files)
407d7be555eSGeorge Liu {
408d7be555eSGeorge Liu if (debugLanLeash)
409d7be555eSGeorge Liu {
410*e9a1c9c0SGeorge Liu lg2::info("Reading '{NAME}'", "NAME", fileName);
411d7be555eSGeorge Liu }
412d7be555eSGeorge Liu std::ifstream sysFile(fileName);
413d7be555eSGeorge Liu if (!sysFile.good())
414d7be555eSGeorge Liu {
415*e9a1c9c0SGeorge Liu lg2::error("Failure reading '{NAME}'", "NAME", fileName);
416d7be555eSGeorge Liu continue;
417d7be555eSGeorge Liu }
418d7be555eSGeorge Liu std::string line;
419d7be555eSGeorge Liu getline(sysFile, line);
420d7be555eSGeorge Liu const uint8_t ifindex = std::stoi(line);
421d7be555eSGeorge Liu // pathSuffix is ASCII of ifindex
422d7be555eSGeorge Liu const std::string& pathSuffix = std::to_string(ifindex + 30);
423d7be555eSGeorge Liu
424d7be555eSGeorge Liu // extract ethNum
425d7be555eSGeorge Liu const std::string& fileStr = fileName.string();
426d7be555eSGeorge Liu const int pos = fileStr.find("eth");
427d7be555eSGeorge Liu const std::string& ethNumStr = fileStr.substr(pos + 3);
428d7be555eSGeorge Liu int ethNum = 0;
429d7be555eSGeorge Liu std::from_chars_result r = std::from_chars(
430d7be555eSGeorge Liu ethNumStr.data(), ethNumStr.data() + ethNumStr.size(), ethNum);
431d7be555eSGeorge Liu if (r.ec != std::errc())
432d7be555eSGeorge Liu {
433*e9a1c9c0SGeorge Liu lg2::error("invalid ethNum string: '{ETH_INDEX}'", "ETH_INDEX",
434*e9a1c9c0SGeorge Liu ethNumStr);
435d7be555eSGeorge Liu continue;
436d7be555eSGeorge Liu }
437d7be555eSGeorge Liu
438d7be555eSGeorge Liu // save pathSuffix
439d7be555eSGeorge Liu pathSuffixMap[pathSuffix] = ethNum;
440d7be555eSGeorge Liu if (debugLanLeash)
441d7be555eSGeorge Liu {
442*e9a1c9c0SGeorge Liu lg2::info(
443*e9a1c9c0SGeorge Liu "ethNum = {ETH_INDEX}, ifindex = {LINE}, pathSuffix = {PATH}",
444*e9a1c9c0SGeorge Liu "ETH_INDEX", ethNum, "LINE", line, "PATH", pathSuffix);
445d7be555eSGeorge Liu }
446d7be555eSGeorge Liu
447d7be555eSGeorge Liu // init lan connected status from networkd
448d7be555eSGeorge Liu conn->async_method_call(
449d7be555eSGeorge Liu [ethNum](boost::system::error_code ec,
450d7be555eSGeorge Liu const std::variant<std::string>& property) {
451d7be555eSGeorge Liu lanStatusMap[ethNum] = false;
452d7be555eSGeorge Liu if (ec)
453d7be555eSGeorge Liu {
454*e9a1c9c0SGeorge Liu lg2::error("Error reading init status of eth{ETH_INDEX}",
455*e9a1c9c0SGeorge Liu "ETH_INDEX", ethNum);
456d7be555eSGeorge Liu return;
457d7be555eSGeorge Liu }
458d7be555eSGeorge Liu const std::string* pState = std::get_if<std::string>(&property);
459d7be555eSGeorge Liu if (pState == nullptr)
460d7be555eSGeorge Liu {
461*e9a1c9c0SGeorge Liu lg2::error("Unable to read lan status value");
462d7be555eSGeorge Liu return;
463d7be555eSGeorge Liu }
464d7be555eSGeorge Liu bool isLanConnected =
465d7be555eSGeorge Liu (*pState == "routable" || *pState == "carrier" ||
466d7be555eSGeorge Liu *pState == "degraded");
467d7be555eSGeorge Liu if (debugLanLeash)
468d7be555eSGeorge Liu {
469*e9a1c9c0SGeorge Liu lg2::info(
470*e9a1c9c0SGeorge Liu "ethNum = {ETH_INDEX}, init LAN status = {STATUS}",
471*e9a1c9c0SGeorge Liu "ETH_INDEX", ethNum, "STATUS",
472*e9a1c9c0SGeorge Liu (isLanConnected ? "true" : "false"));
473d7be555eSGeorge Liu }
474d7be555eSGeorge Liu lanStatusMap[ethNum] = isLanConnected;
475d7be555eSGeorge Liu },
476d7be555eSGeorge Liu "org.freedesktop.network1",
477d7be555eSGeorge Liu "/org/freedesktop/network1/link/_" + pathSuffix,
478d7be555eSGeorge Liu "org.freedesktop.DBus.Properties", "Get",
479d7be555eSGeorge Liu "org.freedesktop.network1.Link", "OperationalState");
480d7be555eSGeorge Liu }
481d7be555eSGeorge Liu return true;
482d7be555eSGeorge Liu }
483d7be555eSGeorge Liu
main()484d7be555eSGeorge Liu int main()
485d7be555eSGeorge Liu {
486d7be555eSGeorge Liu std::shared_ptr<ChassisIntrusionSensor> intrusionSensor;
487d7be555eSGeorge Liu
488d7be555eSGeorge Liu // setup connection to dbus
489d7be555eSGeorge Liu boost::asio::io_context io;
490d7be555eSGeorge Liu auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
491d7be555eSGeorge Liu
492d7be555eSGeorge Liu // setup object server, define interface
493d7be555eSGeorge Liu systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
494d7be555eSGeorge Liu
495d7be555eSGeorge Liu sdbusplus::asio::object_server objServer(systemBus, true);
496d7be555eSGeorge Liu
497d7be555eSGeorge Liu objServer.add_manager("/xyz/openbmc_project/Chassis");
498d7be555eSGeorge Liu
499d7be555eSGeorge Liu createSensorsFromConfig(io, objServer, systemBus, intrusionSensor);
500d7be555eSGeorge Liu
501d7be555eSGeorge Liu // callback to handle configuration change
502d7be555eSGeorge Liu boost::asio::steady_timer filterTimer(io);
503d7be555eSGeorge Liu std::function<void(sdbusplus::message_t&)> eventHandler =
504d7be555eSGeorge Liu [&](sdbusplus::message_t& message) {
505d7be555eSGeorge Liu if (message.is_method_error())
506d7be555eSGeorge Liu {
507*e9a1c9c0SGeorge Liu lg2::error("callback method error");
508d7be555eSGeorge Liu return;
509d7be555eSGeorge Liu }
510d7be555eSGeorge Liu // this implicitly cancels the timer
511d7be555eSGeorge Liu filterTimer.expires_after(std::chrono::seconds(1));
512d7be555eSGeorge Liu filterTimer.async_wait([&](const boost::system::error_code& ec) {
513d7be555eSGeorge Liu if (ec == boost::asio::error::operation_aborted)
514d7be555eSGeorge Liu {
515d7be555eSGeorge Liu // timer was cancelled
516d7be555eSGeorge Liu return;
517d7be555eSGeorge Liu }
518*e9a1c9c0SGeorge Liu lg2::info("rescan due to configuration change");
519d7be555eSGeorge Liu createSensorsFromConfig(io, objServer, systemBus,
520d7be555eSGeorge Liu intrusionSensor);
521d7be555eSGeorge Liu });
522d7be555eSGeorge Liu };
523d7be555eSGeorge Liu
524d7be555eSGeorge Liu std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
525d7be555eSGeorge Liu setupPropertiesChangedMatches(
526d7be555eSGeorge Liu *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
527d7be555eSGeorge Liu
528d7be555eSGeorge Liu if (initializeLanStatus(systemBus))
529d7be555eSGeorge Liu {
530d7be555eSGeorge Liu // add match to monitor lan status change
531d7be555eSGeorge Liu sdbusplus::bus::match_t lanStatusMatch(
532d7be555eSGeorge Liu static_cast<sdbusplus::bus_t&>(*systemBus),
533d7be555eSGeorge Liu "type='signal', member='PropertiesChanged',"
534d7be555eSGeorge Liu "arg0namespace='org.freedesktop.network1.Link'",
535d7be555eSGeorge Liu [](sdbusplus::message_t& msg) { processLanStatusChange(msg); });
536d7be555eSGeorge Liu
537d7be555eSGeorge Liu // add match to monitor entity manager signal about nic name config
538d7be555eSGeorge Liu // change
539d7be555eSGeorge Liu sdbusplus::bus::match_t lanConfigMatch(
540d7be555eSGeorge Liu static_cast<sdbusplus::bus_t&>(*systemBus),
541d7be555eSGeorge Liu "type='signal', member='PropertiesChanged',path_namespace='" +
542d7be555eSGeorge Liu std::string(inventoryPath) + "',arg0namespace='" +
543d7be555eSGeorge Liu configInterfaceName(nicType) + "'",
544d7be555eSGeorge Liu [&systemBus](sdbusplus::message_t& msg) {
545d7be555eSGeorge Liu if (msg.is_method_error())
546d7be555eSGeorge Liu {
547*e9a1c9c0SGeorge Liu lg2::error("callback method error");
548d7be555eSGeorge Liu return;
549d7be555eSGeorge Liu }
550d7be555eSGeorge Liu getNicNameInfo(systemBus);
551d7be555eSGeorge Liu });
552d7be555eSGeorge Liu }
553d7be555eSGeorge Liu
554d7be555eSGeorge Liu io.run();
555d7be555eSGeorge Liu
556d7be555eSGeorge Liu return 0;
557d7be555eSGeorge Liu }
558