Initial Commit
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..5379c0a
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,22 @@
+AM_CPPFLAGS = $(SYSTEMD_CFLAGS) $(CODE_COVERAGE_CPPFLAGS)
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS)
+
+nobase_include_HEADERS =
+pkgconfig_DATA = sdeventplus.pc
+lib_LTLIBRARIES = libsdeventplus.la
+libsdeventplus_la_SOURCES =
+libsdeventplus_la_LIBADD = $(SYSTEMD_LIBS) $(CODE_COVERAGE_LIBS)
+
+nobase_include_HEADERS += sdeventplus/event.hpp
+libsdeventplus_la_SOURCES += sdeventplus/event.cpp
+
+nobase_include_HEADERS += sdeventplus/exception.hpp
+libsdeventplus_la_SOURCES += sdeventplus/exception.cpp
+
+nobase_include_HEADERS += sdeventplus/sdevent.hpp
+libsdeventplus_la_SOURCES += sdeventplus/sdevent.cpp
+
+nobase_include_HEADERS += sdeventplus/sdref.hpp
+
+nobase_include_HEADERS += sdeventplus/test/sdevent.hpp
diff --git a/src/sdeventplus.pc.in b/src/sdeventplus.pc.in
new file mode 100644
index 0000000..3d67d40
--- /dev/null
+++ b/src/sdeventplus.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: sdeventplus
+Version: @VERSION@
+Requires: @AX_PACKAGE_REQUIRES@
+Requires.private: @AX_PACKAGE_REQUIRES_PRIVATE@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lsdeventplus
diff --git a/src/sdeventplus/event.cpp b/src/sdeventplus/event.cpp
new file mode 100644
index 0000000..1baca16
--- /dev/null
+++ b/src/sdeventplus/event.cpp
@@ -0,0 +1,53 @@
+#include <functional>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/exception.hpp>
+
+namespace sdeventplus
+{
+
+Event::Event(sd_event* event, SdEventInterface* intf) :
+    intf(intf), event(event, &SdEventInterface::sd_event_ref,
+                      &SdEventInterface::sd_event_unref, intf)
+{
+}
+
+Event::Event(sd_event* event, std::false_type, SdEventInterface* intf) :
+    intf(intf),
+    event(event, &SdEventInterface::sd_event_ref,
+          &SdEventInterface::sd_event_unref, std::false_type(), intf)
+{
+}
+
+Event Event::get_new(SdEventInterface* intf)
+{
+    sd_event* event = nullptr;
+    int r = intf->sd_event_new(&event);
+    if (r < 0)
+    {
+        throw SdEventError(-r, "sd_event_new");
+    }
+    return Event(event, std::false_type(), intf);
+}
+
+Event Event::get_default(SdEventInterface* intf)
+{
+    sd_event* event = nullptr;
+    int r = intf->sd_event_default(&event);
+    if (r < 0)
+    {
+        throw SdEventError(-r, "sd_event_default");
+    }
+    return Event(event, std::false_type(), intf);
+}
+
+int Event::loop()
+{
+    int r = intf->sd_event_loop(event.get());
+    if (r < 0)
+    {
+        throw SdEventError(-r, "sd_event_loop");
+    }
+    return r;
+}
+
+} // namespace sdeventplus
diff --git a/src/sdeventplus/event.hpp b/src/sdeventplus/event.hpp
new file mode 100644
index 0000000..e2372bb
--- /dev/null
+++ b/src/sdeventplus/event.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <memory>
+#include <sdeventplus/sdevent.hpp>
+#include <sdeventplus/sdref.hpp>
+#include <systemd/sd-event.h>
+
+namespace sdeventplus
+{
+
+class Event
+{
+  public:
+    Event(sd_event* event, SdEventInterface* intf = &sdevent_impl);
+    Event(sd_event* event, std::false_type,
+          SdEventInterface* intf = &sdevent_impl);
+    static Event get_new(SdEventInterface* intf = &sdevent_impl);
+    static Event get_default(SdEventInterface* intf = &sdevent_impl);
+
+    int loop();
+
+  private:
+    SdEventInterface* intf;
+    SdRef<sd_event> event;
+};
+
+} // namespace sdeventplus
diff --git a/src/sdeventplus/exception.cpp b/src/sdeventplus/exception.cpp
new file mode 100644
index 0000000..1cfba4f
--- /dev/null
+++ b/src/sdeventplus/exception.cpp
@@ -0,0 +1,12 @@
+#include <sdeventplus/exception.hpp>
+#include <system_error>
+
+namespace sdeventplus
+{
+
+SdEventError::SdEventError(int r, const char *prefix) :
+    std::system_error(r, std::generic_category(), prefix)
+{
+}
+
+} // namespace sdeventplus
diff --git a/src/sdeventplus/exception.hpp b/src/sdeventplus/exception.hpp
new file mode 100644
index 0000000..eb88649
--- /dev/null
+++ b/src/sdeventplus/exception.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <system_error>
+
+namespace sdeventplus
+{
+
+class SdEventError final : public std::system_error
+{
+  public:
+    SdEventError(int r, const char *prefix);
+};
+
+} // namespace sdeventplus
diff --git a/src/sdeventplus/sdevent.cpp b/src/sdeventplus/sdevent.cpp
new file mode 100644
index 0000000..d145f38
--- /dev/null
+++ b/src/sdeventplus/sdevent.cpp
@@ -0,0 +1,6 @@
+#include <sdeventplus/sdevent.hpp>
+
+namespace sdeventplus
+{
+SdEventImpl sdevent_impl;
+}
diff --git a/src/sdeventplus/sdevent.hpp b/src/sdeventplus/sdevent.hpp
new file mode 100644
index 0000000..129e67f
--- /dev/null
+++ b/src/sdeventplus/sdevent.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <systemd/sd-event.h>
+
+namespace sdeventplus
+{
+
+class SdEventInterface
+{
+  public:
+    virtual ~SdEventInterface() = default;
+
+    virtual int sd_event_default(sd_event **event) const = 0;
+    virtual int sd_event_new(sd_event **event) const = 0;
+    virtual sd_event *sd_event_ref(sd_event *event) const = 0;
+    virtual sd_event *sd_event_unref(sd_event *event) const = 0;
+
+    virtual int sd_event_loop(sd_event *event) const = 0;
+};
+
+class SdEventImpl : public SdEventInterface
+{
+  public:
+    int sd_event_default(sd_event **event) const override
+    {
+        return ::sd_event_default(event);
+    }
+
+    int sd_event_new(sd_event **event) const override
+    {
+        return ::sd_event_default(event);
+    }
+
+    sd_event *sd_event_ref(sd_event *event) const override
+    {
+        return ::sd_event_ref(event);
+    }
+
+    sd_event *sd_event_unref(sd_event *event) const override
+    {
+        return ::sd_event_unref(event);
+    }
+
+    int sd_event_loop(sd_event *event) const override
+    {
+        return ::sd_event_loop(event);
+    }
+};
+
+extern SdEventImpl sdevent_impl;
+
+} // namespace sdeventplus
diff --git a/src/sdeventplus/sdref.hpp b/src/sdeventplus/sdref.hpp
new file mode 100644
index 0000000..492ae70
--- /dev/null
+++ b/src/sdeventplus/sdref.hpp
@@ -0,0 +1,89 @@
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <sdeventplus/sdevent.hpp>
+#include <type_traits>
+
+namespace sdeventplus
+{
+
+template <typename T> class SdRef
+{
+  public:
+    using Func = std::function<T*(SdEventInterface*, T*)>;
+
+    SdRef(T* ref, Func take_ref, Func release_ref,
+          SdEventInterface* intf = sdevent_impl) :
+        SdRef(take_ref(intf, ref), take_ref, release_ref, std::false_type(),
+              intf)
+    {
+    }
+
+    SdRef(T* ref, Func take_ref, Func release_ref, std::false_type,
+          SdEventInterface* intf = sdevent_impl) :
+        intf(intf),
+        take_ref(take_ref), release_ref(release_ref), ref(ref)
+    {
+    }
+
+    SdRef(const SdRef& other) :
+        intf(other.intf), take_ref(other.take_ref),
+        release_ref(other.release_ref), ref(take_ref(intf, other.ref))
+    {
+    }
+
+    SdRef& operator=(const SdRef& other)
+    {
+        if (this != &other)
+        {
+            // release_ref will be invalid if moved
+            if (release_ref)
+                release_ref(intf, ref);
+
+            intf = other.intf;
+            take_ref = other.take_ref;
+            release_ref = other.release_ref;
+            ref = take_ref(intf, other.ref);
+        }
+        return *this;
+    }
+
+    SdRef(SdRef&& other) = default;
+
+    SdRef& operator=(SdRef&& other)
+    {
+        if (this != &other)
+        {
+            // release_ref will be invalid if move
+            if (release_ref)
+                release_ref(intf, ref);
+
+            intf = std::move(other.intf);
+            take_ref = std::move(other.take_ref);
+            release_ref = std::move(other.release_ref);
+            ref = std::move(other.ref);
+        }
+        return *this;
+    }
+
+    virtual ~SdRef()
+    {
+        // release_ref will be invalid after a move
+        if (release_ref)
+            release_ref(intf, ref);
+    }
+
+    T* get() const
+    {
+        return ref;
+    }
+
+  private:
+    SdEventInterface* intf;
+    Func take_ref;
+    Func release_ref;
+    T* ref;
+};
+
+} // namespace sdeventplus
diff --git a/src/sdeventplus/test/sdevent.hpp b/src/sdeventplus/test/sdevent.hpp
new file mode 100644
index 0000000..b467647
--- /dev/null
+++ b/src/sdeventplus/test/sdevent.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <gmock/gmock.h>
+#include <sdeventplus/sdevent.hpp>
+#include <systemd/sd-event.h>
+
+namespace sdeventplus
+{
+
+class SdEventMock : public SdEventInterface
+{
+  public:
+    MOCK_CONST_METHOD1(sd_event_default, int(sd_event **));
+    MOCK_CONST_METHOD1(sd_event_new, int(sd_event **));
+    MOCK_CONST_METHOD1(sd_event_ref, sd_event *(sd_event *));
+    MOCK_CONST_METHOD1(sd_event_unref, sd_event *(sd_event *));
+
+    MOCK_CONST_METHOD1(sd_event_loop, int(sd_event *));
+};
+
+} // namespace sdeventplus