blob: b89f6280b84f32e0f2749561aa48504caa7501c5 [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
Patrick Venture96e97db2019-10-31 13:44:38 -070022#include <array>
Qiang XUe28d1fa2019-02-27 13:50:56 +080023#include <boost/algorithm/string/predicate.hpp>
24#include <boost/asio.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070025#include <boost/container/flat_map.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080026#include <chrono>
27#include <ctime>
Qiang XU88b7f282019-08-14 22:51:43 +080028#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <functional>
Qiang XUe28d1fa2019-02-27 13:50:56 +080030#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070031#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080032#include <sdbusplus/asio/connection.hpp>
33#include <sdbusplus/asio/object_server.hpp>
34#include <sdbusplus/asio/sd_event.hpp>
35#include <sdbusplus/bus.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070036#include <sdbusplus/bus/match.hpp>
Qiang XUe28d1fa2019-02-27 13:50:56 +080037#include <sdbusplus/exception.hpp>
38#include <sdbusplus/server.hpp>
39#include <sdbusplus/timer.hpp>
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
45static constexpr bool DEBUG = false;
46
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";
50static constexpr std::array<const char*, 1> nicTypes = {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(
55 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() &&
95 sdbusplus::message::variant_ns::get<std::string>(
96 findClass->second) == "Gpio")
97 {
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 =
120 (sdbusplus::message::variant_ns::get<std::string>(
121 findGpioPolarity->second) == "Low");
122 }
123 catch (const std::bad_variant_access& e)
124 {
125 std::cerr << "invalid value for gpio info in config. \n";
126 continue;
127 }
128
129 if (DEBUG)
130 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800131 std::cout << "find chassis intrusion sensor polarity inverted "
132 "flag is "
133 << *pGpioInverted << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800134 }
135
136 return true;
137 }
138
139 // case to find I2C info
140 else if (*pType == IntrusionSensorType::pch)
141 {
142 auto findBus = baseConfiguration->second.find("Bus");
143 auto findAddress = baseConfiguration->second.find("Address");
144 if (findBus == baseConfiguration->second.end() ||
145 findAddress == baseConfiguration->second.end())
146 {
147 std::cerr << "error finding bus or address in configuration \n";
148 continue;
149 }
150
151 try
152 {
153 *pBusId = sdbusplus::message::variant_ns::get<uint64_t>(
154 findBus->second);
155 *pSlaveAddr = sdbusplus::message::variant_ns::get<uint64_t>(
156 findAddress->second);
157 }
158 catch (const std::bad_variant_access& e)
159 {
160 std::cerr << "invalid value for bus or address in config. \n";
161 continue;
162 }
163
164 if (DEBUG)
165 {
166 std::cout << "find matched bus " << *pBusId
167 << ", matched slave addr " << *pSlaveAddr << "\n";
168 }
169 return true;
170 }
171 }
172
Qiang XU74ddf862019-09-12 17:12:13 +0800173 std::cerr << "can't find matched I2C or GPIO configuration for intrusion "
174 "sensor. \n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800175 *pBusId = -1;
176 *pSlaveAddr = -1;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800177 return false;
178}
179
Qiang XU88b7f282019-08-14 22:51:43 +0800180static constexpr bool debugLanLeash = false;
181boost::container::flat_map<int, bool> lanStatusMap;
Qiang XU74ddf862019-09-12 17:12:13 +0800182boost::container::flat_map<int, std::string> lanInfoMap;
Qiang XU88b7f282019-08-14 22:51:43 +0800183boost::container::flat_map<std::string, int> pathSuffixMap;
184
Qiang XU74ddf862019-09-12 17:12:13 +0800185static void
186 getNicNameInfo(std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
187{
188 auto getter = std::make_shared<GetSensorConfiguration>(
189 dbusConnection,
190 std::move([](const ManagedObjectType& sensorConfigurations) {
191 // Get NIC name and save to map
192 lanInfoMap.clear();
193 for (const std::pair<sdbusplus::message::object_path, SensorData>&
194 sensor : sensorConfigurations)
195 {
196 const std::pair<
197 std::string,
198 boost::container::flat_map<std::string, BasicVariantType>>*
199 baseConfiguration = nullptr;
200
201 // find base configuration
202 auto sensorBase = sensor.second.find(nicType);
203 if (sensorBase == sensor.second.end())
204 {
205 continue;
206 }
207 baseConfiguration = &(*sensorBase);
208
209 auto findEthIndex = baseConfiguration->second.find("EthIndex");
210 auto findName = baseConfiguration->second.find("Name");
211
212 if (findEthIndex != baseConfiguration->second.end() &&
213 findName != baseConfiguration->second.end())
214 {
215 auto* pEthIndex =
216 std::get_if<uint64_t>(&findEthIndex->second);
217 auto* pName = std::get_if<std::string>(&findName->second);
218 if (pEthIndex != nullptr && pName != nullptr)
219 {
220 lanInfoMap[*pEthIndex] = *pName;
221 if (debugLanLeash)
222 {
223 std::cout << "find name of eth" << *pEthIndex
224 << " is " << *pName << "\n";
225 }
226 }
227 }
228 }
229
230 if (lanInfoMap.size() == 0)
231 {
232 std::cerr << "can't find matched NIC name. \n";
233 }
234 }));
235
236 getter->getConfiguration(
237 std::vector<std::string>{nicTypes.begin(), nicTypes.end()});
238}
239
Qiang XU88b7f282019-08-14 22:51:43 +0800240static void processLanStatusChange(sdbusplus::message::message& message)
241{
242 const std::string& pathName = message.get_path();
243 std::string interfaceName;
244 boost::container::flat_map<std::string, BasicVariantType> properties;
245 message.read(interfaceName, properties);
246
247 auto findStateProperty = properties.find("OperationalState");
248 if (findStateProperty == properties.end())
249 {
250 return;
251 }
Qiang XU74ddf862019-09-12 17:12:13 +0800252 std::string* pState =
253 std::get_if<std::string>(&(findStateProperty->second));
Qiang XU88b7f282019-08-14 22:51:43 +0800254 if (pState == nullptr)
255 {
256 std::cerr << "invalid OperationalState \n";
257 return;
258 }
259
260 bool newLanConnected = (*pState == "routable" || *pState == "carrier" ||
261 *pState == "degraded");
262
263 // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500264 size_t pos = pathName.find("/_");
Qiang XU88b7f282019-08-14 22:51:43 +0800265 if (pos == std::string::npos || pathName.length() <= pos + 2)
266 {
267 std::cerr << "unexpected path name " << pathName << "\n";
268 return;
269 }
270 std::string suffixStr = pathName.substr(pos + 2);
271
272 auto findEthNum = pathSuffixMap.find(suffixStr);
273 if (findEthNum == pathSuffixMap.end())
274 {
275 std::cerr << "unexpected eth for suffixStr " << suffixStr << "\n";
276 return;
277 }
278 int ethNum = findEthNum->second;
Qiang XU74ddf862019-09-12 17:12:13 +0800279
280 // get lan status from map
Qiang XU88b7f282019-08-14 22:51:43 +0800281 auto findLanStatus = lanStatusMap.find(ethNum);
282 if (findLanStatus == lanStatusMap.end())
283 {
284 std::cerr << "unexpected eth " << ethNum << " in lanStatusMap \n";
285 return;
286 }
287 bool oldLanConnected = findLanStatus->second;
288
Qiang XU74ddf862019-09-12 17:12:13 +0800289 // get lan info from map
290 std::string lanInfo = "";
291 if (lanInfoMap.size() > 0)
292 {
293 auto findLanInfo = lanInfoMap.find(ethNum);
294 if (findLanInfo == lanInfoMap.end())
295 {
296 std::cerr << "unexpected eth " << ethNum << " in lanInfoMap \n";
297 }
298 else
299 {
300 lanInfo = "(" + findLanInfo->second + ")";
301 }
302 }
303
Qiang XU88b7f282019-08-14 22:51:43 +0800304 if (debugLanLeash)
305 {
306 std::cout << "ethNum = " << ethNum << ", state = " << *pState
307 << ", oldLanConnected = "
308 << (oldLanConnected ? "true" : "false")
309 << ", newLanConnected = "
310 << (newLanConnected ? "true" : "false") << "\n";
311 }
312
313 if (oldLanConnected != newLanConnected)
314 {
Qiang XU74ddf862019-09-12 17:12:13 +0800315 std::string strEthNum = "eth" + std::to_string(ethNum) + lanInfo;
316 std::string strEvent = strEthNum + " LAN leash " +
317 (newLanConnected ? "connected" : "lost");
318 std::string strMsgId =
319 newLanConnected ? "OpenBMC.0.1.LanRegained" : "OpenBMC.0.1.LanLost";
320 sd_journal_send("MESSAGE=%s", strEvent.c_str(), "PRIORITY=%i", LOG_INFO,
Qiang XU88b7f282019-08-14 22:51:43 +0800321 "REDFISH_MESSAGE_ID=%s", strMsgId.c_str(),
Qiang XU74ddf862019-09-12 17:12:13 +0800322 "REDFISH_MESSAGE_ARGS=%s", strEthNum.c_str(), NULL);
Qiang XU88b7f282019-08-14 22:51:43 +0800323 lanStatusMap[ethNum] = newLanConnected;
324 if (debugLanLeash)
325 {
Qiang XU74ddf862019-09-12 17:12:13 +0800326 std::cout << "log redfish event: " << strEvent << "\n";
Qiang XU88b7f282019-08-14 22:51:43 +0800327 }
328 }
329}
330
331static void
332 monitorLanStatusChange(std::shared_ptr<sdbusplus::asio::connection> conn)
333{
Qiang XU74ddf862019-09-12 17:12:13 +0800334 // init lan port name from configuration
335 getNicNameInfo(conn);
336
337 // get eth info from sysfs
Qiang XU88b7f282019-08-14 22:51:43 +0800338 std::vector<fs::path> files;
339 if (!findFiles(fs::path("/sys/class/net/"), R"(eth\d+/ifindex)", files))
340 {
341 std::cerr << "No eth in system\n";
342 return;
343 }
344
345 // iterate through all found eth files, and save ifindex
346 for (auto& fileName : files)
347 {
348 if (debugLanLeash)
349 {
350 std::cout << "Reading " << fileName << "\n";
351 }
352 std::ifstream sysFile(fileName);
353 if (!sysFile.good())
354 {
355 std::cerr << "Failure reading " << fileName << "\n";
356 continue;
357 }
358 std::string line;
359 getline(sysFile, line);
360 const uint8_t ifindex = std::stoi(line);
361 // pathSuffix is ASCII of ifindex
362 const std::string& pathSuffix = std::to_string(ifindex + 30);
363
364 // extract ethNum
365 const std::string& fileStr = fileName.string();
366 const int pos = fileStr.find("eth");
367 const std::string& ethNumStr = fileStr.substr(pos + 3);
368 int ethNum = 0;
369 try
370 {
371 ethNum = std::stoul(ethNumStr);
372 }
373 catch (const std::invalid_argument& err)
374 {
375 std::cerr << "invalid ethNum string: " << ethNumStr << "\n";
376 continue;
377 }
378
379 // save pathSuffix
380 pathSuffixMap[pathSuffix] = ethNum;
381 if (debugLanLeash)
382 {
383 std::cout << "ethNum = " << std::to_string(ethNum)
384 << ", ifindex = " << line
385 << ", pathSuffix = " << pathSuffix << "\n";
386 }
387
Qiang XU74ddf862019-09-12 17:12:13 +0800388 // init lan connected status from networkd
Qiang XU88b7f282019-08-14 22:51:43 +0800389 conn->async_method_call(
390 [ethNum](boost::system::error_code ec,
391 const std::variant<std::string>& property) {
Qiang XU74ddf862019-09-12 17:12:13 +0800392 lanStatusMap[ethNum] = false;
Qiang XU88b7f282019-08-14 22:51:43 +0800393 if (ec)
394 {
Qiang XU74ddf862019-09-12 17:12:13 +0800395 std::cerr << "Error reading init status of eth" << ethNum
396 << "\n";
Qiang XU88b7f282019-08-14 22:51:43 +0800397 return;
398 }
399 const std::string* pState = std::get_if<std::string>(&property);
400 if (pState == nullptr)
401 {
402 std::cerr << "Unable to read lan status value\n";
403 return;
404 }
405 bool isLanConnected =
406 (*pState == "routable" || *pState == "carrier" ||
407 *pState == "degraded");
408 if (debugLanLeash)
409 {
410 std::cout << "ethNum = " << std::to_string(ethNum)
411 << ", init LAN status = "
412 << (isLanConnected ? "true" : "false") << "\n";
413 }
414 lanStatusMap[ethNum] = isLanConnected;
415 },
416 "org.freedesktop.network1",
417 "/org/freedesktop/network1/link/_" + pathSuffix,
418 "org.freedesktop.DBus.Properties", "Get",
419 "org.freedesktop.network1.Link", "OperationalState");
420 }
421
422 // add match to monitor lan status change
423 static sdbusplus::bus::match::match match(
424 static_cast<sdbusplus::bus::bus&>(*conn),
425 "type='signal', member='PropertiesChanged',"
426 "arg0namespace='org.freedesktop.network1.Link'",
427 [](sdbusplus::message::message& msg) { processLanStatusChange(msg); });
Qiang XU74ddf862019-09-12 17:12:13 +0800428
429 // add match to monitor entity manager signal about nic name config change
430 static sdbusplus::bus::match::match match2(
431 static_cast<sdbusplus::bus::bus&>(*conn),
432 "type='signal', member='PropertiesChanged',path_namespace='" +
433 std::string(inventoryPath) + "',arg0namespace='" + nicType + "'",
434 [&conn](sdbusplus::message::message& msg) {
435 if (msg.is_method_error())
436 {
437 std::cerr << "callback method error\n";
438 return;
439 }
440 getNicNameInfo(conn);
441 });
Qiang XU88b7f282019-08-14 22:51:43 +0800442}
443
Qiang XUe28d1fa2019-02-27 13:50:56 +0800444int main()
445{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800446 int busId = -1;
447 int slaveAddr = -1;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800448 bool gpioInverted = false;
449 IntrusionSensorType type = IntrusionSensorType::gpio;
450
451 // setup connection to dbus
452 boost::asio::io_service io;
453 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
454 auto objServer = sdbusplus::asio::object_server(systemBus);
455
456 // setup object server, define interface
457 systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
458
459 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceChassis =
460 objServer.add_interface(
461 "/xyz/openbmc_project/Intrusion/Chassis_Intrusion",
462 "xyz.openbmc_project.Chassis.Intrusion");
463
464 ChassisIntrusionSensor chassisIntrusionSensor(io, ifaceChassis);
465
466 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800467 &gpioInverted))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800468 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800469 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioInverted);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800470 }
471
472 // callback to handle configuration change
473 std::function<void(sdbusplus::message::message&)> eventHandler =
474 [&](sdbusplus::message::message& message) {
475 if (message.is_method_error())
476 {
477 std::cerr << "callback method error\n";
478 return;
479 }
480
481 std::cout << "rescan due to configuration change \n";
482 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800483 &gpioInverted))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800484 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800485 chassisIntrusionSensor.start(type, busId, slaveAddr,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800486 gpioInverted);
487 }
488 };
489
490 auto match = std::make_unique<sdbusplus::bus::match::match>(
491 static_cast<sdbusplus::bus::bus&>(*systemBus),
492 "type='signal',member='PropertiesChanged',path_namespace='" +
493 std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
494 eventHandler);
495
Qiang XU88b7f282019-08-14 22:51:43 +0800496 monitorLanStatusChange(systemBus);
497
Qiang XUe28d1fa2019-02-27 13:50:56 +0800498 io.run();
499
500 return 0;
501}