blob: 2c8628bfe1813657d3e1f63ae931e620de2c23f9 [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";
39
Qiang XU88b7f282019-08-14 22:51:43 +080040namespace fs = std::filesystem;
41
Qiang XUe28d1fa2019-02-27 13:50:56 +080042static bool getIntrusionSensorConfig(
43 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
44 IntrusionSensorType* pType, int* pBusId, int* pSlaveAddr, int* pGpioIndex,
45 bool* pGpioInverted)
46{
47 // find matched configuration according to sensor type
48 ManagedObjectType sensorConfigurations;
49 bool useCache = false;
50
51 if (!getSensorConfiguration(sensorType, dbusConnection,
52 sensorConfigurations, useCache))
53 {
54 std::cerr << "error communicating to entity manager\n";
55 return false;
56 }
57
58 const SensorData* sensorData = nullptr;
59 const std::pair<std::string,
60 boost::container::flat_map<std::string, BasicVariantType>>*
61 baseConfiguration = nullptr;
62
63 // Get bus and addr of matched configuration
64 for (const std::pair<sdbusplus::message::object_path, SensorData>& sensor :
65 sensorConfigurations)
66 {
67 baseConfiguration = nullptr;
68 sensorData = &(sensor.second);
69
70 // match sensor type
71 auto sensorBase = sensorData->find(sensorType);
72 if (sensorBase == sensorData->end())
73 {
74 std::cerr << "error finding base configuration \n";
75 continue;
76 }
77
78 baseConfiguration = &(*sensorBase);
79
80 // judge class, "Gpio" or "I2C"
81 auto findClass = baseConfiguration->second.find("Class");
82 if (findClass != baseConfiguration->second.end() &&
83 sdbusplus::message::variant_ns::get<std::string>(
84 findClass->second) == "Gpio")
85 {
86 *pType = IntrusionSensorType::gpio;
87 }
88 else
89 {
90 *pType = IntrusionSensorType::pch;
91 }
92
93 // case to find GPIO info
94 if (*pType == IntrusionSensorType::gpio)
95 {
96 auto gpioConfig =
97 sensorData->find(sensorType + std::string(".GpioIntrusion"));
98
99 if (gpioConfig == sensorData->end())
100 {
101 std::cerr
102 << "error finding GpioIntrusion info in configuration \n";
103 continue;
104 }
105
106 auto findGpioIndex = gpioConfig->second.find("Index");
107 auto findGpioPolarity = gpioConfig->second.find("Polarity");
108
109 if (findGpioIndex == gpioConfig->second.end() ||
110 findGpioPolarity == gpioConfig->second.end())
111 {
112 std::cerr << "error finding gpio info in configuration \n";
113 continue;
114 }
115
116 try
117 {
118 *pGpioIndex = sdbusplus::message::variant_ns::get<uint64_t>(
119 findGpioIndex->second);
120 *pGpioInverted =
121 (sdbusplus::message::variant_ns::get<std::string>(
122 findGpioPolarity->second) == "Low");
123 }
124 catch (const std::bad_variant_access& e)
125 {
126 std::cerr << "invalid value for gpio info in config. \n";
127 continue;
128 }
129
130 if (DEBUG)
131 {
132 std::cout << "find matched GPIO index " << *pGpioIndex
133 << ", polarity inverted flag is " << *pGpioInverted
134 << "\n";
135 }
136
137 return true;
138 }
139
140 // case to find I2C info
141 else if (*pType == IntrusionSensorType::pch)
142 {
143 auto findBus = baseConfiguration->second.find("Bus");
144 auto findAddress = baseConfiguration->second.find("Address");
145 if (findBus == baseConfiguration->second.end() ||
146 findAddress == baseConfiguration->second.end())
147 {
148 std::cerr << "error finding bus or address in configuration \n";
149 continue;
150 }
151
152 try
153 {
154 *pBusId = sdbusplus::message::variant_ns::get<uint64_t>(
155 findBus->second);
156 *pSlaveAddr = sdbusplus::message::variant_ns::get<uint64_t>(
157 findAddress->second);
158 }
159 catch (const std::bad_variant_access& e)
160 {
161 std::cerr << "invalid value for bus or address in config. \n";
162 continue;
163 }
164
165 if (DEBUG)
166 {
167 std::cout << "find matched bus " << *pBusId
168 << ", matched slave addr " << *pSlaveAddr << "\n";
169 }
170 return true;
171 }
172 }
173
174 std::cerr << "can't find matched I2C or GPIO configuration. \n";
175 *pBusId = -1;
176 *pSlaveAddr = -1;
177 *pGpioIndex = -1;
178 return false;
179}
180
Qiang XU88b7f282019-08-14 22:51:43 +0800181static constexpr bool debugLanLeash = false;
182boost::container::flat_map<int, bool> lanStatusMap;
183boost::container::flat_map<std::string, int> pathSuffixMap;
184
185static void processLanStatusChange(sdbusplus::message::message& message)
186{
187 const std::string& pathName = message.get_path();
188 std::string interfaceName;
189 boost::container::flat_map<std::string, BasicVariantType> properties;
190 message.read(interfaceName, properties);
191
192 auto findStateProperty = properties.find("OperationalState");
193 if (findStateProperty == properties.end())
194 {
195 return;
196 }
197 std::string* pState = sdbusplus::message::variant_ns::get_if<std::string>(
198 &(findStateProperty->second));
199 if (pState == nullptr)
200 {
201 std::cerr << "invalid OperationalState \n";
202 return;
203 }
204
205 bool newLanConnected = (*pState == "routable" || *pState == "carrier" ||
206 *pState == "degraded");
207
208 // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0
209 int pos = pathName.find("/_");
210 if (pos == std::string::npos || pathName.length() <= pos + 2)
211 {
212 std::cerr << "unexpected path name " << pathName << "\n";
213 return;
214 }
215 std::string suffixStr = pathName.substr(pos + 2);
216
217 auto findEthNum = pathSuffixMap.find(suffixStr);
218 if (findEthNum == pathSuffixMap.end())
219 {
220 std::cerr << "unexpected eth for suffixStr " << suffixStr << "\n";
221 return;
222 }
223 int ethNum = findEthNum->second;
224 auto findLanStatus = lanStatusMap.find(ethNum);
225 if (findLanStatus == lanStatusMap.end())
226 {
227 std::cerr << "unexpected eth " << ethNum << " in lanStatusMap \n";
228 return;
229 }
230 bool oldLanConnected = findLanStatus->second;
231
232 if (debugLanLeash)
233 {
234 std::cout << "ethNum = " << ethNum << ", state = " << *pState
235 << ", oldLanConnected = "
236 << (oldLanConnected ? "true" : "false")
237 << ", newLanConnected = "
238 << (newLanConnected ? "true" : "false") << "\n";
239 }
240
241 if (oldLanConnected != newLanConnected)
242 {
243 std::string strEthNum = "eth" + std::to_string(ethNum);
244 std::string strEvent = strEthNum + " LAN leash lost";
245 std::string strAssert = newLanConnected ? "de-asserted" : "asserted";
246 std::string strMsg = strEthNum + " is " +
247 (newLanConnected ? "connected" : "disconnected");
248 std::string strMsgId = "OpenBMC.0.1.PhysicalSecurity";
249 sd_journal_send("MESSAGE=%s", strMsg.c_str(), "PRIORITY=%i", LOG_INFO,
250 "REDFISH_MESSAGE_ID=%s", strMsgId.c_str(),
251 "REDFISH_MESSAGE_ARGS=%s,%s", strEvent.c_str(),
252 strAssert.c_str(), NULL);
253 lanStatusMap[ethNum] = newLanConnected;
254 if (debugLanLeash)
255 {
256 std::cout << "log redfish event: " << strMsg << "\n";
257 }
258 }
259}
260
261static void
262 monitorLanStatusChange(std::shared_ptr<sdbusplus::asio::connection> conn)
263{
264 std::vector<fs::path> files;
265 if (!findFiles(fs::path("/sys/class/net/"), R"(eth\d+/ifindex)", files))
266 {
267 std::cerr << "No eth in system\n";
268 return;
269 }
270
271 // iterate through all found eth files, and save ifindex
272 for (auto& fileName : files)
273 {
274 if (debugLanLeash)
275 {
276 std::cout << "Reading " << fileName << "\n";
277 }
278 std::ifstream sysFile(fileName);
279 if (!sysFile.good())
280 {
281 std::cerr << "Failure reading " << fileName << "\n";
282 continue;
283 }
284 std::string line;
285 getline(sysFile, line);
286 const uint8_t ifindex = std::stoi(line);
287 // pathSuffix is ASCII of ifindex
288 const std::string& pathSuffix = std::to_string(ifindex + 30);
289
290 // extract ethNum
291 const std::string& fileStr = fileName.string();
292 const int pos = fileStr.find("eth");
293 const std::string& ethNumStr = fileStr.substr(pos + 3);
294 int ethNum = 0;
295 try
296 {
297 ethNum = std::stoul(ethNumStr);
298 }
299 catch (const std::invalid_argument& err)
300 {
301 std::cerr << "invalid ethNum string: " << ethNumStr << "\n";
302 continue;
303 }
304
305 // save pathSuffix
306 pathSuffixMap[pathSuffix] = ethNum;
307 if (debugLanLeash)
308 {
309 std::cout << "ethNum = " << std::to_string(ethNum)
310 << ", ifindex = " << line
311 << ", pathSuffix = " << pathSuffix << "\n";
312 }
313
314 // init lan connected status
315 conn->async_method_call(
316 [ethNum](boost::system::error_code ec,
317 const std::variant<std::string>& property) {
318 if (ec)
319 {
320 return;
321 }
322 const std::string* pState = std::get_if<std::string>(&property);
323 if (pState == nullptr)
324 {
325 std::cerr << "Unable to read lan status value\n";
326 return;
327 }
328 bool isLanConnected =
329 (*pState == "routable" || *pState == "carrier" ||
330 *pState == "degraded");
331 if (debugLanLeash)
332 {
333 std::cout << "ethNum = " << std::to_string(ethNum)
334 << ", init LAN status = "
335 << (isLanConnected ? "true" : "false") << "\n";
336 }
337 lanStatusMap[ethNum] = isLanConnected;
338 },
339 "org.freedesktop.network1",
340 "/org/freedesktop/network1/link/_" + pathSuffix,
341 "org.freedesktop.DBus.Properties", "Get",
342 "org.freedesktop.network1.Link", "OperationalState");
343 }
344
345 // add match to monitor lan status change
346 static sdbusplus::bus::match::match match(
347 static_cast<sdbusplus::bus::bus&>(*conn),
348 "type='signal', member='PropertiesChanged',"
349 "arg0namespace='org.freedesktop.network1.Link'",
350 [](sdbusplus::message::message& msg) { processLanStatusChange(msg); });
351}
352
Qiang XUe28d1fa2019-02-27 13:50:56 +0800353int main()
354{
355 int busId = -1, slaveAddr = -1, gpioIndex = -1;
356 bool gpioInverted = false;
357 IntrusionSensorType type = IntrusionSensorType::gpio;
358
359 // setup connection to dbus
360 boost::asio::io_service io;
361 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
362 auto objServer = sdbusplus::asio::object_server(systemBus);
363
364 // setup object server, define interface
365 systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
366
367 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceChassis =
368 objServer.add_interface(
369 "/xyz/openbmc_project/Intrusion/Chassis_Intrusion",
370 "xyz.openbmc_project.Chassis.Intrusion");
371
372 ChassisIntrusionSensor chassisIntrusionSensor(io, ifaceChassis);
373
374 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
375 &gpioIndex, &gpioInverted))
376 {
377 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioIndex,
378 gpioInverted);
379 }
380
381 // callback to handle configuration change
382 std::function<void(sdbusplus::message::message&)> eventHandler =
383 [&](sdbusplus::message::message& message) {
384 if (message.is_method_error())
385 {
386 std::cerr << "callback method error\n";
387 return;
388 }
389
390 std::cout << "rescan due to configuration change \n";
391 if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
392 &gpioIndex, &gpioInverted))
393 {
394 chassisIntrusionSensor.start(type, busId, slaveAddr, gpioIndex,
395 gpioInverted);
396 }
397 };
398
399 auto match = std::make_unique<sdbusplus::bus::match::match>(
400 static_cast<sdbusplus::bus::bus&>(*systemBus),
401 "type='signal',member='PropertiesChanged',path_namespace='" +
402 std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
403 eventHandler);
404
Qiang XU88b7f282019-08-14 22:51:43 +0800405 monitorLanStatusChange(systemBus);
406
Qiang XUe28d1fa2019-02-27 13:50:56 +0800407 io.run();
408
409 return 0;
410}