xref: /openbmc/dbus-sensors/src/intrusion/IntrusionSensorMain.cpp (revision e9a1c9c02a61bfd36e3d83996ea76894af271cee)
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