Merge "maintainers: Add new maintainers"
diff --git a/example.cpp b/example.cpp
index 55bd4ed..6754b0e 100644
--- a/example.cpp
+++ b/example.cpp
@@ -19,35 +19,39 @@
 #include <iostream>
 #include <memory>
 
-#include <sdbusplus/bus.hpp>
-#include <sdbusplus/message.hpp>
-#include <sdbusplus/server.hpp>
+#include "lpcsnoop/snoop_listen.hpp"
 
-#include "lpcsnoop/snoop.hpp"
-
-/*
- * Handle incoming dbus signal we care about.
- */
-static int DbusHandleSignal(sd_bus_message* msg, void* data, sd_bus_error* err);
-
-/*
- * Get the match signal for dbus.
- */
-static std::string GetMatch(void);
-
-// Example object that listens for dbus updates.
-class SnoopListen
+/* Example PostCode handler which simply prints them */
+static void printPostcode(uint64_t postcode)
 {
-  public:
-    SnoopListen(sdbusplus::bus::bus& bus) :
-        _bus(bus), _signal(bus, GetMatch().c_str(), DbusHandleSignal, this)
-    {
-    }
+    /* Print output to verify the example program is receiving values. */
+    std::printf("recv: 0x%" PRIx64 "\n", postcode);
+}
 
-  private:
-    sdbusplus::bus::bus& _bus;
-    sdbusplus::server::match::match _signal;
-};
+/*
+ * One can also specify custom handler that operates on
+ * sdbusplus::message::message type and pass them to constructor.
+ * e.g.
+ *
+ * static void PrintMessageMap(sdbusplus::message::message& m)
+ * {
+ *     using sdbusplus::message::variant_ns::get;
+ *     std::string messageBusName;
+ *     std::map<std::string, sdbusplus::message::variant<uint64_t>>
+ *         messageData;
+ *
+ *     m.read(messageBusName, messageData);
+ *
+ *     std::cout << "Got message from " << messageBusName << std::endl;
+ *     for (const auto& kv : messageData)
+ *     {
+ *         std::cout << "Key: " << kv.first << std::endl;
+ *         std::cout << "Value: " << get<uint64_t>(kv.second) << std::endl;
+ *     }
+ * }
+ *
+ * lpcsnoop::SnoopListen snoop(ListenBus, PrintMessageMap);
+ */
 
 /*
  * This is the entry point for the application.
@@ -58,7 +62,7 @@
 int main(int argc, char* argv[])
 {
     auto ListenBus = sdbusplus::bus::new_default();
-    SnoopListen snoop(ListenBus);
+    lpcsnoop::SnoopListen snoop(ListenBus, printPostcode);
 
     while (true)
     {
@@ -68,34 +72,3 @@
 
     return 0;
 }
-
-static int DbusHandleSignal(sd_bus_message* msg, void* data, sd_bus_error* err)
-{
-    auto sdbpMsg = sdbusplus::message::message(msg);
-
-    std::string msgSensor, busName{SNOOP_BUSNAME};
-    std::map<std::string, sdbusplus::message::variant<uint64_t>> msgData;
-    sdbpMsg.read(msgSensor, msgData);
-
-    if (msgSensor == busName)
-    {
-        auto valPropMap = msgData.find("Value");
-        if (valPropMap != msgData.end())
-        {
-            uint64_t rawValue = sdbusplus::message::variant_ns::get<uint64_t>(
-                valPropMap->second);
-
-            /* Print output to verify the example program is receiving values.
-             */
-            std::printf("recv: 0x%" PRIx64 "\n", rawValue);
-        }
-    }
-
-    return 0;
-}
-
-static std::string GetMatch(void)
-{
-    return "type='signal',interface='org.freedesktop.DBus.Properties',"
-           "member='PropertiesChanged',path='" SNOOP_OBJECTPATH "'";
-}
diff --git a/lpcsnoop/snoop_listen.hpp b/lpcsnoop/snoop_listen.hpp
index 15b8c0f..4d0ef36 100644
--- a/lpcsnoop/snoop_listen.hpp
+++ b/lpcsnoop/snoop_listen.hpp
@@ -22,26 +22,39 @@
 
 namespace lpcsnoop
 {
-
-using DbusSignalHandler = int (*)(sd_bus_message*, void*, sd_bus_error*);
+using sdbusplus::message::variant_ns::get;
 
 /* Returns matching string for what signal to listen on Dbus */
 static const std::string GetMatchRule()
 {
-    return "type='signal',"
-           "interface='org.freedesktop.DBus.Properties',"
-           "member='PropertiesChanged',"
-           "path='" SNOOP_OBJECTPATH "'";
+    using namespace sdbusplus::bus::match::rules;
+
+    return type::signal() + interface("org.freedesktop.DBus.Properties") +
+           member("PropertiesChanged") + path(SNOOP_OBJECTPATH);
 }
 
 class SnoopListen
 {
+    using message_handler_t = std::function<void(sdbusplus::message::message&)>;
+    using postcode_handler_t = std::function<void(uint64_t)>;
+
   public:
-    SnoopListen(sdbusplus::bus::bus& busIn, DbusSignalHandler handler) :
+    SnoopListen(sdbusplus::bus::bus& busIn, sd_bus_message_handler_t handler) :
         bus(busIn), signal(busIn, GetMatchRule().c_str(), handler, this)
     {
     }
 
+    SnoopListen(sdbusplus::bus::bus& busIn, message_handler_t handler) :
+        bus(busIn), signal(busIn, GetMatchRule(), handler)
+    {
+    }
+
+    SnoopListen(sdbusplus::bus::bus& busIn, postcode_handler_t handler) :
+        SnoopListen(busIn, std::bind(defaultMessageHandler, handler,
+                                     std::placeholders::_1))
+    {
+    }
+
     SnoopListen() = delete; // no default constructor
     ~SnoopListen() = default;
     SnoopListen(const SnoopListen&) = delete;
@@ -52,6 +65,27 @@
   private:
     sdbusplus::bus::bus& bus;
     sdbusplus::server::match::match signal;
+
+    /*
+     * Default message handler which listens to published messages on snoop
+     * DBus path, and calls the given postcode_handler on each value received.
+     */
+    static void defaultMessageHandler(postcode_handler_t& handler,
+                                      sdbusplus::message::message& m)
+    {
+        std::string messageBusName;
+        std::map<std::string, sdbusplus::message::variant<uint64_t>>
+            messageData;
+        constexpr char propertyKey[] = "Value";
+
+        m.read(messageBusName, messageData);
+
+        if (messageBusName == SNOOP_BUSNAME &&
+            messageData.find(propertyKey) != messageData.end())
+        {
+            handler(get<uint64_t>(messageData[propertyKey]));
+        }
+    }
 };
 
 } // namespace lpcsnoop
diff --git a/main.cpp b/main.cpp
index a780335..f1e644a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -16,13 +16,14 @@
 
 #include <fcntl.h>
 #include <getopt.h>
-#include <poll.h>
 #include <unistd.h>
 
 #include <array>
 #include <cstdint>
 #include <iostream>
 #include <memory>
+#include <sys/epoll.h>
+#include <systemd/sd-event.h>
 #include <thread>
 
 #include "lpcsnoop/snoop.hpp"
@@ -37,20 +38,6 @@
  */
 static constexpr size_t BUFFER_SIZE = 256;
 
-/*
- * Process any incoming dbus inquiries, which include introspection etc.
- */
-void ProcessDbus(sdbusplus::bus::bus& bus)
-{
-    while (true)
-    {
-        bus.process_discard();
-        bus.wait(); // wait indefinitely
-    }
-
-    return;
-}
-
 static void usage(const char* name)
 {
     fprintf(stderr,
@@ -76,6 +63,47 @@
 }
 
 /*
+ * Callback handling IO event from the POST code fd. i.e. there is new
+ * POST code available to read.
+ */
+int PostCodeEventHandler(sd_event_source* s, int postFd, uint32_t revents,
+                         void* userdata)
+{
+    PostReporter* reporter = static_cast<PostReporter*>(userdata);
+    std::array<uint8_t, BUFFER_SIZE> buffer;
+    int readb;
+
+    // TODO(kunyi): more error handling for EPOLLPRI/EPOLLERR.
+    if (revents & EPOLLIN)
+    {
+        readb = read(postFd, buffer.data(), buffer.size());
+        if (readb < 0)
+        {
+            /* Read failure. */
+            return readb;
+        }
+
+        if (readb % codeSize != 0)
+        {
+            fprintf(stderr,
+                    "Warning: read size %d not a multiple of "
+                    "POST code length %zu. Some codes may be "
+                    "corrupt or missing\n",
+                    readb, codeSize);
+            readb -= (readb % codeSize);
+        }
+
+        /* Broadcast the values read. */
+        for (int i = 0; i < readb; i += codeSize)
+        {
+            reporter->value(assembleBytes(buffer, i, codeSize));
+        }
+    }
+
+    return 0;
+}
+
+/*
  * TODO(venture): this only listens one of the possible snoop ports, but
  * doesn't share the namespace.
  *
@@ -86,11 +114,8 @@
 {
     int rc = 0;
     int opt;
-    struct pollfd pollset;
-    int pollr;
-    int readb;
     int postFd = -1;
-    std::array<uint8_t, BUFFER_SIZE> buffer;
+    sd_event* event = NULL;
 
     /*
      * These string constants are only used in this method within this object
@@ -143,82 +168,45 @@
         return -1;
     }
 
-    pollset.fd = postFd;
-    pollset.events |= POLLIN;
-
     auto bus = sdbusplus::bus::new_default();
 
     // Add systemd object manager.
     sdbusplus::server::manager::manager(bus, snoopObject);
 
     PostReporter reporter(bus, snoopObject, deferSignals);
-    reporter.emit_object_added();
 
-    bus.request_name(snoopDbus);
+    // Create sdevent and add IO source
+    // TODO(kunyi): the current interface is really C-style. Move to a C++
+    // wrapper when there is a SdEventPlus or some sort of that is ready.
+    rc = sd_event_default(&event);
 
-    /*
-     * I don't see a public interface for getting the underlying sd_bus*
-     * so instead of poll(bus, driver), I'll just create a separate thread.
-     *
-     * TODO(venture): There may be a way to use sdevent to poll both the file
-     * and the dbus in the same event loop.  If I could get the sdbus pointer
-     * from bus directly, I'd grab a file handler from it, and then just poll on
-     * both in one loop.  From a cursory look at sdevent, I should be able to do
-     * something similar with that at some point.
-     */
-    std::thread lt(ProcessDbus, std::ref(bus));
-
-    /* infinitely listen for POST codes and broadcast. */
-    while (true)
+    if (rc < 0)
     {
-        pollr = poll(&pollset, 1, -1); /* polls indefinitely. */
-        if (pollr < 0)
-        {
-            /* poll returned error. */
-            rc = -errno;
-            goto exit;
-        }
-
-        if (pollr > 0)
-        {
-            if (pollset.revents & POLLIN)
-            {
-                readb = read(postFd, buffer.data(), buffer.size());
-                if (readb < 0)
-                {
-                    /* Read failure. */
-                    rc = readb;
-                    goto exit;
-                }
-                else
-                {
-                    if (readb % codeSize != 0)
-                    {
-                        fprintf(stderr,
-                                "Warning: read size %d not a multiple of "
-                                "POST code length %zu. Some codes may be "
-                                "corrupt or missing\n",
-                                readb, codeSize);
-                        readb -= (readb % codeSize);
-                    }
-
-                    /* Broadcast the values read. */
-                    for (int i = 0; i < readb; i += codeSize)
-                    {
-                        reporter.value(assembleBytes(buffer, i, codeSize));
-                    }
-                }
-            }
-        }
+        fprintf(stderr, "Failed to allocate event loop:%s\n", strerror(-rc));
+        goto finish;
     }
 
-exit:
+    sd_event_source* source;
+    rc = sd_event_add_io(event, &source, postFd, EPOLLIN, PostCodeEventHandler,
+                         &reporter);
+    if (rc < 0)
+    {
+        fprintf(stderr, "Failed to add sdevent io source:%s\n", strerror(-rc));
+        goto finish;
+    }
+
+    // Enable bus to handle incoming IO and bus events
+    reporter.emit_object_added();
+    bus.request_name(snoopDbus);
+    bus.attach_event(event, SD_EVENT_PRIORITY_NORMAL);
+
+    rc = sd_event_loop(event);
+
+finish:
     if (postFd > -1)
     {
         close(postFd);
     }
 
-    lt.join();
-
     return rc;
 }