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