blob: 154b2b285ad2c66469d4c1ebbe0a7cfaf81f6678 [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
Qiang XU88b7f282019-08-14 22:51:43 +080017#include <systemd/sd-journal.h>
18
Ed Tanous8a57ec02020-10-09 12:46:52 -070019#include <ChassisIntrusionSensor.hpp>
20#include <Utils.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080021#include <boost/algorithm/string/predicate.hpp>
Ed Tanous8a57ec02020-10-09 12:46:52 -070022#include <boost/asio/io_service.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <boost/container/flat_map.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
26#include <sdbusplus/asio/sd_event.hpp>
27#include <sdbusplus/bus.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <sdbusplus/bus/match.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080029#include <sdbusplus/exception.hpp>
30#include <sdbusplus/server.hpp>
31#include <sdbusplus/timer.hpp>
James Feist38fb5982020-05-28 10:09:54 -070032
33#include <array>
Ed Tanous8a57ec02020-10-09 12:46:52 -070034#include <charconv>
James Feist38fb5982020-05-28 10:09:54 -070035#include <chrono>
36#include <ctime>
37#include <fstream>
38#include <functional>
39#include <iostream>
40#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070041#include <stdexcept>
42#include <string>
43#include <utility>
44#include <vector>
Qiang XUe28d1fa2019-02-27 13:50:56 +080045
Ed Tanous8a57ec02020-10-09 12:46:52 -070046static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080047
48static constexpr const char* sensorType =
49 "xyz.openbmc_project.Configuration.ChassisIntrusionSensor";
Qiang XU74ddf862019-09-12 17:12:13 +080050static constexpr const char* nicType = "xyz.openbmc_project.Configuration.NIC";
51static constexpr std::array<const char*, 1> nicTypes = {nicType};
Qiang XUe28d1fa2019-02-27 13:50:56 +080052
Qiang XU88b7f282019-08-14 22:51:43 +080053namespace fs = std::filesystem;
54
Qiang XUe28d1fa2019-02-27 13:50:56 +080055static bool getIntrusionSensorConfig(
Lei YUba637932021-03-17 10:35:00 +080056 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -080057 IntrusionSensorType* pType, int* pBusId, int* pSlaveAddr,
Qiang XUe28d1fa2019-02-27 13:50:56 +080058 bool* pGpioInverted)
59{
60 // find matched configuration according to sensor type
61 ManagedObjectType sensorConfigurations;
62 bool useCache = false;
63
64 if (!getSensorConfiguration(sensorType, dbusConnection,
65 sensorConfigurations, useCache))
66 {
67 std::cerr << "error communicating to entity manager\n";
68 return false;
69 }
70
71 const SensorData* sensorData = nullptr;
72 const std::pair<std::string,
73 boost::container::flat_map<std::string, BasicVariantType>>*
74 baseConfiguration = nullptr;
75
76 // Get bus and addr of matched configuration
77 for (const std::pair<sdbusplus::message::object_path, SensorData>& sensor :
78 sensorConfigurations)
79 {
80 baseConfiguration = nullptr;
81 sensorData = &(sensor.second);
82
83 // match sensor type
84 auto sensorBase = sensorData->find(sensorType);
85 if (sensorBase == sensorData->end())
86 {
87 std::cerr << "error finding base configuration \n";
88 continue;
89 }
90
91 baseConfiguration = &(*sensorBase);
92
93 // judge class, "Gpio" or "I2C"
94 auto findClass = baseConfiguration->second.find("Class");
95 if (findClass != baseConfiguration->second.end() &&
Patrick Williams94733252020-05-13 11:44:58 -050096 std::get<std::string>(findClass->second) == "Gpio")
Qiang XUe28d1fa2019-02-27 13:50:56 +080097 {
98 *pType = IntrusionSensorType::gpio;
99 }
100 else
101 {
102 *pType = IntrusionSensorType::pch;
103 }
104
105 // case to find GPIO info
106 if (*pType == IntrusionSensorType::gpio)
107 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800108 auto findGpioPolarity =
109 baseConfiguration->second.find("GpioPolarity");
Qiang XUe28d1fa2019-02-27 13:50:56 +0800110
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800111 if (findGpioPolarity == baseConfiguration->second.end())
Qiang XUe28d1fa2019-02-27 13:50:56 +0800112 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800113 std::cerr << "error finding gpio polarity in configuration \n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800114 continue;
115 }
116
117 try
118 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800119 *pGpioInverted =
Patrick Williams94733252020-05-13 11:44:58 -0500120 (std::get<std::string>(findGpioPolarity->second) == "Low");
Qiang XUe28d1fa2019-02-27 13:50:56 +0800121 }
122 catch (const std::bad_variant_access& e)
123 {
124 std::cerr << "invalid value for gpio info in config. \n";
125 continue;
126 }
127
Ed Tanous8a57ec02020-10-09 12:46:52 -0700128 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800129 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800130 std::cout << "find chassis intrusion sensor polarity inverted "
131 "flag is "
132 << *pGpioInverted << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800133 }
134
135 return true;
136 }
137
138 // case to find I2C info
Ed Tanous8a57ec02020-10-09 12:46:52 -0700139 if (*pType == IntrusionSensorType::pch)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800140 {
141 auto findBus = baseConfiguration->second.find("Bus");
142 auto findAddress = baseConfiguration->second.find("Address");
143 if (findBus == baseConfiguration->second.end() ||
144 findAddress == baseConfiguration->second.end())
145 {
146 std::cerr << "error finding bus or address in configuration \n";
147 continue;
148 }
149
150 try
151 {
Patrick Williams94733252020-05-13 11:44:58 -0500152 *pBusId = std::get<uint64_t>(findBus->second);
153 *pSlaveAddr = std::get<uint64_t>(findAddress->second);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800154 }
155 catch (const std::bad_variant_access& e)
156 {
157 std::cerr << "invalid value for bus or address in config. \n";
158 continue;
159 }
160
Ed Tanous8a57ec02020-10-09 12:46:52 -0700161 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800162 {
163 std::cout << "find matched bus " << *pBusId
164 << ", matched slave addr " << *pSlaveAddr << "\n";
165 }
166 return true;
167 }
168 }
169
Qiang XU74ddf862019-09-12 17:12:13 +0800170 std::cerr << "can't find matched I2C or GPIO configuration for intrusion "
171 "sensor. \n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800172 *pBusId = -1;
173 *pSlaveAddr = -1;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800174 return false;
175}
176
Qiang XU88b7f282019-08-14 22:51:43 +0800177static constexpr bool debugLanLeash = false;
178boost::container::flat_map<int, bool> lanStatusMap;
Qiang XU74ddf862019-09-12 17:12:13 +0800179boost::container::flat_map<int, std::string> lanInfoMap;
Qiang XU88b7f282019-08-14 22:51:43 +0800180boost::container::flat_map<std::string, int> pathSuffixMap;
181
Lei YUba637932021-03-17 10:35:00 +0800182static void getNicNameInfo(
183 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Qiang XU74ddf862019-09-12 17:12:13 +0800184{
185 auto getter = std::make_shared<GetSensorConfiguration>(
186 dbusConnection,
187 std::move([](const ManagedObjectType& sensorConfigurations) {
188 // Get NIC name and save to map
189 lanInfoMap.clear();
190 for (const std::pair<sdbusplus::message::object_path, SensorData>&
191 sensor : sensorConfigurations)
192 {
193 const std::pair<
194 std::string,
195 boost::container::flat_map<std::string, BasicVariantType>>*
196 baseConfiguration = nullptr;
197
198 // find base configuration
199 auto sensorBase = sensor.second.find(nicType);
200 if (sensorBase == sensor.second.end())
201 {
202 continue;
203 }
204 baseConfiguration = &(*sensorBase);
205
206 auto findEthIndex = baseConfiguration->second.find("EthIndex");
207 auto findName = baseConfiguration->second.find("Name");
208
209 if (findEthIndex != baseConfiguration->second.end() &&
210 findName != baseConfiguration->second.end())
211 {
212 auto* pEthIndex =
213 std::get_if<uint64_t>(&findEthIndex->second);
214 auto* pName = std::get_if<std::string>(&findName->second);
215 if (pEthIndex != nullptr && pName != nullptr)
216 {
217 lanInfoMap[*pEthIndex] = *pName;
218 if (debugLanLeash)
219 {
220 std::cout << "find name of eth" << *pEthIndex
221 << " is " << *pName << "\n";
222 }
223 }
224 }
225 }
226
227 if (lanInfoMap.size() == 0)
228 {
229 std::cerr << "can't find matched NIC name. \n";
230 }
231 }));
232
233 getter->getConfiguration(
234 std::vector<std::string>{nicTypes.begin(), nicTypes.end()});
235}
236
Qiang XU88b7f282019-08-14 22:51:43 +0800237static void processLanStatusChange(sdbusplus::message::message& message)
238{
239 const std::string& pathName = message.get_path();
240 std::string interfaceName;
241 boost::container::flat_map<std::string, BasicVariantType> properties;
242 message.read(interfaceName, properties);
243
244 auto findStateProperty = properties.find("OperationalState");
245 if (findStateProperty == properties.end())
246 {
247 return;
248 }
Qiang XU74ddf862019-09-12 17:12:13 +0800249 std::string* pState =
250 std::get_if<std::string>(&(findStateProperty->second));
Qiang XU88b7f282019-08-14 22:51:43 +0800251 if (pState == nullptr)
252 {
253 std::cerr << "invalid OperationalState \n";
254 return;
255 }
256
257 bool newLanConnected = (*pState == "routable" || *pState == "carrier" ||
258 *pState == "degraded");
259
260 // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500261 size_t pos = pathName.find("/_");
Qiang XU88b7f282019-08-14 22:51:43 +0800262 if (pos == std::string::npos || pathName.length() <= pos + 2)
263 {
264 std::cerr << "unexpected path name " << pathName << "\n";
265 return;
266 }
267 std::string suffixStr = pathName.substr(pos + 2);
268
269 auto findEthNum = pathSuffixMap.find(suffixStr);
270 if (findEthNum == pathSuffixMap.end())
271 {
272 std::cerr << "unexpected eth for suffixStr " << suffixStr << "\n";
273 return;
274 }
275 int ethNum = findEthNum->second;
Qiang XU74ddf862019-09-12 17:12:13 +0800276
277 // get lan status from map
Qiang XU88b7f282019-08-14 22:51:43 +0800278 auto findLanStatus = lanStatusMap.find(ethNum);
279 if (findLanStatus == lanStatusMap.end())
280 {
281 std::cerr << "unexpected eth " << ethNum << " in lanStatusMap \n";
282 return;
283 }
284 bool oldLanConnected = findLanStatus->second;
285
Qiang XU74ddf862019-09-12 17:12:13 +0800286 // get lan info from map
287 std::string lanInfo = "";
288 if (lanInfoMap.size() > 0)
289 {
290 auto findLanInfo = lanInfoMap.find(ethNum);
291 if (findLanInfo == lanInfoMap.end())
292 {
293 std::cerr << "unexpected eth " << ethNum << " in lanInfoMap \n";
294 }
295 else
296 {
297 lanInfo = "(" + findLanInfo->second + ")";
298 }
299 }
300
Qiang XU88b7f282019-08-14 22:51:43 +0800301 if (debugLanLeash)
302 {
303 std::cout << "ethNum = " << ethNum << ", state = " << *pState
304 << ", oldLanConnected = "
305 << (oldLanConnected ? "true" : "false")
306 << ", newLanConnected = "
307 << (newLanConnected ? "true" : "false") << "\n";
308 }
309
310 if (oldLanConnected != newLanConnected)
311 {
Qiang XU74ddf862019-09-12 17:12:13 +0800312 std::string strEthNum = "eth" + std::to_string(ethNum) + lanInfo;
313 std::string strEvent = strEthNum + " LAN leash " +
314 (newLanConnected ? "connected" : "lost");
315 std::string strMsgId =
316 newLanConnected ? "OpenBMC.0.1.LanRegained" : "OpenBMC.0.1.LanLost";
317 sd_journal_send("MESSAGE=%s", strEvent.c_str(), "PRIORITY=%i", LOG_INFO,
Qiang XU88b7f282019-08-14 22:51:43 +0800318 "REDFISH_MESSAGE_ID=%s", strMsgId.c_str(),
Qiang XU74ddf862019-09-12 17:12:13 +0800319 "REDFISH_MESSAGE_ARGS=%s", strEthNum.c_str(), NULL);
Qiang XU88b7f282019-08-14 22:51:43 +0800320 lanStatusMap[ethNum] = newLanConnected;
321 if (debugLanLeash)
322 {
Qiang XU74ddf862019-09-12 17:12:13 +0800323 std::cout << "log redfish event: " << strEvent << "\n";
Qiang XU88b7f282019-08-14 22:51:43 +0800324 }
325 }
326}
327
Lei YUdd68d4a2021-03-16 22:17:23 +0800328/** @brief Initialize the lan status.
329 *
330 * @return true on success and false on failure
331 */
Lei YUba637932021-03-17 10:35:00 +0800332static bool initializeLanStatus(
333 const std::shared_ptr<sdbusplus::asio::connection>& conn)
Qiang XU88b7f282019-08-14 22:51:43 +0800334{
Qiang XU74ddf862019-09-12 17:12:13 +0800335 // init lan port name from configuration
336 getNicNameInfo(conn);
337
338 // get eth info from sysfs
Qiang XU88b7f282019-08-14 22:51:43 +0800339 std::vector<fs::path> files;
340 if (!findFiles(fs::path("/sys/class/net/"), R"(eth\d+/ifindex)", files))
341 {
342 std::cerr << "No eth in system\n";
Lei YUdd68d4a2021-03-16 22:17:23 +0800343 return false;
Qiang XU88b7f282019-08-14 22:51:43 +0800344 }
345
346 // iterate through all found eth files, and save ifindex
Ed Tanous8a57ec02020-10-09 12:46:52 -0700347 for (const fs::path& fileName : files)
Qiang XU88b7f282019-08-14 22:51:43 +0800348 {
349 if (debugLanLeash)
350 {
351 std::cout << "Reading " << fileName << "\n";
352 }
353 std::ifstream sysFile(fileName);
354 if (!sysFile.good())
355 {
356 std::cerr << "Failure reading " << fileName << "\n";
357 continue;
358 }
359 std::string line;
360 getline(sysFile, line);
361 const uint8_t ifindex = std::stoi(line);
362 // pathSuffix is ASCII of ifindex
363 const std::string& pathSuffix = std::to_string(ifindex + 30);
364
365 // extract ethNum
366 const std::string& fileStr = fileName.string();
367 const int pos = fileStr.find("eth");
368 const std::string& ethNumStr = fileStr.substr(pos + 3);
369 int ethNum = 0;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700370 std::from_chars_result r = std::from_chars(
371 ethNumStr.data(), ethNumStr.data() + ethNumStr.size(), ethNum);
372 if (r.ec != std::errc())
Qiang XU88b7f282019-08-14 22:51:43 +0800373 {
374 std::cerr << "invalid ethNum string: " << ethNumStr << "\n";
375 continue;
376 }
377
378 // save pathSuffix
379 pathSuffixMap[pathSuffix] = ethNum;
380 if (debugLanLeash)
381 {
382 std::cout << "ethNum = " << std::to_string(ethNum)
383 << ", ifindex = " << line
384 << ", pathSuffix = " << pathSuffix << "\n";
385 }
386
Qiang XU74ddf862019-09-12 17:12:13 +0800387 // init lan connected status from networkd
Qiang XU88b7f282019-08-14 22:51:43 +0800388 conn->async_method_call(
389 [ethNum](boost::system::error_code ec,
390 const std::variant<std::string>& property) {
Qiang XU74ddf862019-09-12 17:12:13 +0800391 lanStatusMap[ethNum] = false;
Qiang XU88b7f282019-08-14 22:51:43 +0800392 if (ec)
393 {
Qiang XU74ddf862019-09-12 17:12:13 +0800394 std::cerr << "Error reading init status of eth" << ethNum
395 << "\n";
Qiang XU88b7f282019-08-14 22:51:43 +0800396 return;
397 }
398 const std::string* pState = std::get_if<std::string>(&property);
399 if (pState == nullptr)
400 {
401 std::cerr << "Unable to read lan status value\n";
402 return;
403 }
404 bool isLanConnected =
405 (*pState == "routable" || *pState == "carrier" ||
406 *pState == "degraded");
407 if (debugLanLeash)
408 {
409 std::cout << "ethNum = " << std::to_string(ethNum)
410 << ", init LAN status = "
411 << (isLanConnected ? "true" : "false") << "\n";
412 }
413 lanStatusMap[ethNum] = isLanConnected;
414 },
415 "org.freedesktop.network1",
416 "/org/freedesktop/network1/link/_" + pathSuffix,
417 "org.freedesktop.DBus.Properties", "Get",
418 "org.freedesktop.network1.Link", "OperationalState");
419 }
Lei YUdd68d4a2021-03-16 22:17:23 +0800420 return true;
Qiang XU88b7f282019-08-14 22:51:43 +0800421}
422
Qiang XUe28d1fa2019-02-27 13:50:56 +0800423int main()
424{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800425 int busId = -1;
426 int slaveAddr = -1;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800427 bool gpioInverted = false;
428 IntrusionSensorType type = IntrusionSensorType::gpio;
429
430 // setup connection to dbus
431 boost::asio::io_service io;
432 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
433 auto objServer = sdbusplus::asio::object_server(systemBus);
434
435 // setup object server, define interface
436 systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
437
438 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceChassis =
439 objServer.add_interface(
440 "/xyz/openbmc_project/Intrusion/Chassis_Intrusion",
441 "xyz.openbmc_project.Chassis.Intrusion");
442
443 ChassisIntrusionSensor chassisIntrusionSensor(io, ifaceChassis);
444
445 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800446 &gpioInverted))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800447 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800448 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioInverted);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800449 }
450
451 // callback to handle configuration change
452 std::function<void(sdbusplus::message::message&)> eventHandler =
453 [&](sdbusplus::message::message& message) {
454 if (message.is_method_error())
455 {
456 std::cerr << "callback method error\n";
457 return;
458 }
459
460 std::cout << "rescan due to configuration change \n";
461 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800462 &gpioInverted))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800463 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800464 chassisIntrusionSensor.start(type, busId, slaveAddr,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800465 gpioInverted);
466 }
467 };
468
Lei YUdd68d4a2021-03-16 22:17:23 +0800469 auto eventMatch = std::make_unique<sdbusplus::bus::match::match>(
Qiang XUe28d1fa2019-02-27 13:50:56 +0800470 static_cast<sdbusplus::bus::bus&>(*systemBus),
471 "type='signal',member='PropertiesChanged',path_namespace='" +
472 std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
473 eventHandler);
474
Lei YUdd68d4a2021-03-16 22:17:23 +0800475 if (initializeLanStatus(systemBus))
476 {
477 // add match to monitor lan status change
478 sdbusplus::bus::match::match lanStatusMatch(
479 static_cast<sdbusplus::bus::bus&>(*systemBus),
480 "type='signal', member='PropertiesChanged',"
481 "arg0namespace='org.freedesktop.network1.Link'",
482 [](sdbusplus::message::message& msg) {
483 processLanStatusChange(msg);
484 });
485
486 // add match to monitor entity manager signal about nic name config
487 // change
488 sdbusplus::bus::match::match lanConfigMatch(
489 static_cast<sdbusplus::bus::bus&>(*systemBus),
490 "type='signal', member='PropertiesChanged',path_namespace='" +
491 std::string(inventoryPath) + "',arg0namespace='" + nicType +
492 "'",
493 [&systemBus](sdbusplus::message::message& msg) {
494 if (msg.is_method_error())
495 {
496 std::cerr << "callback method error\n";
497 return;
498 }
499 getNicNameInfo(systemBus);
500 });
501 }
Qiang XU88b7f282019-08-14 22:51:43 +0800502
Qiang XUe28d1fa2019-02-27 13:50:56 +0800503 io.run();
504
505 return 0;
506}