blob: dab04ef596227f79e94a5e64a1257526979dafb5 [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
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "ChassisIntrusionSensor.hpp"
18#include "Utils.hpp"
19
Qiang XU88b7f282019-08-14 22:51:43 +080020#include <systemd/sd-journal.h>
21
Qiang XUe28d1fa2019-02-27 13:50:56 +080022#include <boost/algorithm/string/predicate.hpp>
23#include <boost/asio.hpp>
24#include <chrono>
25#include <ctime>
Qiang XU88b7f282019-08-14 22:51:43 +080026#include <fstream>
Qiang XUe28d1fa2019-02-27 13:50:56 +080027#include <iostream>
28#include <sdbusplus/asio/connection.hpp>
29#include <sdbusplus/asio/object_server.hpp>
30#include <sdbusplus/asio/sd_event.hpp>
31#include <sdbusplus/bus.hpp>
32#include <sdbusplus/exception.hpp>
33#include <sdbusplus/server.hpp>
34#include <sdbusplus/timer.hpp>
35
36static constexpr bool DEBUG = false;
37
38static constexpr const char* sensorType =
39 "xyz.openbmc_project.Configuration.ChassisIntrusionSensor";
Qiang XU74ddf862019-09-12 17:12:13 +080040static constexpr const char* nicType = "xyz.openbmc_project.Configuration.NIC";
41static constexpr std::array<const char*, 1> nicTypes = {nicType};
Qiang XUe28d1fa2019-02-27 13:50:56 +080042
Qiang XU88b7f282019-08-14 22:51:43 +080043namespace fs = std::filesystem;
44
Qiang XUe28d1fa2019-02-27 13:50:56 +080045static bool getIntrusionSensorConfig(
46 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
47 IntrusionSensorType* pType, int* pBusId, int* pSlaveAddr, int* pGpioIndex,
48 bool* pGpioInverted)
49{
50 // find matched configuration according to sensor type
51 ManagedObjectType sensorConfigurations;
52 bool useCache = false;
53
54 if (!getSensorConfiguration(sensorType, dbusConnection,
55 sensorConfigurations, useCache))
56 {
57 std::cerr << "error communicating to entity manager\n";
58 return false;
59 }
60
61 const SensorData* sensorData = nullptr;
62 const std::pair<std::string,
63 boost::container::flat_map<std::string, BasicVariantType>>*
64 baseConfiguration = nullptr;
65
66 // Get bus and addr of matched configuration
67 for (const std::pair<sdbusplus::message::object_path, SensorData>& sensor :
68 sensorConfigurations)
69 {
70 baseConfiguration = nullptr;
71 sensorData = &(sensor.second);
72
73 // match sensor type
74 auto sensorBase = sensorData->find(sensorType);
75 if (sensorBase == sensorData->end())
76 {
77 std::cerr << "error finding base configuration \n";
78 continue;
79 }
80
81 baseConfiguration = &(*sensorBase);
82
83 // judge class, "Gpio" or "I2C"
84 auto findClass = baseConfiguration->second.find("Class");
85 if (findClass != baseConfiguration->second.end() &&
86 sdbusplus::message::variant_ns::get<std::string>(
87 findClass->second) == "Gpio")
88 {
89 *pType = IntrusionSensorType::gpio;
90 }
91 else
92 {
93 *pType = IntrusionSensorType::pch;
94 }
95
96 // case to find GPIO info
97 if (*pType == IntrusionSensorType::gpio)
98 {
99 auto gpioConfig =
100 sensorData->find(sensorType + std::string(".GpioIntrusion"));
101
102 if (gpioConfig == sensorData->end())
103 {
104 std::cerr
105 << "error finding GpioIntrusion info in configuration \n";
106 continue;
107 }
108
109 auto findGpioIndex = gpioConfig->second.find("Index");
110 auto findGpioPolarity = gpioConfig->second.find("Polarity");
111
112 if (findGpioIndex == gpioConfig->second.end() ||
113 findGpioPolarity == gpioConfig->second.end())
114 {
115 std::cerr << "error finding gpio info in configuration \n";
116 continue;
117 }
118
119 try
120 {
121 *pGpioIndex = sdbusplus::message::variant_ns::get<uint64_t>(
122 findGpioIndex->second);
123 *pGpioInverted =
124 (sdbusplus::message::variant_ns::get<std::string>(
125 findGpioPolarity->second) == "Low");
126 }
127 catch (const std::bad_variant_access& e)
128 {
129 std::cerr << "invalid value for gpio info in config. \n";
130 continue;
131 }
132
133 if (DEBUG)
134 {
135 std::cout << "find matched GPIO index " << *pGpioIndex
136 << ", polarity inverted flag is " << *pGpioInverted
137 << "\n";
138 }
139
140 return true;
141 }
142
143 // case to find I2C info
144 else if (*pType == IntrusionSensorType::pch)
145 {
146 auto findBus = baseConfiguration->second.find("Bus");
147 auto findAddress = baseConfiguration->second.find("Address");
148 if (findBus == baseConfiguration->second.end() ||
149 findAddress == baseConfiguration->second.end())
150 {
151 std::cerr << "error finding bus or address in configuration \n";
152 continue;
153 }
154
155 try
156 {
157 *pBusId = sdbusplus::message::variant_ns::get<uint64_t>(
158 findBus->second);
159 *pSlaveAddr = sdbusplus::message::variant_ns::get<uint64_t>(
160 findAddress->second);
161 }
162 catch (const std::bad_variant_access& e)
163 {
164 std::cerr << "invalid value for bus or address in config. \n";
165 continue;
166 }
167
168 if (DEBUG)
169 {
170 std::cout << "find matched bus " << *pBusId
171 << ", matched slave addr " << *pSlaveAddr << "\n";
172 }
173 return true;
174 }
175 }
176
Qiang XU74ddf862019-09-12 17:12:13 +0800177 std::cerr << "can't find matched I2C or GPIO configuration for intrusion "
178 "sensor. \n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800179 *pBusId = -1;
180 *pSlaveAddr = -1;
181 *pGpioIndex = -1;
182 return false;
183}
184
Qiang XU88b7f282019-08-14 22:51:43 +0800185static constexpr bool debugLanLeash = false;
186boost::container::flat_map<int, bool> lanStatusMap;
Qiang XU74ddf862019-09-12 17:12:13 +0800187boost::container::flat_map<int, std::string> lanInfoMap;
Qiang XU88b7f282019-08-14 22:51:43 +0800188boost::container::flat_map<std::string, int> pathSuffixMap;
189
Qiang XU74ddf862019-09-12 17:12:13 +0800190static void
191 getNicNameInfo(std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
192{
193 auto getter = std::make_shared<GetSensorConfiguration>(
194 dbusConnection,
195 std::move([](const ManagedObjectType& sensorConfigurations) {
196 // Get NIC name and save to map
197 lanInfoMap.clear();
198 for (const std::pair<sdbusplus::message::object_path, SensorData>&
199 sensor : sensorConfigurations)
200 {
201 const std::pair<
202 std::string,
203 boost::container::flat_map<std::string, BasicVariantType>>*
204 baseConfiguration = nullptr;
205
206 // find base configuration
207 auto sensorBase = sensor.second.find(nicType);
208 if (sensorBase == sensor.second.end())
209 {
210 continue;
211 }
212 baseConfiguration = &(*sensorBase);
213
214 auto findEthIndex = baseConfiguration->second.find("EthIndex");
215 auto findName = baseConfiguration->second.find("Name");
216
217 if (findEthIndex != baseConfiguration->second.end() &&
218 findName != baseConfiguration->second.end())
219 {
220 auto* pEthIndex =
221 std::get_if<uint64_t>(&findEthIndex->second);
222 auto* pName = std::get_if<std::string>(&findName->second);
223 if (pEthIndex != nullptr && pName != nullptr)
224 {
225 lanInfoMap[*pEthIndex] = *pName;
226 if (debugLanLeash)
227 {
228 std::cout << "find name of eth" << *pEthIndex
229 << " is " << *pName << "\n";
230 }
231 }
232 }
233 }
234
235 if (lanInfoMap.size() == 0)
236 {
237 std::cerr << "can't find matched NIC name. \n";
238 }
239 }));
240
241 getter->getConfiguration(
242 std::vector<std::string>{nicTypes.begin(), nicTypes.end()});
243}
244
Qiang XU88b7f282019-08-14 22:51:43 +0800245static void processLanStatusChange(sdbusplus::message::message& message)
246{
247 const std::string& pathName = message.get_path();
248 std::string interfaceName;
249 boost::container::flat_map<std::string, BasicVariantType> properties;
250 message.read(interfaceName, properties);
251
252 auto findStateProperty = properties.find("OperationalState");
253 if (findStateProperty == properties.end())
254 {
255 return;
256 }
Qiang XU74ddf862019-09-12 17:12:13 +0800257 std::string* pState =
258 std::get_if<std::string>(&(findStateProperty->second));
Qiang XU88b7f282019-08-14 22:51:43 +0800259 if (pState == nullptr)
260 {
261 std::cerr << "invalid OperationalState \n";
262 return;
263 }
264
265 bool newLanConnected = (*pState == "routable" || *pState == "carrier" ||
266 *pState == "degraded");
267
268 // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0
269 int pos = pathName.find("/_");
270 if (pos == std::string::npos || pathName.length() <= pos + 2)
271 {
272 std::cerr << "unexpected path name " << pathName << "\n";
273 return;
274 }
275 std::string suffixStr = pathName.substr(pos + 2);
276
277 auto findEthNum = pathSuffixMap.find(suffixStr);
278 if (findEthNum == pathSuffixMap.end())
279 {
280 std::cerr << "unexpected eth for suffixStr " << suffixStr << "\n";
281 return;
282 }
283 int ethNum = findEthNum->second;
Qiang XU74ddf862019-09-12 17:12:13 +0800284
285 // get lan status from map
Qiang XU88b7f282019-08-14 22:51:43 +0800286 auto findLanStatus = lanStatusMap.find(ethNum);
287 if (findLanStatus == lanStatusMap.end())
288 {
289 std::cerr << "unexpected eth " << ethNum << " in lanStatusMap \n";
290 return;
291 }
292 bool oldLanConnected = findLanStatus->second;
293
Qiang XU74ddf862019-09-12 17:12:13 +0800294 // get lan info from map
295 std::string lanInfo = "";
296 if (lanInfoMap.size() > 0)
297 {
298 auto findLanInfo = lanInfoMap.find(ethNum);
299 if (findLanInfo == lanInfoMap.end())
300 {
301 std::cerr << "unexpected eth " << ethNum << " in lanInfoMap \n";
302 }
303 else
304 {
305 lanInfo = "(" + findLanInfo->second + ")";
306 }
307 }
308
Qiang XU88b7f282019-08-14 22:51:43 +0800309 if (debugLanLeash)
310 {
311 std::cout << "ethNum = " << ethNum << ", state = " << *pState
312 << ", oldLanConnected = "
313 << (oldLanConnected ? "true" : "false")
314 << ", newLanConnected = "
315 << (newLanConnected ? "true" : "false") << "\n";
316 }
317
318 if (oldLanConnected != newLanConnected)
319 {
Qiang XU74ddf862019-09-12 17:12:13 +0800320 std::string strEthNum = "eth" + std::to_string(ethNum) + lanInfo;
321 std::string strEvent = strEthNum + " LAN leash " +
322 (newLanConnected ? "connected" : "lost");
323 std::string strMsgId =
324 newLanConnected ? "OpenBMC.0.1.LanRegained" : "OpenBMC.0.1.LanLost";
325 sd_journal_send("MESSAGE=%s", strEvent.c_str(), "PRIORITY=%i", LOG_INFO,
Qiang XU88b7f282019-08-14 22:51:43 +0800326 "REDFISH_MESSAGE_ID=%s", strMsgId.c_str(),
Qiang XU74ddf862019-09-12 17:12:13 +0800327 "REDFISH_MESSAGE_ARGS=%s", strEthNum.c_str(), NULL);
Qiang XU88b7f282019-08-14 22:51:43 +0800328 lanStatusMap[ethNum] = newLanConnected;
329 if (debugLanLeash)
330 {
Qiang XU74ddf862019-09-12 17:12:13 +0800331 std::cout << "log redfish event: " << strEvent << "\n";
Qiang XU88b7f282019-08-14 22:51:43 +0800332 }
333 }
334}
335
336static void
337 monitorLanStatusChange(std::shared_ptr<sdbusplus::asio::connection> conn)
338{
Qiang XU74ddf862019-09-12 17:12:13 +0800339 // init lan port name from configuration
340 getNicNameInfo(conn);
341
342 // get eth info from sysfs
Qiang XU88b7f282019-08-14 22:51:43 +0800343 std::vector<fs::path> files;
344 if (!findFiles(fs::path("/sys/class/net/"), R"(eth\d+/ifindex)", files))
345 {
346 std::cerr << "No eth in system\n";
347 return;
348 }
349
350 // iterate through all found eth files, and save ifindex
351 for (auto& fileName : files)
352 {
353 if (debugLanLeash)
354 {
355 std::cout << "Reading " << fileName << "\n";
356 }
357 std::ifstream sysFile(fileName);
358 if (!sysFile.good())
359 {
360 std::cerr << "Failure reading " << fileName << "\n";
361 continue;
362 }
363 std::string line;
364 getline(sysFile, line);
365 const uint8_t ifindex = std::stoi(line);
366 // pathSuffix is ASCII of ifindex
367 const std::string& pathSuffix = std::to_string(ifindex + 30);
368
369 // extract ethNum
370 const std::string& fileStr = fileName.string();
371 const int pos = fileStr.find("eth");
372 const std::string& ethNumStr = fileStr.substr(pos + 3);
373 int ethNum = 0;
374 try
375 {
376 ethNum = std::stoul(ethNumStr);
377 }
378 catch (const std::invalid_argument& err)
379 {
380 std::cerr << "invalid ethNum string: " << ethNumStr << "\n";
381 continue;
382 }
383
384 // save pathSuffix
385 pathSuffixMap[pathSuffix] = ethNum;
386 if (debugLanLeash)
387 {
388 std::cout << "ethNum = " << std::to_string(ethNum)
389 << ", ifindex = " << line
390 << ", pathSuffix = " << pathSuffix << "\n";
391 }
392
Qiang XU74ddf862019-09-12 17:12:13 +0800393 // init lan connected status from networkd
Qiang XU88b7f282019-08-14 22:51:43 +0800394 conn->async_method_call(
395 [ethNum](boost::system::error_code ec,
396 const std::variant<std::string>& property) {
Qiang XU74ddf862019-09-12 17:12:13 +0800397 lanStatusMap[ethNum] = false;
Qiang XU88b7f282019-08-14 22:51:43 +0800398 if (ec)
399 {
Qiang XU74ddf862019-09-12 17:12:13 +0800400 std::cerr << "Error reading init status of eth" << ethNum
401 << "\n";
Qiang XU88b7f282019-08-14 22:51:43 +0800402 return;
403 }
404 const std::string* pState = std::get_if<std::string>(&property);
405 if (pState == nullptr)
406 {
407 std::cerr << "Unable to read lan status value\n";
408 return;
409 }
410 bool isLanConnected =
411 (*pState == "routable" || *pState == "carrier" ||
412 *pState == "degraded");
413 if (debugLanLeash)
414 {
415 std::cout << "ethNum = " << std::to_string(ethNum)
416 << ", init LAN status = "
417 << (isLanConnected ? "true" : "false") << "\n";
418 }
419 lanStatusMap[ethNum] = isLanConnected;
420 },
421 "org.freedesktop.network1",
422 "/org/freedesktop/network1/link/_" + pathSuffix,
423 "org.freedesktop.DBus.Properties", "Get",
424 "org.freedesktop.network1.Link", "OperationalState");
425 }
426
427 // add match to monitor lan status change
428 static sdbusplus::bus::match::match match(
429 static_cast<sdbusplus::bus::bus&>(*conn),
430 "type='signal', member='PropertiesChanged',"
431 "arg0namespace='org.freedesktop.network1.Link'",
432 [](sdbusplus::message::message& msg) { processLanStatusChange(msg); });
Qiang XU74ddf862019-09-12 17:12:13 +0800433
434 // add match to monitor entity manager signal about nic name config change
435 static sdbusplus::bus::match::match match2(
436 static_cast<sdbusplus::bus::bus&>(*conn),
437 "type='signal', member='PropertiesChanged',path_namespace='" +
438 std::string(inventoryPath) + "',arg0namespace='" + nicType + "'",
439 [&conn](sdbusplus::message::message& msg) {
440 if (msg.is_method_error())
441 {
442 std::cerr << "callback method error\n";
443 return;
444 }
445 getNicNameInfo(conn);
446 });
Qiang XU88b7f282019-08-14 22:51:43 +0800447}
448
Qiang XUe28d1fa2019-02-27 13:50:56 +0800449int main()
450{
451 int busId = -1, slaveAddr = -1, gpioIndex = -1;
452 bool gpioInverted = false;
453 IntrusionSensorType type = IntrusionSensorType::gpio;
454
455 // setup connection to dbus
456 boost::asio::io_service io;
457 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
458 auto objServer = sdbusplus::asio::object_server(systemBus);
459
460 // setup object server, define interface
461 systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
462
463 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceChassis =
464 objServer.add_interface(
465 "/xyz/openbmc_project/Intrusion/Chassis_Intrusion",
466 "xyz.openbmc_project.Chassis.Intrusion");
467
468 ChassisIntrusionSensor chassisIntrusionSensor(io, ifaceChassis);
469
470 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
471 &gpioIndex, &gpioInverted))
472 {
473 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioIndex,
474 gpioInverted);
475 }
476
477 // callback to handle configuration change
478 std::function<void(sdbusplus::message::message&)> eventHandler =
479 [&](sdbusplus::message::message& message) {
480 if (message.is_method_error())
481 {
482 std::cerr << "callback method error\n";
483 return;
484 }
485
486 std::cout << "rescan due to configuration change \n";
487 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
488 &gpioIndex, &gpioInverted))
489 {
490 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioIndex,
491 gpioInverted);
492 }
493 };
494
495 auto match = std::make_unique<sdbusplus::bus::match::match>(
496 static_cast<sdbusplus::bus::bus&>(*systemBus),
497 "type='signal',member='PropertiesChanged',path_namespace='" +
498 std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
499 eventHandler);
500
Qiang XU88b7f282019-08-14 22:51:43 +0800501 monitorLanStatusChange(systemBus);
502
Qiang XUe28d1fa2019-02-27 13:50:56 +0800503 io.run();
504
505 return 0;
506}