Add chassis intrusion sensor daemon

Chassis intrusion signal is read from PCH via I2C or from GPIO.
Create a new daemon to poll its status and expose to dbus.

Related patches to run test:
- meta-phosphor: dbus-sensors: Enable new service of intrusion sensor
https://gerrit.openbmc-project.xyz/#/c/openbmc/meta-phosphor/+/17063/
- redfish: chassis: add property of physical security
https://gerrit.openbmc-project.xyz/#/c/openbmc/bmcweb/+/17691/

Tested-by: Check intrusion status value by redfish,
verified PCH solution on WFP and GPIO solution on STP.

Change-Id: Id5e67abbd80bbf2ef502db49fa183d92d0d31bda
Signed-off-by: Qiang XU <qiang.xu@linux.intel.com>
diff --git a/src/IntrusionSensorMain.cpp b/src/IntrusionSensorMain.cpp
new file mode 100644
index 0000000..2def3c1
--- /dev/null
+++ b/src/IntrusionSensorMain.cpp
@@ -0,0 +1,231 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <ChassisIntrusionSensor.hpp>
+#include <Utils.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/asio.hpp>
+#include <chrono>
+#include <ctime>
+#include <iostream>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/sd_event.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
+#include <sdbusplus/server.hpp>
+#include <sdbusplus/timer.hpp>
+
+static constexpr bool DEBUG = false;
+
+static constexpr const char* sensorType =
+    "xyz.openbmc_project.Configuration.ChassisIntrusionSensor";
+
+static bool getIntrusionSensorConfig(
+    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+    IntrusionSensorType* pType, int* pBusId, int* pSlaveAddr, int* pGpioIndex,
+    bool* pGpioInverted)
+{
+    // find matched configuration according to sensor type
+    ManagedObjectType sensorConfigurations;
+    bool useCache = false;
+
+    if (!getSensorConfiguration(sensorType, dbusConnection,
+                                sensorConfigurations, useCache))
+    {
+        std::cerr << "error communicating to entity manager\n";
+        return false;
+    }
+
+    const SensorData* sensorData = nullptr;
+    const std::pair<std::string,
+                    boost::container::flat_map<std::string, BasicVariantType>>*
+        baseConfiguration = nullptr;
+
+    // Get bus and addr of matched configuration
+    for (const std::pair<sdbusplus::message::object_path, SensorData>& sensor :
+         sensorConfigurations)
+    {
+        baseConfiguration = nullptr;
+        sensorData = &(sensor.second);
+
+        // match sensor type
+        auto sensorBase = sensorData->find(sensorType);
+        if (sensorBase == sensorData->end())
+        {
+            std::cerr << "error finding base configuration \n";
+            continue;
+        }
+
+        baseConfiguration = &(*sensorBase);
+
+        // judge class, "Gpio" or "I2C"
+        auto findClass = baseConfiguration->second.find("Class");
+        if (findClass != baseConfiguration->second.end() &&
+            sdbusplus::message::variant_ns::get<std::string>(
+                findClass->second) == "Gpio")
+        {
+            *pType = IntrusionSensorType::gpio;
+        }
+        else
+        {
+            *pType = IntrusionSensorType::pch;
+        }
+
+        // case to find GPIO info
+        if (*pType == IntrusionSensorType::gpio)
+        {
+            auto gpioConfig =
+                sensorData->find(sensorType + std::string(".GpioIntrusion"));
+
+            if (gpioConfig == sensorData->end())
+            {
+                std::cerr
+                    << "error finding GpioIntrusion info in configuration \n";
+                continue;
+            }
+
+            auto findGpioIndex = gpioConfig->second.find("Index");
+            auto findGpioPolarity = gpioConfig->second.find("Polarity");
+
+            if (findGpioIndex == gpioConfig->second.end() ||
+                findGpioPolarity == gpioConfig->second.end())
+            {
+                std::cerr << "error finding gpio info in configuration \n";
+                continue;
+            }
+
+            try
+            {
+                *pGpioIndex = sdbusplus::message::variant_ns::get<uint64_t>(
+                    findGpioIndex->second);
+                *pGpioInverted =
+                    (sdbusplus::message::variant_ns::get<std::string>(
+                         findGpioPolarity->second) == "Low");
+            }
+            catch (const std::bad_variant_access& e)
+            {
+                std::cerr << "invalid value for gpio info in config. \n";
+                continue;
+            }
+
+            if (DEBUG)
+            {
+                std::cout << "find matched GPIO index " << *pGpioIndex
+                          << ", polarity inverted flag is " << *pGpioInverted
+                          << "\n";
+            }
+
+            return true;
+        }
+
+        // case to find I2C info
+        else if (*pType == IntrusionSensorType::pch)
+        {
+            auto findBus = baseConfiguration->second.find("Bus");
+            auto findAddress = baseConfiguration->second.find("Address");
+            if (findBus == baseConfiguration->second.end() ||
+                findAddress == baseConfiguration->second.end())
+            {
+                std::cerr << "error finding bus or address in configuration \n";
+                continue;
+            }
+
+            try
+            {
+                *pBusId = sdbusplus::message::variant_ns::get<uint64_t>(
+                    findBus->second);
+                *pSlaveAddr = sdbusplus::message::variant_ns::get<uint64_t>(
+                    findAddress->second);
+            }
+            catch (const std::bad_variant_access& e)
+            {
+                std::cerr << "invalid value for bus or address in config. \n";
+                continue;
+            }
+
+            if (DEBUG)
+            {
+                std::cout << "find matched bus " << *pBusId
+                          << ", matched slave addr " << *pSlaveAddr << "\n";
+            }
+            return true;
+        }
+    }
+
+    std::cerr << "can't find matched I2C or GPIO configuration. \n";
+    *pBusId = -1;
+    *pSlaveAddr = -1;
+    *pGpioIndex = -1;
+    return false;
+}
+
+int main()
+{
+    int busId = -1, slaveAddr = -1, gpioIndex = -1;
+    bool gpioInverted = false;
+    IntrusionSensorType type = IntrusionSensorType::gpio;
+
+    // setup connection to dbus
+    boost::asio::io_service io;
+    auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+    auto objServer = sdbusplus::asio::object_server(systemBus);
+
+    // setup object server, define interface
+    systemBus->request_name("xyz.openbmc_project.IntrusionSensor");
+
+    std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceChassis =
+        objServer.add_interface(
+            "/xyz/openbmc_project/Intrusion/Chassis_Intrusion",
+            "xyz.openbmc_project.Chassis.Intrusion");
+
+    ChassisIntrusionSensor chassisIntrusionSensor(io, ifaceChassis);
+
+    if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
+                                 &gpioIndex, &gpioInverted))
+    {
+        chassisIntrusionSensor.start(type, busId, slaveAddr, gpioIndex,
+                                     gpioInverted);
+    }
+
+    // callback to handle configuration change
+    std::function<void(sdbusplus::message::message&)> eventHandler =
+        [&](sdbusplus::message::message& message) {
+            if (message.is_method_error())
+            {
+                std::cerr << "callback method error\n";
+                return;
+            }
+
+            std::cout << "rescan due to configuration change \n";
+            if (getIntrusionSensorConfig(systemBus, &type, &busId, &slaveAddr,
+                                         &gpioIndex, &gpioInverted))
+            {
+                chassisIntrusionSensor.start(type, busId, slaveAddr, gpioIndex,
+                                             gpioInverted);
+            }
+        };
+
+    auto match = std::make_unique<sdbusplus::bus::match::match>(
+        static_cast<sdbusplus::bus::bus&>(*systemBus),
+        "type='signal',member='PropertiesChanged',path_namespace='" +
+            std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
+        eventHandler);
+
+    io.run();
+
+    return 0;
+}