Add bus bindings

Add C++ bindings for the sd_bus_(bus) class interfaces.  This
binding creates a unique_ptr wrapper around sd_bus*, using the
appropriate sd_bus deref calls when the unique_ptr leaves scope.

Change-Id: I0bb3842e384217ac5606bb3bf350d7fc3babf1ad
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/sdbusplus/bus.hpp b/sdbusplus/bus.hpp
new file mode 100644
index 0000000..8ca107d
--- /dev/null
+++ b/sdbusplus/bus.hpp
@@ -0,0 +1,166 @@
+#pragma once
+
+#include <memory>
+#include <systemd/sd-bus.h>
+
+namespace sdbusplus
+{
+namespace bus
+{
+
+using busp_t = sd_bus*;
+class bus;
+
+/** @brief Get an instance of the 'default' bus. */
+bus new_default();
+/** @brief Get an instance of the 'user' session bus. */
+bus new_user();
+/** @brief Get an instance of the 'system' bus. */
+bus new_system();
+
+namespace details
+{
+
+/** @brief unique_ptr functor to release a bus reference. */
+struct BusDeleter
+{
+    void operator()(sd_bus* ptr) const
+    {
+        sd_bus_flush_close_unref(ptr);
+    }
+};
+
+/* @brief Alias 'bus' to a unique_ptr type for auto-release. */
+using bus = std::unique_ptr<sd_bus, BusDeleter>;
+
+} // namespace details
+
+/** @class bus
+ *  @brief Provides C++ bindings to the sd_bus_* class functions.
+ */
+struct bus
+{
+        /* Define all of the basic class operations:
+         *     Not allowed:
+         *         - Default constructor to avoid nullptrs.
+         *         - Copy operations due to internal unique_ptr.
+         *     Allowed:
+         *         - Move operations.
+         *         - Destructor.
+         */
+    bus() = delete;
+    bus(const bus&) = delete;
+    bus& operator=(const bus&) = delete;
+    bus(bus&&) = default;
+    bus& operator=(bus&&) = default;
+    ~bus() = default;
+
+    /** @brief Conversion constructor from 'busp_t'.
+     *
+     *  Takes ownership of the bus-pointer and releases it when done.
+     */
+    explicit bus(busp_t b) : _bus(b) {}
+
+    /** @brief Release ownership of the stored bus-pointer. */
+    busp_t release() { return _bus.release(); }
+
+    /** @brief Wait for new dbus messages or signals.
+     *
+     *  @param[in] timeout_us - Timeout in usec.
+     */
+    void wait(uint64_t timeout_us = 0)
+    {
+        sd_bus_wait(_bus.get(), timeout_us);
+    }
+
+    /** @brief Process waiting dbus messages or signals. */
+    auto process()
+    {
+        sd_bus_message* m = nullptr;
+        sd_bus_process(_bus.get(), &m);
+
+        return m;
+    }
+
+    /** @brief Claim a service name on the dbus.
+     *
+     *  @param[in] service - The service name to claim.
+     */
+    void request_name(const char* service)
+    {
+        sd_bus_request_name(_bus.get(), service, 0);
+    }
+
+    /** @brief Create a method_call message.
+     *
+     *  @param[in] service - The service to call.
+     *  @param[in] objpath - The object's path for the call.
+     *  @param[in] interf - The object's interface to call.
+     *  @param[in] method - The object's method to call.
+     *
+     *  @return A newly constructed message.
+     */
+    auto new_method_call(const char* service, const char* objpath,
+                         const char* interf, const char* method)
+    {
+        sd_bus_message* m = nullptr;
+        sd_bus_message_new_method_call(_bus.get(), &m, service, objpath,
+                                       interf, method);
+
+        return m;
+    }
+
+    /** @brief Perform a message call.
+     *
+     *  @param[in] m - The method_call message.
+     *  @param[in] timeout_us - The timeout for the method call.
+     *
+     *  @return The response message.
+     */
+    auto call(sd_bus_message* m, uint64_t timeout_us = 0)
+    {
+        sd_bus_message* reply = nullptr;
+        sd_bus_call(_bus.get(), m, timeout_us, nullptr, &reply);
+
+        return reply;
+    }
+
+    /** @brief Perform a message call, ignoring the reply.
+     *
+     *  @param[in] m - The method_call message.
+     *  @param[in] timeout_us - The timeout for the method call.
+     */
+    void call_noreply(sd_bus_message* m, uint64_t timeout_us = 0)
+    {
+        sd_bus_call(_bus.get(), m, timeout_us, nullptr, nullptr);
+    }
+
+    private:
+        details::bus _bus;
+};
+
+bus new_default()
+{
+    sd_bus* b = nullptr;
+    sd_bus_open(&b);
+    return bus(b);
+}
+
+bus new_user()
+{
+    sd_bus* b = nullptr;
+    sd_bus_open_user(&b);
+    return bus(b);
+}
+
+bus new_system()
+{
+    sd_bus* b = nullptr;
+    sd_bus_open_system(&b);
+    return bus(b);
+}
+
+
+} // namespace bus
+
+} // namespace sdbusplus
diff --git a/test/message/append.cpp b/test/message/append.cpp
index 1b0f70b..c9c2e47 100644
--- a/test/message/append.cpp
+++ b/test/message/append.cpp
@@ -1,6 +1,7 @@
 #include <iostream>
 #include <sdbusplus/message/append.hpp>
 #include <cassert>
+#include <sdbusplus/bus.hpp>
 
 // Global to share the dbus type string between client and server.
 static std::string verifyTypeString;
@@ -11,29 +12,27 @@
 static constexpr auto QUIT_METHOD = "quit";
 
 // Open up the sdbus and claim SERVICE name.
-void serverInit(sd_bus** b)
+auto serverInit()
 {
-    assert(0 <= sd_bus_open(b));
-    assert(0 <= sd_bus_request_name(*b, SERVICE, 0));
+    auto b = sdbusplus::bus::new_default();
+    b.request_name(SERVICE);
+
+    return std::move(b);
 }
 
 // Thread to run the dbus server.
 void* server(void* b)
 {
-    auto bus = reinterpret_cast<sd_bus*>(b);
+    auto bus = sdbusplus::bus::bus(reinterpret_cast<sdbusplus::bus::busp_t>(b));
 
     while(1)
     {
         // Wait for messages.
-        sd_bus_message *m = nullptr;
-        if (0 == sd_bus_process(bus, &m))
-        {
-            sd_bus_wait(bus, 0);
-            continue;
-        }
+        sd_bus_message *m = bus.process();
 
-        if(!m)
+        if(m == nullptr)
         {
+            bus.wait();
             continue;
         }
 
@@ -54,12 +53,10 @@
     }
 }
 
-void newMethodCall__test(sd_bus* b, sd_bus_message** m)
+void newMethodCall__test(sdbusplus::bus::bus& b, sd_bus_message** m)
 {
     // Allocate a method-call message for INTERFACE,TEST_METHOD.
-    assert(0 <= sd_bus_message_new_method_call(b, m, SERVICE, "/", INTERFACE,
-                                               TEST_METHOD));
-    sd_bus_message_set_expect_reply(*m, true);
+    *m = b.new_method_call(SERVICE, "/", INTERFACE, TEST_METHOD);
 }
 
 void runTests()
@@ -67,17 +64,14 @@
     using namespace std::literals;
 
     sd_bus_message* m = nullptr;
-    sd_bus* b = nullptr;
-
-    // Connect to dbus.
-    assert(0 <= sd_bus_open(&b));
+    auto b = sdbusplus::bus::new_default();
 
     // Test r-value int.
     {
         newMethodCall__test(b, &m);
         sdbusplus::message::append(m, 1);
         verifyTypeString = "i";
-        sd_bus_call(b, m, 0, nullptr, nullptr);
+        b.call_noreply(m);
     }
 
     // Test l-value int.
@@ -86,7 +80,7 @@
         int a = 1;
         sdbusplus::message::append(m, a, a);
         verifyTypeString = "ii";
-        sd_bus_call(b, m, 0, nullptr, nullptr);
+        b.call_noreply(m);
     }
 
     // Test multiple ints.
@@ -94,7 +88,7 @@
         newMethodCall__test(b, &m);
         sdbusplus::message::append(m, 1, 2, 3, 4, 5);
         verifyTypeString = "iiiii";
-        sd_bus_call(b, m, 0, nullptr, nullptr);
+        b.call_noreply(m);
     }
 
     // Test r-value string.
@@ -102,7 +96,7 @@
         newMethodCall__test(b, &m);
         sdbusplus::message::append(m, "asdf"s);
         verifyTypeString = "s";
-        sd_bus_call(b, m, 0, nullptr, nullptr);
+        b.call_noreply(m);
     }
 
     // Test multiple strings, various forms.
@@ -113,12 +107,12 @@
         sdbusplus::message::append(m, 1, "asdf", "ASDF"s, str,
                                    std::move(str2), 5);
         verifyTypeString = "issssi";
-        sd_bus_call(b, m, 0, nullptr, nullptr);
+        b.call_noreply(m);
     }
 
     // Shutdown server.
-    sd_bus_call_method(b, SERVICE, "/", INTERFACE, QUIT_METHOD,
-                       nullptr, nullptr, nullptr);
+    m = b.new_method_call(SERVICE, "/", INTERFACE, QUIT_METHOD);
+    b.call_noreply(m);
 }
 
 int main()
@@ -126,9 +120,8 @@
     // Initialize and start server thread.
     pthread_t t;
     {
-        sd_bus* b;
-        serverInit(&b);
-        pthread_create(&t, NULL, server, b);
+        auto b = serverInit();
+        pthread_create(&t, NULL, server, b.release());
     }
 
     runTests();