blob: 2f737afeb1634e64873914d203b4c36191528463 [file] [log] [blame]
Qiang XUe28d1fa2019-02-27 13:50:56 +08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Ed Tanous8a57ec02020-10-09 12:46:52 -070017#include <ChassisIntrusionSensor.hpp>
18#include <Utils.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080019#include <boost/algorithm/string/predicate.hpp>
Ed Tanous8a57ec02020-10-09 12:46:52 -070020#include <boost/asio/io_service.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070021#include <boost/container/flat_map.hpp>
Patrick Williams0c42f402021-08-27 16:05:45 -050022#include <phosphor-logging/lg2.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
25#include <sdbusplus/asio/sd_event.hpp>
26#include <sdbusplus/bus.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070027#include <sdbusplus/bus/match.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080028#include <sdbusplus/exception.hpp>
29#include <sdbusplus/server.hpp>
30#include <sdbusplus/timer.hpp>
James Feist38fb5982020-05-28 10:09:54 -070031
32#include <array>
Ed Tanous8a57ec02020-10-09 12:46:52 -070033#include <charconv>
James Feist38fb5982020-05-28 10:09:54 -070034#include <chrono>
35#include <ctime>
36#include <fstream>
37#include <functional>
38#include <iostream>
39#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070040#include <stdexcept>
41#include <string>
42#include <utility>
43#include <vector>
Qiang XUe28d1fa2019-02-27 13:50:56 +080044
Ed Tanous8a57ec02020-10-09 12:46:52 -070045static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080046
47static constexpr const char* sensorType =
48 "xyz.openbmc_project.Configuration.ChassisIntrusionSensor";
Qiang XU74ddf862019-09-12 17:12:13 +080049static constexpr const char* nicType = "xyz.openbmc_project.Configuration.NIC";
Brandon Kim66558232021-11-09 16:53:08 -080050static constexpr auto nicTypes{std::to_array<const char*>({nicType})};
Qiang XUe28d1fa2019-02-27 13:50:56 +080051
Qiang XU88b7f282019-08-14 22:51:43 +080052namespace fs = std::filesystem;
53
Qiang XUe28d1fa2019-02-27 13:50:56 +080054static bool getIntrusionSensorConfig(
Lei YUba637932021-03-17 10:35:00 +080055 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -080056 IntrusionSensorType* pType, int* pBusId, int* pSlaveAddr,
Qiang XUe28d1fa2019-02-27 13:50:56 +080057 bool* pGpioInverted)
58{
59 // find matched configuration according to sensor type
60 ManagedObjectType sensorConfigurations;
61 bool useCache = false;
62
63 if (!getSensorConfiguration(sensorType, dbusConnection,
64 sensorConfigurations, useCache))
65 {
66 std::cerr << "error communicating to entity manager\n";
67 return false;
68 }
69
70 const SensorData* sensorData = nullptr;
71 const std::pair<std::string,
72 boost::container::flat_map<std::string, BasicVariantType>>*
73 baseConfiguration = nullptr;
74
75 // Get bus and addr of matched configuration
76 for (const std::pair<sdbusplus::message::object_path, SensorData>& sensor :
77 sensorConfigurations)
78 {
79 baseConfiguration = nullptr;
80 sensorData = &(sensor.second);
81
82 // match sensor type
83 auto sensorBase = sensorData->find(sensorType);
84 if (sensorBase == sensorData->end())
85 {
86 std::cerr << "error finding base configuration \n";
87 continue;
88 }
89
90 baseConfiguration = &(*sensorBase);
91
92 // judge class, "Gpio" or "I2C"
93 auto findClass = baseConfiguration->second.find("Class");
94 if (findClass != baseConfiguration->second.end() &&
Patrick Williams94733252020-05-13 11:44:58 -050095 std::get<std::string>(findClass->second) == "Gpio")
Qiang XUe28d1fa2019-02-27 13:50:56 +080096 {
97 *pType = IntrusionSensorType::gpio;
98 }
99 else
100 {
101 *pType = IntrusionSensorType::pch;
102 }
103
104 // case to find GPIO info
105 if (*pType == IntrusionSensorType::gpio)
106 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800107 auto findGpioPolarity =
108 baseConfiguration->second.find("GpioPolarity");
Qiang XUe28d1fa2019-02-27 13:50:56 +0800109
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800110 if (findGpioPolarity == baseConfiguration->second.end())
Qiang XUe28d1fa2019-02-27 13:50:56 +0800111 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800112 std::cerr << "error finding gpio polarity in configuration \n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800113 continue;
114 }
115
116 try
117 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800118 *pGpioInverted =
Patrick Williams94733252020-05-13 11:44:58 -0500119 (std::get<std::string>(findGpioPolarity->second) == "Low");
Qiang XUe28d1fa2019-02-27 13:50:56 +0800120 }
121 catch (const std::bad_variant_access& e)
122 {
123 std::cerr << "invalid value for gpio info in config. \n";
124 continue;
125 }
126
Ed Tanous8a57ec02020-10-09 12:46:52 -0700127 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800128 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800129 std::cout << "find chassis intrusion sensor polarity inverted "
130 "flag is "
131 << *pGpioInverted << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800132 }
133
134 return true;
135 }
136
137 // case to find I2C info
Ed Tanous8a57ec02020-10-09 12:46:52 -0700138 if (*pType == IntrusionSensorType::pch)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800139 {
140 auto findBus = baseConfiguration->second.find("Bus");
141 auto findAddress = baseConfiguration->second.find("Address");
142 if (findBus == baseConfiguration->second.end() ||
143 findAddress == baseConfiguration->second.end())
144 {
145 std::cerr << "error finding bus or address in configuration \n";
146 continue;
147 }
148
149 try
150 {
Patrick Williams94733252020-05-13 11:44:58 -0500151 *pBusId = std::get<uint64_t>(findBus->second);
152 *pSlaveAddr = std::get<uint64_t>(findAddress->second);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800153 }
154 catch (const std::bad_variant_access& e)
155 {
156 std::cerr << "invalid value for bus or address in config. \n";
157 continue;
158 }
159
Ed Tanous8a57ec02020-10-09 12:46:52 -0700160 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800161 {
162 std::cout << "find matched bus " << *pBusId
163 << ", matched slave addr " << *pSlaveAddr << "\n";
164 }
165 return true;
166 }
167 }
168
Qiang XU74ddf862019-09-12 17:12:13 +0800169 std::cerr << "can't find matched I2C or GPIO configuration for intrusion "
170 "sensor. \n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800171 *pBusId = -1;
172 *pSlaveAddr = -1;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800173 return false;
174}
175
Qiang XU88b7f282019-08-14 22:51:43 +0800176static constexpr bool debugLanLeash = false;
177boost::container::flat_map<int, bool> lanStatusMap;
Qiang XU74ddf862019-09-12 17:12:13 +0800178boost::container::flat_map<int, std::string> lanInfoMap;
Qiang XU88b7f282019-08-14 22:51:43 +0800179boost::container::flat_map<std::string, int> pathSuffixMap;
180
Lei YUba637932021-03-17 10:35:00 +0800181static void getNicNameInfo(
182 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Qiang XU74ddf862019-09-12 17:12:13 +0800183{
184 auto getter = std::make_shared<GetSensorConfiguration>(
Ed Tanousbb679322022-05-16 16:10:00 -0700185 dbusConnection,
186 [](const ManagedObjectType& sensorConfigurations) {
187 // Get NIC name and save to map
188 lanInfoMap.clear();
189 for (const std::pair<sdbusplus::message::object_path, SensorData>&
190 sensor : sensorConfigurations)
191 {
192 const std::pair<std::string, boost::container::flat_map<
193 std::string, BasicVariantType>>*
194 baseConfiguration = nullptr;
195
196 // find base configuration
197 auto sensorBase = sensor.second.find(nicType);
198 if (sensorBase == sensor.second.end())
Qiang XU74ddf862019-09-12 17:12:13 +0800199 {
Ed Tanousbb679322022-05-16 16:10:00 -0700200 continue;
201 }
202 baseConfiguration = &(*sensorBase);
Qiang XU74ddf862019-09-12 17:12:13 +0800203
Ed Tanousbb679322022-05-16 16:10:00 -0700204 auto findEthIndex = baseConfiguration->second.find("EthIndex");
205 auto findName = baseConfiguration->second.find("Name");
206
207 if (findEthIndex != baseConfiguration->second.end() &&
208 findName != baseConfiguration->second.end())
209 {
210 auto* pEthIndex = std::get_if<uint64_t>(&findEthIndex->second);
211 auto* pName = std::get_if<std::string>(&findName->second);
212 if (pEthIndex != nullptr && pName != nullptr)
Qiang XU74ddf862019-09-12 17:12:13 +0800213 {
Ed Tanousbb679322022-05-16 16:10:00 -0700214 lanInfoMap[*pEthIndex] = *pName;
215 if (debugLanLeash)
Qiang XU74ddf862019-09-12 17:12:13 +0800216 {
Ed Tanousbb679322022-05-16 16:10:00 -0700217 std::cout << "find name of eth" << *pEthIndex << " is "
218 << *pName << "\n";
Qiang XU74ddf862019-09-12 17:12:13 +0800219 }
220 }
221 }
Ed Tanousbb679322022-05-16 16:10:00 -0700222 }
Qiang XU74ddf862019-09-12 17:12:13 +0800223
Ed Tanousbb679322022-05-16 16:10:00 -0700224 if (lanInfoMap.size() == 0)
225 {
226 std::cerr << "can't find matched NIC name. \n";
227 }
Ed Tanous8a17c302021-09-02 15:07:11 -0700228 });
Qiang XU74ddf862019-09-12 17:12:13 +0800229
230 getter->getConfiguration(
231 std::vector<std::string>{nicTypes.begin(), nicTypes.end()});
232}
233
Qiang XU88b7f282019-08-14 22:51:43 +0800234static void processLanStatusChange(sdbusplus::message::message& message)
235{
236 const std::string& pathName = message.get_path();
237 std::string interfaceName;
238 boost::container::flat_map<std::string, BasicVariantType> properties;
239 message.read(interfaceName, properties);
240
241 auto findStateProperty = properties.find("OperationalState");
242 if (findStateProperty == properties.end())
243 {
244 return;
245 }
Qiang XU74ddf862019-09-12 17:12:13 +0800246 std::string* pState =
247 std::get_if<std::string>(&(findStateProperty->second));
Qiang XU88b7f282019-08-14 22:51:43 +0800248 if (pState == nullptr)
249 {
250 std::cerr << "invalid OperationalState \n";
251 return;
252 }
253
254 bool newLanConnected = (*pState == "routable" || *pState == "carrier" ||
255 *pState == "degraded");
256
257 // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500258 size_t pos = pathName.find("/_");
Qiang XU88b7f282019-08-14 22:51:43 +0800259 if (pos == std::string::npos || pathName.length() <= pos + 2)
260 {
261 std::cerr << "unexpected path name " << pathName << "\n";
262 return;
263 }
264 std::string suffixStr = pathName.substr(pos + 2);
265
266 auto findEthNum = pathSuffixMap.find(suffixStr);
267 if (findEthNum == pathSuffixMap.end())
268 {
269 std::cerr << "unexpected eth for suffixStr " << suffixStr << "\n";
270 return;
271 }
272 int ethNum = findEthNum->second;
Qiang XU74ddf862019-09-12 17:12:13 +0800273
274 // get lan status from map
Qiang XU88b7f282019-08-14 22:51:43 +0800275 auto findLanStatus = lanStatusMap.find(ethNum);
276 if (findLanStatus == lanStatusMap.end())
277 {
278 std::cerr << "unexpected eth " << ethNum << " in lanStatusMap \n";
279 return;
280 }
281 bool oldLanConnected = findLanStatus->second;
282
Qiang XU74ddf862019-09-12 17:12:13 +0800283 // get lan info from map
284 std::string lanInfo = "";
285 if (lanInfoMap.size() > 0)
286 {
287 auto findLanInfo = lanInfoMap.find(ethNum);
288 if (findLanInfo == lanInfoMap.end())
289 {
290 std::cerr << "unexpected eth " << ethNum << " in lanInfoMap \n";
291 }
292 else
293 {
294 lanInfo = "(" + findLanInfo->second + ")";
295 }
296 }
297
Qiang XU88b7f282019-08-14 22:51:43 +0800298 if (debugLanLeash)
299 {
300 std::cout << "ethNum = " << ethNum << ", state = " << *pState
301 << ", oldLanConnected = "
302 << (oldLanConnected ? "true" : "false")
303 << ", newLanConnected = "
304 << (newLanConnected ? "true" : "false") << "\n";
305 }
306
307 if (oldLanConnected != newLanConnected)
308 {
Qiang XU74ddf862019-09-12 17:12:13 +0800309 std::string strEthNum = "eth" + std::to_string(ethNum) + lanInfo;
Patrick Williams0c42f402021-08-27 16:05:45 -0500310 auto strState = newLanConnected ? "connected" : "lost";
311 auto strMsgId =
Qiang XU74ddf862019-09-12 17:12:13 +0800312 newLanConnected ? "OpenBMC.0.1.LanRegained" : "OpenBMC.0.1.LanLost";
Patrick Williams0c42f402021-08-27 16:05:45 -0500313
314 lg2::info("{ETHDEV} LAN leash {STATE}", "ETHDEV", strEthNum, "STATE",
315 strState, "REDFISH_MESSAGE_ID", strMsgId,
316 "REDFISH_MESSAGE_ARGS", strEthNum);
317
Qiang XU88b7f282019-08-14 22:51:43 +0800318 lanStatusMap[ethNum] = newLanConnected;
Qiang XU88b7f282019-08-14 22:51:43 +0800319 }
320}
321
Lei YUdd68d4a2021-03-16 22:17:23 +0800322/** @brief Initialize the lan status.
323 *
324 * @return true on success and false on failure
325 */
Lei YUba637932021-03-17 10:35:00 +0800326static bool initializeLanStatus(
327 const std::shared_ptr<sdbusplus::asio::connection>& conn)
Qiang XU88b7f282019-08-14 22:51:43 +0800328{
Qiang XU74ddf862019-09-12 17:12:13 +0800329 // init lan port name from configuration
330 getNicNameInfo(conn);
331
332 // get eth info from sysfs
Qiang XU88b7f282019-08-14 22:51:43 +0800333 std::vector<fs::path> files;
334 if (!findFiles(fs::path("/sys/class/net/"), R"(eth\d+/ifindex)", files))
335 {
336 std::cerr << "No eth in system\n";
Lei YUdd68d4a2021-03-16 22:17:23 +0800337 return false;
Qiang XU88b7f282019-08-14 22:51:43 +0800338 }
339
340 // iterate through all found eth files, and save ifindex
Ed Tanous8a57ec02020-10-09 12:46:52 -0700341 for (const fs::path& fileName : files)
Qiang XU88b7f282019-08-14 22:51:43 +0800342 {
343 if (debugLanLeash)
344 {
345 std::cout << "Reading " << fileName << "\n";
346 }
347 std::ifstream sysFile(fileName);
348 if (!sysFile.good())
349 {
350 std::cerr << "Failure reading " << fileName << "\n";
351 continue;
352 }
353 std::string line;
354 getline(sysFile, line);
355 const uint8_t ifindex = std::stoi(line);
356 // pathSuffix is ASCII of ifindex
357 const std::string& pathSuffix = std::to_string(ifindex + 30);
358
359 // extract ethNum
360 const std::string& fileStr = fileName.string();
361 const int pos = fileStr.find("eth");
362 const std::string& ethNumStr = fileStr.substr(pos + 3);
363 int ethNum = 0;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700364 std::from_chars_result r = std::from_chars(
365 ethNumStr.data(), ethNumStr.data() + ethNumStr.size(), ethNum);
366 if (r.ec != std::errc())
Qiang XU88b7f282019-08-14 22:51:43 +0800367 {
368 std::cerr << "invalid ethNum string: " << ethNumStr << "\n";
369 continue;
370 }
371
372 // save pathSuffix
373 pathSuffixMap[pathSuffix] = ethNum;
374 if (debugLanLeash)
375 {
376 std::cout << "ethNum = " << std::to_string(ethNum)
377 << ", ifindex = " << line
378 << ", pathSuffix = " << pathSuffix << "\n";
379 }
380
Qiang XU74ddf862019-09-12 17:12:13 +0800381 // init lan connected status from networkd
Qiang XU88b7f282019-08-14 22:51:43 +0800382 conn->async_method_call(
383 [ethNum](boost::system::error_code ec,
384 const std::variant<std::string>& property) {
Ed Tanousbb679322022-05-16 16:10:00 -0700385 lanStatusMap[ethNum] = false;
386 if (ec)
387 {
388 std::cerr << "Error reading init status of eth" << ethNum
389 << "\n";
390 return;
391 }
392 const std::string* pState = std::get_if<std::string>(&property);
393 if (pState == nullptr)
394 {
395 std::cerr << "Unable to read lan status value\n";
396 return;
397 }
398 bool isLanConnected =
399 (*pState == "routable" || *pState == "carrier" ||
400 *pState == "degraded");
401 if (debugLanLeash)
402 {
403 std::cout << "ethNum = " << std::to_string(ethNum)
404 << ", init LAN status = "
405 << (isLanConnected ? "true" : "false") << "\n";
406 }
407 lanStatusMap[ethNum] = isLanConnected;
Qiang XU88b7f282019-08-14 22:51:43 +0800408 },
409 "org.freedesktop.network1",
410 "/org/freedesktop/network1/link/_" + pathSuffix,
411 "org.freedesktop.DBus.Properties", "Get",
412 "org.freedesktop.network1.Link", "OperationalState");
413 }
Lei YUdd68d4a2021-03-16 22:17:23 +0800414 return true;
Qiang XU88b7f282019-08-14 22:51:43 +0800415}
416
Qiang XUe28d1fa2019-02-27 13:50:56 +0800417int main()
418{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800419 int busId = -1;
420 int slaveAddr = -1;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800421 bool gpioInverted = false;
422 IntrusionSensorType type = IntrusionSensorType::gpio;
423
424 // setup connection to dbus
425 boost::asio::io_service io;
426 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
427 auto objServer = sdbusplus::asio::object_server(systemBus);
428
429 // setup object server, define interface
430 systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
431
432 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceChassis =
433 objServer.add_interface(
434 "/xyz/openbmc_project/Intrusion/Chassis_Intrusion",
435 "xyz.openbmc_project.Chassis.Intrusion");
436
437 ChassisIntrusionSensor chassisIntrusionSensor(io, ifaceChassis);
438
439 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800440 &gpioInverted))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800441 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800442 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioInverted);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800443 }
444
445 // callback to handle configuration change
446 std::function<void(sdbusplus::message::message&)> eventHandler =
447 [&](sdbusplus::message::message& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700448 if (message.is_method_error())
449 {
450 std::cerr << "callback method error\n";
451 return;
452 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800453
Ed Tanousbb679322022-05-16 16:10:00 -0700454 std::cout << "rescan due to configuration change \n";
455 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
456 &gpioInverted))
457 {
458 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioInverted);
459 }
460 };
Qiang XUe28d1fa2019-02-27 13:50:56 +0800461
Lei YUdd68d4a2021-03-16 22:17:23 +0800462 auto eventMatch = std::make_unique<sdbusplus::bus::match::match>(
Qiang XUe28d1fa2019-02-27 13:50:56 +0800463 static_cast<sdbusplus::bus::bus&>(*systemBus),
464 "type='signal',member='PropertiesChanged',path_namespace='" +
465 std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
466 eventHandler);
467
Lei YUdd68d4a2021-03-16 22:17:23 +0800468 if (initializeLanStatus(systemBus))
469 {
470 // add match to monitor lan status change
471 sdbusplus::bus::match::match lanStatusMatch(
472 static_cast<sdbusplus::bus::bus&>(*systemBus),
473 "type='signal', member='PropertiesChanged',"
474 "arg0namespace='org.freedesktop.network1.Link'",
475 [](sdbusplus::message::message& msg) {
Ed Tanousbb679322022-05-16 16:10:00 -0700476 processLanStatusChange(msg);
Lei YUdd68d4a2021-03-16 22:17:23 +0800477 });
478
479 // add match to monitor entity manager signal about nic name config
480 // change
481 sdbusplus::bus::match::match lanConfigMatch(
482 static_cast<sdbusplus::bus::bus&>(*systemBus),
483 "type='signal', member='PropertiesChanged',path_namespace='" +
484 std::string(inventoryPath) + "',arg0namespace='" + nicType +
485 "'",
486 [&systemBus](sdbusplus::message::message& msg) {
Ed Tanousbb679322022-05-16 16:10:00 -0700487 if (msg.is_method_error())
488 {
489 std::cerr << "callback method error\n";
490 return;
491 }
492 getNicNameInfo(systemBus);
Lei YUdd68d4a2021-03-16 22:17:23 +0800493 });
494 }
Qiang XU88b7f282019-08-14 22:51:43 +0800495
Qiang XUe28d1fa2019-02-27 13:50:56 +0800496 io.run();
497
498 return 0;
499}